guardia-mall/backend/src/main/java/com/zioinfo/mall/rag/RagToggleService.java

135 lines
5.8 KiB
Java

package com.zioinfo.mall.rag;
import com.zioinfo.mall.admin.dto.MallSetting;
import com.zioinfo.mall.admin.mapper.SettingMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Mall RAG 기법/신뢰도 토글 서비스.
*
* <p>기존 {@code mall_setting}(key-value) 테이블을 재사용해 RAG 토글을 격리 저장한다(신규 테이블
* 신설 없음, admin 설정 화면과 자연 통합). 키 네임스페이스 {@code rag.*} 로 다른 설정과 충돌 회피.
*
* <p>무거운 기법(graphrag·rerank·tool_use/agent·stream)은 서버 RAM 제약상 <b>기본 off</b>.
* retrieval_mode 기본 vector, agent step 상한 강제.
*/
@Service
@RequiredArgsConstructor
public class RagToggleService {
private final SettingMapper settingMapper;
public static final String K_ENABLED = "rag.enabled";
public static final String K_MODE = "rag.retrieval_mode"; // vector|hybrid|graph
public static final String K_RERANK = "rag.rerank"; // true|false (무거움→기본 off)
public static final String K_GRAPHRAG = "rag.graphrag"; // true|false (무거움→기본 off)
public static final String K_TOOL_USE = "rag.tool_use"; // true|false (에이전틱 /agent, 무거움→off)
public static final String K_STRUCTURED = "rag.structured"; // true|false (추천/검색 결정론 JSON, 기본 on)
public static final String K_STREAM = "rag.stream"; // true|false (SSE, 기본 off)
public static final String K_TOP_K = "rag.top_k";
public static final String K_AGENT_STEPS = "rag.agent_max_steps";
public static final String K_FAITHFULNESS = "rag.faithfulness_threshold";
public static final String K_TEMPERATURE = "rag.temperature";
public static final String K_GEN_MODEL = "rag.generation_model";
/** 현재 토글 전부 조회(기본값 머지). 화면/배선 공용. */
public RagToggles current() {
Map<String, String> m = new LinkedHashMap<>();
List<MallSetting> all = settingMapper.findAll();
if (all != null) for (MallSetting s : all) m.put(s.getKey(), s.getValue());
RagToggles t = new RagToggles();
t.ragEnabled = bool(m.get(K_ENABLED), true);
t.retrievalMode = mode(m.get(K_MODE));
t.rerank = bool(m.get(K_RERANK), false); // 무거움→off 기본
t.graphrag = bool(m.get(K_GRAPHRAG), false); // 무거움→off 기본
t.toolUse = bool(m.get(K_TOOL_USE), false); // 에이전틱→off 기본(옵트인)
t.structured = bool(m.get(K_STRUCTURED), true); // 결정론 출력→on 기본
t.stream = bool(m.get(K_STREAM), false); // SSE→off 기본
t.topK = clampInt(m.get(K_TOP_K), 6, 1, 20);
t.agentMaxSteps = clampInt(m.get(K_AGENT_STEPS), 4, 1, 8);
t.faithfulnessThreshold = clampDouble(m.get(K_FAITHFULNESS), 0.5, 0.0, 1.0);
t.temperature = clampDouble(m.get(K_TEMPERATURE), 0.2, 0.0, 1.0);
t.generationModel = blankTo(m.get(K_GEN_MODEL), "llama3.2:1b");
return t;
}
/** retrieval_mode 결정: graphrag 토글 on 이면 graph 우선, 아니면 설정 mode. */
public String effectiveMode(RagToggles t) {
if (t.graphrag) return "graph";
return t.retrievalMode;
}
// ── 정규화/클램프 (상한 강제 — 폭주 차단) ───────────────────────────────
private boolean bool(String v, boolean def) {
if (v == null) return def;
return "true".equalsIgnoreCase(v.trim()) || "1".equals(v.trim()) || "on".equalsIgnoreCase(v.trim());
}
private String mode(String v) {
if (v == null) return "vector";
String s = v.trim().toLowerCase();
return (s.equals("hybrid") || s.equals("graph") || s.equals("vector")) ? s : "vector";
}
private int clampInt(String v, int def, int min, int max) {
try {
int n = Integer.parseInt(v.trim());
return Math.max(min, Math.min(max, n));
} catch (Exception e) {
return def;
}
}
private double clampDouble(String v, double def, double min, double max) {
try {
double n = Double.parseDouble(v.trim());
return Math.max(min, Math.min(max, n));
} catch (Exception e) {
return def;
}
}
private String blankTo(String v, String def) {
return (v == null || v.isBlank()) ? def : v.trim();
}
/** 토글 스냅샷 DTO(응답 metadata·화면 공용). */
public static class RagToggles {
public boolean ragEnabled = true;
public String retrievalMode = "vector";
public boolean rerank = false;
public boolean graphrag = false;
public boolean toolUse = false;
public boolean structured = true;
public boolean stream = false;
public int topK = 6;
public int agentMaxSteps = 4;
public double faithfulnessThreshold = 0.5;
public double temperature = 0.2;
public String generationModel = "llama3.2:1b";
public Map<String, Object> toMap() {
Map<String, Object> m = new LinkedHashMap<>();
m.put("ragEnabled", ragEnabled);
m.put("retrievalMode", retrievalMode);
m.put("rerank", rerank);
m.put("graphrag", graphrag);
m.put("toolUse", toolUse);
m.put("structured", structured);
m.put("stream", stream);
m.put("topK", topK);
m.put("agentMaxSteps", agentMaxSteps);
m.put("faithfulnessThreshold", faithfulnessThreshold);
m.put("temperature", temperature);
m.put("generationModel", generationModel);
return m;
}
}
}