거절하는 에이전트: neatlogs, Entire 및 cfo.ai를 사용한 지급(payout) 에이전트 구축기
요약
본 글은 해커톤 상금 지급 경로를 계획하는 'Cair'라는 에이전트를 구축한 과정을 다룹니다. 이 시스템은 neatlogs, Entire, cfo.ai 등의 도구를 활용하여 모델의 결정 범위를 제한하고, 모든 출력을 추적 가능한 증거 기반으로 만듭니다. 이를 통해 AI가 잘못된 정보를 자신감 있게 제시하는 문제를 해결합니다.
핵심 포인트
- AI 에이전트 구축을 통한 지급 경로 계획 시스템(Cair) 소개
- neatlogs와 Entire를 활용하여 모든 결정 과정을 추적하고 검증함
- 모델의 출력을 도구 출력 기반으로 제한하여 신뢰도를 높임
- 단순한 LLM 호출보다 구조화된 코드가 더 정확한 결과를 보장함을 입증
저는 $7,000의 해커톤 상금을 받았습니다. 지급 양식은 Wise를 통해 저에게 돈을 보내주겠다고 했습니다. Wise 자체 도움말 페이지에는 인도네시아 주소지 개인 계좌는 '더 이상 계좌에 돈이나 통화를 보유할 수 없다'고 나와 있습니다.
이것이 제가 **Cair**를 구축하여 막고자 했던 종류의 실수입니다: 상금이나 고객 대금이 은행 계좌로 어떻게 전달되는지 계획하고, 전달할 수 없는 경로는 거절하는 에이전트입니다. 이 시스템은 neatHack이라는 48시간 에이전트 해커톤을 위해 neatlogs (추적 기록), Entire (로컬 코드 그래프) 및 cfo.ai (비즈니스 계획)와 함께 구축되었습니다. 코드는 github.com/edycutjong/cair입니다. 라이브 데모는 app.cair.edycu.dev에서 확인하실 수 있습니다.
이 게시물은 제가 가장 중요하다고 예상하지 못했던 부분, 즉 모델에게 허용되는 결정의 범위가 얼마나 적은지에 관한 내용입니다.
기준선(Baseline)은 자신감 넘치는 단락이다
저는 먼저 명백한 답을 측정하는 것부터 시작했습니다: 도구 없이 모델을 한 번 호출하는 방식이었습니다. 저는 48가지 시나리오(3개 국가 x 4개 지불자 x 3개 금액, 그리고 12개의 선행 프롬프트)를 고정하고, 어떤 플래너 코드를 작성하기 전에 출처가 되는 규칙 시트에서 정확한 레이블을 계산했습니다. 그런 다음 이 한 번 호출 기준선을 세 번 실행했습니다.
| v1: 단일 호출 | v2: Cair | |
|---|---|---|
| 자체 제공자 규칙으로 거부되는 경로를 추천함 | 48개 중 18개, 모든 실행에서 발생 | 48개 중 0개 |
| ... | ||
| 기준선은 세 번의 모든 실행에서 인도네시아 Devpost 수상자에게 Wise를 추천했습니다. 전체 규칙 시트를 프롬프트에 붙여넣는 두 번째 기준선 역시 여전히 죽은 경로를 두 번 추천했고, 지원되지 않는 세금 포지션을 11번 언급했습니다. 모델은 눈앞에 사실을 가지고 있었음에도 불구하고 간극(gap) 주변으로 자신감 있는 답변을 작성했습니다. |
(주의할 점: 이것은 _출처가 된 시트_에 대한 충실도를 측정하는 것이지, 세금의 정확성을 측정하는 것은 아닙니다. 레이블과 에이전트는 동일한 시트를 읽었습니다.)
그래서 코드가 결정하고, 모델은 단지 그것을 말할 뿐이다
Cair는 서론(prose)을 작성하기 전에 항상 확정적인 증거 계획(fixed evidence plan)을 실행합니다. 여기에는 지급자(payer)가 제시하는 내용, 각 옵션이 해당 국가에서 수령 가능한지 여부, 미국 조세협약(US treaty position), ECB 기준 금리(ECB reference rate), 그리고 십진수 산술(Decimal arithmetic) 등이 포함됩니다. 각 단계는 neatlogs span으로 처리되므로, 모든 계획은 하나의 추적(trace)이 됩니다:
@neatlogs.span(kind="TOOL", tool_name="fx_quote", description="ECB 일일 기준 환율 USD -> 현지 통화")
def fx_quote(quote_ccy: str) -> dict:
_maybe_fail("fx_quote")
...
경로(route)와 W-8BEN의 10번 항목은 모델로부터가 아니라 소스된 행(sourced rows)에서 가져옵니다. 모델이 설명을 작성하면, 이후 출처 확인 가드레일(provenance guardrail)이 이를 검사합니다. 도구 출력에 포함되지 않은 금액, 환율 또는 조약 기사는 답변을 차단하며, 이는 한 번의 재시도 후 도구 출력을 기반으로 생성된 템플릿으로 이어집니다.
with neatlogs.trace("provenance_guardrail", kind="GUARDRAIL") as sp:
verdict = check(text, tool_outputs, card, question)
sp.set_attribute("neatlogs.guardrail.passed", verdict["passed"])
...
가드레일이 span이기 때문에, 차단된 모든 답변은 neatlogs에서 검색 가능했습니다. 이것이 디버깅 도구가 되었습니다.
추적(trace) 검색을 통해 발견한 것들
neatlogs MCP 서버에서 search_traces "provenance_guardrail BLOCKED"를 실행하자, 설명 부분이 거부된 v2 계획들이 반환되었습니다. get_trace_context를 사용하니 가드레일 판정(guardrail verdict) 옆에 모델의 텍스트가 표시되었습니다. 이를 통해 두 가지 실제 버그가 발견되었습니다:
- 제가 만든 금액 규칙이
2000.00 USD를 _연도_로 읽었기 때문에, 정확한 답변이 차단되었습니다. 천 단위 구분 기호 없이 작성된 1,900부터 2,099까지의 모든 금액에 영향을 미쳤습니다. 이는 오탐(false block)이었고, span 덕분에 발견할 수 없었을 것입니다. - 사용자 본인이 제시한
두 가지 모두 고정되어 있으며, 결함 이름으로 지정된 테스트에 의해 고정되고 재실행됩니다. 트레이스 ID가 포함된 상세 내용은 리포지토리(evidence/rca-01.md)에 있습니다.
전체 로컬 코드 그래프는 해당 루프의 두 번째 도구였습니다. 연도 수정 후, entire graph search를 사용하여 버그를 일반적인 말로 설명했을 때, 수정된 규칙을 첫 번째로, 그 테스트를 두 번째로 순위화했습니다. 이 역시 교육적인 방식으로 잘못되었습니다: rail_eligibility에 대한 impact가 0 호출자를 보고했는데, 이는 아마도 플래너(planner)가 도구를 함수 값으로 전달하기 때문일 것입니다 (att.call("fx_quote", tools.fx_quote, ...)). 그래프는 이를 추적하지 못합니다. 저는 이것을 숨기는 대신 발견된 것으로 기록했습니다.
도구가 실패할 때 (When a tool fails)
해커톤에서는 실패한 액션 복구 실행(failed-action recovery run)을 요청했기 때문에, 제가 의도적으로 하나를 만들었습니다. scripts/eval.py --planner v2 --fail fx_quote는 FX 도구가 호출될 때마다 오류를 발생시키도록 만듭니다:
for attempt in (1, 2):
try:
return fn(*args)
...
에이전트는 한 번 재시도한 후, 날짜로 스탬프가 찍힌 마지막 ECB 환율로 폴백(fallback)하고 종료합니다. 24 USD 지급자 시나리오에서는 총 24번의 폴백이 발생했습니다. 모든 48개 계획이 완료되었고, 배송 불가능한 경로는 0건이었으며, 350/350 수치가 확보되었습니다. neatlogs 트레이스는 두 개의 ERROR 스팬과 폴백 스팬을 보여주며, 대시보드 바닥글에는 오류가 발생한 도구 스팬을 계산하기 때문에 "오류와 함께 종료됨(ended with error)"이라고 표시됩니다. 저는 이를 깔끔하게 정리하는 대신 페이지에 그대로 남겨두었습니다. 에이전트는 모델에게 환율을 요청하지 않습니다.
비용 (What it costs)
명세가 적용된 토큰 측정치: 계획당 모델 비용 $0.0033, 제 노트북에서 p50은 5.50초, p95는 10.31초입니다. cfo.ai에서는 기본 시나리오(가정 가격, 계획되었지만 구축되지 않음)를 실행하는 데 월 $18.887이 소요되며, 이로 인해 $25 AWS 크레딧은 수익 발생 전까지 1.35개월 동안 지속됩니다. 모델은 여기서 공유됩니다: cfo.ai/s/PQ2HUGmiJl3P0RQB.
제한 사항 (Limits)
- 세 나라는 완전히 출처가 확보되었습니다 (인도네시아, 인도, 필리핀). 나머지 모든 것은 의도적으로 불분명합니다(UNCLEAR).
- 가드레일은 규칙 기반입니다. 새로 분리된 테스트 세트에서 62/66개의 위반 사항과 3/66개의 정직한 문장을 차단했으며, 이는 제가 누락된 부분을 수정하기 전의 수치입니다. 새로운 표현들이 대략 그 비율로 통과할 것으로 예상되므로, 경로와 모든 금액은 코드로 결정됩니다.
- Payoneer의 수취 수수료는 하나의 숫자로 공개되지 않기 때문에 '실제 입금액'은 상한선(upper bound)입니다.
- 지금까지 확보된 영수증은 제가 직접 만든 것입니다: 알려진 지급 건에 대한 재현 (최대 $4,900 약정, $4,897 도착)과 테스트 영수증 1건입니다. 실제 사용자로부터의 전방위 영수증은 아직 없습니다.
- Cair는 양식을 설명할 뿐, 실제로 제출하지는 않습니다. 세금 자문이 아닙니다.
모든 수치를 재현하기 위한 저장소(Repo), 증거 자료 및 명령어: github.com/edycutjong/cair.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기