* 이 게시물은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
(RAG 시리즈 ⑤) 생성
검색까지 잘 됐으면 마지막은 의외로 싱겁다. 찾아온 조각들을 프롬프트에 넣고, 모델한테 “이 문서에 있는 내용만 근거로 답해”라고 시키면 된다. 싱겁지만, 이 지시문 한 줄이 환각을 제일 많이 잡는다.
내가 쓰는 최소 형태는 이렇다. Claude로 붙였고, 조각마다 번호를 매겨서 답에 근거를 표시하게 했다.
import anthropic
client = anthropic.Anthropic()
def answer(query, contexts):
joined = "\n\n".join(f"[{i+1}] {c}" for i, c in enumerate(contexts))
resp = client.messages.create(
model="claude-opus-4-8", # 빠르고 싸게 가려면 claude-sonnet-5 / claude-haiku-4-5
max_tokens=1024,
system=[{
"type": "text",
"text": (
"아래 <문서>에 있는 내용만 근거로 답한다. "
"근거가 없으면 '문서에서 찾을 수 없습니다'라고 답하고, "
"답에는 [1]처럼 근거 번호를 붙인다."
),
"cache_control": {"type": "ephemeral"},
}],
messages=[{
"role": "user",
"content": f"<문서>\n{joined}\n</문서>\n\n질문: {query}",
}],
)
return resp.content[0].text
챙길 건 두 가지다. “없으면 모른다고 해”를 명시하는 것, 그리고 근거 번호를 붙이게 하는 것. 앞엣것을 안 시키면 모델이 그럴듯하게 지어내고, 시키면 못 찾을 때 솔직하게 못 찾았다고 한다. 뒤엣것은 사용자가 답을 검증할 수 있게 해준다 — 근거 [2]를 눌러 원문을 확인하는 순간, 챗봇이 “믿을 만한 것”이 된다.
조각을 넣는 순서도 은근히 중요하다. 모델은 프롬프트 가운데 낀 내용을 양 끝보다 잘 놓치는 경향이 있다(흔히 “lost in the middle”이라 부른다). 그래서 리랭킹으로 매긴 제일 관련 있는 조각을 맨 앞이나 맨 뒤에 두는 게 유리하다. 스무 개를 욱여넣지 않고 서너 개만 넣는 이유도 여기 있다. 많이 넣을수록 정작 중요한 게 묻힌다.
system 블록에 걸어둔 cache_control도 그냥 넣은 게 아니다. 지시문처럼 매번 똑같은 부분을 캐싱해두면 그 부분은 다음 호출부터 정가의 10분의 1 수준으로 처리된다. 요령은 안 변하는 걸 앞에, 매번 바뀌는 질문을 뒤에 두는 것. 캐시는 앞에서부터 일치하는 데까지만 유효해서, 앞쪽에 타임스탬프 하나만 끼어도 통째로 깨진다.
모델 선택은 문서 양과 난이도로 가른다. 답이 복잡하고 정확도가 중요하면 claude-opus-4-8, 속도·비용이 더 중요한 대량 질의는 claude-sonnet-5나 claude-haiku-4-5로 내려도 RAG에선 체감 차이가 크지 않을 때가 많다. 근거를 이미 쥐여줬으니 모델이 대단히 똑똑할 필요는 없는 셈이다.
근거 번호를 링크로 — 후처리 코드
모델이 답에 [2]를 달아줘도, 그 번호가 클릭되는 출처로 이어져야 완성이다. 후처리는 정규식 몇 줄이다.
import re
def link_citations(answer: str, chunks: list) -> str:
"""[2] → 해당 조각의 출처 링크로 치환"""
def to_link(m):
i = int(m.group(1)) - 1
if i < 0 or i >= len(chunks):
return m.group(0) # 모델이 지어낸 번호는 그대로 둔다
c = chunks[i]
return f'[{i+1}]'
return re.sub(r"\[(\d+)\]", to_link, answer)
주석의 한 줄이 중요하다 — 모델이 넘긴 조각에 없는 번호([7]인데 조각은 5개)를 지어내기도 한다. 그 번호가 살아 있는 링크로 둔갑하면 신뢰가 무너지므로, 범위 밖 번호는 링크로 만들지 않는다(로그로 남겨 환각 지표로 쓰면 더 좋다).
출처 표기가 신뢰를 만든다
답에 [2] 같은 근거 번호를 달고, 그 번호가 어느 문서인지 매핑을 함께 준다. 사용자가 출처를 눌러 원문을 확인할 수 있으면 신뢰가 확 오른다. 특히 사내 지식·법규 같은 도메인에선 ‘출처 없는 답‘은 오히려 안 쓰느니만 못하다. RAG의 강점이 바로 이 ‘근거를 댈 수 있음’이다.
자주 묻는 질문 (FAQ)
검색한 문서를 프롬프트에 전부 넣으면 되나요?
안 됩니다. 컨텍스트 한도·비용, 그리고 긴 컨텍스트에서 중간 정보가 묻히는 문제 때문에, 관련도 높은 상위 몇 개만 넣고 출처를 명시하는 게 좋습니다.
답변에 출처를 어떻게 붙이나요?
각 청크에 문서 ID·URL 같은 메타데이터를 달아 두고, 생성 시 인용하도록 프롬프트로 지시한 뒤 후처리로 링크를 매핑하면 됩니다.

답글 남기기