모두의포트폴리오
PE · FDE 직군을 위한 포트폴리오 밀키트
예전 판입니다 (1판)이 문서는 2026-08-01 에 새 판으로 개정됐습니다. 1주차 기능 축을 자료 정규화와 인용 대조 둘로 줄이고, 저장소 두 계층·워크플로우 라우팅·실시간 스트리밍을 걷어내 산출물을 실사 메모 파일을 내는 CLI로 바꿨습니다.최신 판 보기 →
경력링크알파코리아 · Forward Deployed Engineer (Agent & Workflows)

애널리스트 질문을 알맞은 분석 방식으로 보내고, 답의 문장마다 근거 문서를 인용해 돌려주는 금융 리서치 에이전트

링크알파코리아 공고에서 뽑은 세 가지 시그널.

링크알파는 기관투자자의 리서치를 돕는 AI 제품을 만들고, 에이전트의 프롬프트·도구·모델·라우팅을 코드 배포 없이 DB 설정으로 조립·버저닝하는 시스템으로 이미 구현해 두었습니다. 이 포트폴리오는 바로 그 ‘에이전트의 행동은 하드코딩이 아니라 버전 관리되는 설정/데이터’라는 관점을 그대로 재현합니다. 마켓 브리핑·DD 리포트·문서 인용·정형 데이터 질의(TTSQL)를 한 에이전트가 설정에 따라 라우팅하고, 금융 답변의 필수 요건인 출처 귀속을 규칙으로 검증하는 이 사슬은 링크알파의 워크플로우 구성과 출처 귀속 품질 기준이 있어야 의미가 있어, 다른 회사 데이터로는 그대로 만들 수 없습니다. 다만 이 포트폴리오가 입증하는 것은 설정 기반 에이전트 조립·라우팅·인용 grounding·회귀 평가라는 기술이며, buy/sell-side 리서치의 도메인 전문성은 주장하지 않습니다. 모르는 금융 깊이는 면접에서 모른다고 말하는 편이 낫습니다.

01

에이전트 행동 = 버전 관리되는 설정

프롬프트·도구·모델·라우팅을 코드 배포 없이 DB 설정으로 조립·버저닝하고 feature flag·조건 규칙으로 분기한다는 관점이 링크알파 제품의 골조입니다. 하드코딩이 아니라 설정/데이터로 사고하는 사람을 찾습니다.

코드 배포 없이DB 설정으로 조립·버저닝feature flag버전 관리되는 설정/데이터
02

코어(에이전트)와 엣지(워크플로우)의 경계 설계

한 고객에서 배운 것을 일반화해 코어 에이전트 행동으로 환원할 것과, 도메인 특화 워크플로우로 깎아낼 것을 구분합니다. 마켓 브리핑·DD 리포트·문서 인용·정형 데이터 질의(TTSQL)를 한 에이전트가 호출·조율하도록 라우팅합니다.

코어엣지일반화라우팅
03

분포 위에서 증명 + 출처 귀속

‘좋아 보인다’가 아니라 고정된 데이터 분포 위에서 변경 효과를 증명하는 것이 기준입니다. 금융 답변은 출처 귀속이 품질의 필수 요건이라 LLM-as-judge와 인용 grounding 검증을 함께 씁니다.

distributional shiftLLM-as-judgegrounding출처 귀속

포트폴리오 요약

기관투자자 리서치용 금융 에이전트입니다. 애널리스트가 던진 질문을 설정에 담긴 라우팅 규칙으로 마켓 브리핑·DD 리포트·문서 인용 검색·정형 데이터 질의(TTSQL) 중 알맞은 워크플로우로 보냅니다. 에이전트의 프롬프트·도구·모델·라우팅은 코드 배포 없이 버전 관리되는 설정(agent_configs)으로 조립되고, feature flag와 조건 규칙으로 고객사별로 분기합니다. 답변은 어닝콜·공시·broker note 청크를 인용해 만들고, 인용한 청크가 실제 원문에 존재하며 인용 문구가 원문 안에 있는지 규칙으로 검증해 근거 없는 주장은 ‘미근거’로 표시합니다. 부분 답은 SSE로 실시간 스트리밍하고, 한 요청을 비즈니스 계층(설정)과 런타임 계층(세션·트레이스) 두 저장소를 가로질러 끝까지 추적합니다. 근거는 채용공고 정렬이고, 데이터는 작성자가 만든 더미입니다.

