diff --git a/backend/src/main/java/com/zioinfo/esn/wise/RagClient.java b/backend/src/main/java/com/zioinfo/esn/wise/RagClient.java
new file mode 100644
index 0000000..7355e31
--- /dev/null
+++ b/backend/src/main/java/com/zioinfo/esn/wise/RagClient.java
@@ -0,0 +1,108 @@
+package com.zioinfo.esn.wise;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 얇은 중앙 guardia-rag REST 클라이언트 (WISE AI — zioinfo-esn 전용). [ZIOINFO-ESN]
+ *
+ *
핵심 설계: ESN 은 검색·생성·검증 로직을 Java 에 재구현하지 않는다. 중앙 Python
+ * guardia-rag 의 {@code /rag/answer}(검색+근거생성+인용+guardrail) 를 REST 로 호출하기만 한다.
+ * 미가용/타임아웃 시 예외를 전파하지 않고 {@code degraded:true} 폴백 결과를 돌려준다.
+ *
+ *
보안 불변: 온프레미스 전용(서버 내부 루프백). 응답에서 자격증명/PII/스택트레이스 미노출.
+ * 오류는 1줄 요약만 로깅. 외부 API 아님(중앙 rag 경유만 — 솔루션에서 직접 LLM 호출 신설 금지).
+ */
+@Slf4j
+@Component
+public class RagClient {
+
+ /** WISE AI 솔루션 식별자(컬렉션·피드백 격리 키) — 기존 LearningStore.SOLUTION 과 일치. */
+ private static final String SOLUTION = "zioinfo-esn";
+
+ /** /rag/answer 는 소형모델 콜드로드를 감안해 240s. */
+ private static final Duration ANSWER_TIMEOUT = Duration.ofSeconds(240);
+
+ /** 중앙 guardia-rag 베이스 URL(서버 내부 루프백 기본). 외부 URL 설정 금지. */
+ @Value("${guardia.rag-url:http://localhost:8020}")
+ private String ragUrl;
+
+ private final HttpClient http = HttpClient.newBuilder()
+ .connectTimeout(Duration.ofSeconds(5))
+ .build();
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ /**
+ * POST /rag/answer — 검색+생성+검증+guardrail (근거+인용, 근거 부족 시 보류).
+ *
+ * @return rag 응답 필드 통과({@code answer, sources, grounded, faithfulness, abstained,
+ * degraded, degraded_reason, trace_id, answer_id}). 미가용 시 {@code degraded:true}.
+ */
+ @SuppressWarnings("unchecked")
+ public Map answer(String query) {
+ try {
+ Map options = new LinkedHashMap<>();
+ options.put("stream", false);
+ options.put("verify", true);
+
+ Map body = new LinkedHashMap<>();
+ body.put("solution", SOLUTION);
+ body.put("query", query == null ? "" : query);
+ body.put("retrieval_mode", "vector");
+ body.put("top_k", 5);
+ body.put("options", options);
+
+ String payload = mapper.writeValueAsString(body);
+ HttpRequest req = HttpRequest.newBuilder()
+ .uri(URI.create(trimTrailingSlash(ragUrl) + "/rag/answer"))
+ .timeout(ANSWER_TIMEOUT)
+ .header("Content-Type", "application/json")
+ .header("X-Solution-Key", SOLUTION)
+ .POST(HttpRequest.BodyPublishers.ofString(payload))
+ .build();
+ HttpResponse resp = http.send(req, HttpResponse.BodyHandlers.ofString());
+ if (resp.statusCode() / 100 != 2 || resp.body() == null || resp.body().isBlank()) {
+ log.warn("중앙 rag /answer 비2xx({}) — ESN 폴백", resp.statusCode());
+ return degraded("rag_no_response");
+ }
+ Map res = mapper.readValue(resp.body(), Map.class);
+ return res != null ? res : degraded("rag_no_response");
+ } catch (Exception e) {
+ log.warn("중앙 rag /answer 일시 불가 — ESN 폴백: {}", summarize(e));
+ return degraded("rag_unavailable");
+ }
+ }
+
+ private Map degraded(String reason) {
+ Map out = new LinkedHashMap<>();
+ out.put("degraded", true);
+ out.put("degraded_reason", reason);
+ return out;
+ }
+
+ private static String trimTrailingSlash(String s) {
+ if (s == null || s.isBlank()) {
+ return "http://localhost:8020";
+ }
+ return s.endsWith("/") ? s.substring(0, s.length() - 1) : s;
+ }
+
+ /** 스택트레이스 미노출: 메시지 1줄만 요약. */
+ private static String summarize(Exception e) {
+ String m = e.getMessage();
+ if (m == null) {
+ return e.getClass().getSimpleName();
+ }
+ return m.length() > 160 ? m.substring(0, 160) : m;
+ }
+}
diff --git a/backend/src/main/java/com/zioinfo/esn/wise/WiseAskController.java b/backend/src/main/java/com/zioinfo/esn/wise/WiseAskController.java
new file mode 100644
index 0000000..5e1e1ae
--- /dev/null
+++ b/backend/src/main/java/com/zioinfo/esn/wise/WiseAskController.java
@@ -0,0 +1,33 @@
+package com.zioinfo.esn.wise;
+
+import com.zioinfo.esn.common.ApiResponse;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+/**
+ * WISE AI — 중앙 guardia-rag {@code /rag/answer} 프록시(인증 사용자). [ZIOINFO-ESN]
+ *
+ * 브라우저 → ESN 백엔드 → 중앙 rag 경유. 근거·인용 기반 답변, 근거 부족 시 보류(abstained),
+ * rag 미가용 시 degraded 폴백. 기존 {@code /api/ai/*} 는 불변 — 본 컨트롤러는 별개 레이어.
+ *
+ *
- POST /api/wise/ask {query} → 근거 답변(sources·abstained·degraded 포함)
+ */
+@RestController
+@RequestMapping("/api/wise")
+@RequiredArgsConstructor
+public class WiseAskController {
+
+ private final RagClient ragClient;
+
+ @PostMapping("/ask")
+ public ApiResponse