一键导入
verify-rag-pipeline
RAG 파이프라인 (답변 작성 + 분석 + 다운로드) 검증. 답변 작성/분석/다운로드 관련 코드 수정 후 사용.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
RAG 파이프라인 (답변 작성 + 분석 + 다운로드) 검증. 답변 작성/분석/다운로드 관련 코드 수정 후 사용.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | verify-rag-pipeline |
| description | RAG 파이프라인 (답변 작성 + 분석 + 다운로드) 검증. 답변 작성/분석/다운로드 관련 코드 수정 후 사용. |
chunk=UUID score=0.xxx documentId=UUID fileName=... pageStart=... pageEnd=... 형식 확인| File | Purpose |
|---|---|
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/OpenAiComposeStep.java | AI 답변 생성 (시스템 프롬프트 + buildPrompt) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/DefaultComposeStep.java | 폴백 답변 템플릿 (격식체) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/AnswerComposerService.java | 답변 오케스트레이션 + citations 조립 + @Transactional |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/AnswerController.java | REST 엔드포인트 (draft, review, approve, send, ai-review, ai-approve, auto-workflow, edit-draft) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/EditDraftRequest.java | 답변 본문 수정 요청 DTO |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/agent/ReviewAgentService.java | AI 리뷰 에이전트 (LLM 품질 검토) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/agent/ApprovalAgentService.java | AI 승인 에이전트 (규칙 기반 4단 게이트) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/agent/ReviewResult.java | AI 리뷰 결과 record |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/agent/ApprovalResult.java | AI 승인 결과 record |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/agent/GateResult.java | 게이트 결과 record |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/AnswerDraftResponse.java | 답변 초안 응답 record (workflowRunCount 포함) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/AnswerHistoryDetailResponse.java | 답변 이력 상세 응답 (AI 리뷰 이력 포함) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/analysis/EvidenceItem.java | 근거 데이터 record |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/analysis/AnalysisService.java | 근거 검색 + 판정 + 배치 조인 + 번역 + 하이브리드 검색 |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/analysis/AnalyzeResponse.java | 분석 결과 record (translatedQuery 포함) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/HybridSearchService.java | 벡터+키워드 RRF 결합 검색 |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/QueryTranslationService.java | 질문 번역 인터페이스 (한→영) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/OpenAiQueryTranslationService.java | OpenAI GPT 기반 번역 구현 |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/PostgresKeywordSearchService.java | PostgreSQL tsvector 키워드 검색 |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/vector/QdrantVectorStore.java | Qdrant 벡터 스토어 구현 (검색 + upsert + delete) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/vector/VectorStore.java | 벡터 스토어 인터페이스 (search + upsert + deleteByDocumentId) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/document/DocumentDownloadController.java | 문서 다운로드 + PDF 페이지 추출 |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/AnswerOrchestrationService.java | 답변 오케스트레이션 (DECOMPOSE → per-Q RETRIEVE → VERIFY → COMPOSE) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/QuestionDecomposerService.java | 규칙 기반 질문 분해 (multi-question → sub-questions) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/SubQuestion.java | 하위 질문 record (index, text) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/DecomposedQuestion.java | 질문 분해 결과 record (subQuestions, isMultiQuestion) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/PerQuestionEvidence.java | 하위 질문별 증거 record (subQuestion, evidences, sufficient) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/DefaultRetrieveStep.java | 기본 검색 (단일 + SearchFilter + per-question 오버로드) + enrichEvidenceMetadata |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/SelfReviewStep.java | 규칙 기반 품질 검토 (중복, 일관성, 절차, 인용, 하위질문) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/EvidenceQualityGate.java | 5단계 증거 품질 게이트 |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/EvidenceDeduplicator.java | 증거 중복 제거 |
backend/app-api/src/main/java/com/biorad/csrag/infrastructure/rag/budget/TokenBudgetManager.java | 토큰 예산 관리 |
backend/app-api/src/main/java/com/biorad/csrag/infrastructure/rag/budget/TokenUsage.java | 토큰 사용량 VO |
backend/app-api/src/main/java/com/biorad/csrag/infrastructure/rag/cache/SemanticCacheService.java | 의미론적 캐시 |
backend/app-api/src/main/java/com/biorad/csrag/infrastructure/rag/config/RagPipelineProperties.java | 하이퍼파라미터 설정 |
backend/app-api/src/main/java/com/biorad/csrag/infrastructure/rag/cost/RagCostGuardService.java | 비용 알림/한도 |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/vector/VectorStoreCircuitBreaker.java | Circuit Breaker |
backend/app-api/src/main/resources/prompts/adaptive-search-unified.txt | 통합 적응형 검색 프롬프트 |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/SearchFilter.java | 검색 필터 DTO (inquiryId, productFamily, documentIds, sourceTypes) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/ProductExtractorService.java | 제품명 추출 (12 Bio-Rad 제품 패턴) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/KeywordSearchService.java | 키워드 검색 인터페이스 (tsvector) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/ProductFamilyRegistry.java | 10개 제품군 + 6개 카테고리 매핑 + expand() |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/ProductFamilyController.java | GET /api/v1/product-families 엔드포인트 |
backend/app-api/src/test/java/com/biorad/csrag/interfaces/rest/search/NormalizeKoreanTest.java | 한국어 정규화 단위 테스트 |
backend/app-api/src/test/java/com/biorad/csrag/interfaces/rest/search/ProductExtractorServiceTest.java | 멀티 제품 추출 테스트 |
backend/app-api/src/test/java/com/biorad/csrag/interfaces/rest/search/SearchFilterTest.java | 복수 제품 필터 테스트 |
backend/app-api/src/test/java/com/biorad/csrag/interfaces/rest/search/ProductFamilyRegistryTest.java | 제품군 레지스트리 테스트 |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/AdaptiveRetrievalAgent.java | Adaptive Retrieval 에이전트 인터페이스 |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/OpenAiAdaptiveRetrievalAgent.java | OpenAI Adaptive Retrieval 구현 (쿼리 복잡도 분석 → 전략 선택) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/MockAdaptiveRetrievalAgent.java | Mock Adaptive Retrieval (테스트/오프라인용) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/MultiHopRetriever.java | Multi-Hop 검색 인터페이스 |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/OpenAiMultiHopRetriever.java | OpenAI Multi-Hop 구현 (반복 검색 + 정보 합성) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/MockMultiHopRetriever.java | Mock Multi-Hop (테스트/오프라인용) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/SearchToolAgent.java | Search Tool 에이전트 인터페이스 |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/OpenAiSearchToolAgent.java | OpenAI Search Tool 구현 (도구 호출 기반 검색) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/MockSearchToolAgent.java | Mock Search Tool (테스트/오프라인용) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/RerankingService.java | 리랭킹 서비스 인터페이스 |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/OpenAiRerankingService.java | OpenAI Cross-Encoder 리랭킹 구현 |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/CriticAgentService.java | Critic 에이전트 인터페이스 (답변 품질 자가 비평) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/OpenAiCriticAgentService.java | OpenAI Critic 구현 (LLM 기반 비평 + 개선 루프) |
backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/MockCriticAgentService.java | Mock Critic (테스트/오프라인용) |
backend/app-api/src/main/java/com/biorad/csrag/infrastructure/prompt/PromptRegistry.java | 프롬프트 외부화 레지스트리 (@PostConstruct 로드, {{var}} 템플릿) |
backend/app-api/src/main/resources/prompts/*.txt | 외부화된 프롬프트 파일 (17개) |
파일: OpenAiComposeStep.java
검사: 시스템 프롬프트에 격식체, 마크다운 금지, 번호 인용 금지 등 핵심 규칙이 포함되어 있는지 확인.
grep -n "격식체\|마크다운.*금지\|번호 인용 금지\|이모지.*금지" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/OpenAiComposeStep.java
PASS: 격식체 존댓말, 마크다운 서식 금지, [1],[2] 번호 인용 금지, 이모지 금지, 인라인 인용 형식 (파일명, p.XX) 규칙 모두 존재
FAIL: 핵심 규칙 누락 또는 마크다운 허용 문구 존재
파일: OpenAiComposeStep.java
검사: 시스템 프롬프트와 buildPrompt에 인라인 인용 형식 (파일명, p.XX) 또는 (파일명, p.XX-YY) 지시가 있는지 확인.
grep -n "파일명.*p\.\|인용.*괄호\|출처.*표기" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/OpenAiComposeStep.java
PASS: 인라인 인용 형식 지시 존재 (시스템 프롬프트 규칙 6 + buildPrompt 요구사항 7) FAIL: 인라인 인용 형식 지시 없음
파일: OpenAiComposeStep.java
검사: buildPrompt()에서 [분석 결과], [참고 자료], [요구사항] 평문 섹션을 사용하는지 확인. ##, ** 마크다운 미사용 확인.
grep -n "\[분석 결과\]\|\[참고 자료\]\|\[요구사항\]\|##\|\\*\\*" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/OpenAiComposeStep.java
PASS: [분석 결과], [참고 자료], [요구사항] 존재하고 ##/** 마크다운 없음
FAIL: 마크다운 헤더(##) 또는 볼드(**) 사용
파일: DefaultComposeStep.java
검사: 모든 톤 템플릿이 격식체 존댓말("드립니다", "바랍니다", "겠습니다")을 사용하는지 확인.
grep -n "드립니다\|바랍니다\|겠습니다\|주시기" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/DefaultComposeStep.java
PASS: 격식체 어미 다수 존재 FAIL: 반말 또는 비격식체 어미 사용
파일: DefaultComposeStep.java
검사: email 채널에 "안녕하세요"/"감사합니다", messenger 채널에 "[요약]" 태그 사용 확인.
grep -n "안녕하세요\|감사합니다\|\[요약\]" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/DefaultComposeStep.java
PASS: email에 인사/마무리, messenger에 [요약] 태그 존재 FAIL: 채널별 포맷 규칙 미준수
파일: AnswerComposerService.java
검사: citations 조립 시 chunk=, score=, documentId=, fileName=, pageStart=, pageEnd= 키를 포함하는지 확인.
grep -n "chunk=\|score=\|documentId=\|fileName=\|pageStart=\|pageEnd=" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/AnswerComposerService.java
PASS: 6개 키 모두 존재 (fileName, pageStart, pageEnd는 조건부) FAIL: 필수 키 (chunk, score, documentId) 누락
파일: EvidenceItem.java
검사: EvidenceItem record에 9개 필드가 모두 존재하는지 확인.
grep -n "chunkId\|documentId\|score\|excerpt\|sourceType\|fileName\|pageStart\|pageEnd\|productFamily" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/analysis/EvidenceItem.java
PASS: chunkId, documentId, score, excerpt, sourceType, fileName, pageStart, pageEnd, productFamily 모두 존재 FAIL: 필드 누락
파일: AnalysisService.java
검사: chunk/document 조회 시 findAllById 배치 쿼리를 사용하는지 확인. 반복문 내 개별 조회(N+1)가 없는지 확인.
grep -n "findAllById\|findById\|allLookupIds\|sourceId" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/analysis/AnalysisService.java
PASS: findAllById 사용 (chunkIds, docIds 배치) + sourceId fallback으로 allLookupIds 확장 조회
FAIL: 반복문 내 findById 개별 호출 존재 또는 sourceId fallback 누락
파일: DocumentDownloadController.java
검사: /download과 /pages 엔드포인트가 존재하고, Content-Disposition + UTF-8 인코딩을 사용하는지 확인.
grep -n "download\|pages\|Content-Disposition\|UTF-8" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/document/DocumentDownloadController.java
PASS: 두 엔드포인트 존재, Content-Disposition 헤더에 UTF-8 파일명 인코딩 FAIL: 엔드포인트 누락 또는 Content-Disposition 미설정
파일: DocumentDownloadController.java
검사: PDF 페이지 추출 시 from/to 범위 검증이 있는지 확인.
grep -n "from < 1\|to < from\|BAD_REQUEST\|NOT_FOUND" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/document/DocumentDownloadController.java
PASS: from >= 1, to >= from 검증 + 적절한 HTTP 상태 코드 (400, 404) FAIL: 범위 검증 없이 PDF 페이지 추출 시도
파일: DocumentDownloadController.java
검사: /pages 엔드포인트에 download boolean 파라미터가 있고, true=attachment / false=inline으로 Content-Disposition이 설정되는지 확인.
grep -n "boolean download\|attachment\|inline" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/document/DocumentDownloadController.java
PASS: @RequestParam(defaultValue = "false") boolean download + disposition 분기
FAIL: download 파라미터 없음 또는 항상 attachment
파일: DocumentDownloadController.java
검사: 문서 조회 시 Inquiry 문서와 KB 문서 양쪽을 모두 확인하는 이중 경로가 있는지 확인.
grep -n "documentRepository\|kbDocRepository\|resolveDocument" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/document/DocumentDownloadController.java
PASS: documentRepository.findById() → fallback kbDocRepository.findById() 이중 조회
FAIL: 한쪽 리포지토리만 조회
파일: AnswerComposerService.java
검사: 클래스 레벨 @Transactional이 있고, 읽기 전용 메서드(history, latest)에 readOnly=true가 있는지 확인.
grep -n "@Transactional\|readOnly" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/AnswerComposerService.java
PASS: 클래스 레벨 @Transactional + history/latest에 @Transactional(readOnly = true) FAIL: @Transactional 없음 또는 readOnly 누락
파일: ApprovalAgentService.java
검사: 4개 게이트(confidence, reviewScore, noCriticalIssues, noHighRiskFlags)가 존재하고, noHighRiskFlags가 SAFETY/REGULATORY/CONFLICTING만 차단하는지 확인.
grep -n "confidence\|reviewScore\|noCriticalIssues\|noHighRiskFlags\|SAFETY_CONCERN\|REGULATORY_RISK\|CONFLICTING_EVIDENCE" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/agent/ApprovalAgentService.java
PASS: 4개 게이트 + noHighRiskFlags가 3가지 위험 플래그만 차단 FAIL: 게이트 누락 또는 모든 riskFlags를 차단
파일: AnswerController.java
검사: PATCH /{answerId}/edit-draft 엔드포인트가 존재하고, SENT 상태 편집을 차단하는지 확인.
grep -n "edit-draft\|PatchMapping\|SENT.*edit\|Cannot edit" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/AnswerController.java
PASS: PATCH edit-draft 존재 + SENT 상태 차단 (409 Conflict) FAIL: 엔드포인트 누락 또는 SENT 상태 편집 허용
파일: QdrantVectorStore.java
검사: ensureCollection이 (1) ReentrantLock으로 동기화하고 (synchronized 사용 금지 — virtual thread 피닝 유발), (2) GET으로 존재 확인 후 PUT 생성하고, (3) 실패 시 collectionReady를 true로 설정하지 않는지 확인.
grep -n "ReentrantLock\|collectionLock\|collectionReady\|synchronized" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/vector/QdrantVectorStore.java
PASS: ReentrantLock + collectionLock.lock()/unlock() (try-finally) + GET 확인 → 조건부 PUT 생성 → 실패 시 early return, synchronized 키워드 없음
FAIL: synchronized 사용 또는 무조건 PUT 또는 실패해도 collectionReady=true 설정
파일: QdrantVectorStore.java
검사: deleteByDocumentId가 실패 시 예외를 전파하는지 확인 (조용한 무시 방지).
grep -n "throw.*RuntimeException\|deleteByDocumentId" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/vector/QdrantVectorStore.java
PASS: catch에서 RuntimeException throw (예외 전파) FAIL: warn 로그만 남기고 예외 삼킴
파일: AnalysisService.java
검사: retrieve()와 analyze() 모두 queryTranslationService.translate() 호출 후 번역된 텍스트로 hybridSearchService.search()를 사용하는지 확인. 번역 우회 방지.
grep -n "queryTranslationService\|hybridSearchService\|doRetrieve\|translate" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/analysis/AnalysisService.java
PASS: retrieve()에서 translate() 호출 + doRetrieve() 위임, analyze()에서도 translate() 호출
FAIL: retrieve()에서 번역 없이 직접 검색 (오케스트레이션 파이프라인 번역 우회)
파일: AnalyzeResponse.java
검사: translatedQuery 필드가 record에 존재하는지 확인.
grep -n "translatedQuery" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/analysis/AnalyzeResponse.java
PASS: translatedQuery 필드 존재 (nullable)
FAIL: 필드 누락
파일: HybridSearchService.java
검사: RRF(Reciprocal Rank Fusion) 결합이 벡터 + 키워드 양쪽 결과를 병합하고, matchSource 필드로 출처를 표시하는지 확인.
grep -n "rrf\|fusedScore\|matchSource\|VECTOR.*KEYWORD" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/HybridSearchService.java
PASS: RRF 점수 계산 + matchSource (VECTOR / KEYWORD / VECTOR+KEYWORD) 표시 FAIL: 단순 합산 또는 matchSource 미구분
파일: OpenAiQueryTranslationService.java, MockQueryTranslationService.java
검사: OpenAI 구현이 openai.enabled=true 조건, Mock 구현이 fallback으로 설정되는지 확인.
grep -n "ConditionalOnProperty\|Primary" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/OpenAiQueryTranslationService.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/MockQueryTranslationService.java
PASS: OpenAi에 @ConditionalOnProperty(prefix = "openai", name = "enabled", havingValue = "true") + @Primary
FAIL: 조건 어노테이션 누락
파일: AnalysisService.java
검사: SUPPORTED 임계값이 0.70, CONDITIONAL 임계값이 0.45인지 확인. 이전 과도한 임계값(0.82, 0.64)이 아닌지 검증.
grep -n "avg >= 0\.\|>= 0\." backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/analysis/AnalysisService.java
PASS: avg >= 0.70 → SUPPORTED, avg >= 0.45 → CONDITIONAL 존재
FAIL: 0.82 또는 0.64 등 과도한 임계값 사용
파일: AnalysisService.java
검사: summarize() 메서드가 chunk 내용을 절단(substring)하지 않고 공백 정규화만 수행하는지 확인. 160자 절단은 LLM에 불충분한 컨텍스트를 전달하여 hedging 답변 유발.
grep -n "summarize\|substring\|160\|Math.min.*length" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/analysis/AnalysisService.java
PASS: summarize()가 replaceAll("\\s+", " ").trim()만 수행, substring/절단 없음
FAIL: substring 호출 또는 문자수 제한 로직 존재
파일: OpenAiComposeStep.java
검사: buildPrompt()에서 verdict, confidence, riskFlags를 LLM에 전달하지 않는지 확인. 이 값들이 프롬프트에 포함되면 LLM이 자체 hedging하여 답변 품질 저하.
grep -n "verdict\|confidence\|riskFlags" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/OpenAiComposeStep.java
PASS: buildPrompt 내에 verdict/confidence/riskFlags 관련 문자열 없음 FAIL: 프롬프트에 verdict, confidence, riskFlags 중 하나라도 노출
파일: DefaultComposeStep.java
검사: applyGuardrails()가 SAFETY_CONCERN 또는 REGULATORY_RISK riskFlag가 있을 때만 면책 문구를 삽입하는지 확인. confidence 임계값 기반 삽입은 불필요한 hedging 유발.
grep -n "SAFETY_CONCERN\|REGULATORY_RISK\|confidence.*0\.75\|applyGuardrails" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/DefaultComposeStep.java
PASS: SAFETY_CONCERN/REGULATORY_RISK 조건만 존재, confidence < 0.75 조건 없음
FAIL: confidence < 0.75 등 점수 기반 면책 문구 삽입 로직 존재
파일: AnalysisService.java
검사: 키워드 기반 verdict 선판정 메서드 classifyQuestionPrior()가 제거되었는지 확인. 고객 질문에 "risk", "condition" 같은 단어가 포함되면 내용과 무관하게 CONDITIONAL로 판정하는 오판 위험.
grep -n "classifyQuestionPrior\|QuestionPrior" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/analysis/AnalysisService.java
PASS: classifyQuestionPrior 문자열 없음 (메서드 제거됨)
FAIL: classifyQuestionPrior 메서드 또는 호출 존재
| # | 검사 항목 | 결과 | 상세 |
|---|---|---|---|
| 1 | 시스템 프롬프트 8대 규칙 | PASS/FAIL | 누락 규칙 목록 |
| 2 | 프롬프트 평문 섹션 | PASS/FAIL | 마크다운 사용 여부 |
| 3 | 톤 템플릿 격식체 | PASS/FAIL | 비격식체 위치 |
| 4 | 채널 포맷 | PASS/FAIL | email/messenger 규칙 |
| 5 | Citations 형식 | PASS/FAIL | 누락 키 목록 |
| 6 | EvidenceItem 필드 | PASS/FAIL | 누락 필드 목록 |
| 7 | N+1 방지 배치 조인 | PASS/FAIL | 개별 쿼리 위치 |
| 8 | 다운로드 엔드포인트 | PASS/FAIL | 누락 항목 |
| 1a | 인라인 인용 형식 | PASS/FAIL | (파일명, p.XX) 지시 |
| 9 | 페이지 범위 검증 | PASS/FAIL | 검증 누락 항목 |
| 9a | download 파라미터 | PASS/FAIL | inline/attachment 분기 |
| 10 | 이중 문서 조회 | PASS/FAIL | 단일 경로 위치 |
| 11 | @Transactional 설정 | PASS/FAIL | readOnly 누락 |
| 12 | 4단 승인 게이트 | PASS/FAIL | 게이트 누락/과도한 차단 |
| 13 | EditDraft 엔드포인트 | PASS/FAIL | SENT 차단 여부 |
| 14 | ensureCollection ReentrantLock + 안전성 | PASS/FAIL | ReentrantLock + GET 확인 + early return |
| 15 | deleteByDocumentId 예외 전파 | PASS/FAIL | 예외 삼킴 여부 |
| 16 | 번역+하이브리드 검색 통합 | PASS/FAIL | retrieve()/analyze() 번역 호출 |
| 17 | AnalyzeResponse translatedQuery | PASS/FAIL | 필드 존재 |
| 18 | HybridSearch RRF 결합 | PASS/FAIL | matchSource 구분 |
| 19 | QueryTranslation ConditionalOnProperty | PASS/FAIL | 조건 어노테이션 |
| 20 | Verdict 임계값 (0.70/0.45) | PASS/FAIL | 과도한 임계값 사용 |
| 21 | summarize() 비절단 | PASS/FAIL | substring/절단 로직 |
| 22 | 프롬프트 verdict 미노출 | PASS/FAIL | verdict/confidence 포함 |
| 23 | Guardrails 조건부 적용 | PASS/FAIL | confidence 기반 삽입 |
| 24 | classifyQuestionPrior 미존재 | PASS/FAIL | 선판정 메서드 존재 |
| 25 | history-detail 엔드포인트 | PASS/FAIL | GET history-detail 존재 |
| 26 | autoWorkflow 재실행 제한 | PASS/FAIL | 5회 제한 + SENT 가드 |
| 27 | 리뷰 7개 카테고리 | PASS/FAIL | CITATION/HALLUCINATION 포함 |
| 28 | 리뷰 고객 질문 전달 | PASS/FAIL | 프롬프트에 질문 포함 |
파일: AnswerController.java
검사: GET /history-detail 엔드포인트가 존재하고 AnswerHistoryDetailResponse 목록을 반환하는지 확인.
grep -n "history-detail\|AnswerHistoryDetailResponse" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/AnswerController.java
PASS: @GetMapping("/history-detail") + 반환 타입 List<AnswerHistoryDetailResponse> 존재
FAIL: 엔드포인트 누락 또는 반환 타입 불일치
파일: AnswerController.java
검사: autoWorkflow() 엔드포인트에 SENT 상태 거부(ConflictException)와 5회 실행 제한(ValidationException)이 있는지 확인.
grep -n "SENT\|workflowRunCount\|>= 5\|resetForReReview\|incrementWorkflowRunCount" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/AnswerController.java
PASS: SENT 상태 → 409 Conflict, workflowRunCount >= 5 → 400 Validation, 비-DRAFT 상태 시 resetForReReview() 호출 FAIL: SENT 가드 없음 또는 실행 횟수 제한 없음
파일: ReviewAgentService.java
검사: 시스템 프롬프트에 ACCURACY, COMPLETENESS, CITATION, HALLUCINATION, TONE, RISK, FORMAT 7개 카테고리가 정의되어 있는지 확인.
grep -n "ACCURACY\|COMPLETENESS\|CITATION\|HALLUCINATION\|TONE\|RISK\|FORMAT" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/agent/ReviewAgentService.java
PASS: 7개 카테고리 모두 시스템 프롬프트에 존재 (특히 CITATION, HALLUCINATION 신규 추가) FAIL: 5개 카테고리만 존재 (CITATION/HALLUCINATION 누락)
파일: ReviewAgentService.java
검사: buildPrompt()에서 고객 질문 원문이 포함되고, InquiryRepository에서 질문을 조회하는지 확인.
grep -n "고객 질문\|customerQuestion\|InquiryRepository\|inquiryRepository" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/agent/ReviewAgentService.java
PASS: inquiryRepository를 통해 고객 질문 조회 + 프롬프트에 ## 고객 질문 섹션 포함
FAIL: 고객 질문 전달 없이 답변 초안만으로 리뷰
파일: OpenAiQuestionDecomposerService.java, RegexQuestionDecomposerService.java
검사: multi-question 분해 패턴 (질문 N), N), N., #N))이 정의되어 있고, 단일 질문은 분해하지 않는지 확인. QuestionDecomposerService는 인터페이스이며, 실제 구현은 OpenAiQuestionDecomposerService와 RegexQuestionDecomposerService에 존재.
grep -n "질문.*N\|Pattern.*compile\|isMultiQuestion\|SubQuestion" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/OpenAiQuestionDecomposerService.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/RegexQuestionDecomposerService.java
PASS: 4개 이상 분해 패턴 존재 + 단일 질문 시 isMultiQuestion=false 반환 FAIL: 분해 패턴 누락 또는 단일 질문도 분해
파일: ProductExtractorService.java
검사: Bio-Rad 주요 제품 패턴(naica, vericheck, QX, CFX, ddPCR, Bio-Plex 등)이 등록되어 있고, 가장 긴 매치를 선택하는지 확인.
grep -n "naica\|vericheck\|QX\|CFX\|ddPCR\|Bio-Plex\|longestMatch\|confidence\|extractAll" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/ProductExtractorService.java
PASS: 12개 이상 제품 패턴 + extractAll() 멀티 추출 (최대 3개, family 중복 제거) + longest match 선택 + confidence 점수 (0.9 exact, 0.6 partial) FAIL: 주요 제품 패턴 누락 또는 첫 번째 매치만 반환
파일: SearchFilter.java, DefaultRetrieveStep.java
검사: SearchFilter에 팩토리 메서드(none, forInquiry, forProduct, forDocuments)가 있고, DefaultRetrieveStep에 SearchFilter 파라미터를 받는 오버로드가 있는지 확인.
grep -n "none()\|forInquiry\|forProduct\|forProducts\|forDocuments\|SearchFilter" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/SearchFilter.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/DefaultRetrieveStep.java
PASS: 5개 팩토리 메서드 (none, forInquiry, forProduct, forProducts, forDocuments) + DefaultRetrieveStep에 SearchFilter 오버로드 존재 FAIL: 팩토리 메서드 누락 또는 SearchFilter 오버로드 없음
파일: AnswerOrchestrationService.java, DefaultRetrieveStep.java
검사: 오케스트레이션에서 하위 질문별 독립 검색(executePerQuestion)을 수행하고, PerQuestionEvidence에 sufficient 플래그가 있는지 확인.
grep -n "executePerQuestion\|PerQuestionEvidence\|sufficient\|MIN_RELEVANCE" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/AnswerOrchestrationService.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/DefaultRetrieveStep.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/PerQuestionEvidence.java
PASS: executePerQuestion 호출 + PerQuestionEvidence.sufficient 필드 + MIN_RELEVANCE_THRESHOLD (0.40) FAIL: 모든 하위 질문에 동일 증거 사용 또는 sufficient 판단 없음
파일: DefaultComposeStep.java, OpenAiComposeStep.java
검사: 증거 부족 시 "확인 후 별도로 답변드리겠습니다" 문구가 생성되는지 확인. DefaultComposeStep과 OpenAiComposeStep 모두에 I Don't Know 경로가 있는지 확인.
grep -n "확인 후 별도\|확인 후.*답변\|I Don't Know\|증거.*부족\|sufficient.*false\|아니오" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/DefaultComposeStep.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/OpenAiComposeStep.java
PASS: 양쪽 ComposeStep에 증거 부족 시 "확인 후 별도로 답변드리겠습니다" 생성 로직 존재 FAIL: 증거 부족해도 답변을 생성하거나 빈 문자열 반환
파일: AnswerComposerService.java
검사: 답변 생성 전 INDEXED 문서가 있는지 확인하는 가드 로직이 있는지 확인. 인덱싱되지 않은 문서로 답변 생성 시도를 차단.
grep -n "INDEXED\|NOT_INDEXED\|documentMeta\|indexing.*guard\|ValidationException" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/AnswerComposerService.java
PASS: INDEXED 문서 확인 → 없으면 ValidationException("NOT_INDEXED") 발생 FAIL: 인덱싱 상태 확인 없이 바로 오케스트레이션 호출
파일: SelfReviewStep.java
검사: checkSubQuestionCompleteness()가 질문의 하위 질문 수와 답변의 하위 답변 수를 비교하고, "확인 후" 패턴도 유효한 답변으로 카운트하는지 확인.
grep -n "checkSubQuestionCompleteness\|SUB_QUESTION_INCOMPLETE\|확인 후\|questionNumbers\|answerNumbers" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/SelfReviewStep.java
PASS: 하위 질문 수 감지 + 답변 수 비교 + "확인 후" 카운트 포함 + 부족 시 CRITICAL 이슈 FAIL: 하위 질문 완전성 검사 없음 또는 "확인 후" 미카운트
| # | 검사 항목 | 결과 | 상세 |
|---|---|---|---|
| ... | (기존 1~28 항목) | ... | ... |
| 29 | 질문 분해 패턴 | PASS/FAIL | 분해 패턴 존재 여부 |
| 30 | 제품명 추출 | PASS/FAIL | 12 패턴 + longest match |
| 31 | SearchFilter 기반 검색 | PASS/FAIL | 팩토리 메서드 + 오버로드 |
| 32 | per-question 증거 매핑 | PASS/FAIL | 독립 검색 + sufficient |
| 33 | I Don't Know 경로 | PASS/FAIL | 양쪽 ComposeStep |
| 34 | 인덱싱 가드 | PASS/FAIL | NOT_INDEXED 예외 |
| 35 | 하위 질문 완전성 | PASS/FAIL | SelfReview 검사 |
| 36 | normalizeKorean 전처리 (RF-01) | PASS/FAIL | 조사/어미 제거 + search() 호출 |
| 37 | resolveForVectorSearch (RF-04) | PASS/FAIL | inquiryId→documentIds 변환 |
| 38 | NormalizeKorean 테스트 (RF-01) | PASS/FAIL | 조사/어미/기술용어 케이스 |
| 39 | ProductFamilyRegistry 카테고리 매핑 | PASS/FAIL | 10 제품군 + 6 카테고리 + expand() |
| 40 | 3단계 retrieval fallback | PASS/FAIL | EXACT → CATEGORY_EXPANDED → UNFILTERED |
| 41 | SubQuestion productFamilies | PASS/FAIL | 필드 + enrichWithProductFamilies |
| 42 | 멀티 제품 필터 검색 | PASS/FAIL | Qdrant any + Postgres IN |
| 43 | PromptRegistry 외부화 | PASS/FAIL | 인라인 프롬프트 없음 + promptRegistry.get() 사용 |
| 44 | Agentic RAG @ConditionalOnProperty | PASS/FAIL | OpenAi/Mock 이중 구현 일관성 |
| 45 | 모델 티어링 (HEAVY/MEDIUM/LIGHT) | PASS/FAIL | 서비스별 올바른 모델 티어 사용 |
| 46 | CriticAgent 자가 비평 루프 | PASS/FAIL | 개선 루프 + maxIterations 제한 |
파일: PostgresKeywordSearchService.java
검사: search() 호출 전 normalizeKorean()으로 한국어 조사/어미를 제거하는지 확인. 영문/숫자/기술 용어(ddPCR, CFX96 등)는 보존되어야 함.
grep -n "normalizeKorean\|을/를\|조사.*제거\|JOSA_PATTERN\|ENDING_PATTERN" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/PostgresKeywordSearchService.java
PASS: normalizeKorean() 메서드 존재 + search() 내 쿼리 정규화 호출 + 조사/어미 패턴 정의
FAIL: normalizeKorean() 없음 또는 search()에서 호출하지 않음
파일: HybridSearchService.java
검사: resolveForVectorSearch()가 SearchFilter의 inquiryId를 벡터 검색용 documentIds로 변환하는지 확인. 벡터 스토어는 inquiryId SQL 서브쿼리를 지원하지 않으므로 사전 변환 필수.
grep -n "resolveForVectorSearch\|documentIds\|inquiryId\|chunkRepository\|findBySourceId" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/HybridSearchService.java
PASS: resolveForVectorSearch() 메서드 존재 + inquiryId → documentIds 변환 + 키워드 검색에 원본 필터, 벡터 검색에 변환된 필터 사용
FAIL: 벡터 검색에 inquiryId가 그대로 전달되거나 resolveForVectorSearch() 없음
파일: NormalizeKoreanTest.java
검사: 한국어 정규화 테스트가 존재하고 조사 제거, 어미 제거, 영문 보존 케이스를 검증하는지 확인.
grep -n "@Test\|normalizeKorean\|ddPCR\|QX700\|을\|합니다" backend/app-api/src/test/java/com/biorad/csrag/interfaces/rest/search/NormalizeKoreanTest.java
PASS: 조사 제거, 어미 제거, 기술 용어 보존 테스트 케이스 존재 FAIL: 테스트 파일 없음 또는 핵심 케이스 미커버
파일: ProductFamilyRegistry.java
검사: 10개 제품군이 6개 카테고리에 매핑되어 있고, expand()가 동일 카테고리 관련 제품을 반환하는지 확인.
grep -n "allFamilies\|categoryOf\|relatedFamilies\|expand\|ddPCR_Systems\|Real_Time_PCR" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/ProductFamilyRegistry.java
PASS: 10개 제품군 + 6개 카테고리 + allFamilies(), categoryOf(), relatedFamilies(), expand() 메서드 존재 FAIL: 제품군 수 부족 또는 expand() 메서드 누락
파일: AnswerOrchestrationService.java
검사: 제품 필터 검색 → 카테고리 확장 검색 → 필터 없는 전체 검색 3단계 fallback이 구현되어 있는지 확인. RetrievalQuality enum (EXACT, CATEGORY_EXPANDED, UNFILTERED)이 존재하는지 확인.
grep -n "RetrievalQuality\|EXACT\|CATEGORY_EXPANDED\|UNFILTERED\|doRetrieveWithFilter\|expand" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/AnswerOrchestrationService.java
PASS: 3단계 fallback 로직 + RetrievalQuality enum + doRetrieveWithFilter 헬퍼 + ProductFamilyRegistry.expand() 호출 FAIL: 단일 검색만 수행하거나 fallback 없음
파일: SubQuestion.java, QuestionDecomposerService.java
검사: SubQuestion record에 Set<String> productFamilies 필드가 있고, QuestionDecomposerService에서 하위질문별 제품명을 추출하여 설정하는지 확인.
grep -n "productFamilies\|enrichWithProductFamilies\|extractAll" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/SubQuestion.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/QuestionDecomposerService.java
PASS: SubQuestion.productFamilies 필드 + QuestionDecomposerService.enrichWithProductFamilies() + ProductExtractorService.extractAll() 호출 FAIL: productFamilies 필드 없음 또는 enrichWithProductFamilies 미호출
파일: QdrantVectorStore.java, MockVectorStore.java, PostgresKeywordSearchService.java, MockKeywordSearchService.java
검사: SearchFilter의 복수 productFamilies가 벡터 검색에서 "match": {"any": [...]} (Qdrant), 키워드 검색에서 product_family IN (?,?,?) 패턴으로 변환되는지 확인.
grep -n "any\|productFamilies\|IN (" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/vector/QdrantVectorStore.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/vector/MockVectorStore.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/PostgresKeywordSearchService.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/MockKeywordSearchService.java
PASS: Qdrant "any": [...] + Mock anyMatch() + Postgres IN (?) 동적 플레이스홀더 + MockKeyword 동일 패턴
FAIL: 단일 제품 "match": {"value": ...} 또는 = ? 단일 비교
파일: 13개 OpenAi 서비스 파일
검사: 프롬프트를 사용하는 모든 OpenAi 서비스에서 promptRegistry.get() 호출이 존재하고, 서비스 클래스 내에 인라인 시스템 프롬프트(멀티라인 문자열로 작성된 LLM 지시문)가 없는지 확인.
grep -rn "promptRegistry" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/OpenAiComposeStep.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/OpenAiVerifyStep.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/OpenAiCriticAgentService.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/agent/ReviewAgentService.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/OpenAiAdaptiveRetrievalAgent.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/OpenAiMultiHopRetriever.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/OpenAiSearchToolAgent.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/OpenAiHydeQueryTransformer.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/OpenAiRerankingService.java
PASS: 모든 OpenAi 서비스에서 promptRegistry.get("xxx") 호출 존재, 인라인 프롬프트 없음
FAIL: promptRegistry import/사용 없이 인라인 시스템 프롬프트 사용
파일: OpenAi/Mock 서비스 쌍 (AdaptiveRetrieval, MultiHop, SearchTool, Reranking, Critic)
검사: 각 인터페이스에 OpenAi(enabled=true) 구현과 Mock(enabled=false/기본) 구현이 쌍으로 존재하고, @ConditionalOnProperty가 올바르게 설정되는지 확인.
grep -rn "@ConditionalOnProperty\|@Primary" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/OpenAiAdaptiveRetrievalAgent.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/MockAdaptiveRetrievalAgent.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/OpenAiMultiHopRetriever.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/MockMultiHopRetriever.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/OpenAiSearchToolAgent.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/search/MockSearchToolAgent.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/OpenAiCriticAgentService.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/MockCriticAgentService.java
PASS: 모든 OpenAi 구현에 @ConditionalOnProperty(prefix="openai", name="enabled", havingValue="true") + @Primary, Mock 구현은 기본 fallback
FAIL: @ConditionalOnProperty 누락 또는 OpenAi/Mock 쌍 불완전
파일: application.yml, OpenAi 서비스 파일들
검사: application.yml에 3-tier 모델 설정(chat-heavy, chat-medium, chat-light)이 존재하고, 서비스들이 복잡도에 맞는 모델 티어를 사용하는지 확인.
grep -n "chat-heavy\|chat-medium\|chat-light\|chatHeavy\|chatMedium\|chatLight" backend/app-api/src/main/resources/application.yml
PASS: 3개 티어 설정 존재 (heavy: 복잡 추론용, medium: 중간 복잡도, light: 경량 작업) FAIL: 단일 모델만 설정되어 있거나 티어 구분 없음
파일: OpenAiCriticAgentService.java, CriticAgentService.java
검사: Critic 에이전트가 답변 품질을 평가하고, 일정 점수 미달 시 개선을 시도하며, maxIterations로 무한 루프를 방지하는지 확인.
grep -n "maxIterations\|MAX_ITERATIONS\|iterate\|critique\|score\|threshold\|CriticResult" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/OpenAiCriticAgentService.java backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/CriticAgentService.java
PASS: CriticAgentService 인터페이스 + OpenAi 구현에 반복 제한 (maxIterations ≤ 3) + 임계점 이상 시 조기 종료 FAIL: 무한 루프 가능성 또는 반복 제한 없음
파일: AnswerOrchestrationService.java
검사: Adaptive Retrieve 성공 후 rebuildPerQuestionMapping이 호출되어 하위 질문별 증거 매핑이 재구성되는지 확인.
grep -n "rebuildPerQuestionMapping" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/AnswerOrchestrationService.java
PASS: rebuildPerQuestionMapping 호출이 Adaptive Retrieve 후 존재
FAIL: Adaptive Retrieve 후 rebuildPerQuestionMapping 호출 없음 (하위 질문별 증거 매핑 갱신 누락)
파일: AnswerOrchestrationService.java
검사: toEvidenceItems 메서드 내에서 enrichEvidenceMetadata가 호출되어 증거에 파일명/페이지 등 메타데이터가 보강되는지 확인.
grep -n "enrichEvidenceMetadata\|toEvidenceItems" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/AnswerOrchestrationService.java
PASS: toEvidenceItems 내에서 enrichEvidenceMetadata 호출 존재
FAIL: enrichEvidenceMetadata 호출 없음 (증거 메타데이터 누락)
파일: backend/app-api/src/main/resources/prompts/compose-system.txt
검사: compose-system.txt에 영문 증거(Evidence)에 대한 처리 지시가 포함되어 있는지 확인. 증거가 영문으로 제공될 수 있으므로 한국어 답변에 적절히 반영하라는 지시 필요.
grep -n "영문\|English\|evidence.*language\|번역" backend/app-api/src/main/resources/prompts/compose-system.txt
PASS: 영문 증거 처리에 대한 지시 존재 (번역/한국어 반영 포함) FAIL: 영문 증거 처리 지시 없음
파일: AnalysisService.java
검사: EvidenceQualityGate가 AnalysisService에 의존성 주입되어 증거 품질 게이트가 분석 파이프라인에 통합되었는지 확인.
grep -n "EvidenceQualityGate\|evidenceQualityGate" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/analysis/AnalysisService.java
PASS: EvidenceQualityGate 필드 선언 + 생성자 주입 존재
FAIL: EvidenceQualityGate 주입 없음 (증거 품질 필터링 미적용)
파일: QdrantVectorStore.java
검사: VectorStoreCircuitBreaker가 QdrantVectorStore에 주입되어 벡터 스토어 장애 시 Circuit Breaker가 작동하는지 확인.
grep -n "VectorStoreCircuitBreaker\|circuitBreaker" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/vector/QdrantVectorStore.java
PASS: VectorStoreCircuitBreaker 필드 선언 + 생성자 주입 존재
FAIL: VectorStoreCircuitBreaker 주입 없음 (장애 전파 방지 미적용)
파일: AnswerOrchestrationService.java
검사: TokenBudgetManager가 오케스트레이션 요청별로 새로 생성되어 토큰 예산이 요청 간 공유되지 않는지 확인.
grep -n "TokenBudgetManager\|tokenBudget" backend/app-api/src/main/java/com/biorad/csrag/interfaces/rest/answer/orchestration/AnswerOrchestrationService.java
PASS: new TokenBudgetManager() 또는 팩토리 메서드가 오케스트레이션 메서드 내부에서 호출 (per-request 생성)
FAIL: TokenBudgetManager가 필드로 공유되거나 싱글톤으로 사용 (요청 간 토큰 예산 오염)
| # | 검사 항목 | 결과 | 상세 |
|---|---|---|---|
| ... | (기존 1~46 항목) | ... | ... |
| 47 | rebuildPerQuestionMapping 호출 | PASS/FAIL | Adaptive Retrieve 후 매핑 재구성 |
| 48 | enrichEvidenceMetadata 호출 | PASS/FAIL | toEvidenceItems 내 메타데이터 보강 |
| 49 | compose-system.txt 영문 증거 지시 | PASS/FAIL | 영문 증거 처리 지시 존재 |
| 50 | EvidenceQualityGate 주입 | PASS/FAIL | AnalysisService 의존성 주입 |
| 51 | VectorStoreCircuitBreaker 주입 | PASS/FAIL | QdrantVectorStore 의존성 주입 |
| 52 | TokenBudgetManager per-request | PASS/FAIL | 요청별 새 인스턴스 생성 |
다음은 위반이 아닙니다:
OPENAI_ENABLED=false에서 DefaultComposeStep이 primary로 동작하는 것은 정상세션 변경사항을 분석하여 검증 스킬 누락을 탐지합니다. 기존 스킬을 동적으로 탐색하고, 새 스킬을 생성하거나 기존 스킬을 업데이트한 뒤 CLAUDE.md를 관리합니다.
REST API 응답 DTO 일관성 및 에러 코드 표준 검증. API 엔드포인트 추가/수정, DTO 변경, 에러 처리 수정 후 사용. API 계약 변경이 프론트엔드에 영향을 줄 수 있을 때 사용.
Flyway DB 마이그레이션 일관성 검증. 마이그레이션 추가/수정 후 사용.
프론트엔드 UI 컴포넌트 품질 검증 (shadcn/ui + Tailwind 기준). UI 컴포넌트 수정 후 사용.
KB 비동기 인덱싱 파이프라인 검증. 인덱싱 관련 코드 수정 후 사용.
문의 상세 페이지 UI 컴포넌트 검증 (shadcn/ui + Tailwind 기준). 문의 탭(Info/Analysis/Answer/History) 및 업로드 컴포넌트 수정 후 사용.