고객 정의

링크알파 FDE. 고객사(헤지펀드·운용사)마다 리서치 방식이 달라 에이전트 동작을 매번 코드로 고쳐 배포하던 상황을 끝내고, 설정만 바꿔 고객별로 분기하며 그 변경이 답 품질을 실제로 올렸는지 배포 전에 고정 질문셋으로 확인하고 싶은 사람입니다.

고민 지점

에이전트 행동을 버전 관리되는 설정으로 분리하면, (a) 질문의 유형에 따라 알맞은 워크플로우로 보내는 라우팅을 설정만으로 결정론적으로 바꿀 수 있고, (b) 답의 각 문장이 인용한 청크가 원문에 존재하는지 규칙으로 검증해 근거 없는 주장을 걸러내며, (c) 설정 버전 A/B를 고정 질문셋에 다시 돌려 인용 정확도와 충실도가 실제로 올랐는지 배포 전에 확인할 수 있습니다.

주차별 일정

1주차여기까지로 완성

질문 라우팅 + 인용 답변 + 근거 검증이 도는 에이전트

질문을 설정 기반으로 알맞은 워크플로우로 라우팅하고, 근거 청크가 인용된 답을 SSE로 스트리밍하며, 인용이 원문과 맞는지 규칙으로 검증해 미근거 주장을 걸러내는 동작하는 에이전트입니다. 실 MCP 도구 계층과 세션 격리 런타임은 단일 프로세스·메타 API 가정으로 시작하고, 문서는 합성 코퍼스로 시작합니다.

  1. 1-2일 · 두 계층 저장소와 정규화 사전 만들기문서 코퍼스(doc_chunks, 런타임 검색 대상)와 버전 관리 설정(agent_configs, 비즈니스 계층)을 나눠 담고, ticker 별칭(‘삼성전자/005930/삼전’)을 한 종목으로 묶는 정규화 사전을 붙입니다.
  2. 3-4일 · 질문을 알맞은 워크플로우로 보내는 라우팅설정의 route 규칙으로 market_briefing·dd_report·doc_citation·ttsql 중 하나로 분기합니다(LangGraph 그래프). 규칙에 안 걸리는 질문은 억지로 답하지 않고 ‘라우팅 불가’로 알립니다.
  3. 5일 · 답의 각 문장에 근거 청크를 인용하고 규칙으로 검증인용한 청크가 실제 원문에 존재하고 인용 문구가 원문 안에 있는지 확인해, 없는 근거는 답에 넣지 않고 unresolved(미근거)로 표시해 환각을 차단합니다.
  4. 6일 · 부분 답을 SSE로 실시간 스트리밍백그라운드 검색은 계속 돌리면서 먼저 나온 조각부터 응답해 레이턴시를 줄입니다(FastAPI + Redis 버퍼).
  5. 7일 · 7일치 시연 영상 + 직접 만든 정답 질문셋과 비교한 기준선여기까지가 1주차 완성품입니다.
2주차선택 성장

설정 버전 A/B 회귀 평가 + 배포 전 게이트

