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 기법/신뢰도 토글 서비스. * *

기존 {@code mall_setting}(key-value) 테이블을 재사용해 RAG 토글을 격리 저장한다(신규 테이블 * 신설 없음, admin 설정 화면과 자연 통합). 키 네임스페이스 {@code rag.*} 로 다른 설정과 충돌 회피. * *

무거운 기법(graphrag·rerank·tool_use/agent·stream)은 서버 RAM 제약상 기본 off. * 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 m = new LinkedHashMap<>(); List 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 toMap() { Map 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; } } }