0011: LLM 답변 합성을 분석 변형으로 추가
0011: LLM 답변 합성을 분석 변형으로 추가
(원래 #142 에서 ADR 0007 로 머지됐으나, 먼저 머지된 governance ADR 0007-issue-linked-branch-naming.md 과 파일명 충돌로 0011 로 번호 재지정.)
- Status: proposed
- Date: 2026-05-11
- Related: extends ADR 0001; preserves ADR 0003; reuses backend pattern from ADR 0006; complemented by ADR 0024 (API 프리셋 기본값은
agentic_full_llm로 전환; 백엔드 기본은stub유지); 구현 워크스루는docs/agentic/answer-policy.md - Deciders: hskim
- Update (PR-I, issue #405, 2026-05-12): API 표면 기본 프리셋이
agentic_full→agentic_full_llm로 전환 (ADR 0024). 본 ADR 이 정한 백엔드 추가성 계약은 불변 —BIDMATE_SYNTHESIS_BACKEND=stub가 여전히 기본이라 공개 API 호출도 결정론적 stub 렌더러에서 LLM 합성 프리셋을 실행. CLI (naive_baseline, ADR 0001) 와 함수 레벨 (agentic_full) 기본은 그대로.
TL;DR
- 파이프라인은 end-to-end 추출형 —
generate_answer가 검색된 문장을 그대로 이어붙임. - LLM 합성을 추가 분석 변형 으로 도입: 추출형 기준선·ADR 0003 계약 보존,
summary/answer_text만 재작성. - 백엔드 dispatch (
stub기본 /anthropic/openai_compatible) 로 공개 CI 결정론 유지 + 실데이터·라이브 데모만 라이브 LLM.
배경
파이프라인은 end-to-end 추출형. rag_core.py:L2242 의 generate_answer 가 검색된 문장으로 claims 를 만들고 chunk-id 접미사를 붙여 render_answer_text (L2494) 가 answer_text 를 연결. 공개 데모를 결정론·무료·환각 한계로 묶기 위한 설계로, 인용된 모든 문자열은 근거 chunk 의 verbatim.
근거 연결 엄밀성 면에서 옳은 트레이드오프지만 시스템 reader 에게 보이는 세 가지 갭이 남는다.
- 읽기 흐름이 기계적.
render_answer_text가 claim 문자열에[chunk_id]를 붙여 잇기만 함 — 사람이 쓴 답변처럼 읽히지 않고, 비교 답변은 분석이 아닌 병렬 bullet 리스트로 보임. - 파이프라인에 LLM 표면 자체가 없음. 프롬프트 엔지니어링, 구조화 출력, tool use, prompt caching — 2026 년 AI 엔지니어 표면의 기본기 — 가 들어설 자리가 없음. 검색 시스템으로는 평가되지만 LLM 애플리케이션으로는 평가 불가.
- 비용 / 지연 / 모델 트레이드오프가 eval 매트릭스에 부재. ADR 0006 이 실데이터 eval 표면에 평가자 로서 LLM 을 도입했으나, 시스템 자체에 대한 비교 표면이 없음.
추출형 기본을 뒤집으면 ADR 0001 (단순 경로 기준선 보존) 과 충돌하고 ADR 0003 인용 계약이 위험. 올바른 수는 ADR 0001 이 naive baseline 을 지킨 방식과 같음 — 추출형 경로를 일급 기준선으로 두고 LLM 합성을 그 옆에 추가.
결정
LLM 답변 합성은 추가 분석 변형 경로로 허용 (대체 아님). 구체적으로:
rag_core.py에 새prompt_profile값llm_synthesis를minimal_grounded_extractive(naive),structured_grounded_claims(agentic_full 추출형) 와 함께 도입.- 새
PIPELINE_PRESETS항목agentic_full_llm이prompt_profile=llm_synthesis로 두고 나머지 검색·검증기 설정은agentic_full상속. eval/config.yaml에agentic_full(추출형) +agentic_full_llm(LLM) 둘 다 분석 변형 run 으로 등록 — 모든 eval 호출이 side-by-side 비교를 생성.agentic_full는 회귀 가드 유지,agentic_full_llm가 새 컬럼.naive_baseline은 불변. ADR 0001 불변식 유지.
계약 보존 (ADR 0003)
generate_answer 는 계속 schema_version: 2 JSON 반환. LLM 합성 경로는:
build_claims를 재사용 해 claim 리스트 생산. claim 과 인용은 여전히 추출형 —chunk_id참조는 동일한evidence로 resolve.summary와answer_text만 재작성. LLM 에(query, analysis, claims, evidence_chunks)를 주고 사람이 읽기 좋은 summary + 장형answer_text생산. 둘 다 ADR 0003 의 검증 가능 계약 밖 (“answer_text는 … 검증 가능 계약의 일부 아님; tooling 은 여기에 의존 금지”).- 새 인용을 도입 불가. LLM 이
evidence에 없는 chunk id 를 emit 하면 합성 reject + 추출형render_answer_text로 폴백. 이 가드는 soft check 아닌 hard postcondition. status,claims,insufficiency,status_reason변경 불가. 합성 이전 결정론 검증기가 계산한 입력만 받음.
status != supported 면 합성 전체 skip + 오늘과 동일한 추출형 경로. 보류 메시지는 결정론 유지.
백엔드 pluggability
ADR 0006 패턴 재사용: BIDMATE_SYNTHESIS_BACKEND:
stub(기본) — 결정론 fixture; claim 을 템플릿 단락으로 연결. 네트워크 없음.make smoke,pr-eval.yml, 테스트에서 사용.anthropic— Claude API (Sonnet 4.6 기본,BIDMATE_SYNTHESIS_MODEL로 Haiku 4.5 opt-in).ANTHROPIC_API_KEY필요. 시스템 프롬프트 + few-shot 예시 prompt caching 사용 (실 eval run 전반 ≥ 80% 토큰 감소). tool use 로 출력 형태{summary: str, answer_text: str, used_chunk_ids: list[str]}강제.openai_compatible— 일반 OpenAI 호환 엔드포인트; 동일 형태·가드. vLLM / llama.cpp / Solar / KURE-finetuned 모델을 파이프라인 손 안 대고 한국 스택 스토리에 스왑 가능.
주기
- 공개 fixture smoke CI:
BIDMATE_SYNTHESIS_BACKEND=stub. Eval 델타 job 은naive_baselinevsagentic_full(둘 다 결정론) 비교 유지 +agentic_full_llm를 stub 백엔드로 추가 컬럼 보고. stub 은 plumbing 운동 + 계약 lock 용도 — 실 LLM 품질 주장 아님. - 실데이터 eval:
BIDMATE_SYNTHESIS_BACKEND=anthropic. LLM 컬럼 집계 메트릭은 ADR 0005 commit 경계 넘김; raw 프롬프트·raw 응답은 로컬. 토큰 수·쿼리당 비용은 집계되어 commit 가능. - 라이브 데모:
anthropic백엔드, rate-limit, prompt-cached.
결과
Wins
- 시스템이 LLM 표면 (프롬프트 엔지니어링, 구조화 출력, tool use, prompt cache, streaming) 획득 + ADR 0003 위험 없음. “no new chunk_ids” 가드가 인용 계약을 기계적으로 보존.
- Eval 매트릭스 1 컬럼 추가.
agentic_full(추출형·결정론) 와agentic_full_llm(LLM·stub or 라이브) 가 side-by-side → LLM 경로가 ADR 0001 하에서 agentic 파이프라인이 그랬듯 자기 슬롯을 earn 해야 함. - 지연·비용 frontier 가 가시화: 추출형 ~ms, stub-LLM 무시할 오버헤드, anthropic-LLM 토큰 + ms. 캐싱·모델 선택에 대한 향후 ADR 이 측정 가능한 기준선 보유.
- ADR 0006 백엔드 패턴 재사용 → 코드베이스에 “LLM 추가 방법” 의 일관 관용구 1개.
Costs
- 답변 렌더링 경로 3개 유지: 추출형, stub-LLM, 라이브-LLM. 공유 입력 계약 (
(answer_dict, evidence)→answer_text) 으로 완화. 회귀 테스트 1개가 셋 다 운동. - 라이브-eval run 당 토큰 소비. 실데이터 수동 주기 (~ 100 케이스 × cached prompt) + 공개 CI
stub기본으로 bound. 비용은reports/real100/aggregate.json에 노출. - 사용자가 이해할 환경 변수 family 1개 추가. 기본이
stub(오프라인·무키) 으로 완화.
Constraints (불변)
- ADR 0001:
naive_baseline가pipeline_cli_choices()유지 + CLI 기본. - ADR 0003:
schema_version: 2,status값,claims[].citations,evidence[]불변.schema_versionbump 안 함. - ADR 0005: 실데이터 케이스별 LLM 출력은 로컬. 집계 (평균 비용·지연·
citation_precision델타) 만 commit.
추가 opt-in 패턴 (일반화)
ADR 0011 이 후속 ADR 0015, 0017, 0027 이 verbatim 재사용한 반복 패턴 확립. 해당 ADR 들은 여기 Superseded 로 통합; 결정 자체는 불변. 패턴:
- 기본은 결정론·무료.
stub(또는regex) 백엔드가 모든 CI 호출에서 실행 — 네트워크·비용·재현성 없음. -
단일 env-var 로 opt-in. BIDMATE_<FEATURE>_BACKEND가stub(기본)anthropicopenai_compatibledispatch. 미상·실패 백엔드는 조용히 stub 로 degrade. - 상류 계약 변경 금지. 기능은 진단 또는 추가 분석 변형 row 표면에만 기록 —
answer.status,claims,citations,naive_baseline메트릭은 수정 불가. eval/config.yaml에 새 분석 변형 row 1개. 기능은 코드 경로뿐 아니라 컬럼으로도 측정 가능.- stub-matches-baseline 불변식이 계약 테스트.
test_*_baseline_invariant.py가 stub-백엔드 출력과 결정론 기준선의 byte-equal 검증.
통합된 instance:
| ADR | 기능 | Env-var | Stub 불변식 |
|---|---|---|---|
| 0015 | 비용 telemetry (토큰, USD 추정) | n/a — 진단 전용 | SYNTHESIS_SCHEMA_VERSION bump; 미상 모델 → None |
| 0017 | LLM 메타데이터 추출 | BIDMATE_METADATA_BACKEND |
stub 가 regex 위임; byte-equal |
| 0027 | LoRA 임베딩 어댑터 | BIDMATE_EMBEDDING_LORA_ADAPTER |
unset = pre-#434 byte-identical; lazy PEFT import |
검토한 대안
- 추출형 경로를 LLM 합성으로 전면 대체. Reject: ADR 0001 보존 논증과 충돌 + LLM 인용 드리프트에 대한 회귀 가드 제거. 결정론 CI 표면 상실.
- 명명 프리셋 없이 CLI 플래그 뒤에만 LLM 합성 추가. Reject: ADR 0001 의 “조용한 경로는 썩는다” 가 적용. 출시 가치가 있다면 모든 eval 호출에서 돌아가는 명명 프리셋이 되어
agentic_full대비 델타가 항상 가시. - LLM 이
claims와citations도 재작성. 현재 reject: 구성상 ADR 0003 위반 (인용이evidence로 resolve 보장 안 됨). 더 엄격한 인용 검증 pass (emit 한 모든 chunk_id 가 resolve 필요; 모든 claim 이 ≥ 1 chunk 인용 필요) 와schema_version: 3동반 후속 ADR 로 revisit. - tool use / 구조화 출력 없는 free-text LLM 엔드포인트. Reject:
used_chunk_ids ⊆ evidence.chunk_idspostcondition 이 parse 에 brittle. tool use 가 가드를 단순 집합 멤버십 체크로 만듦.