고정 질문셋 위에서 설정 버전 A/B를 회귀 평가해(결정론적 인용 정확도 + LLM-as-judge 충실도) 새 설정이 답 품질을 실제로 올렸는지 배포 전에 확인하는 draft→시뮬레이션→배포 게이트와, 한 요청을 비즈니스·런타임 두 계층 저장소를 가로질러 추적하는 트레이스 뷰입니다.

  1. 8-9일 · 고정 질문셋(canonical) 위 설정 버전 A/B 회귀 평가결정론적 인용 정확도와 LLM-as-judge 충실도(답이 근거에만 기대는 정도)를 버전별로 재서, 변경이 고정 분포 위에서 답을 실제로 개선했는지 봅니다.
  2. 10-11일 · draft→시뮬레이션→배포 게이트새 설정을 시뮬레이션으로 먼저 돌려 인용 정확도·충실도가 기준선 아래로 떨어지지 않을 때만 배포로 승격합니다.
  3. 12-13일 · 두 계층을 가로지르는 요청 트레이스 뷰 + 실패 케이스 태깅한 번의 질문을 설정(비즈니스) → 세션·검색·생성(런타임)까지 끝까지 따라가며, 라우팅 오분류·미근거 답을 실패 케이스로 태깅합니다.
3주차선택 성장

고객사별 설정 분기 + 배포 자동화

feature flag·조건 규칙으로 고객사별 설정을 분기하고, 회귀 평가를 CI 게이트로 걸어 통과분만 배포하며, 에이전트 런타임을 K8s(Helm/ArgoCD)로 올려 세션 격리·오토스케일 가정과 연결하는 단계입니다.

  1. 15-16일 · 고객·맥락별 설정 분기feature flag와 조건 규칙으로 같은 질문도 고객사별로 다른 워크플로우·도구·프롬프트를 쓰도록 설정만으로 분기합니다.
  2. 17-18일 · 회귀 평가를 CI 게이트로설정 변경 PR마다 회귀 평가를 자동 실행하고, 통과분만 배포되게 파이프라인에 겁니다.
  3. 19-20일 · 에이전트 런타임을 K8s로 배포Helm/ArgoCD(GitOps)로 세션 격리 런타임을 올리고, 오토스케일·warm pool 은 가정으로 연결(Terraform 으로 인프라 정의)합니다.
  4. 21일 · 3주 통합 시연 영상 + 실도입 계획합성 코퍼스는 실 어닝콜·공시로, 단일 프로세스는 세션 격리 런타임으로, 시뮬 judge 는 실 애널리스트 검수로, MCP 도구는 실 연동으로 교체하는 도입 단계 계획을 분리해 정리합니다.

데모 · 측정 지표

01

고정 질문 20개를 넣으면 각 질문이 설정 규칙대로 알맞은 워크플로우로 라우팅되고, 답의 문장마다 근거 청크가 인용된 결과가 SSE로 스트리밍되는 걸 시연합니다.

02

일부러 만든 결측·별칭·orphan·상충 수치 30종을 섞어도 출력 형식(JSON Schema)이 한 번도 안 깨지고, 근거가 없는 주장은 unresolved(미근거)로 빠져 환각이 답에 섞이지 않습니다.

03

내가 만든 정답 인용 40건을 규칙으로 검증해(인용 청크가 원문에 존재하고 인용 문구가 원문 안에 있는지) 36건(90%)이 일치합니다. 단, 정답·문서·질문을 모두 작성자가 만든 합성 기준이라 ‘성능 검증’이 아니라 ‘데모 조건 동작’입니다.

04

설정 버전을 바꿔 고정 질문셋에 다시 돌리면, 결정론적 인용 정확도와 LLM-as-judge 충실도 점수(답이 근거에만 기대는 정도, 0~1)가 버전별로 어떻게 달라지는지 배포 전에 비교해 보여줍니다.

정직 경계선

진짜 성능 검증은 여기서 멈춥니다

위 수치는 전부 작성자가 만든 합성 문서·질문·정답 위 자가채점이며, 실제 인용 정확도·충실도는 링크알파의 실 금융 문서와 실 애널리스트 검수가 있어야 측정 가능합니다. 이 도구는 그 검증을 바로 돌릴 수 있는 상태까지 만듭니다.

미리 알아둬야 할 것들

이건 고객·실데이터 없이 혼자 만든 포트폴리오입니다. 모든 수치는 합성 데이터 자가채점이라 ‘성능 증명’이 아닙니다. 면접에서 ‘데모 조건 동작 + 실검증 준비 상태’로 솔직히 설명하는 편이 오히려 신뢰를 줍니다.

