글·문서

기사에 없는 행동을 권한 AI — 실행 한 줄 검증기를 고친 기록

부검 L2

"지어내지 마라"를 프롬프트에 적는 대신, 모델 출력을 기계가 다시 재는 검증 규칙을 설계해요.

공개일 · 갱신일

증상 원문

코드 주석에 남아 있다. 모델이 기사에 없는 기능으로 "업로드해 보세요 — 10분" 같은 실행 한 줄을 돌려주면, 그 문장이 그대로 독자에게 발행됐다.

진짜 원인

한 문장으로 적혀 있다 — "프롬프트의 '지어내지 말 것'은 단언이지 검증이 아니다."

규칙을 쓰는 일과 지켜졌는지 확인하는 일은 다르다. 당시 use_line 경로에는 앞의 것만 있었고, 프롬프트 지시만으로는 이 날조를 막지 못했다.

고친 방법 — 출력에 계약을 걸고 파서가 강제한다

  1. 형식 규칙: 1문장, 80자 이하, 행동 표지 포함. 표지는 동사 화이트리스트(써보·해보·실행·입력·업로드·설정·시도·만들어·바꿔·비교·올려·넣어·맡겨·물어·적용·켜보·돌려)에 더해 어미 '세요'·'보기'도 인정한다.
  2. 근거 규칙: 문장에서 뽑은 내용어 중 하나 이상이 대조 대상에 실재해야 한다.
  3. 행동 대상 규칙: 행동 동사가 겨냥하는 목적어의 머리 낱말도 실재해야 한다. 낱말이 흩어져 있기만 하면 모델이 재조합해 없는 행동을 만든다("모델·API" → "API를 삭제해 보세요").
  4. 미충족이면 비운다. 기록의 판단은 "층이 없는 기사가 날조된 층보다 싸다"였다. 재호출도 없다.

대조 방식은 알아 두세요. 대조 대상은 제목 + 기사 본문이고(호출부가 summary 인자에 본문을 넘긴다), 검사는 부분 문자열이며, 한글 낱말은 앞 2글자까지 줄여 가며 맞춰 본다. 느슨한 "그 글자가 원문에 있었나"일 뿐, 의미·사실의 일치는 보장하지 않는다.

편집 예외도 좁다. 사람이 쓴 기사를 다시 라벨링할 때만 근거 대조를 끄는 옵션이 있고 형식 검사는 그대로 적용된다. 그 경로는 편집 검수를 따로 거쳐야 해요.

기각한 가설 — 첫 규칙은 너무 빡빡했다

처음 규칙은 명사구를 절 전체로 잡고, 그 낱말이 전부, 원문의 같은 문장 안에 있기를 요구했다. 결과는 과잉 차단 — 17건 중 16건 반려(근거 없는 행동 11 / 근거 없음 5), 해당 층은 사실상 비었다(발행 1/14). 반려된 16건에 후보 규칙을 다시 걸어 본 실측은 이렇다.

후보 규칙 회수 판정
대조 범위만 문장 → 전체로 확대 0/16 효과 없음
목적어 머리 한 낱말 + 범위 전체 5/16 채택
"내용어 2개 이상 실재" 밀도 규칙 10/16 지어낸 동작까지 통과 → 기각

날조가 새는 쪽보다 층이 얇은 쪽이 싸다는 이유로 5/16을 골랐다. 반대 방향의 실패도 있었다 — 사람이 쓴 정당한 행동 문장 5건이 같은 잣대에 걸려 폐기된 것이 재라벨에서 확인됐고, 그래서 편집용 우회 옵션이 생겼다. 최종 크리틱의 "사실 서술 재조합" 항목은 위험 수용으로 주간 감사에 넘겼다.

독자용 체크리스트

  • 프롬프트의 금지 문장은 검증이 아니에요. 출력을 기계가 다시 재는 규칙을 따로 만드세요.
  • 형식과 근거를 둘 다 보고, 무엇을 보증하지 않는지도 적어 두세요.
  • 못 만들면 비우세요. 폴백 문구는 날조의 다른 이름이 되기 쉬워요.

실행일·도구 버전

검증기 코드 기준일 2026-09-12. 정당한 문장 5건 폐기의 정확한 날짜는 기록마다 달라요(코드 주석은 9월 12일, 운영 메모는 9월 13일). 우회 옵션 완료는 2026-09-13 01:40 기록이에요. 사용한 도구 버전은 기록에 남기지 않았어요. 재현할 때는 현재 버전을 기준으로 확인하세요. 에디션 층별 사실 불일치 주장은 확인되지 않아 이 글에서는 뺐어요.

근거

  • ai-news-pipeline src/processors/classifier.py — validate_use_line docstring, 검증 섹션 주석, 호출부
  • 운영 핸드오프 2026-09-11 §8·§10 — 12:40·13:20·01:40 기록