없는 주문번호가 들어왔을 때 — 지어내지 않게 만들기
한 줄 요약
1장에서 본 환각을 막는 실질적인 방법은 "모른다고 말해 줄 사람"을 만들어 두는 것입니다. 도구가 사실을 돌려주면 모델은 그 사실을 전달합니다. 모델을 설득하는 것보다 도구를 잘 만드는 편이 확실합니다.
1. 실행 결과 다시 보기
고객 : 주문 O999999 어떻게 됐나요?
상담원: 주문번호 O999999를 찾을 수 없습니다. 번호를 다시 확인해 주세요.
지어내지 않았습니다. 1장에서 반품 기간을 술술 지어냈던 그 모델이 말이죠.
무엇이 달라졌을까요?
2. 지어낼 여지가 없어졌습니다
두 상황을 비교해 봅시다.
1장 — 정보가 없는 상태
모델이 받은 것: "불량 상품 반품은 며칠 이내에 신청해야 하나요?"
모델이 아는 것: 일반적인 전자상거래법 지식
모델이 한 것: 아는 것으로 메움 → "3개월 이내 또는 30일 이내"
3장 — 도구 결과가 있는 상태
모델이 받은 것: "주문 O999999 어떻게 됐나요?"
+ 도구 결과: "주문번호 O999999 를 찾을 수 없습니다."
모델이 한 것: 도구 결과를 전달 → "찾을 수 없습니다"
"찾을 수 없다"는 것도 정보입니다. 그 정보가 입력에 들어 있으면 모델은 그걸 씁니다. 없을 때만 지어냅니다.
환각은 모델이 거짓말을 하는 게 아니라, 빈칸을 메우려는 성질에서 비롯됩니다. 그러니 대책은 "거짓말하지 마"라고 부탁하는 것이 아니라 빈칸을 채워 주는 것입니다. 도구가 하는 일이 정확히 그것입니다.
3. 그래서 도구는 이렇게 만듭니다
아래는 code/ch03_order_agent.py 의 일부입니다 — 위에서 이미 붙여 넣었으니 다시 넣지 마세요.
if hit.empty:
return f"주문번호 {oid} 를 찾을 수 없습니다. 번호를 다시 확인해 주세요."
네 가지 선택지 중 이걸 고른 것입니다.
| 방식 | 결과 |
|---|---|
raise ValueError(...) |
프로그램이 멈춘다. 고객은 답을 못 받는다. |
return None |
모델이 None 을 어떻게 해석할지 모른다 |
return "" |
빈 문자열 → 모델이 다시 빈칸을 메우려 한다 |
return "찾을 수 없습니다..." |
모델이 읽고 그대로 전달한다 ✓ |
세 번째가 특히 위험합니다. 빈 결과를 받으면 모델은 다시 "아는 것으로 메우는" 모드가 됩니다.
실패했을 때도 상황을 설명하는 문장을 돌려주세요.
4. 세 겹의 방어
이 장의 실습 코드에는 지어내기를 막는 장치가 세 겹으로 들어 있습니다. 다만 세 장치의 성격이 서로 다릅니다. 질문이 거치는 관문으로 그려 보면 이렇습니다.
질문
│
═══════▼════════════════ ① 도구가 사실을 돌려준다
→ 코드가 보장한다. 넘을 수 없는 벽.
│
┈┈┈┈┈┈┈▼┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ ② 시스템 지시 "도구 결과만 근거로"
→ 확률을 기울일 뿐. 가끔 새어 나간다.
│
┈┈┈┈┈┈┈▼┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ ③ temperature = 0
→ 확률을 기울일 뿐. 가끔 새어 나간다.
│
▼ ④ 도구가 없는 영역
답 → 벽이 아예 없다. 5절에서 다룹니다.
굵은 실선(═)은 코드가 보장하는 벽이고, 점선(┈)은 원하는 답이 나올 가능성만 높이는 벽입니다. 셋은 대등하지 않습니다.
① 도구가 사실을 돌려준다
아래는 code/ch03_order_agent.py 의 일부입니다 — 위에서 이미 붙여 넣었으니 다시 넣지 마세요.
return f"주문번호 {oid} 를 찾을 수 없습니다. 번호를 다시 확인해 주세요."
가장 확실한 방어입니다. 코드가 보장하는 것이기 때문입니다.
② 시스템 지시가 근거를 제한한다
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
SYSTEM = ("... 주문 관련 질문에는 반드시 get_order_status 도구로 조회한 "
"결과만 근거로 답한다. ...")
"결과만 근거로" — 도구 밖의 지식을 쓰지 말라는 뜻입니다. 지시를 따를 가능성을 높이는 장치입니다.
③ temperature=0
답이 흔들리지 않게 합니다. 온도가 높으면 낮은 확률의 표현이 튀어나오면서 없는 말이 섞일 수 있습니다.
세 겹의 성격이 다릅니다. ①은 코드가 보장하고, ②③은 원하는 답이 나올 가능성을 높일 뿐입니다. 2장에서 "시스템 지시는 강한 부탁이지 절대 명령이 아니다"라고 한 것을 기억하세요. 반드시 지켜져야 하는 것은 ①로 해결해야 합니다.
5. 그래도 완벽하지는 않습니다
이런 질문을 던져 보세요.
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
ask("주문 O001203이 왜 이렇게 늦어지나요? 배송 기사님 연락처 좀 알려주세요.")
도구는 배송 기사 연락처를 모릅니다. 모델은 어떻게 할까요? "확인 후 안내드리겠습니다" 라고 할 수도 있고, 그럴듯한 안내를 만들어 낼 수도 있습니다.
도구가 없는 영역은 여전히 위험합니다. 그래서 실제 서비스에서는 이런 장치를 더 씁니다.
- 답변에 근거를 함께 표시한다 → 6장에서 출처를 붙이는 법과, 그것이 도구 안에서 어떻게 사라지는지를 함께 봅니다
- 도구가 못 하는 질문은 사람에게 넘긴다
- 답변을 검증하는 단계를 하나 더 둔다
6. 정리 — AI가 한 것과 사람이 한 것
[사람이 결정한 것]
· 없는 주문번호일 때 무엇을 돌려줄지 ← 이 절의 주제
· 도구 결과만 근거로 답하라는 규칙
· temperature = 0
[AI가 한 것]
· "주문 조회 도구를 써야겠다" 판단
· 'O999999' 를 인자로 추출
· 도구 결과를 사람이 읽을 문장으로 정리
환각을 막은 것은 AI가 아니라 사람의 설계입니다. 도구가 "찾을 수 없습니다"를 돌려주게 만든 그 한 줄이 결정적이었습니다.
스스로 해 보기
if hit.empty:의 반환값을return ""(빈 문자열)로 바꾸고ask("주문 O999999 어떻게 됐나요?")를 실행해 보세요. 답이 어떻게 달라지나요? 왜 그런지 설명해 보세요.SYSTEM에서 "도구로 조회한 결과만 근거로" 를 빼고, 없는 주문번호를 물어 보세요.ask("주문 O001203의 배송 예정일이 언제인가요?")—orders.csv에 없는 정보입니다. 모델은 어떻게 답하나요? 이 답이 위험한가요, 안전한가요?
핵심 정리
- 환각은 거짓말이 아니라 빈칸을 메우려는 성질에서 비롯됩니다. 대책은 빈칸을 채워 주는 것입니다.
- 도구가 "찾을 수 없습니다" 를 돌려주면, 그것도 정보라서 모델이 그대로 전달합니다.
- 실패했을 때도 상황을 설명하는 문장을 돌려주세요. 예외·
None·빈 문자열은 전부 나쁩니다. - 방어는 세 겹 — ① 도구가 사실을 돌려준다(코드가 보장) · ② 시스템 지시 · ③
temperature=0. - 반드시 지켜져야 하는 것은 ①로 해결합니다. ②③은 원하는 답이 나올 가능성을 높일 뿐입니다.
- 도구가 없는 영역은 여전히 위험합니다. 6장에서 출처를 붙이는 법과, 그것이 도구 안에서 어떻게 사라지는지를 함께 봅니다.