정답을 내가 만들었으니 잘 맞는 게 당연합니다. 그래서 일부러 헷갈리는 결측(ticker·날짜 null)·별칭 표기·orphan 청크·상충 수치를 더미에 섞어, 쉬운 문제만 푸는 게 아님을 보여줍니다.

금융 답변은 출처 귀속이 품질의 필수 요건이지만, 인용 청크가 원문에 존재한다고 해서 그 근거가 질문에 ‘맞는’ 근거인지(관련성)까지 규칙으로 다 잡지는 못합니다. 상충하는 수치(가이던스 vs 컨센서스) 중 무엇이 맞는지는 도메인 판단이 필요하므로, 인용 존재와 해석 정확은 분리해서 봅니다.

이 포트폴리오가 입증하는 것은 설정 기반 에이전트 조립·라우팅·결정론적 인용 검증·회귀 평가라는 기술이며, buy/sell-side 리서치의 도메인 전문성은 주장하지 않습니다. 모르는 금융 깊이는 면접에서 모른다고 말하는 편이 낫습니다.

실제 도입 시 달라지는 것을 분리해 둡니다. 합성 코퍼스는 실 어닝콜·공시 분포와 다르므로 검색·grounding 임계값을 다시 맞춰야 하고, 단일 프로세스 런타임은 세션 격리 K8s 런타임으로, 시뮬 judge 는 실 애널리스트 검수로, MCP 도구는 실 연동으로 교체해야 합니다(도입 단계 과제로 분리).

더미 데이터

① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.

agent_configsTABLE· 12 rows

에이전트 행동을 코드 배포 없이 담는 버전 관리 설정. 라우팅 규칙·프롬프트 블록·도구 정책·모델·feature flag·고객 조건을 한 행에 담습니다. 라우팅 결정이 이 설정으로 결정론적으로 정해지는지 검증하는 입력입니다. 운영 현실대로 결측·orphan·비활성·버전 충돌을 일부러 섞어, 쉬운 데이터만 푸는 게 아님을 보여줍니다.

② agent_configs.seed.json 다운로드
① 인라인 샘플 (프리뷰 5 / 12)
config_idversioncustomer_idroute_keymodelprompt_block_idtool_policyenabledcondition
cfg_doc_citation3—doc_citationclaude-sonnet-4.5pb_cite_v3["doc_search","chunk_fetch"]true—
cfg_doc_citation2—doc_citationclaude-sonnet-4.5pb_cite_v2["doc_search"]false—
cfg_market_briefing1—market_briefingclaude-sonnet-4.5pb_brief_v1["doc_search","news_fetch"]true—
cfg_dd_report2cust_hedge_add_reportclaude-sonnet-4.5pb_dd_v2["doc_search","chunk_fetch","ttsql_run"]trueasset_class == 'equity'
cfg_ttsql1—ttsqlclaude-haiku-4.5pb_ttsql_v1["ttsql_run"]true—
③ 증강 프롬프트 (본인 LLM 에 붙여서 N 배 늘리기)
아래 JSON Schema 에 맞는 agent_configs 40행 JSON 배열로 생성. 라우팅·프롬프트·도구·모델·feature flag·고객 조건을 담은 버전 관리 설정.

{"type":"object","properties":{"config_id":{"type":"string"},"version":{"type":"integer"},"customer_id":{"type":["string","null"]},"route_key":{"type":"string","enum":["market_briefing","dd_report","doc_citation","ttsql"]},"model":{"type":"string"},"prompt_block_id":{"type":["string","null"]},"tool_policy":{"type":"array","items":{"type":"string"}},"enabled":{"type":"boolean"},"condition":{"type":["string","null"]}},"required":["config_id","version","route_key","model","tool_policy","enabled"]}

쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라(쉬운 문제만 푸는 함정 회피): (1) 같은 config_id 에 version 여러 개(하나만 enabled=true 여야 라우팅이 정해지는 버전 충돌 후보), (2) customer_id=null 인 전역 기본 설정과 특정 고객 설정 혼재, (3) prompt_block_id=null(프롬프트 블록 미지정) 1~2건, (4) prompt_block_id 가 프롬프트 마스터에 없는 orphan 1건, (5) enabled=false(feature flag 꺼짐)와 tool_policy=[](도구 없음) 경계값 각 1건, (6) condition 이 채워진 조건 분기 행 몇 개. JSON 배열만.
schema (3 층의 단일 출처)
{"type":"object","properties":{"config_id":{"type":"string"},"version":{"type":"integer"},"customer_id":{"type":["string","null"]},"route_key":{"type":"string","enum":["market_briefing","dd_report","doc_citation","ttsql"]},"model":{"type":"string"},"prompt_block_id":{"type":["string","null"]},"tool_policy":{"type":"array","items":{"type":"string"}},"enabled":{"type":"boolean"},"condition":{"type":["string","null"]}},"required":["config_id","version","route_key","model","tool_policy","enabled"]}
doc_chunksTABLE· 12 rows

에이전트가 검색·인용하는 금융 문서 코퍼스(청크 단위). 어닝콜·공시·broker note·뉴스. 인용 grounding 검증(인용 청크가 존재하고 문구가 원문 안에 있는지)의 대상입니다. 결측·별칭 표기·orphan doc·상충 수치·초단문 경계값을 일부러 섞어, 인용 검증이 하드 케이스에서도 안 깨지는지 보여줍니다.

② doc_chunks.seed.json 다운로드
① 인라인 샘플 (프리뷰 5 / 12)
chunk_iddoc_iddoc_typetickerticker_rawpublished_atsectiontext
ch_0001doc_smsn_q1_callearnings_call005930삼성전자2026-04-24guidance경영진은 2분기 DS 부문 영업이익 가이던스를 전분기 대비 15% 상…
ch_0002doc_smsn_brokerbroker_note005930Samsung Electronics2026-04-25estimate컨센서스는 2분기 DS 영업이익을 전분기 대비 9% 증가로 보고 있어…
ch_0003doc_smsn_q1_callearnings_call005930삼전2026-04-24capex연간 설비투자 계획은 전년과 유사한 수준을 유지한다고 언급했습니다.
ch_0004doc_hynix_filingfiling000660SK하이닉스2026-04-22risk_factors공시는 메모리 가격 변동성과 고객사 재고 조정을 주요 위험으로 적시했…
ch_0005doc_hynix_filingfiling—하이닉스—unknown출처 페이지 메타데이터가 유실되어 발행일과 종목 코드가 확인되지 않는…
③ 증강 프롬프트 (본인 LLM 에 붙여서 N 배 늘리기)
아래 JSON Schema 에 맞는 금융 문서 청크 코퍼스 40행 JSON 배열로 생성. 어닝콜·공시·broker note·뉴스 혼합, 국내 종목 위주.

{"type":"object","properties":{"chunk_id":{"type":"string"},"doc_id":{"type":"string"},"doc_type":{"type":"string","enum":["earnings_call","filing","broker_note","news"]},"ticker":{"type":["string","null"]},"ticker_raw":{"type":"string"},"published_at":{"type":["string","null"]},"section":{"type":"string"},"text":{"type":"string"}},"required":["chunk_id","doc_id","doc_type","ticker_raw","text"]}

쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라(쉬운 문제만 푸는 함정 회피): (1) ticker_raw 별칭 표기(같은 종목이 '삼성전자/Samsung Electronics/삼전' 처럼 들어와 ticker 로 정규화돼야 함) 3~4종, (2) ticker=null + published_at=null(출처 메타 유실) 1~2건, (3) 문서 마스터에 없는 orphan doc_id 1건(인용 추적 끊김), (4) 같은 종목·같은 지표를 다른 수치로 말하는 상충 청크 2건(회사 가이던스 vs 컨센서스 갭), (5) 초단문(text 10자 내외) 경계값 1건. text 는 한국어 금융 문장, JSON 배열만.
schema (3 층의 단일 출처)
{"type":"object","properties":{"chunk_id":{"type":"string"},"doc_id":{"type":"string"},"doc_type":{"type":"string","enum":["earnings_call","filing","broker_note","news"]},"ticker":{"type":["string","null"]},"ticker_raw":{"type":"string"},"published_at":{"type":["string","null"]},"section":{"type":"string"},"text":{"type":"string"}},"required":["chunk_id","doc_id","doc_type","ticker_raw","text"]}

기술 상세 · 아키텍처·운영·출력 스키마

기술 스택 매핑
Python에이전트 런타임·설정 로더(agent_configs → 프롬프트/도구/모델/라우팅 조립), 결정론적 인용 검증기, 회귀 평가 하니스
TypeScriptNext.js 콘솔(설정 편집기 + SSE 스트림 뷰어 + 회귀 평가 대시보드) 프런트 로직과 런타임 스키마 검증
LangGraph라우팅 → 검색 → 생성 → 근거 검증 노드를 잇는 워크플로우 그래프 + stateful 세션 상태
Claude Code SDK에이전트 오케스트레이션 골격(도구 호출·부분 결과 스트리밍) 구현 수단
FastAPI에이전트 런타임을 노출하는 백엔드 + SSE 스트리밍 엔드포인트
PostgreSQL두 계층 저장소로 나눕니다. 비즈니스 계층(agent_configs 버전 관리) vs 런타임 계층(sessions·traces)
Redis런타임 세션 상태 + 백그라운드 태스크 부분 결과 스트림 버퍼
MCP에이전트가 호출하는 도구 계층(문서 검색·TTSQL 실행) 인터페이스
SSE백그라운드 검색을 돌리면서 먼저 나온 조각부터 실시간 스트리밍해 레이턴시 최소화
Kubernetes3주차에 세션 격리 에이전트 런타임을 Helm/ArgoCD(GitOps)로 배포하고 오토스케일 가정을 연결합니다
Terraform3주차에 런타임 인프라를 코드로 정의(IaC)해 회귀 평가 통과분과 함께 배포합니다
LLM 선택
model
Claude Sonnet 4.5 (리서치 답변 생성·요약) + Claude Haiku 4.5 (질문 라우팅 분류 + LLM-as-judge 충실도 평가)
prompt
설정(agent_configs)에서 조립한 프롬프트 블록 + 도구 정책 주입, 출력 JSON Schema 강제, 인용 청크 id 강제(근거 없는 문장은 unresolved 로 빼도록 지시), few-shot 3 예시, hedge 토큰 가드
rationale
답변 생성은 인용 충실도가 핵심이라 Sonnet, 라우팅 분류와 judge 는 비용·속도로 Haiku. 문서 코퍼스를 인용해야 하므로 RAG 가 필요합니다. 청크 검색 후 인용 grounding 을 규칙으로 검증하고, 라우팅·정답 판정은 결정론(설정 규칙·청크 존재·문구 일치)으로, LLM 은 문구 변환·요약·judge 보조로만 씁니다.
결정 게이트
1주차 끝질문이 설정 규칙대로 알맞은 워크플로우로 라우팅되고, 답의 문장마다 인용된 청크가 원문에 존재하며 미근거 주장이 걸러지는가. 여기까지가 그 자체로 제출 가능한 완성품입니다.

✓ pass → 그대로 멈춰도 완결입니다. 더 키우고 싶으면 2주차(설정 버전 A/B 회귀 평가와 배포 전 게이트)로 진행합니다.

2주차 끝고정 질문셋 위에서 새 설정이 인용 정확도·충실도를 실제로 올렸는지 배포 전에 확인되고, 한 요청을 두 계층 저장소를 가로질러 추적할 수 있는가.

