근거 연결(Grounding) 및 eval 강화(hardening)
근거 연결(Grounding) 및 eval 강화(hardening)
이 문서는 phase 1 grounding/eval hardening 변경을 리뷰어가 재현할 수 있도록 정리한다.
무엇이 바뀌었나
- Public eval slice를
single_doc,comparison,follow_up,abstention으로 표준화했다. 기존multi_docconfig 값은 호환 alias로 계속 읽는다. - 공개 eval에 claim-level citation spec과 partial comparison 케이스를 추가했다.
answer객체를 schema v2로 고정하고schema_version,status_reason을 추가했다.run_rag_query결과에trace를 추가해 planner 선택과 query rewrite/context resolution을 한 곳에서 볼 수 있게 했다. 필드 정의와 해석 가이드는 planner-trace.md 참고.eval/run_eval.py는 각 run/case의 trace를reports/traces/<run>/<case>.trace.json에 쓴다.reports/는 gitignored라 private/local trace가 커밋되지 않는다.--redact_trace플래그로 doc ID / entity 마스킹을 켤 수 있다.claim_citation_alignment과claim_citation_error_counts를 추가해 whole-answer citation precision과 claim-level drift를 분리했다.
왜 바뀌었나
기존 report는 전체 답변의 expected term/doc match를 잘 보여줬지만, 다음 질문에는 답하기 어려웠다.
- 비교 질문에서 어떤 target이 빠졌는가?
- follow-up query가 실제로 어떤 query로 rewrite 되었는가?
supported,partial,insufficient가 같은 JSON 계약으로 안정적으로 나오는가?- 답변 전체는 맞아 보여도 claim이 엉뚱한 chunk를 citation으로 달고 있지 않은가?
이번 변경은 retrieval/chunking 구조를 바꾸지 않고, reviewer가 위 질문을 report와 trace artifact에서 직접 확인하도록 만든다.
검증 방법
python3 scripts/build_index.py --input_dir eval/fixtures/smoke_rfp/raw --output_dir data/index
python3 app.py --input_dir data/index --output_dir outputs --query "기관 A와 기관 B의 AI 요구사항 차이 알려줘"
python3 eval/run_eval.py --index_dir data/index --output_dir reports --config eval/config.yaml
python3 scripts/update_readme_metrics.py --report reports/eval_summary.json --readme README.md --check
python3 -m pytest tests/test_eval_metrics.py tests/test_fuzzy_retrieval.py
make test-regression # P0 retrieval-loop / answerable-smoke regression guards (#68)
다음 산출물을 점검하라:
outputs/answer.json:answer.schema_version == 2,answer.status_reason,trace.planner,trace.query_rewritereports/eval_summary.json:by_slice,claim_citation_alignment,claim_citation_error_counts,case_results[*].trace_pathreports/traces/full/*.trace.json: 더 강한 agentic run 의 가독성 있는 planner 및 rewrite tracereports/traces/naive_baseline/*.trace.json: 비교용 baseline 대조군 trace
실패 해석
- 낮은
citation_precision: 전체 답변의 근거(evidence) 문서/용어 품질이 약함. - 낮은
claim_citation_alignment: 전체 답변이 올바른 문서를 찾았더라도, 방출된 claim 중 적어도 하나가 인용된 chunk 로 직접 뒷받침되지 않음. expected_claim_missing: eval 이 요구한 target 특정 claim 이 방출되지 않음.claim_text_not_supported_by_citation: citation drift 가능성; 케이스 trace 와 claim citation chunk 를 점검하라.status_match와 함께 나타나는answer_format_compliance실패: 답변 스키마 상태가 기대한supported,partial,insufficient와 일치하지 않음.trace.query_rewrite.rewritten == true: follow-up 컨텍스트가 쿼리 rewrite 에 사용됨.rewrite_type,context_entities,active_doc_ids를 확인하라.
Issue 커버리지
- #58: public eval 이
by_slice와 확장된 comparison/partial/claim 케이스를 통해 더 넓은 slice-aware 리포팅을 갖는다. - #60: 로컬 planner/rewrite trace 가
outputs/answer.json과reports/traces/양쪽에 방출된다. - #63: answer 스키마 v2 가
supported,partial,insufficient를 기계 판독 가능한 status reason 과 함께 명시적으로 표현한다. - #64: claim 단위 citation alignment 가 전체 답변 citation precision 과 별도로 측정된다.
- #69: relaxed (last-attempt) verifier 단계의 partial-topic grounding — 아래 참고.
범위 밖으로 남는 것은 chunking 재설계(#62)와 기밀성/리포팅 흐름(#65)이다.
Partial-topic 근거 연결(grounding) (issue #69)
docs/real-data/real-data-failure-taxonomy.md C6 는 issue #69 당시 배경을
설명하기 위한 historical v1 real100 archive-only context 로만 인용한다
(현재 private eval 근거 아님): 그 snapshot 에서는 false abstention 12 개 중
9 개가 retry_trigger_reason: topic_not_grounded × 2 로 끝났다. 아래 내용은
그때 partial-topic grounding 을 시도한 배경 기록이며, 새 작업·PR·claim 의
정량 근거로 재사용하려면 별도 real100_v2 검증을 먼저 추가해야 한다. 당시
해석은 strict 와 relaxed 단계 모두 사용 가능한 근거를 거부했고 verifier 가
모든 verification topic 이 결합된 근거 텍스트에 나타나도록 요구했다는
것이었다. 의도된 abstention 케이스(P-13~15)는 이미 강건했다는 해석도 같은
historical snapshot 범위에 한정한다.
무엇이 바뀌었나
verify_evidence(..., allow_partial_topic=False)에 명시적 플래그가 생겼다. 검색 루프는 strict 단계가 현재 기준을 유지하도록 마지막 예정 시도에서만 이를True로 설정한다.- 마지막 시도에서, 적어도 하나의 topic 과 모든 verification topic 의 적어도
PARTIAL_TOPIC_GROUNDING_MIN_FRACTION(현재0.5) 이 근거에 나타나면, verifier 는 non-blockingpartial_topic_groundingreason 과 함께verified=True를 반환한다. answer_status는 그 reason 을ANSWER_STATUS_PARTIAL(supported아님) 로 매핑하고,answer_status_reason은code: partial_topic_grounding을 방출하므로 partial 경로가 기존partial_comparison경로와 구분된다. 둘 다answer-policy.md에 문서화돼 있다.- 다른 모든 바닥값(floor)은 strict 를 유지한다:
low_top_score, comparison entity / doc coverage, per-entity comparison topic 체크는 여전히 blocking 이다. Partial-topic grounding 이 hallucination floor 를 무료 통과시켜주지는 않는다.
Trade-off
이는 ADR 0004 에서 예견한 strict-vs-relaxed 노브(knob)다. trade-off 는 명시적이다:
- 얻은 것: 현재 in-tree 로 증명된 이득은 public fixture smoke eval 의
새
partial_topic_security_quantum케이스가insufficient대신partial로 회복되는 것이다. 현재 real-data 영향은 별도real100_v2aggregate 검증 전까지 미확인이다. - 비용:
citation_precision이 약간 떨어질 수 있는데, partial 답변이 요청된 topic 중 일부만 근거 짓는 chunk 에 대한 citation 을 포함할 수 있기 때문이다. 상태 자체(partial)가 답변이 완전히 근거 지어지지 않았음을 호출자에게 알리는 계약이다.
로컬 검증 방법
python3 scripts/build_index.py --input_dir eval/fixtures/smoke_rfp/raw --output_dir data/index --embedding_backend hashing
python3 eval/run_eval.py --index_dir data/index --output_dir reports --config eval/config.yaml
python3 -m pytest tests/test_partial_topic_grounding.py -v
answer_status == "partial" 이고 verification_reasons 에
partial_topic_grounding 을 포함하는
case_results[*].id == "partial_topic_security_quantum" 를 찾아라. 동일한 eval 은
unanswerable slice 에서
abstention (의도된 abstention) 지표를 변하지 않게 유지해야 한다.