내 앱의 LLM은 아무것도 결정할 수 없다
요약
LLM 기반 서비스 개발 시 불확실성을 처리하고 환각을 방지하기 위한 엔지니어링 전략을 다룹니다. 결정론적 불확실성 처리, 명시적 공백 표시, 그리고 출력 검증기(Validator) 구축의 중요성을 강조합니다.
핵심 포인트
- 불확실성을 숨기지 말고 명시적인 제품 기능으로 전환할 것
- 프롬프트는 정책일 뿐이므로 반드시 별도의 검증 로직이 필요함
- 검증기는 단일 문자가 아닌 패턴 매칭을 통해 오탐을 줄여야 함
- 검증 함수 자체도 실패 케이스를 통해 엄격히 테스트해야 함
- 다국어 환경에서 프롬프트 언어의 영향력을 고려해야 함
나는 LLM의 진실성(truthfulness) 측면에서 최악의 도메인인 점술 분야의 소프트웨어를 개발하고 있습니다. 모델의 역할은 현명한 스승처럼 들리는 것이며, 경쟁 AI 제품들에 대한 사용자 리뷰는
해결책은 엔진이 불확실성 (uncertainty)을 결정론적 (deterministically)으로 처리하도록 만드는 것입니다. 알 수 없는 시간(Unknown hour) → 가능한 차트(chart)는 정확히 12개입니다. 12개 모두를 계산한 다음, 교집합을 구합니다. 즉, 모든 후보 차트에서 유효한 사실만이 프롬프트 (prompt)에 남게 됩니다. 12개 모두에서 오행의 강약 (element strength)이 일치하나요? 그렇다면 명시합니다. 만약 7대 5로 나뉜다면? 그러면 프롬프트는 다음과 같이 글자 그대로 말합니다: "강약 판별 불가 (12개 후보 중 7개가 강함으로 기울어짐) — 이 정보를 바탕으로 판단하지 마십시오."
그리고 결정적으로, 정보의 공백 (hole) 자체를 누락하는 대신 명시적으로 드러냅니다:
차트: 己未 丙寅 庚午 ▢ (시주(hour pillar) 알 수 없음 — 12개의 후보 차트가 계산되었으며, 모든 차트에서 참인 사실만 나열됨)
저 ▢ 기호는 그 자리를 얻을 자격이 있습니다. 침묵하는 공백보다는 명시된 공백이 낫습니다. 슬롯을 비워두면 모델이 내용을 채워 넣지만(backfills), 이를 표시하고 지침을 내리면 ("시주를 언급하지 마십시오; 시주가 관장하는 삶의 영역을 논하지 마십시오; 임의로 만들어내는 것은 거짓말입니다") 모델은 그 부분을 우회합니다. 프롬프트는 심지어 모델에게 어떻게 우아하게 마무리할지도 알려줍니다. 사용자가 자신의 태어난 시간을 알게 될 경우 무엇을 더 볼 수 있는지 언급하는 한 문장을 덧붙이는 식입니다. 불확실성이 환각 (hallucination)의 장소가 아닌, 제품의 기능 (product feature)이 된 것입니다.
2. 두 번째 관문: 첫 번째 관문을 믿지 않는 것처럼 출력을 검증하라
프롬프트 (prompts)는 정책 (policy)이지, 강제 집행 (enforcement)이 아닙니다. 따라서 생성된 텍스트는 저장되기 전에 검증기 (validator)를 통과해야 합니다:
-
조작된 기둥 탐지 (Invented-pillar detection). 알 수 없는 시간의 차트(unknown-hour charts)의 경우, 출력물에서 12가지 후보 시간 기둥(hour pillars)이 있는지 스캔합니다. 핵심은 두 글자의 간지(stem-branch) 쌍을 매칭하는 것이지, 단일 문자를 매칭하는 것이 아닙니다. 예를 들어 '子'는 '孩子(아이)'라는 일반 단어 안에 포함되어 있고, '金'은 '资金(자금)' 안에 포함되어 있습니다. 쌍(pair) 매칭은 실질적으로 오탐(false positive)이 없지만, 단일 문자 매칭은 거의 모든 문장에 플래그를 표시하게 될 것입니다.
-
금지 패턴 스캔 (Forbidden-pattern scan). 숙명론적/공포 조장적 문구(
-
작동하지 않는 가드레일(guardrail)은 가드레일이 없는 것보다 더 나쁩니다. 이는 모든 아키텍처 다이어그램과 코드 리뷰에서 "우리는 ~를 검증합니다"라는 문구로 등장하며, 그 순간 모두가 그에 대한 생각을 멈추게 만듭니다.
-
코드를 테스트하는 방식 그대로 검증기(validators)를 테스트하십시오. 반드시 실패해야 하는 입력값(inputs)을 사용해야 합니다. 실패하는 테스트 케이스가 없는 검증 함수는 관문(gate)이 아니라 가설(hypothesis)일 뿐입니다.
보너스 전투: 프롬프트 언어의 중력 (prompt-language gravity)
페르소나와 모든 지침이 중국어로 작성되어 있는데, 앱은 영어 읽기 서비스도 제공하는 상황을 가정해 봅시다. "영어로 응답하세요"라는 한 줄의 지시사항은 2,000자 달하는 중국어 프롬프트와의 접촉에서 살아남지 못합니다. 모델은 프로덕션 환경에 혼종 문장을 내보낼 것입니다. 실제 예시: "Your盘的里,其实事业和财这两条线比性格更有讲头" — 이는 페르소나 내부의 중국어 예시 문장에서 그대로 가져온 것입니다. 효과적이었던 해결책은 다음과 같습니다: 위에 인용된 모든 문장은 어조(tone)를 보여주기 위한 예시일 뿐이며, 복사하거나 번역해서는 안 되고, 출력물에는 주석이 달린 병음(pinyin)을 제외하고 중국어 문자가 포함되어서는 안 된다는 명시적인 단락을 추가하는 것입니다. 프롬프트가 이중 언어로 작성될 때, 언어 지시사항은 프롬프트의 나머지 전체 내용보다 더 크게 외쳐져야(out-shout) 합니다.
진실성(truthfulness) 외에 왜 굳이 이렇게 해야 하는가
- 비용. 모델이 차트에 대해 "추론(reasoning)"하는 대신 섹션당 300단어의 스타일이 가미된 산문을 작성하게 됩니다. 사고의 사슬(chain-of-thought, CoT)이 필요 없습니다. 사고 모드가 꺼져 있으므로 호출 시간은 약 1.5초이며 비용은 1센트의 몇 분의 일 수준입니다.
- 일관성. 동일한 차트를 가진 두 사용자에게 동일한 사실에 대해 스타일의 변화는 있을지언정, 서로 다른 운명(결과)이 주어지지는 않습니다. 다시 읽어도 내용이 모순되지 않습니다.
- 경계선(the line)은 감사(auditable) 가능합니다. 사용자가 "왜 그렇게 말하나요?"라고 물을 때, 사이트의 방법론 페이지에 게시된 것과 동일한 엔진의 사실(engine fact)을 지목할 수 있습니다.
솔직히 이 패턴은 오래된 것입니다. 사실을 출력하는 컴파일러와 이를 렌더링하는 프리티 프린터(pretty-printer)의 조합일 뿐입니다. 유일하게 새로운 부분은 프리티 프린터가 미대에 가서, 내버려 두면 사실을 지어낼 수도 있다는 점입니다. 그렇게 두지 마십시오. 진실을 계산하고, 빈틈을 표시하며, 출력을 검증하고, 여러분의 검증기가 실제로 실패할 수 있는지 테스트하십시오.
해당 앱: auspiceoracle.com — 엔진의 점수 산정 상수(scoring constants)는 방법론 페이지에 공개되어 있으며, 이는 마케팅에도 동일하게 적용되는 "과정을 보여주기 (show your work)" 규칙입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기