✓ pass → 여기서 멈춰도 완결입니다. 더 키우면 3주차(고객사별 설정 분기와 CI 배포 게이트, K8s 런타임 배포)로 진행합니다.

재사용 레이어 · 코어 70% / 도메인 30%
core (재사용 가능)
  • 버전 관리 설정 → 프롬프트/도구/모델/라우팅 조립기(코드 배포 없이 설정만 교체)
  • 결정론적 인용 grounding 검증기(인용 청크 존재 + 원문 문구 일치, 미근거는 unresolved 로 격리)
  • 고정 질문셋 위 설정 A/B 회귀 평가 하니스(인용 정확도 + LLM-as-judge 충실도)
  • SSE 부분 결과 스트리밍 런타임 + 백그라운드 태스크 버퍼
  • draft→시뮬레이션→배포 게이트 + CI 회귀 평가 훅
  • 비즈니스/런타임 두 계층 저장소를 가로지르는 요청 트레이스 뷰
domain (교체 대상)
  • 금융 문서 코퍼스(어닝콜·공시·broker note) + ticker 별칭 정규화 사전
  • route_key enum(market_briefing / dd_report / doc_citation / ttsql)을 도메인별로 워크플로우 종류에 맞춰 갈아끼웁니다
  • 금융 judge 루브릭(출처 귀속 필수 요건) + 고객사별 조건 규칙
출력 스키마 · grounded_research_answer

1) 질문을 어느 워크플로우로 보냈는지(route)와 어떤 설정 버전을 썼는지, 2) 답을 문장(claim) 단위로 쪼개 각 문장이 인용한 청크와 그 인용이 원문에 존재하는지(grounded), 3) 근거를 못 찾은 문장은 unresolved 로 격리, 4) 모든 문장이 근거를 가졌는지(source_attribution_complete)와 judge 충실도. 라우팅·인용·검증 한 사이클을 한 스키마에 담습니다.

