에이전트가 내리는 가장 어려운 결정은 테스트할 수 없는 결정이다
요약
에이전트의 판단(judgement)을 프롬프트 내에서 요청하는 것은 구조적인 문제입니다. 판단은 주장되거나 테스트될 수 없기 때문에, 에이전트가 두 가지 선택지 중 하나를 고르는 과정을 툴 호출(tool call)로 재구성해야 합니다. 이를 통해 결정 과정 자체가 검증 가능하고, 근접도와 이유까지 체계적으로 관리할 수 있습니다.
핵심 포인트
- 판단은 프롬프트에 존재하면 컴포넌트가 아닌 단락이 된다.
- 하나의 호출로는 판단을 내리고 그것을 측정할 수 없다.
- 결정 과정을 툴 호출로 만들어 검증 가능한 계약(contract)으로 만드세요.
- 검증 대상은 답변 자체가 아니라, 선택지와 이유를 포함한 '계약' 자체여야 한다.
에이전트가 내리기 가장 어려운 결정은 테스트할 수 없는 결정이다
거의 모든 에이전트 루프에는 두 가지 선택지가 모두 방어 가능하며, 그중 하나를 반드시 골라야 하는 순간이 있습니다.
두 개의 마이그레이션 계획. 두 번의 리팩토링. 두 가지 진단. 두 공급업체. 두 초안.
당신은 이 모든 것을 프롬프트에 넣고 선택을 요청한 뒤, 자신감 있는 답변을 받습니다. 그리고 그것을 배포합니다. 하지만 그 선택이 근거가 탄탄했는지, 아니면 모델이 단순히 읽은 두 번째 항목을 선호했을 뿐인지는 전혀 알지 못합니다.
여기서 제가 너무 오래 알아차리지 못한 부분이 있습니다.
판단이 프롬프트에 존재할 때, 그것은 컴포넌트가 아니라 단락이다
단락은 주장(assert)될 수 없습니다. 값을 로그로 남길 수도 없고, 회귀 테스트(regression-test)를 할 수도 없으며, 모델 버전 간의 차이점 분석(diff)을 할 수도 없고, 확신에 찬 선택과 단순한 추측을 구분할 수도 없습니다. 잘못되었음을 알게 되는 것은 프로덕션 환경에서이며, 그때 배우는 교훈은 "에이전트가 B를 선택했다"일 뿐, 그 결정의 근접도(how close the call was)는 아닙니다.
같은 장소에는 두 번째, 더 조용한 실패가 숨어 있습니다. 모델에게 A와 B 중 하나를 고르도록 요청하고 그리고 얼마나 확신하는지 보고하도록 요청하면, 보통 한 번에 둘 다 수행합니다. 당신이 돌려받은 숫자는 이전에 측정된 것이 아니라, 이미 내린 답변과 일관되게 생성된 것입니다. 만약 그 숫자에 의존하여 "70% 미만이면 에스컬레이션"이라고 라우팅한다면, 당신은 퍼센트 기호가 붙은 느낌(vibe)에 따라 라우팅하는 것입니다.
이것은 프롬프팅의 실수가 아닙니다. 구조적인 문제입니다: 하나의 호출로는 판단을 내리고 그것을 측정할 수 없습니다.
재구성: 에이전트에게 의견을 가질 것이 아니라, 선택할 무언가를 제공하라
만약 어떤 결정이 테스트할 만큼 중요하다면, 그것은 계약(contract)을 가진 컴포넌트여야 합니다. 즉, 입력값, 타입이 지정된 결과, 그리고 그 결과를 주장할 수 있는 방법이어야 합니다.
구체적으로 말하자면: 판단을 프롬프트에서 빼내어 툴 호출(tool call)로 만드십시오. 에이전트는 중립적인 작업과 두 가지 선택지를 모두 전송하고; 어떤 것이 승리했는지, 그들이 얼마나 떨어져서 평가되었는지, 그리고 이유를 돌려받습니다.
이것이 코드상에서 가져다주는 이점
핵심은 호출(call) 자체가 아니라 그 호출이 **검증 가능(assertable)**해진다는 점입니다. 판단(judgement)을 구성 요소로 포함하면, 동일한 시나리오를 다른 어떤 것과 마찬가지로 테스트 스위트에 넣을 수 있습니다:
# tests/test_escalation.py
def test_close_call_escalates_to_human(decide):
r = decide(
...
여기서 주목할 두 가지 사항이 있는데, 이것들이 바로 이 작업을 수행해야 하는 전체 이유입니다:
- 검증은 답안에 대한 것이 아니라 계약(contract) 자체에 대한 것입니다. "모델이 B라고 말한다"를 검증하는 것이 아닙니다. 모델이 변경될 때마다 답변을 재기록하게 될 것입니다. 대신, 선택지 하나가 반환되었고, 그 선택지가 이유(reason)를 가지고 있으며, 사용자의 임계값(threshold)이 제 역할을 했다는 것을 검증합니다. 마지막 줄은 _사용자_의 정책입니다: 서비스는 두 옵션이 판단상 얼마나 떨어져 있었는지 보고할 뿐이며, 이에 대해 무엇을 해야 하는지 알려주지는 않습니다.
- 이유(reason)는 일급 출력(first-class output)입니다. 호출이 근접했을 때 인간에게 보여주는 부분입니다. 선택지만 얻게 된다면, 에스컬레이션(escalation)할 내용물이 없습니다.
연결하기 (Wiring it up)
MCP를 지원하는 모든 클라이언트가 이를 처리할 수 있습니다. 원격 서버 URL과 정적 bearer 토큰을 사용하는 클라이언트의 경우, 설정(config)이 지루한 부분인데—그것이 바로 되어야 하는 방식입니다:
{
"mcpServers": {
"decider": {
...
제가 겪었던 세 가지 필드 레벨 트랩(field-level traps)이 있습니다:
- "type"은 장식이 아닙니다. 인기 있는 클라이언트 중 적어도 하나는 타입이 해당 클라이언트가 예상하는 방식으로 철자가 지정되지 않은 경우 조용히 SSE(Server-Sent Events)로 폴백합니다. 그리고 Streamable-HTTP-only 엔드포인트에서 SSE를 사용하면
405오류가 발생합니다. 블로그 게시물(이 글 포함)이 아닌, 클라이언트 자체의 문서를 참조하여 값을 복사하세요. - 검색과 호출은 별개입니다. 도구 목록을 읽는 것은 자격 증명이 필요하지 않지만, 실제로 호출하는 것은 필요합니다. 따라서 "열림 — 자격 증명 불필요"라고 보고하는 스캐너는 잘못된 절반을 읽고 있는 것이며, 첫 번째 실제 호출에서
WWW-Authenticate헤더와 함께401오류를 만나게 될 것입니다. 어떤 자격 증명도 도구 인수에 포함되어서는 안 됩니다. 이는 헤더에 들어가거나 트랜스크립트에 남겨지게 되어 있습니다. - 밀리초가 아닌 분 단위로 시간을 할당하세요. 두 가지 옵션 사이에서 판단을 내리는 것은 긴 호출입니다. 클라이언트에서 180–300초의 예산을 책정하고, 타임아웃이 클라이언트 설정이라는 점에 유의하십시오. 도구 호출 시 전달할 만한 것은 아무것도 없습니다. 60초 기본값은 답변이 도착하기 전에 끊어버릴 것입니다.
대부분의 글에서 놓치는 부분: 돌아오지 않은 호출
긴 호출은 중단될 수 있습니다. 클라이언트, 호스트 또는 네트워크 문제로 인해 말입니다. 설계 단계에서 제거해야 할 실패 모드는 비용이 많이 드는 경우, 즉 조용히 재실행하는 것입니다. 재시도는 두 번째 유료 호출이며, 선언은 가짜 안전(pretend-safe)하기보다는 명시적이어야 합니다. 이 작업은 Idempotent(멱등성)하지 않으므로, 그렇지 않은 것처럼 암시하기보다 그렇게 말해야 합니다.
따라서 복구 경로는 반복이 아니라 검색입니다:
try:
r = decide(task=t, optionA=a, optionB=b) # 중단될 수 있음
except TimeoutError:
...
아예 ID를 받지 못한 경우 — 핸드셰이크 전에 끊어진 경우 — 인자 없이 검색을 수행하면 이 자격 증명이 지난 7일 동안 생성한 ID 목록을 가져옵니다. 이것이 "호출을 놓쳤다"와 "호출을 놓치고 두 번 비용을 지불했다"의 차이점입니다.
무료 등급 대신 숫자를 공개하는 이유
이것은 무료 등급이 없는 유료 호출이므로, 시범 사용판 대신 증거를 제공하는 것이 정직합니다.
실제 기록된 결정은 27건이며, 질문, 두 가지 선택지, 선호된 선택지, 보고된 신뢰도, 그리고 전체 이유가 포함되어 https://github.com/TuringCorp-net/poe-demo-public에 게시되었습니다. 이 세트에서 신뢰도는 27.3%–88.3%, 중앙값 74.0%, 90%를 넘는 것은 없습니다. 왜냐하면 그것들은 쉬운 결정이라기보다는 일상적인 아슬아슬한 순간들이기 때문입니다. 그러한 질문에 대해 99%의 신뢰도를 보고하는 서비스는 잘못된 정보를 제공하고 있는 것입니다.
가장 공정한 테스트는 한 번의 호출을 비용으로 합니다. 이미 답을 알고 있는 결정을 실행하여, 그 이유가 동료에게서 받아들일 만한 것인지 확인해 보는 것입니다.
핵심적으로 가져갈 점
어떤 판단이 행동할 가치가 있다면, 그것은 테스트할 수 있을 가치가 있습니다. 프롬프트에서 분리하여 계약(contract)을 부여하고, 그 계약에 대해 단언(assert)하며, 이유를 기록하세요. 그러면 에이전트가 내리는 가장 어려운 결정도 실제로 회귀 테스트(regression-test)할 수 있는 루프의 일부가 됩니다.
Decider는 https://mcp.turingcorp.net에 있습니다. 정답이 없는 호출을 위한 의사결정 모델입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기