에이전트·RAG

AGENT / 51번째 글

청크마다 자기가 어디서 왔는지 적어 두기

청크는 문서에서 잘려 나온 조각이라 「이번 분기」가 몇 분기인지 안에 없습니다. 색인하기 전에 맥락 한두 문장을 앞에 붙여 두는 방법과 그 비용, 흔히 어긋나는 자리를 정리합니다.

PALDYN Team14 MIN READ

지난 글에서는 검색기 쪽을 바꿔 정확도를 올리는 이야기를 했다. 토큰마다 벡터를 남기고, 저장 용량을 감수하고, 색인을 다시 만드는 일이다. 그런데 그 앞에 훨씬 싸게 더 크게 오르는 자리가 하나 있다. 검색기는 그대로 두고 색인에 넣는 글자를 바꾸는 것이다.

청크는 문맥을 잃은 조각이다

문서를 800자씩 잘라 색인에 넣었다고 하자. 열두 번째 조각은 이렇게 시작한다.

이번 분기 매출은 전년 대비 3% 늘었습니다. 주된 요인은 갱신률 개선이며, 신규 유입은 전 분기와 비슷한 수준을 유지했습니다.

문서를 처음부터 읽은 사람에게는 아무 문제가 없다. 표지에 「팔딘 2026년 2분기 실적 보고서」라고 적혀 있고, 목차에 「구독 부문」이라고 적혀 있으니까. 그런데 이 조각은 그 정보를 하나도 안 들고 나왔다.

같은 청크인데 한쪽만 걸린다

「팔딘 2026년 2분기 구독 매출」로 검색하면 이 조각은 안 걸린다. 키워드 검색은 「2026」·「2분기」·「구독」이 글자로 없으니 못 찾고, 벡터 검색도 조각 안에 없는 정보를 벡터에 담을 수 없으니 못 찾는다. 하이브리드 검색의 손잡이를 아무리 돌려도 이 자리는 안 고쳐진다 — 두 검색기가 보는 글자 자체에 답이 없기 때문이다.

청킹 전략에서 겹침을 두거나 문단 경계를 지키는 이야기를 했는데, 그것으로도 이 문제는 안 풀린다. 표지와 목차는 열두 번째 조각에서 수천 자 떨어져 있어 겹침으로 닿지 않는다.

맥락을 붙이는 자리는 색인할 때다

해결은 단순하다. 조각을 색인에 넣기 전에, 그 조각이 어디서 왔는지 한두 문장을 앞에 붙여 둔다. 이 방식을 맥락 검색(contextual retrieval)이라고 부른다 — 청크마다 문서 안에서의 위치와 주제를 적어 함께 색인하는 방법이다.

맥락은 검색할 때가 아니라 색인할 때 붙인다

맥락 문장은 사람이 적지 않고 모델이 만든다. 문서 전체와 그 조각을 함께 넣고 「이 조각이 문서의 어느 부분이고 무엇에 관한 것인지 한두 문장으로 적어라」라고 시킨다. 나온 문장을 조각 맨 앞에 붙이면 끝이다.

여기서 중요한 것이 언제 하는가다. 검색할 때가 아니라 색인할 때 한다. 이유가 셋이다.

한 번만 하면 되기 때문이다. 청크 하나당 딱 한 번 계산하고 그 결과를 저장한다. 검색 때 하려면 질의마다 후보 전체에 대해 다시 해야 한다.

검색 지연이 전혀 안 늘기 때문이다. 색인이 끝난 뒤에는 그냥 글자가 조금 긴 청크일 뿐이다. 검색 경로에 모델 호출이 한 개도 늘지 않는다.

두 검색기가 함께 좋아지기 때문이다. 붙인 글자가 BM25 색인에도 들어가고 임베딩에도 들어간다. 검색기 한쪽만 고치는 대부분의 방법과 다른 점이다.

프롬프트는 이렇게 생겼다

실제로 부르는 모양은 이 정도다.