schema (JSON Schema)
{"type":"object","properties":{"question":{"type":"string"},"route":{"type":"string","enum":["market_briefing","dd_report","doc_citation","ttsql"]},"config_version":{"type":"integer"},"claims":{"type":"array","minItems":1,"items":{"type":"object","properties":{"claim":{"type":"string"},"citations":{"type":"array","items":{"type":"string"}},"grounded":{"type":"boolean"}},"required":["claim","citations","grounded"]}},"unresolved":{"type":"array","items":{"type":"string"}},"source_attribution_complete":{"type":"boolean"},"judge":{"type":"object","properties":{"faithfulness":{"type":"number"},"note":{"type":["string","null"]}}},"hedge":{"type":["string","null"]}},"required":["question","route","config_version","claims","source_attribution_complete"]}
모범 출력 (3)
[
  {
    "input_ref": "질문: ‘삼성전자 2분기 DS 가이던스와 컨센서스 갭 정리해줘’ (설정 cfg_doc_citation v3, enabled)",
    "output": {
      "question": "삼성전자 2분기 DS 가이던스와 컨센서스 갭 정리해줘",
      "route": "doc_citation",
      "config_version": 3,
      "claims": [
        {
          "claim": "회사는 2분기 DS 영업이익 가이던스를 전분기 대비 15% 상향한다고 밝혔습니다.",
          "citations": [
            "ch_0001"
          ],
          "grounded": true
        },
        {
          "claim": "컨센서스는 같은 기간 9% 증가로 보고 있어 회사 가이던스와 6%p 갭이 있습니다.",
          "citations": [
            "ch_0002"
          ],
          "grounded": true
        }
      ],
      "unresolved": [
        "동일 문서에 5% 상향이라는 상충 조각(ch_0010)이 있어 두 수치 중 무엇이 최종인지는 도메인 확인이 필요합니다."
      ],
      "source_attribution_complete": true,
      "judge": {
        "faithfulness": 0.92,
        "note": "두 인용 모두 원문 문구 일치. 상충 수치를 답에 단정하지 않고 unresolved 로 뺀 점 양호."
      },
      "hedge": "가이던스와 컨센서스는 서로 다른 문서 출처이며, 동일 지표에 상충 조각이 존재합니다."
    }
  },
  {
    "input_ref": "질문: ‘하이닉스 최근 위험요인 근거 문서로 알려줘’ (설정 cfg_doc_citation v3). 근거 청크 일부가 출처 메타 유실",
    "output": {
      "question": "하이닉스 최근 위험요인 근거 문서로 알려줘",
      "route": "doc_citation",
      "config_version": 3,
      "claims": [
        {
          "claim": "공시는 메모리 가격 변동성과 고객사 재고 조정을 주요 위험으로 적시했습니다.",
          "citations": [
            "ch_0004"
          ],
          "grounded": true
        }
      ],
      "unresolved": [
        "관련 조각 ch_0005 는 발행일과 종목 코드가 유실(null)돼 출처를 특정할 수 없어 답에 포함하지 않았습니다.",
        "orphan 청크 ch_0008 은 문서 마스터에 없어 원문 추적이 끊겨 인용에서 제외했습니다."
      ],
      "source_attribution_complete": true,
      "judge": {
        "faithfulness": 0.88,
        "note": "출처 불명 조각을 근거로 쓰지 않고 격리한 점 적절."
      },
      "hedge": "출처 메타가 유실된 조각은 인용에서 제외했으므로 위험요인 목록이 전부가 아닐 수 있습니다."
    }
  },
  {
    "input_ref": "질문: ‘네이버 클라우드 흑자 전환 시점 매출로 계산해줘’. 문서엔 시점만 있고 정형 매출 데이터가 없어 라우팅/근거 경계",
    "output": {
      "question": "네이버 클라우드 흑자 전환 시점 매출로 계산해줘",
      "route": "doc_citation",
      "config_version": 3,
      "claims": [
        {
          "claim": "경영진은 질의응답에서 클라우드 부문 흑자 전환 시점을 하반기로 제시했습니다.",
          "citations": [
            "ch_0007"
          ],
          "grounded": true
        }
      ],
      "unresolved": [
        "‘매출로 계산’ 부분은 코퍼스에 정형 매출 수치가 없어 근거를 찾지 못했습니다. 정형 질의(ttsql)로 재라우팅하거나 데이터 연결이 필요합니다."
      ],
      "source_attribution_complete": true,
      "judge": {
        "faithfulness": 0.9,
        "note": "근거 없는 수치를 지어내지 않고 미근거로 명시. 재라우팅 제안 적절."
      },
      "hedge": "질문의 계산 요구는 현재 코퍼스만으로 답할 수 없어 데이터 경계를 벗어납니다."
    }
  }
]
검증 노트 (자동 vs 수동)

[자동 검증: 규칙·결정론으로 객관 채점] 1. JSON Schema parse 통과(출력 형식이 안 깨지는지). 2. route 가 해당 질문에 대해 agent_configs 의 enabled=true 인 설정 라우팅 규칙과 일치하는지(라우팅은 설정으로 결정론적). 3. 각 claim 의 citations 청크가 doc_chunks 에 실제로 존재하고, claim 문구의 핵심 수치·표현이 그 청크 text 안에 있는지(인용 grounding = 청크 존재 + 문구 일치). 4. source_attribution_complete = 모든 claim 이 grounded=true 인 인용을 하나 이상 가졌는지. 근거 못 찾은 문장은 claim 이 아니라 unresolved 로 빠졌는지. 5. orphan doc_id·출처 유실(null) 청크가 인용에 쓰이지 않았는지. [수동 검증: 사람·LLM 보조] 1. LLM-as-judge 충실도(faithfulness, 답이 인용 근거에만 기대는 정도 0~1)를 고정 질문셋에 재서 bootstrap 신뢰구간 하한을 봅니다. 2. 인용이 ‘존재’할 뿐 아니라 질문에 ‘맞는’ 근거인지(관련성)와 상충 수치 판단은 실 애널리스트 spot check 5%로 확인합니다.