MCP 서버가 소스를 반환할 때, 답변은 여전히 출처를 인용하는가?
요약
본 글은 검색 서버가 제공한 출처(citation) 정보가 AI 에이전트의 최종 답변 과정에서 어떻게 유지되는지 벤치마크를 통해 분석합니다. 특히, RAG 지식 기반을 MCP(Model Context Protocol)와 같은 외부 도구로 활용할 때 인용 정보가 손실될 수 있다는 가설을 검증했습니다.
핵심 포인트
- RAG 출처는 에이전트의 반복적인 작업 과정에서 사라질 것이라는 초기 가설은 틀렸습니다.
- MCP 경계에서의 벤치마크를 통해, 답변 생성 시 인용 정보가 상당 부분 유지됨을 확인했습니다.
- 정확한 출처 표기는 문서 지원 어시스턴트의 신뢰성과 검증 가능성을 높이는 핵심 요소입니다.
검색 서버가 결과를 AI 에이전트에게 넘긴 후, 인용(citation)에 어떤 일이 발생하는지에 대한 벤치마크.
시리즈 'MCP 경계에서의 인용 (Citations at the MCP Boundary)'의 1/6편. 이 부분이 시리즈의 시작점입니다. 이후 부분들은 각 발견 사항을 더 깊이 파고들며, 전체 목록은 본문 마지막에 있습니다.
매우 빠르게 일반화되고 있는 상황이 있습니다.
한 회사가 자체 문서를 검색 도구로 만듭니다. 그리고 그 도구를 MCP (Model Context Protocol)를 통해 외부에 노출하여, 어떤 에이전트든 사용할 수 있게 합니다: Claude, ChatGPT, 누군가의 IDE에 포함된 코딩 어시스턴트, LangGraph나 OpenAI Agents SDK를 사용하여 고객이 만든 에이전트 등. 이 도구는 제 역할을 잘 수행합니다. 모든 질의에 대해 가장 관련성 높은 문서 조각(chunk)들을 반환하며, 각 조각에는 출처 URL이 함께 제공됩니다:
{
"source_url": "https://fastapi.tiangolo.com/tutorial/body-updates/",
"content": "# Body - Updates ..."
...
그 후 에이전트가 작업을 이어받습니다. 에이전트는 조각들을 읽고, 무엇이 중요한지 판단하여 최종 답변을 작성합니다.
검색 서버는 이 최종 답변을 볼 수 없습니다. 따라서 제공자는 올바른 출처가 _들어갔는지_는 확신할 수 있지만, 인용(citation)이 _나왔는지_는 전혀 알 길이 없습니다.
이는 실제 문제가 되는 문서 지원 어시스턴트의 경우에 더욱 그렇습니다. API에 대한 답변이 정확하더라도 출처가 명시되지 않으면 검증할 수 없고, 사용자는 엣지 케이스를 설명하는 페이지로 이동(click through)할 수 없습니다. 잘못된 페이지를 인용하는 답변은 더 심각합니다. 실제로는 그렇지 않은데도 마치 검증된 것처럼 보이기 때문입니다.
저는 이 부분을 측정하는 사람을 찾을 수 없어서, 직접 벤치마크를 만들었습니다.
질문
RAG 지식 기반이 MCP를 통해 AI 에이전트에 소비될 때, 단일한 검색-답변 호출 대신 인용은 살아남는가?
당연한 가설은 에이전트들이 인용을 잃어버린다는 것이었습니다. 에이전트는 반복적으로 작동합니다: 검색하고, 읽고, 어쩌면 다시 검색하며, 그리고 답변을 작성합니다. 첫 번째 호출에서 가져온 출처들은 답변이 작성될 무렵에는 컨텍스트의 훨씬 뒤쪽으로 밀려납니다. 이 과정에서 사라지는 것이 자연스러워 보였습니다.
그 가설은 대부분 틀렸다는 것이 밝혀졌고, 그 틀린 방식이 가설 자체보다 더 유용했습니다.
설정
저는 이 경계의 완전하고 재현 가능한 사본을 만들었습니다:
- 코퍼스: FastAPI 문서입니다. 153페이지가 2,085개의 헤딩 인식 청크로 나뉘어 있습니다. FastAPI는 깔끔한 Markdown 형식에 안정적인 공개 URL을 가지고 있으며 광범위하게 사용되므로 누구나 이 작업을 확인할 수 있습니다.
- MCP 서버:
search_fastapi_knowledge_sources라는 하나의 도구입니다. 스트리밍 가능한 HTTP를 통해 정확히{source_url, content}를 반환합니다. 이는 명명 패턴까지 실제 호스팅되는 MCP 검색 제품의 계약을 반영합니다. - 질문: 250개가 초안 작성되었고, 실제 문서 페이지와 대조하여 제가 하나하나 확인한 후 127개만 유지했습니다: 한 페이지에서 답변 가능한 질문 82개, 두 개 이상의 페이지가 필요한 질문 29개, 그리고 문서에서 전혀 다루지 않아 가짜 답변을 포착하기 위한 질문 16개입니다.
- 세 가지 파이프라인, 최대한 동등하게 구성: 동일한 모델(gpt-4o-mini), 동일한 문서, 동일한 시스템 프롬프트, 그리고 필수 인용 형식(
[url]을 각 주장 바로 뒤에 배치)을 사용했습니다:- 단일샷 RAG (Single-shot RAG). 한 번 검색하고 답변합니다. 이것이 대조군입니다.
- LangGraph 에이전트. 언제, 얼마나 자주 MCP 도구를 호출할지 결정하는 ReAct 에이전트입니다.
- OpenAI Agents SDK 에이전트. 동일한 아이디어를 OpenAI의 에이전트 프레임워크와 내장된 MCP 지원을 사용하여 구축했습니다.
측정 지표: 인용 생존율 (Citation Survival Rate)
'인용하는지 여부'를 측정하는 것이 제가 예상했던 것보다 더 많은 주의가 필요하다는 것을 알게 되었습니다. 답변이 URL로 가득 차 있어도 부적절하게 출처가 명시될 수 있습니다. 실제 페이지가 문장을 뒷받침하지 않는데 인용할 수도 있고, 에이전트가 실제로 검색한 적 없는 그럴듯한 FastAPI URL을 인용할 수도 있습니다 (모델들은 FastAPI URL이 어떻게 생겼는지 알고 있기 때문입니다).
따라서 **CSR(Citation Survival Rate)**은 모든 인용이 두 가지 검사를 통과해야 합니다:
- 지원 확인(The support check). 인용된 청크가 실제로 주장을 뒷받침하는가? 각 답변은 작은 원자적 주장(FActScore 접근법)으로 분할되며, 개연성 모델이 해당 주장이 인용한 청크 각각을 검사한다(ALCE 접근법).
- 추적 확인(The trace check). 그 URL이 에이전트가 실제로 검색한 내용에 포함되어 있었는가? 모든 도구 호출은 기록되므로, 에이전트가 본 적 없는 페이지를 인용하는 것은 조작된 것으로 포착된다.
CSR = 에이전트가 실제로 검색한 올바른 출처 중 최종 답변에 포함되고, 해당 출처가 주장을 진정으로 뒷받침하는 주장에 인용되는 비율.
다음 조건을 주목하라: 에이전트가 실제로 검색한 출처. 만약 에이전트가 올바른 페이지를 찾지 못했다면, 그것은 검색 실패(retrieval failure)이며 CSR은 이를 제외한다. CSR은 올바른 출처가 경계를 넘은 후에 발생하는 것만을 측정한다.
결과와 내가 거의 발표할 뻔했던 실수
첫 번째 테스트에서 이 지표는 다음과 같이 나왔다:
| 파이프라인 (gpt-4o-mini) | CSR | 95% CI |
|---|---|---|
| Single-shot RAG | 0.618 | [0.528, 0.708] |
| ... |
The LangGraph 에이전트는 single-shot RAG와 일치했으며(질문별로 +0.000의 차이), 따라서 에이전트 루프 자체는 인용에 비용을 발생시키지 않았다. OpenAI Agents SDK 에이전트는 절반 정도를 잃은 것으로 보였다: 127개의 답변 중 66개에서 프롬프트가 요청한 [url] 형식의 인용이 없었다.
나는 그것을 너무 오래 액면 그대로 받아들였다. 마침내 그 66개의 답변들을 하나씩 읽어보니, 대부분은 출처를 인용하고 있었다:
- 51개는 주장 바로 뒤에 URL을 인용했지만, Markdown 링크 형식이었다:
[source](https://...) - 7개는
[source: https://...]와 같은 다른 형식을 사용했다. - URL이 전혀 없는 경우는 단 8개에 불과했다 (single-shot RAG는 이 중 19개를 가지고 있다).
나의 채점기는 요청된 [url] 형식만 인식했기 때문에, 그 Markdown 링크들 각각을 누락된 인용으로 계산했다. 나는 정의를 수정했다(재채점 전에 기록: [url], Markdown 링크 및 다른 대괄호 형식이 모두 카운트). 그리고 모든 것을 재채점했다:
| 파이프라인 (gpt-4o-mini) | 요청된 형식만 계산했을 때 | 모든 인용 형식 계산했을 때 |
|---|---|---|
| Single-shot RAG | 0.618 | 0.618 |
| ... | ||
| 계산 가능한 모든 형식에서, 해당 실행 동안 SDK의 single-shot에 대한 남은 격차는 원래 스코어러(scorer) 기준으로는 −0.16이었지만 (유의미함), gpt-4o judge 기준으로는 −0.10이었습니다 (유의미하지 않음). 한 번의 실행으로는 결론을 내릴 수 없었기 때문에 모든 설정을 세 번씩 실행했습니다. 모든 실행과 두 스코어러 모두에서 더 작은 손실 값이 나타났습니다: −0.12 (원래 스코어러)와 −0.09 (gpt-4o judge), 이를 평균하여 약 −0.30을 요청된 형식만 계산했을 때의 값과 비교했습니다. 따라서 감소 폭은 형식에 의한 것이 약 60%이고, 실제 더 작은 인용 손실이 40%입니다. 온도(temperature)가 0일 때는 손실이 감지되지 않습니다. |
더 크고 안정적인 효과는 더 간단합니다:
OpenAI Agents SDK는 기본 API에서 총 127개 답변 중 51개 (40%)에서 요청된 인용 형식을 무시했으며, 두 번의 반복 실행에서는 각각 53개와 46개였습니다. Single-shot RAG는 이를 0번 했고, LangGraph 에이전트는 8번 했습니다.
이는 들리는 것보다 더 중요합니다. 고정된 형식으로 인용을 읽는 모든 다운스트림(downstream) 시스템(문서 위젯,
- 에이전트 루프가 아닙니다. LangGraph도 루프를 돌며 형식을 유지했습니다. 에이전트들도 거의 루프를 돌았는데, 답변당 약 1.2~1.3번 검색을 수행했습니다.
- 온도가 아닙니다. 적어도 단독으로는 그렇지 않습니다. SDK 파이프라인은 온도를 설정한 적이 없었습니다 (LangGraph는 0을 사용). 이를 일치시켜도 형식 드리프트가 크게 변하지 않았습니다 (
[url]없이 60개 → 54개의 답변). - 전송 계층 문제가 아닙니다. 저는 SDK를 통해 모델이 받는 내용을 정확히 덤프했습니다. 모든 청크는
source_url을 온전히 가지고 도착했으며, 118개의 마크다운 링크 중 98개가 에이전트가 검색한 페이지를 가리킵니다. 출처 정보는 경계를 넘나듭니다. - API 문제입니다. OpenAI Agents SDK는 기본적으로 OpenAI의 Responses API를 호출하고; LangGraph의 OpenAI 통합은 Chat Completions를 사용합니다. 동일한 SDK 에이전트를 Chat Completions로 전환하자 잘못된 형식의 답변 개수가 41개에서 9개로 줄었습니다. 출처 기반 답변을 위한 게시된 시스템 프롬프트는 이를 20개로 줄였습니다. 두 경우 모두 세 번의 실행에 걸쳐 유지되었습니다. 어느 쪽도 출처 인용 여부 자체에 유의미한 차이를 만들지 못했습니다 (단일 실행 기준으로 측정): 단지 형식을 고칠 뿐입니다.
- 서버 측 수정이 필요합니다. 이 두 가지는 검색 제공자가 제어할 수 없는 에이전트 내부에 존재합니다. 그래서 저는 서버가 할 수 있는 것을 테스트해 보았습니다: 모든 청크에 짧은 출처 ID(
[S3fa21c])를 태그하고,[ID]인용을 요청한 다음, 답변 작성 후 ID를 URL로 매핑하는 것입니다. 제가 자체 호스팅한 미러에서는 잘못된 형식의 답변이 실행당 약 50개에서 약 5개로 줄었고, SDK 에이전트의 CSR은 0.10~0.13 증가했으며, 단일 호출과의 격차도 해소되었습니다. (Part 6에는 이 출처 인용을 아직 사람이 감사하지 않았다는 내용과 함께 수치 및 주의사항이 포함되어 있습니다.)
이러한 숫자를 신뢰할 수 있습니까?
벤치마크는 평가자만큼이나 좋으므로, 저는 실험을 실행하는 시간만큼 측정 확인에 시간을 썼습니다:
- 두 가지 종류의 스코어러. 추론 모델(entailment model)과 LLM 심사관(gpt-4o)을 사용했습니다. 저는 115개의 자체 블라인드 판단에 대해 벤치마킹 검사기들이 이를 선택했으며, 심사관이 저와 가장 잘 일치하는 결과를 보였습니다 (κ 0.86). 의견이 다른 경우에는 그 점을 명시합니다.
- 인간에게 확인받음. 30개의 블라인드 사례에 대해 CSR의 평결은 스코어러에 따라 25개에서 27개가 저와 일치했습니다. (그 중 두 가지 사례는 제가 발견한 방식의 일부인 동일한 Markdown 링크 블라인드 스팟을 가지고 있었습니다.)
- 반복 수행. 모든 gpt-4o-mini 설정은 견고성 규칙(robustness rule)을 먼저 작성하고 세 번씩 실행되었습니다: 모든 실행에서 같은 부호를 유지하며, 0을 제외하는 통합 구간(pooled interval)을 사용했습니다.
- 출판 전 감사. 적대적인 검토자처럼 파이프라인을 거치면서 저는 다섯 개의 버그를 발견했는데, 그중 네 개는 그렇지 않았다면 출하되었을 것입니다. (그 이야기는 Part 4입니다.) 위의 형식 문제는 여섯 번째이며, 제가 가장 자랑스럽지 않은 부분입니다: 이 과장된 숫자를 포착하기 전에 이미 사람들에게 전송했었고, 수정해야 했습니다.
그리고 명확하게 밝히는 한계점들: 하나의 코퍼스, 두 개의 모델, gpt-4o-mini 설정당 세 번의 실행(Claude 실행을 위한 것 중 하나), 소규모 인간 검토, 그리고 누군가의 프로덕션 시스템 대신 자체 호스팅된 MCP 서버입니다.
여기서 얻어갈 교훈
- MCP를 통해 RAG를 제공하는 경우, 정확도 보장은 경계에서 중단됩니다. 사용자는 검색(retrieval)은 제어하지만, 답변을 작성하는 에이전트는 제어하지 못합니다. 인용(citation)이 제품에 중요하다면, 그 너머에서 발생하는 것을 측정해야 합니다.
- 프레임워크의 기본값도 시스템의 일부입니다. 동일한 모델과 프롬프트를 사용했음에도 불구하고, 개발자가 결코 보지 않을 수 있는 단 하나의 기본 설정 때문에 두 에이전트가 인용 형식(citation format)을 매우 다르게 따랐습니다.
- 형식과 존재 여부를 분리하여 측정하세요. '내가 파싱하는 형식에 인용 없음'과 '인용 없음'은 다르며, 각각 다른 해결책을 필요로 합니다. 둘 다 계산해야 합니다.
- 메트릭뿐만 아니라 출력을 읽으세요. '인용되지 않은 답변'의 개수만으로는 대부분의 답변이 실제 소스를 포함하고 있었다는 사실을 숨겼습니다. 66개의 답변을 읽어본 것이 결론을 바꾸었고, 이 숫자를 누구에게 말하기 전에 했어야 했습니다.
- 여러 번 실행하세요. 남아 있는 격차(gap)를 한 번만 실행했을 경우 -0.05에서 -0.16 사이의 값이 나올 수 있습니다. 세 번 실행한 결과 그것이 실제였으며 처음 보였던 것보다 작다는 것을 보여주었습니다.
- 검색 측면에서 형식을 수정할 수 있습니다. 에이전트에게 긴 URL을 재형식화하기 위한 복사본 대신 짧은 소스 ID를 제공하고, 직접 매핑하세요.
시리즈: MCP 경계에서의 인용(Citations at the MCP Boundary)
- MCP 서버가 소스를 반환할 때, 답변은 여전히 출처를 인용하는가?
- 하나의 에이전트 프레임워크가 절반의 인용을 잃어버린 것처럼 보였다. 대부분은 단지 재형식화된 것일 뿐이다.
- 평가(Eval) 시스템이 자신의 숙제를 채점할 때
- 내가 직접 만든 벤치마크를 적대적인 리뷰어처럼 감사했다. 다섯 가지 버그가 있었다.
- 에이전트를 측정하기 전에 코퍼스를 수정하라: 내 청크의 20%는 코드가 없었다.
- MCP를 통해 RAG 제공? 인용을 유지하기 위한 실질적인 플레이북
다음 내용: Part 2: One Agent Framework Seemed to Lose Half Its Citations. Most Were Just Reformatted.
매주 새로운 부분이 게시됩니다.
저장소, 추적 기록 및 모든 숫자: csr-mcp-benchmark
저는 프로덕션 RAG 및 에이전트 시스템과 그것들이 작동함을 증명하는 평가(evals)를 구축하는 AI 엔지니어입니다. 현재 미국 또는 유럽의 스타트업에서 원격 AI 엔지니어링 역할을 찾고 있습니다. 만약 귀하의 팀이 RAG, 에이전트 또는 MCP로 구축하고 있다면 이야기 나누는 것을 좋아하겠습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기