PROMPT = """<document>
{doc}
</document>

위 문서에서 잘라 낸 조각입니다.

<chunk>
{chunk}
</chunk>

이 조각이 문서의 어느 부분이며 무엇에 관한 내용인지
한두 문장으로 적으세요. 검색에 쓸 것이므로 문서 제목,
기간, 부문처럼 조각 안에 없는 고유 정보를 우선 담습니다.
조각의 내용을 요약하지 말고 맥락만 적으세요."""

context = call_model(PROMPT.format(doc=doc, chunk=chunk))
indexed_text = context + "\n\n" + chunk

마지막 줄이 전부다. 붙인 문자열을 임베딩하고, 같은 문자열을 키워드 색인에도 넣는다. 원본 청크는 따로 보관해 두었다가 모델에 넘길 때 쓴다 — 답변 생성에 쓰는 것은 붙이기 전의 원문이어도 되고, 붙인 쪽이어도 큰 차이는 없다.

프롬프트에서 두 문장이 값을 한다. 「조각 안에 없는 고유 정보를 우선」이 없으면 모델이 조각에 이미 있는 말을 되풀이하고, 「요약하지 말라」가 없으면 조각 내용을 줄여 쓰는 바람에 원문에 있던 숫자와 낱말이 사라진다.

비용은 캐싱이 정한다

문서 하나를 청크 30개로 잘랐다면 모델을 30번 부른다. 그런데 매번 문서 전체를 함께 넣으므로, 그냥 하면 문서를 30번 읽는 셈이 된다.

문서가 2만 토큰, 청크가 500토큰, 맥락 출력이 80토큰이라고 하자.

방식 청크 하나당 입력 문서 하나당 입력
그냥 30번 호출 20,500 토큰 615,000 토큰
문서 부분을 캐시로 20,000은 캐시 읽기 + 500 캐시 쓰기 20,000 + 이후 캐시 읽기

프롬프트 캐싱에서 다룬 그대로다. 문서 부분을 프롬프트 맨 앞에 두고 캐시 경계를 그 뒤에 잡으면, 첫 호출에서만 문서를 정가로 쓰고 나머지 29번은 캐시 읽기 값으로 지나간다. 캐시 읽기가 정가의 10분의 1이라고 보면 입력 비용이 5분의 1 아래로 떨어진다.

호출 순서도 여기에 맞춰야 한다. 같은 문서의 청크들을 연달아 처리해야 캐시가 살아 있다. 문서를 섞어 가며 병렬로 돌리면 캐시가 계속 밀려나 이득이 사라진다. 문서 단위로 묶어 처리하고, 문서 안에서만 병렬로 도는 구조가 맞다.

모델은 작은 것으로 충분하다. 맥락 한두 문장을 뽑는 일은 어려운 추론이 아니다.

무엇이 얼마나 좋아지는가

효과가 나는 자리는 정해져 있다.

대명사와 생략이 많은 문서에서 크게 오른다. 보고서, 회의록, 약관, 매뉴얼처럼 앞을 읽었다고 전제하고 쓴 글이다. 반대로 FAQ처럼 항목마다 자족적인 문서에서는 별로 안 오른다.

긴 문서일수록 크게 오른다. 조각과 표지 사이가 멀수록 잃어버린 정보가 많기 때문이다.

리랭커와 겹치지 않는다. 리랭킹은 1차 검색이 건져 온 후보의 순위를 고치는 것이라, 1차에서 아예 안 올라온 문서는 손대지 못한다. 맥락을 붙이는 것은 1차 검색이 건지는 목록 자체를 바꾼다. 그래서 둘을 함께 쓰면 각각의 효과가 그대로 더해지는 편이다.

측정은 RAG 평가의 recall@N으로 한다. 순위가 아니라 「건졌는가」가 바뀌는 방법이므로, recall을 먼저 보고 그다음에 최종 답변 지표를 본다.

어긋나는 자리 넷

맥락이 청크보다 길어지는 경우. 200자짜리 청크에 300자짜리 맥락이 붙으면 임베딩이 맥락 쪽으로 끌려간다. 그러면 모든 청크가 「이 문서는 실적 보고서다」 쪽으로 몰려 서로 구별이 안 된다. 맥락은 한두 문장으로 못 박고, 청크가 짧으면 청크를 먼저 키운다.

