14장. 근거 기반 답변
세 가지 규칙과 기권 — 없으면 없다고 말하게 한다
한 줄 요약
발췌를 넣어 주는 것만으로는 부족합니다. 발췌 안에서만 답하고, 출처를 적고, 근거가 없으면 정해진 문장으로 물러나라는 규칙을 시스템 프롬프트에 적습니다. 근거가 없을 때 답하지 않는 것을 기권(abstain) 이라고 합니다.
1. RAG용 시스템 프롬프트
이번 장에서 쓰는 시스템 프롬프트의 전문입니다.
당신은 하루마켓 고객지원 상담원 '하루'입니다.
아래 [정책 발췌]에 있는 내용으로만 답합니다.
규칙:
1. 발췌에 근거가 있으면: answer에 답변을 쓰고, evidence_ids에 실제로 사용한
발췌 번호를 넣습니다. 답변의 모든 정책 설명을 뒷받침하는 발췌를 선택합니다.
출처는 코드가 붙이므로 answer에 문서명·조항명·발췌 번호 등 출처 표기를 쓰지 않습니다.
2. 발췌에 근거가 없으면: evidence_ids는 빈 목록으로, answer는 아래 문장으로 씁니다.
해당 내용은 정책 문서에서 확인되지 않아 상담원 확인이 필요합니다.
절대 추측해서 답하지 않습니다.
3. 금액·기간 등 숫자와 영업일 같은 조건은 발췌 그대로 씁니다. 존댓말, 3~5문장.
| 부분 | 요구 | 막는 것 |
|---|---|---|
| 둘째 줄 | 발췌 안에서만 답한다 | 모델의 일반 지식이 섞이는 것 |
| 규칙 1 | 근거 발췌 번호를 선택한다 | 확인할 수 없는 답 |
| 규칙 2 | 근거가 없으면 기권한다 | 환각 |
| 규칙 3 | 숫자는 발췌 그대로 | 금액·기간이 바뀌는 것 |
2. 규칙 1 — 근거 선택과 출처 표기를 나눈다
모델은 answer와 evidence_ids를 JSON으로 반환합니다. answer는 출처를 제외한 고객 안내문이고, evidence_ids는 답변에 사용한 발췌 번호 목록입니다. PolicyAnswer가 이 구조를 정의합니다.
코드는 번호가 검색 결과의 범위 안에 있는지 확인한 뒤, 선택한 청크의 메타데이터로 (근거: doc_title · article)을 붙입니다. 같은 번호나 같은 출처는 중복 표시하지 않습니다. 조항 이름을 모델이 다시 작성하지 않으므로 메타데이터의 이름이 그대로 보존됩니다.
단, 번호가 유효하다는 것과 그 청크가 답변의 모든 내용을 뒷받침한다는 것은 다릅니다. 근거 선택의 적절성은 본문과 대조해 확인합니다.
3. 규칙 2 — 기권할 때 할 말까지 정해 준다
"모르면 모른다고 하라"는 지시만으로는 모델이 이렇게 미끄러지기 쉽습니다.
정확한 규정은 확인이 필요하지만, 일반적으로 해외 배송 상품은 ...
모른다고 하면서 뒤에 짐작을 덧붙이는 답입니다. 그래서 근거가 없으면 evidence_ids를 빈 목록으로 반환하게 합니다. 코드가 빈 목록을 받으면 answer에 다른 말이 있더라도 표준 기권 문구 한 문장만 출력하며, 출처를 붙이지 않습니다.
JSON 구조가 잘못되거나 발췌 번호가 범위를 벗어나거나 필요한 메타데이터가 없어도 같은 확인 필요 문구를 반환합니다. 이 경우는 문서에 답이 없다는 모델의 판단과 달리, 응답을 검증하지 못해 안내를 보류하는 경우입니다.
기권 문구를 정해 두면 얻는 것이 둘 있습니다.
- 고객에게 늘 같은 안내가 나갑니다. "상담원 확인이 필요합니다"라는 다음 행동도 함께 전달됩니다.
- 답에 그 문구가 들어 있는지 코드로 검사할 수 있습니다. 기권이 몇 번 일어났는지 셀 수 있습니다.
기권은 실패가 아닙니다. 문서에 없는 것을 없다고 말한 것은 설계한 대로 동작한 것입니다. 틀린 정책을 자신 있게 안내하는 것보다 훨씬 낫습니다.
4. 검색과 생성의 역할 나누기
13장에서 확인한 사실이 있습니다. 검색은 관련이 없어도 항상 k개를 돌려줍니다. "해외 배송도 반품 되나요?"를 물어도 청크 네 개가 나옵니다.
그러면 "이 발췌에 답이 없다"는 판단은 누가 할까요?
| 단계 | 하는 일 | 하지 못하는 일 |
|---|---|---|
| 검색 | 가장 가까운 청크 4개를 고른다 | 그 4개가 충분히 가까운지 판단하지 못한다 |
| 생성(LLM) | 발췌를 읽고, 답이 있으면 답하고 없으면 기권한다 | 발췌에 없는 청크를 찾아오지 못한다 |
기권의 판단은 생성 단계가 맡습니다. 모델이 발췌 네 개를 읽고 "여기에 해외 배송 이야기가 없다"고 알아차려야 합니다. 규칙 2가 그 일을 시킵니다.
반대 방향도 기억해 둡니다. 생성 단계는 검색이 가져오지 못한 것을 만회할 수 없습니다. 문서에 답이 있어도 검색이 그 청크를 못 찾으면, 모델이 할 수 있는 최선은 기권입니다. 이 문제는 15장에서 다룹니다.
5. temperature는 0.1
config=types.GenerateContentConfig(
system_instruction=RAG_SYSTEM, temperature=1.0,
response_mime_type="application/json", response_schema=PolicyAnswer),
정책 안내는 같은 질문에 같은 내용이 나와야 하는 일입니다. 그래서 낮은 값을 씁니다. 다만 3장에서 본 대로 온도가 낮아도 문장은 실행마다 달라집니다. 금액과 기간 같은 내용이 같은지를 봅니다.
6. 프롬프트로 정한 것과 정하지 못한 것
규칙은 적는 것으로 끝나지 않고 지켜졌는지 확인하는 코드가 따라붙어야 합니다. 형식을 정해 둔 덕분에 그 확인을 코드로 할 수 있습니다(실습문제 1).
| 프롬프트로 정한 것 | 코드로 확인해야 하는 것 |
|---|---|
| 발췌 안에서만 답하라 | 답의 금액·기간이 발췌에 실제로 있는가 |
| 사용한 발췌 번호를 선택하라 | 번호가 유효한가, doc_title · article이 그대로 표시되는가 |
| 근거가 없으면 정해진 문장으로 기권하라 | 기권 문구가 들어 있는가, 기권하면서 출처를 붙이지는 않았는가 |
핵심 정리
- RAG용 시스템 프롬프트는 발췌 한정, 출처 표기, 기권, 숫자 그대로 네 가지를 요구합니다.
- 모델은 근거 발췌를 선택하고, 코드는 번호를 검증한 뒤 메타데이터로 출처를 조립합니다.
- 기권은 할 말까지 정해 줍니다. 그래야 짐작을 덧붙이지 않고, 기권한 횟수를 셀 수 있습니다. 코드는 빈 근거 목록에 출처를 붙이지 않습니다.
- "이 발췌에 답이 없다"는 판단은 검색이 아니라 생성 단계가 합니다.
- 규칙은 적는 것으로 끝나지 않습니다. 지켜졌는지 코드로 확인합니다.