프로덕션 환경에서 LLM 출력 평가하기: 검증, 복구, 제거
요약
LLM 출력의 신뢰성을 확보하기 위해 검증(Validator) 단계가 필수적입니다. 이 글은 LLM이 생성한 스크립트에서 오류를 발견했을 때, 단순히 실패 처리하는 것을 넘어 복구하거나 제거하는 실질적인 방법을 제시합니다. 특히 '실패 이유'를 활용하여 재작성 및 수정에 적용하는 것이 핵심입니다.
핵심 포인트
- LLM 출력은 반드시 검증기(Validator)를 거쳐야 합니다.
- 검증기는 스크립트와 사실을 비교해 PASS/FAIL 여부를 판정합니다.
- 오류가 발견되면, 단순히 실패 처리보다 복구 또는 제거 전략이 필요합니다.
- 재작성 시 '실패 이유'를 기억하고 이를 수정 과정에 반영하는 것이 중요합니다.
LLM이 무엇을 쓰는지 확인하는 것은 쉬운 부분입니다. 어려운 부분은 그 확인 결과가 FAIL일 때 무엇을 할 것인가 하는 것입니다. 저는 실제 제품에 많은 것을 시도해 보았고, 결국 두 가지 단계로 결론지었습니다. 실패한 문장을 복구하고, 그것으로 안 되면 잘라내는 것입니다.
1분 만에 전체 아이디어: 잘못된 날짜는 복구가 고치지만, 지어낸 문장은 복구할 수 없습니다.
상황
저는 StreetLens를 구축했습니다. 걸으면 눈앞의 장소에 대한 이야기를 귀로 들려줍니다.
저희는 모든 장소에 사실들, 즉 날짜, 이름, 측정값, 그 배경 이야기가 있습니다. 모델은 이것들을 약 800단어 분량의 스크립트로 만듭니다. 그리고 음성이 그것을 읽습니다.
모델이 800단어를 작성할 때 표류(drift)합니다. 높이는 반올림되고, 날짜는 약간 틀어지며, 사실에 전혀 없는 세부 사항이 생겨납니다. 청취자는 길거리에 서 있기 때문에 확인할 수 없습니다. 지어내는 가이드는 안내자가 없는 것보다 더 나쁩니다.
검증(Check)은 쉬운 부분입니다
작성자 프롬프트에 첫 번째 규칙이 명시되어 있습니다:
- 날짜, 이름, 위치, 사건, 측정값 등 모든 사실적 주장은 출처의 사실에서 나와야 합니다. 예외는 없습니다.
하지만 프롬프트 속의 규칙은 보장이 아니라 요청일 뿐입니다. 따라서 모든 스크립트는 **검증기(validator)**를 거칩니다. 이는 스크립트와 그 사실들을 나란히 읽고 PASS 또는 FAIL을 판정하는 두 번째 모델입니다.
검증기는 필수입니다. 이 부분은 결코 의문이 아니었습니다. 수천 개의 스크립트, 8개 언어에 걸쳐 모든 것을 검사해야 하거나, 무엇을 배포할지 알 수 없습니다.
검증기는 다음과 같은 JSON 형식으로 답변합니다:
{ "result": "FAIL", "reason": "스크립트는 거의 2헥타르의 정원을 언급하지만, 출처에는 빌라가 1.5헥타르 내에 있다고 나와 있습니다" }
이 '이유(reason)'를 기억하십시오. 그것이 가장 중요한 부분이었습니다.
FAIL이라고 합니다. 이제 어떻게 할까요?
여기에 제가 시간을 보냈습니다. FAIL을 얻는 것은 쉽습니다. 그것으로 무엇을 해야 하는지 아는 것이 아닙니다.
스크립트 전체를 재작성했습니다. 틀린 숫자는 사라졌습니다. 그리고 이전에 괜찮았던 문장 어딘가에 새로운 문제가 생겼습니다. 800개의 새로운 단어, 드리프트할 수 있는 800번의 기회입니다.
손으로 스크립트를 수정했습니다. 열 개 정도는 괜찮았습니다. 하지만 8개 언어로 된 수백 군데에는 그렇지 못합니다. 게다가 프롬프트가 바뀔 때마다 매번 할 수는 없습니다.
모델에게 '이 스크립트를 수정해 줘'라고 요청했습니다. 모델은 수정했고, 그 과정에서 절반을 다시 작성했습니다. 더 멋진 문장들, 다른 어조, 검증기(validator)가 좋아하지 않는 새로운 세부 정보까지요.
이런 시도가 수십 번 있었습니다. 매번 똑같은 교훈을 얻었습니다. 수정하는 부분이 많아질수록, 깨지는 부분도 많아집니다.
제가 원했던 것은 간단했습니다. 실패한 문장만 건드리는 수정 기능입니다.
제가 완성한 것: 복구(repair), 그리고 정제(scrub)
복구(The repair)
복구는 새로운 규칙을 가진 모델이 아닙니다. 바로 작가 자신입니다. 같은 프롬프트에 끝 부분만 하나 더 추가한 것입니다:
## 🔧 REPAIR MODE
You previously generated an audio script for this POI, but validation
...
세 가지 정보가 들어갑니다. 스크립트. 사실(facts). 그리고 검증기의 이유(validator's reason).
이 '이유'가 실제로 작동하게 만드는 핵심입니다.
한 문장만 바뀌고 나머지는 단어 그대로 유지됩니다. 톤도 같고, 멈추는 지점도 같고, 이야기도 같습니다. 그리고 수정 작업의 주체가 작가이기 때문에, 고쳐진 문장은 다른 문장들과 같은 느낌을 줍니다.
그 후 수리된 스크립트는 다시 동일한 검증기(validator)로 돌아갑니다. 수정 작업 자체가 성공했다고 결정하지 않습니다. 검증기가 그렇게 합니다.
제 로그에 따르면, 이 수정 작업은 첫 시도에서 실패했던 73개 중 65개 지점을 고쳤습니다. 89%입니다.
스크러버(The scrubber)
때로는 수정 작업이 문제를 해결할 수 없습니다. 사실 자체가 존재하지 않거나, 모델이 그것에 대해 계속 무언가를 말하려고 시도하는 경우입니다.
세 번의 시도가 끝난 후, 스크러버가 작동합니다. 다른 역할입니다. 더 이상 사실을 고치려고 하지 않습니다. 그저 잘라냅니다:
You are a factual scrubber for audio tour scripts.
For those parts ONLY:
...
문장 하나가 줄어든 스크립트도 여전히 좋은 스크립트입니다. 그리고 다시, 검증기(validator)를 거칩니다. 그것마저 실패하면, 해당 지점은 앱으로 가지 않고 오류 폴더로 갑니다.
이것이 새로운 것인가요?
오류를 되돌려 보내는 아이디어 자체는 아닙니다. Guardrails AI에서는 이를
대부분의 실패는 모델 때문이 아니라 제 잘못이었습니다. 검증기(validator)는 약어 전체를 풀어서 쓰기를 원했습니다. 작가에게는 알려지지 않았던 내용입니다. 작가에게는 소수점 반올림을 하라고 지시받았습니다. 그런데 검증기는 반올림된 숫자를 실패로 처리했습니다. 서로 일치하지 않는 두 개의 프롬프트도 있었습니다. 복구(repair) 과정에서 그것들까지 고쳤지만, 하나의 공유 규칙 세트만 있었다면 공짜로 모두 피할 수 있었을 것입니다.
배운 교훈
검사 자체는 어려운 부분이 아닙니다. 모든 것에 대해 해야 합니다. 어려운 부분은 '실패'입니다.
재생성(regenerate)하지 말고, 복구(repair)하세요. 새로운 스크립트는 새로운 실수를 가져옵니다. 복구는 한 문장만 건드립니다.
복구에 이유를 제시하세요. 스크립트와 사실, 그리고 왜 실패했는지 그 이유입니다. 이유가 없으면 모델은 추측합니다. 이유가 있으면 고칩니다.
복구를 작가처럼 만드세요. 동일한 프롬프트, 동일한 규칙에 더해
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기