요약으로 바뀌는 경우. 프롬프트를 느슨하게 쓰면 모델이 조각을 요약해 버린다. 그러면 원문에만 있던 숫자와 고유명사가 사라져 오히려 검색이 나빠진다. 붙이는 것이지 바꾸는 것이 아니다 — 원본 청크는 반드시 그대로 뒤에 남긴다.

문서가 문맥 창에 안 들어가는 경우. 300쪽짜리 매뉴얼은 통째로 못 넣는다. 이때는 문서 전체 대신 「제목 + 목차 + 그 조각이 속한 절의 앞부분」만 넣는다. 필요한 것은 문서 전체가 아니라 위치를 말해 줄 정보뿐이다.

없는 사실을 지어내는 경우. 모델이 「2분기」라고 적었는데 문서에는 분기가 안 적혀 있는 일이 생긴다. 그러면 틀린 글자가 색인에 들어가 엉뚱한 질의에 걸린다. 표본 100개를 사람이 읽어 보는 것이 유일한 방어이고, 「문서에 없는 정보는 적지 말라」를 프롬프트에 넣어 두면 눈에 띄게 준다.

모델을 안 부르고 하는 방법

맥락의 대부분은 사실 문서 메타데이터다. 그것만으로도 상당 부분이 해결된다.

방법 비용 담기는 것 언제 충분한가
제목·경로만 붙이기 0 문서 이름, 폴더, 작성일 문서 제목이 이미 구체적일 때
상위 헤딩 이어 붙이기 0 「3장 > 3.2 환불」 헤딩 구조가 있는 문서
앞뒤 문단 요약 붙이기 낮음 바로 앞 흐름 서술형 긴 글
모델로 맥락 생성 중간 위 전부 + 조각별 판단 위 셋으로 안 될 때

제목과 헤딩 경로를 붙이는 것부터 해 보는 편이 낫다. 공짜이고, 파서가 헤딩을 살려 두기만 했으면 바로 된다. 모델 호출은 그것으로 안 되는 문서에만 쓴다.

여기서 조건이 하나 드러난다. 헤딩 경로를 붙이려면 파서가 헤딩을 헤딩으로 알아봤어야 한다. PDF에서 잘라 온 문서라면 그 보장이 없다 — 그 이야기가 다음 자리다.

정리

  • 청크는 문서에서 잘려 나온 조각이라 자기가 어디서 왔는지 모른다
  • 맥락 한두 문장을 앞에 붙여 색인하면 키워드 검색과 벡터 검색이 함께 좋아진다
  • 붙이는 일은 색인할 때 한 번만 한다. 검색 지연은 안 늘어난다
  • 비용은 프롬프트 캐싱이 정한다. 같은 문서의 청크를 연달아 처리한다
  • 맥락은 짧게, 원본 청크는 그대로 — 요약으로 바꾸면 오히려 나빠진다
  • 제목·헤딩 경로만 붙여도 되는 문서라면 모델을 부르지 않는다

읽어주셔서 감사합니다. 😊

LATEST

에이전트·RAG의 최신 글

에이전트·RAG2026.08.23

에이전트는 어디서 어긋나기 시작하는가

에이전트가 이상한 답을 낼 때 증상은 마지막에 보이지만 어긋난 자리는 그보다 앞입니다. 한 걸음이 무너지는 여섯 자리를 나누고, 증상에서 원인을 거슬러 찾는 방법과 자리별 처방을 정리합니다.

11 MIN
에이전트·RAG2026.08.23

에이전트가 무엇을 했는지 나중에 알 수 있게 만들기

에이전트는 같은 입력에도 다른 경로로 갑니다. 그래서 로그 몇 줄로는 왜 그렇게 됐는지 복원이 안 됩니다. 트레이스를 어떻게 나누고 구간마다 무엇을 붙이며 어떤 지표를 볼지 정리합니다.

11 MIN
에이전트·RAG2026.08.23

에이전트 비용은 걸음 수보다 빨리 늘어난다

걸음이 두 배면 비용은 두 배가 아니라 서너 배입니다. 왜 그렇게 되는지, 어디서 새는지, 상한을 몇 겹으로 어떻게 거는지와 실제로 효과가 큰 순서대로의 대응을 정리합니다.

13 MIN