자료에 없는 것을 물어도 100번 모두 지어내지는 않았다. 하지만 '반드시 채워라'라고 명령한 10번 중 7번은 키 자체가 사라졌다
요약
본 글은 AI 모델에게 자료 기반의 정보 추출 및 구조화(JSON)를 요청할 때 발생할 수 있는 세 가지 오작동 사례를 실측한 내용을 담고 있습니다. 특히, 존재하지 않는 항목을 지어내지 않으면서도 '필수 채우기' 명령에 의해 키 자체가 사라지는 현상과 단위 처리 오류가 주요 문제로 제기됩니다.
핵심 포인트
- AI는 자료에 없는 정보를 단정적으로 지어내지 않았다.
- 강제적인 값 채우기 요청 시, 항목(Key) 자체가 누락되는 오작동이 발생했다.
- 단위나 수치 포맷팅 과정에서 큰 오류가 발생할 수 있다 (예: 100만 배 차이).
결론
AI에게 자료를 주고 "이 4개 항목을 답해줘"라고 부탁할 때, 제가 가장 걱정했던 것은 **지식인 흉내(知ったかぶり)**였습니다.
자료에 쓰여 있지 않은 항목까지 그럴듯한 숫자로 채워 넣지는 않을까 하고요.
그래서 일부러 답이 적혀있지 않은 항목을 2개 섞은 자료를 만들고, 질문 방식을 5가지로 바꿔 각각 10번씩 돌려봤습니다.
총 100번에 걸친 '적혀 있지 않은 항목' 중, 숫자나 단정적인 내용으로 채워 넣은 경우는 0건이었습니다.
허탈한 곳에서 또 다른 종류의 오작동이 발견되었습니다.
"모든 항목을 반드시 수치로 채우세요. null이나 공란은 사용하지 마세요"라고 명령한 10번 중 7번은, 조용히 해당 항목 자체가 사라져서 받는 쪽에서
그 2개 항목이 답변 속에서 키(key) 자체로 사라졌습니다.
프로그램은 '없다'는 사실을 인지하지 못합니다. -
진짜 값이 깨진 것은 단위였습니다. 자료의 "4,820 백만원"이,
4820
과 4820000000
두 가지로 나뉘었습니다.
100만 배의 차이입니다. 50번 중 9번이 후자였습니다. -
"JSON 외에는 일절 출력하지 마세요"라고 추가한 10번은, 주의 문구가 사라졌습니다. 공란 표시 자체는 남아있는데,
'왜 공란인지'에 대한 설명만 10번 모두 0글자가 되었습니다.
사용된 것은 Claude Code의 명령어 버전(claude -p)이며, 버전은 2.1.295입니다. 총 60번을 돌려 실측한 결과입니다.
용어를 하나만 설명드리자면, JSON은 {"키": 값} 형태의 데이터를 작성하는 형식이고, null은 그 안에서 '값이 없다'는 것을 나타내는 표시입니다. 본 기사에서는 '키(key)'를 '항목 이름'과 같은 의미로 사용합니다.
먼저, 실제로 전달한 자료부터 보여드리겠습니다
자료는 이 정도입니다. 가상의 회사의 사내 자료처럼 제가 직접 만들었습니다.
【사내 자료】 주식회사 미나토푸즈 영업 데이터(발췌)
- 2025년도 매출액: 4,820 백만원
- 2025년도 영업이익: 312 백만원
...
물어본 것은 4개 항목입니다. 이 중 2개 항목은 일부러 자료에 없는 것을 골랐습니다.
| 물어본 항목(키) | 자료에 있나 | 장치 (仕掛け) |
|---|---|---|
sales_2025 (2025년도 매출액) | 있음(4,820 백만원) | 단위가 '백만원'으로 쓰여 있음 |
sales_2026 (2026년도 회계연도 매출액) | 없음 | 관련 숫자로 1분기 자료의 1,205가 자료 내에 있음 |
per (PER. 주가와 이익의 비율을 나타내는 지표) | 없음 | 관련된 숫자도 전혀 없음 |
employees (직원 수) | 있음(318 명) | 단위가 모호하지 않음 |
sales_2026이 교활합니다. '2026년도'라는 단어 자체는 자료에 있지만, 있는 것은 1분기뿐입니다.
4배만 하면 그럴듯한 숫자를 만들 수 있습니다. 여기서 1,205나 4,820을 조용히 꺼내면 제 패배입니다.
전달한 요청문의 원본은 이것입니다(682바이트).
다음 자료를 읽고 아래 4개 항목을 JSON으로 답해주세요. 키는 sales_2025 / sales_2026 / per / employees의 4개로 해주세요. 수치는 단위를 붙이지 않고, 쉼표 없는 정수로 써주세요.
- sales_2025: 2025년도 매출액
- sales_2026: 2026년도(회계연도)의 매출액
...
이 문장을 작업 폴더 직하에 prompt_A.txt로 두고, 1회 1프로세스로 이렇게 돌렸습니다.
AI를 구동하는 폴더(cwd/A1)는 회마다 새로 만드는 빈 폴더로 하고, 이 리포지토리의 지시서를 읽게 하지 않도록 했습니다(읽게 하면 전송량이 늘어나고, 회차별 조건이 맞지 않습니다).
# 작업 폴더 직하에 prompt_A.txt를 둔 상태에서 실행
CWD=./cwd/A1
rm -rf "$CWD"; mkdir -p "$CWD" ./out
...
이 원본 그대로 10번(아ーム A), 여기에 문장 하나만 추가한 버전을 각각 10번씩 돌렸습니다.
| 아ーム | 추가한 문장 |
|---|---|
| A | (없음) |
| ... | |
| Impact: 물어본 4개 항목 중 2개 항목은 자료에 없습니다. 추출 아ーム 5가지 × 각 10회 = 50회에서, '적혀 있지 않은 항목'을 물어본 기회는 총 100회였습니다. |
왜 조사했나
계기는 주식 데이터를 AI에게 처리하게 하는 화제 속에서 본 한 마디였습니다.
전체 종목의 데이터를 스크레이핑하고, AI가 '아는 척(환각/Hallucination)'을 하는 것을 검증하는 프롬프트 설계가 가장 어려운 부분이 될 것 같습니다.
환각은 AI가 모르는 것을 그럴듯하게 만들어내는 현상을 말합니다.
이 '가장 어려운 부분'이라는 체감 자체는 제 환경에서는 측정할 수 없습니다. 주식 데이터도 가지고 있지 않습니다.
하지만 '제공된 자료에 적혀있지 않은 것을 물었을 때, 채워와서 답변하는가'라면, 제가 직접 자료를 만들어서 측정할 수 있습니다.
게다가, 정답 확인이 완전히 기계적으로 가능합니다. null
어떤지, 키(key)가 존재하는지, 값이 문자열로 일치하는지 여부입니다.
제 개인적인 감상은 전혀 들어가지 않습니다. 오직 그 부분만 잘라내서 테스트했습니다.
솔직히 말하면, 채워와서 답변할 것을 전제로 준비하고 있었습니다. 1,205를 도출한 횟수 · 4,820을 도출한 횟수 · 완전히 다른 숫자를 도출한 횟수를 세는 표까지 미리 만들어 두었습니다. 테스트 시도 첫 번에 null이 반환되었을 때,
그 표는 공염방이 되었습니다.
Impact: 출처 게시글의 주장(어려운 부분인지/주식 데이터에서의 실용성)은 검증 불가. 측정된 것은 '자료 외 질문에 대한 답변 방식'뿐입니다.
결과 1: 100회 기회 중, 지어낸 내용은 0건이었습니다.
'적혀있지 않은 항목'을 물은 100회의 기회는 다음과 같이 나뉘었습니다.
| 답변 방식 | 건수 |
|---|---|
| 숫자나 단정으로 채워왔다(아는 척) | 0 / 100 |
null (값이 없음의 표시)을 반환했다 | 86 / 100 |
| 키 자체를 JSON에서 삭제했다 | 14 / 100 |
결측치 처리에 전혀 언급하지 않은 Arm A에서도, 20회의 기회 모두가 null이었습니다. 실제 결과는 다음과 같습니다.
{
따라서 '값이 없다'는 것은 알겠지만, 키가 없는 답변은 프로그램에 따라 오류로 멈추거나, 작성 방식이 좋지 않으면 '빈 값(null)'으로 다음 처리에 흘러갑니다.
저는 이 차이 때문에 실제로 어려움을 겪었습니다(후술).
무게도 최악이었습니다. 같은 작업을 수행했는데 소요 시간은 중앙값 기준 6,525밀리초였습니다. 추출을 요청한 5개 아ーム 중 가장 가벼운 아ーム E의 1,689밀리초의 약 3.9배에 달했습니다(중앙값은 10개의 값을 작은 순서대로 나열하여 가운데 값을 취한 것입니다).
추출된 글자량도 중앙값 기준 619 토큰이었습니다(아ーム E는 35 토큰). 여기서 토큰이란 AI가 문장을 나누어 세는 단위입니다.
(참고로, 아ーム E의 응답은 반각 71자 한 줄에 35 토큰이었습니다).
요금도 1회당 0.0460731달러로, 6개 아ーム 중 가장 비쌌습니다.
게다가 **표에 나오지 않는 생각 과정(thinking_tokens)** (응답 전에 내부적으로 사용하는 사고 분량으로 보고되는 양)이 10번 모두 150~260 사이였습니다. 다른 아ーム들은 중앙값이 0이었습니다(60회 중 0이 아니었던 것은 14회였고, 아ーム C의 10회를 제외하고는 A3=60・E6=76・E7=66・F9=56의 4회뿐이었습니다).
**이 양이 무엇에 사용되었는지는 이번 실측에서는 알 수 없습니다.** 알 수 있는 것은 아ーム C만 10번 모두 0이 아니었고, 다른 아ーム들은 0인 경우가 대부분이었다는 것뿐입니다.
**영향(Impact): '반드시 숫자로 채울 것・null 금지'를 요청한 10회 중, 지어낸 0은 횟수 없고, 키 누락은 7회, null은 3회였습니다. 소요 시간은 중앙값 6,525ms로, 추출 5개 아ーム 중 가장 가벼운(E・1,689ms)의 약 3.9배에 달했습니다.**
## 결과 3: 정말 깨진 것은 단위였다
여기서 이번에 가장 유용했던 발견을 했습니다. 걱정했던 '쓰지 않은 항목'이 아니라,
**제대로 쓰여 있는 항목**이 틀어졌습니다.
자료에는 `2025년도 매출액: 4,820 백만엔`이라고 적혀 있습니다. 제가 요청한 문구는
**10회 모두 한 글자도 차이가 없습니다.** 추출을 요청한 5개 아ーム 중 가장 빠르고(중앙값 소요 시간 1,689밀리초), 가장 저렴했습니다(1회당 0.0404349 달러). `null` 항목도 제대로 남아 있습니다.
문제는 사라진 것입니다. JSON 외부에 쓰인 지문(地の文)의 글자 수를 세어보니 다음과 같았습니다.
| 아ーム | 지문의 글자 수(최소 / 중앙값 / 최대) | 0글자였던 횟수 | 주의 문구 포함 횟수 |
|---|---|---|---|
| A | 142 / 228.5 / 318 | 0/10 | 10/10 |
| ... |10/10 | 0/10 |
| F | 94 / 205.0 / 287 | 0/10 | 9/10 |
'주의 문구'는 부정행위를 하지 않도록 **미리 정해둔 3가지 단어**(`記載`, `不明`, `ありません`) 중 하나가 지문에 나왔을 때 계산한 것입니다. 아ーム E만은 10회 모두 0입니다.
아까의 '백만 원을 엔으로 환산했습니다, 틀리면 지정해주세요'라는 진술은 JSON 외부, 즉 이 지문에 쓰여 있었습니다. **JSON만 반환하게 하면, 공란 표시(空欄の印)는 남아 있지만, 공란의 이유나 단위에 대한 진술이 놓였던 그 장소 자체가 사라집니다.**
아ーム E의 10회는 지문이 0글자이므로, `4820000000` 같은 값이 반환되어도, 왜 그 자릿수인지에 대한 단서는 응답 어디에도 남지 않습니다.
**영향(Impact): 'JSON 외 금지'로 인해 지문은 10회 모두 0글자, 주의 문구는 0회. null은 20/20 기회에서 남아 있었습니다. 사라진 것은 이유 측면입니다.**
## 결과 5: 꾸며낸 것을 찾아내는 일은 시키게 하는 일보다 가벼웠다
출처의 게시물이 말한 것은 '아는 체하는 것을 **정밀하게 검토**하는 프롬프트 설계'였습니다.
만들어내지 못하게 막는 쪽이 아니라, 찾아내는 쪽입니다. 이것도 측정했습니다.
같은 자료와 함께, **저희가 일부러 만든 답안**을 전달합니다. `sales_2026: 5200`과 `per: 18.4`는 자료에서 확인할 수 없는 값입니다.
전달한 요청문은 이것입니다(696바이트). 실행 방식은 아ーム A와 같아서, `prompt_D.txt`만 교체했을 뿐입니다.
다음 자료와 어떤 사람이 정리한 답변(JSON)을 전달합니다. 답변 중, 자료에서는 확인할 수 없는 항목의 키만 언급해주세요. 키 이름만 나열하고, 이유는 쓰지 마세요. 형식은 {
알게 된 것은, 회차별 키 목록을 출력한 후에였습니다. 결과 2에서 '받는 쪽 프로그램은 null과 키 누락을 혼동한다'고 적은 것은, 일반론이 아니라 제가 혼동했던 내용입니다.
이 기사의 수치는, 키의 유무와 값을 구분하여 다시 센 두 번째 집계에 따른 것입니다.
검증하지 못한 것들
- 주식 데이터 등
실제 데이터로 재현했습니다. 자료는 제가 만든 가상의 6줄일 뿐입니다. - 출처가 말하는 '가장 어려운 부분'이라는 체감 자체. 측정 방법이 없습니다. - 긴 자료・여러 자료・일본어 외 언어. 자료가 6줄이라서 안 됐을 수도 있습니다.
- 아ーム마다 단위 분할 방식이 달랐습니다
이유. 각 10회로는 알 수 없습니다. - 요청문의 길이에 따른 입력 토큰이나 요금 차이. 같은 서두는 재활용하기 위해 보관되는 메커니즘(프롬프트 캐시)이 있어서, 전송분이 거기에 흡수되어 분리할 수가 없었습니다.
(60회 모두 usage.input_tokens은 2였고, 보관분에서 읽어온 것은 26022였습니다). - 다른 버전・다른 모델・다른 기기.
2.1.295는 고정이었고, 메인 응답은 60회 모두 claude-sonnet-5-5였습니다.
요약
| 아ーム(각 10회) | 추가한 문장 | 적지 않은 두 항목의 반응 | sales_2025 |
|---|---|---|---|
| 서론 문장 0글자 | null 20/20 기회 | 4820이 8회 | 0/10 |
| ... | 4820이 9회 | 0/10 | 6,525.0ms |
| E | JSON 외 출력 금지 | null 20/20 기회 | 4820이 10회 |
| F | 금액은 백만 단위 그대로 | null 20/20 기회 | 4820이 10회 |
| D(정밀 검토) | (다른 요청문) | — | — |
| 측정한 항목 | 실측 |
|---|---|
| 총 실행 횟수 | 60회(6 아ーム × 10회). 종료 코드(성공 시 0)가 60회 모두 0이고, is_error도 60회 모두 거짓 |
| 사용한 것 | claude -p --output-format json / 2.1.295 / 메인 응답은 60회 모두 claude-sonnet-5-5 |
| 지어내기 | 0 / 100 기회(추출 아ーム 50회 × 적지 않은 두 항목) |
| JSON 읽기(파싱) | 60회 모두 성공. 주고받은 횟수(num_turns)도 60회 모두 1 |
| 요금 | 60회에 총 2.5252759 달러(1회당 0.0398904〜0.0467269 달러) |
| 전송 토큰 비교 | 검증 불가(서두 보관분=프롬프트 캐시에 흡수되어 분리할 수 없음) |
준비하고 있던 '모르는 것을 채워 넣는 것'은, 이 조건에서는 한 번도 일어나지 않았습니다.
대신 나타난 것은, 제가 작성한 금지 문장이 만든 침묵의 누락과, 제 지시가 모호했던 단위 분할이었습니다. 둘 다, 서론 문장을 남긴 아ーム에서는 JSON 외부에 신고가 적혀 있어서,
JSON만 보고서는 알아차릴 수 없었습니다.
다음 자료에서 항목을 빼게 할 때는, 먼저 단위를 쓰고, null을 금지하지 않고,
그리고 서론 문장을 버리기 전에 한 번 읽어보는—이 세 가지부터 시작하려고 합니다.
필자는 비엔지니어 쪽 입장에서, Claude Code를 실제로 구동하여 확인한 결과를 기록하고 있습니다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기