AI 도구 계약 테스트 (AI Tool Contract Testing): 프로덕션에 반영되기 전 잘못된 에이전트 호출을 차단하는 방법
요약
에이전트가 도구를 호출할 때 발생할 수 있는 오류를 방지하기 위한 'AI 도구 계약 테스트'의 중요성을 다룹니다. 단순한 프롬프트 평가를 넘어 CI 환경에서 검증 가능한 계약을 통해 에이전트의 신뢰성을 확보하는 방법을 제시합니다.
핵심 포인트
- 에이전트의 잘못된 도구 호출은 워크플로우 파괴 및 비용 상승을 초래함
- 도구 계약 테스트는 추론과 행동 사이의 경계를 안전하게 검증함
- 사용자 의도, 전제 조건, 인자, 결과 형태 등을 포함한 출시 합의가 필요함
- 단위 테스트와 프롬프트 평가 사이의 공백을 메우는 필수적인 단계임
도구를 사용하는 에이전트(tool-using agent)는 채팅 중에는 매우 똑똑해 보일 수 있지만, 단 한 번의 API 호출만으로도 워크플로우를 망가뜨릴 수 있습니다.
위험한 부분은 모델이 create_invoice, search_docs, 또는 update_customer 중 무엇을 선택했는지 여부만이 아닙니다. 진짜 위험한 부분은 해당 호출이 허용되었는지, 신뢰할 수 있는 컨텍스트(context)에 근거했는지, 재시도(retry)하기에 안전한지, 현재 스키마(schema)와 호환되는지, 그리고 다운스트림 상태(downstream state)에 의해 검증되었는지 여부입니다.
이것이 바로 AI 도구 계약 테스트(AI tool contract testing)가 프로덕션 에이전트(production agents)를 출시하는 빌더들에게 유용한 출시 습관이 되고 있는 이유입니다. 이는 도구 호출을 "모델이 아마 올바른 일을 했을 것이다"라는 추측에서, 팀이 CI(지속적 통합)에서 실행할 수 있는 검토 가능한 계약(contracts)으로 전환해 줍니다.
도구 호출에 계약이 필요한 이유
대부분의 에이전트 버그는 극적이지 않습니다. 지루하고, 비용이 많이 들며, 디버깅하기 어렵습니다:
- 에이전트가 잘못된 테넌트 ID(tenant ID)로 올바른 도구를 호출함.
- 스키마(schema)는 유효하지만, 값이 신뢰할 수 없는 페이지에서 유입됨.
- 재시도(retry)로 인해 두 개의 캘린더 이벤트, 두 개의 티켓, 또는 두 번의 환불이 발생함.
- 이름이 변경된 열거형(enum)이 기존 프롬프트(prompts)를 조용히 망가뜨림.
- 도구 호출은 성공했지만, 에이전트가 사용자에게 다른 내용을 전달함.
- 사용자가 작업을 승인하지 않았음에도 폴백(fallback) 도구가 실행됨.
단위 테스트(Unit tests)는 결정론적 코드(deterministic code)를 잡아냅니다. 프롬프트 평가(Prompt evals)는 답변 품질을 잡아냅니다. 관측성(Observability)은 사고가 발생한 후에 이를 잡아냅니다. 도구 계약 테스트(Tool contract tests)는 그 중간에 위치합니다. 즉, 에이전트가 추론(reasoning)과 행동(action) 사이의 경계를 안전하게 넘을 수 있음을 증명합니다.
도구 계약을 다음과 같은 작은 출시 합의(release agreement)로 생각하십시오:
이 사용자 의도(user intent)에 대해, 이러한 전제 조건(preconditions) 하에, 에이전트는 이러한 인자(arguments)를 사용하여 이 도구 버전(tool version)을 호출할 수 있으며, 이러한 결과 형태(result shapes)를 받고, 이러한 부수 효과(side effects)를 생성하며, 이 승인된 방식으로 응답할 수 있다.
어느 한 부분이라도 실패한다면, 해당 워크플로우는 출시되어서는 안 됩니다.
빌더들이 주목해야 할 현재 트렌드 신호
최근 AI 플랫폼의 활동은 동일한 방향을 가리키고 있습니다. 즉, 더 많은 에이전트, 더 많은 도구 사용, 그리고 비용과 신뢰성을 제어해야 한다는 더 큰 압박입니다.
검색 및 뉴스 스캔 결과 몇 가지 활발한 신호가 나타났습니다:
- 최신 모델 API들은 프로그래밍 방식의 도구 호출 (programmatic tool calling), 병렬 에이전트 작업 (parallel agent work), 프롬프트 캐싱 (prompt caching), 그리고 모델 라우팅 (model routing)을 강조하고 있습니다.
- 에이전트 워크플로 플랫폼 (agent workflow platforms), 오픈 소스 자동화 도구, 그리고 MCP 스타일의 통합 (MCP-style integrations)은 여전히 개발자들의 관심을 끌고 있습니다.
- AI 게이트웨이 (AI gateways)는 지출 한도 (spend caps), 감사 추적 (audit trails), 지역별 처리 (regional processing), 그리고 라우팅 제어 (routing controls) 기능을 추가하고 있습니다.
- 도구 사용 평가 (tool-use evaluation) 콘텐츠가 등장하고 있지만, 그 중 상당수는 도구가 호출되었는지 여부에만 집중할 뿐, 전체 비즈니스 계약 (business contract)이 유지되었는지 여부는 다루지 않습니다.
- ToolFuzz와 같은 연구는 도구 문서 (tool documentation)와 자연어 도구 설명 (natural-language tool descriptions)이 불충분하게 정의될 수 있으며, 이로 인해 에이전트가 런타임 오류 (runtime errors)나 잘못된 응답을 생성할 수 있음을 강조합니다.
실질적인 함의는 간단합니다. 에이전트가 더 많은 시스템을 접하게 됨에 따라, 도구 호출 (tool calls)은 하나의 릴리스 표면 (release surface)이 됩니다. 빌더들은 스키마 (schema), 의도 (intent), 권한 (permissions), 부작용 (side effects), 그리고 복구 (recovery)를 모두 다루는 테스트가 필요합니다.
이 주제 뒤에 숨겨진 SEO 및 콘텐츠 격차
“AI 에이전트 테스트 (AI agent testing)”와 “LLM 평가 (LLM evaluation)”라는 광범위한 용어들은 이미 포화 상태입니다. 롱테일 키워드인 “AI 도구 계약 테스트 (AI tool contract testing)”는 더 좁고 실용적입니다.
SERP 스캔 결과, 도구 호출 테스트 (tool-call testing), 도구 호출 평가 (tool-call evaluation), 음성 에이전트 도구 계약 (voice-agent tool contracts), 에이전트 테스트 키트 (agent testing kits), 그리고 자동화된 도구 테스트 (automated tool testing)와 관련된 결과들이 나타났습니다. 공통적인 헤딩(headings)은 다음과 같습니다:
- 도구가 호출되었는지 확인
- 도구 인자 (tool arguments) 검증
- 도구 응답 모킹 (mocking tool responses)
- 의도 정렬 (intent alignment) 평가
- 폴백 (fallbacks) 및 실패 테스트
- 도구 이름, 인자, 타임스탬프 및 결과 로깅
격차는 많은 가이드가 완전한 프로덕션 계약 (production contract) 단계에 도달하기 전에 멈춘다는 점입니다. 소규모 AI SaaS 팀에는 다음 여섯 가지를 하나로 묶는 벤더 중립적인 패턴이 필요합니다:
- 도구 스키마 검증 (tool schema validation)
- 비즈니스 전제 조건 (business preconditions)
- 각 인자에 대한 신뢰할 수 있는 증거 (trusted evidence for each argument)
- 샌드박스 처리 또는 모킹된 실행 (sandboxed or mocked execution)
- 다운스트림 부작용 검증 (downstream side-effect verification)
- CI에서의 릴리스 게이트 (release gates in CI)
이것이 이 가이드의 관점입니다.
스키마 유효성이 계약 유효성은 아니다
JSON 스키마는 이 페이로드 (payload)가 올바른 형태를 갖추고 있는지 알려줄 수 있습니다:
{
"customer_id": "cus_123",
"plan": "pro",
...
다음 사항들은 증명할 수 없습니다:
cus_123이 현재 테넌트 (tenant)에 속해 있는지 여부- 사용자가 플랜 (plan)을 변경할 권한이 있는지 여부
- 해당 플랜이 고객 지원 문서에서 추론된 것이 아니라 사용자에 의해 요청된 것인지 여부
- 날짜가 과금 규칙 (billing rules)에 따라 허용되는지 여부
- 업데이트가 멱등성 (idempotent)을 갖는지 여부
- 최종 사용자에게 표시되는 메시지가 영구적인 시스템 상태 (durable system state)와 일치하는지 여부
이러한 차이는 매우 중요합니다. 스키마 유효성 (Schema validity)은 형태 (shape)에 관한 것이고, 계약 유효성 (Contract validity)은 안전한 동작 (safe behavior)에 관한 것입니다.
훌륭한 도구 계약 (tool contract)은 호출이 실제 인프라에 도달하기 전에 다음 질문들에 답합니다:
- 어떤 도구를 실행할 수 있는가?
- 도구 스키마의 어떤 버전이 허용되는가?
- 어떤 사용자 의도 (user intent)가 존재해야 하는가?
- 각 인자 (argument)를 뒷받침하기 위해 어떤 신뢰할 수 있는 컨텍스트 (trusted context)가 필요한가?
- 어떤 역할 (roles), 테넌트 (tenants), 또는 플랜 (plans)이 이를 실행할 수 있는가?
- 어떤 부작용 (side effects)이 허용되는가?
- 어떤 결과가 성공, 부분적 성공, 또는 실패로 간주되는가?
- 재시도 (retries)는 어떻게 동작해야 하는가?
- 어떤 증거 (evidence)를 로그에 남겨야 하는가?
- 도구가 반환된 후 에이전트 (agent)는 무엇이라고 말해야 하는가?
실용적인 도구 계약 형식
시작하기 위해 복잡한 프레임워크가 필요하지는 않습니다. 일반적인 YAML 또는 JSON 계약만으로도 충분합니다.
id: update_subscription_plan_contract
risk: blocking
owner: growth-platform
...
계약은 작게 유지하십시오. 만약 계약이 코드 리뷰를 하기에 너무 길어진다면, 개발자들은 더 이상 이를 신뢰하지 않게 될 것입니다.
에이전트 도구를 위한 테스트 피라미드
도구 계약 테스트 (Tool contract testing)는 하나의 거대한 평가 스위트 (eval suite)가 아니라, 작은 피라미드 형태로 구성될 때 가장 효과적입니다.
1. 정적 도구 정의 검사 (Static tool definition checks)
모델이 개입하기 전에 다음 사항들을 실행합니다.
모든 도구가 다음을 갖추고 있는지 확인하십시오:
- 안정적인 이름 (stable name)
- 스키마 버전 (schema version)
- 각 필드에 대한 명확한 설명 (descriptions)
- 올바르게 표시된 필수 필드 (required fields)
- 개방형 문자열 대신 엄격한 열거형 (tight enums)
- 문서화된 부작용 (side effects)
- 멱등성 (idempotency) 기대치
- 권한 요구 사항 (permission requirements)
- 유효한 호출과 유효하지 않은 호출의 예시
잘못된 도구 설명(tool descriptions)은 잘못된 모델 동작을 유발합니다. 만약 어떤 필드가 실제로는 "테넌트 범위의 고객 ID (tenant-scoped customer id)"를 의미함에도 "사용자 ID (user id)"라고 되어 있다면, 에이전트가 이를 사용하기 전에 테스트가 실패해야 합니다.
2. 골든 패스 계약 테스트 (Golden-path contract tests)
이 테스트들은 예상되는 해피 패스 (happy path)를 증명합니다.
예시:
test("에이전트가 승인 후에만 플랜을 업데이트하는지 확인", async () => {
const result = await runAgentScenario({
user: "오늘부터 내 계정을 Team 플랜으로 변경해줘. 변경에 동의해.",
...
이는 단순히 에이전트가 도구를 호출했는지만을 테스트하는 것이 아닙니다. 요청(request), 모의 결과(mock result), 지속 가능한 상태(durable state), 그리고 최종 응답(final response)을 함께 테스트합니다.
3. 네거티브 계약 테스트 (Negative contract tests)
네거티브 테스트(Negative tests)는 많은 팀이 실제 버그를 발견하는 지점입니다.
테스트 케이스에는 다음 사항들이 포함되어야 합니다:
- 사용자의 권한 부족
- 사용자의 모호한 질문
- 대화 도중 사용자의 변심
- 검색된 컨텍스트(context)에 악의적인 지시사항 포함
- 도구 결과가 부분적임
- 도구 타임아웃 (timeout)
- 중복된 요청 도착
- 오래된 스키마 (schema) 버전 등장
- 테넌트 ID (tenant ID) 불일치 시도
- 쓰기 작업 (write action)에 대한 승인 누락
각 케이스에 대해 안전한 동작을 정의하세요. 때로는 명확한 질문을 던지는 것을 의미할 수도 있고, 때로는 동작을 거부하는 것을 의미할 수도 있습니다. 혹은 사람의 검토 큐 (human review queue)로 에스컬레이션(escalating)하는 것을 의미할 수도 있습니다.
4. 프로덕션 트레이스 기반의 리플레이 테스트 (Replay tests from production traces)
에이전트가 라이브 상태가 되면, 실제 실패 사례를 계약 테스트로 변환하세요.
리플레이 패킷 (replay packet)에는 다음이 포함될 수 있습니다:
{
"trace_id": "tr_789",
"messages": "비식별 처리된 대화 (redacted conversation)",
...
가공되지 않은 비밀 정보(raw secrets)나 불필요한 개인 데이터를 저장하지 마세요. 먼저 비식별 처리(Redact)를 한 다음, 리플레이(replay) 하세요.
리플레이 테스트는 사고(incidents)를 영구적인 가드레일 (guardrails)로 바꿉니다. 이것이 소규모 팀이 대규모 QA 그룹을 고용하지 않고도 신뢰성을 구축하는 방법입니다.
도구 호출 인자(tool-call arguments) 테스트 방법
인자 테스트는 단순히 "JSON 파싱 여부"보다 더 엄격해야 합니다.
다음 네 가지 확인 절차를 사용하세요:
소스 확인 (Source check)
모든 중요한 인자는 신뢰할 수 있는 소스 (trusted source)를 가져야 합니다.
customer_id는 세션(session) 또는 인증 컨텍스트(auth context)에서 가져와야 합니다.amount는 검증된 인보이스(invoice) 또는 사용자 확인(user confirmation)에서 가져와야 합니다.email은 고객 기록(customer record) 또는 확인된 사용자 메시지에서 가져와야 합니다.document_id는 테넌트 필터링된 검색(tenant-filtered retrieval)을 통해 가져와야 합니다.
만약 소스가 "모델이 추측함(model guessed it)"이라면, 계약(contract)은 실패해야 합니다.
권한 확인 (Permission check)
행위자(actor)는 해당 동작을 수행할 수 있는 권한이 있어야 합니다.
이 확인 작업은 프롬프트(prompt)뿐만 아니라 애플리케이션 코드(application code)에 포함되어야 합니다. 계약(contract)은 이러한 가드(guard)가 존재함을 증명해야 합니다.
비즈니스 규칙 확인 (Business rule check)
스키마(Schema)는 필드가 유효하다고 말하지만, 비즈니스 규칙(Business rules)은 해당 값이 허용되는지를 말합니다.
예를 들어:
- 환불(refunds)은 결제된 금액(captured payment)을 초과할 수 없습니다.
- 체험 기간 연장(trial extensions)은 정책 제한(policy limits)을 초과할 수 없습니다.
- 무료 계정(free account)의 지원 우선순위(support priority)를 엔터프라이즈 전용(enterprise-only)으로 설정할 수 없습니다.
- 계정 삭제(account deletion)에는 두 번째 확인(second confirmation)이 필요합니다.
일관성 확인 (Consistency check)
최종 메시지는 결과와 일치해야 합니다.
만약 도구(tool)가 status: queued를 반환한다면, 에이전트(agent)는 "완료되었습니다(done)"라고 말해서는 안 됩니다. 만약 도구가 partial_success를 반환한다면, 응답은 무엇이 완료되었고 무엇이 후속 조치가 필요한지를 설명해야 합니다.
현실을 숨기지 않으면서 도구 모킹하기 (Mocking tools without hiding reality)
모킹(Mocks)은 계약 테스트(contract tests)를 빠르고 결정론적(deterministic)으로 만듭니다. 하지만 모킹은 거짓을 말할 수도 있습니다.
세 가지 모킹 계층(mock layers)을 사용하세요:
형태 모킹 (Shape mocks)
이는 에이전트가 유효한 결과 스키마와 유효하지 않은 결과 스키마를 모두 처리할 수 있음을 증명합니다.
{ "status": "success", "ticket_id": "tkt_123" }
동작 모킹 (Behavior mocks)
이는 타임아웃(timeouts), 중복 응답(duplicate responses), 부분적 실패(partial failures), 권한 오류(permission errors)를 시뮬레이션합니다.
{ "status": "error", "code": "permission_denied" }
상태 모킹 (State mocks)
이는 백엔드 상태(backend state)가 예상대로 변경되었음을 증명합니다.
쓰기 도구(write tools)의 경우, 상태 모킹(state mocks)이 가장 중요합니다. 통과된 테스트는 생성된 티켓(ticket), 업데이트된 기록(record), 전송된 메시지(message) 또는 예약된 이벤트(scheduled event)가 정확히 한 번 존재하는지 확인해야 합니다.
만약 도구에 부수 효과(side effects)가 있다면, 절대 "함수가 성공을 반환했다"에서 멈추지 마세요. 상태(state)를 확인하십시오.
에이전트 도구를 위한 CI 릴리스 게이트 (CI release gates for agent tools)
간단한 CI 게이트(CI gate)만으로도 프로덕션(production)에서 발생하는 놀라울 정도로 많은 문제들을 방지할 수 있습니다.
다음 중 하나라도 변경될 때 차단 테스트(blocking tests)를 실행하세요:
- 프롬프트 파일 (prompt files)
- 도구 설명 (tool descriptions)
- JSON 스키마 (JSON schemas)
- MCP 서버 정의 (MCP server definitions)
- 모델 라우팅 규칙 (model routing rules)
- 승인 정책 (approval policies)
- 검색 필터 (retrieval filters)
- 워크플로우 코드 (workflow code)
- SDK 버전 (SDK versions)
유용한 릴리스 명령은 다음과 같을 수 있습니다:
npm run test:tool-contracts -- --risk=blocking
저위험 조회 도구(low-risk lookup tools)보다 고위험 도구(high-risk tools)에 대해 더 엄격하게 게이트(gate)를 적용하세요.
| 도구 유형 | 예시 | 권장 게이트 |
|---|---|---|
| 읽기 전용 조회 (Read-only lookup) | 문서 검색 (search docs) | 스키마 + 의도 테스트 (schema + intent tests) |
| ... |
이렇게 하면 테스트 스위트(test suite)를 실용적으로 유지할 수 있습니다. 모든 도구에 동일한 수준의 격식(ceremony)이 필요하지는 않습니다.
계약(contracts)이 요구해야 할 관측성(Observability) 필드
모든 실행이 동일한 증거를 방출할 때 도구 계약(Tool contracts)을 디버깅하기가 더 쉬워집니다.
민감한 데이터는 마스킹(redacted) 처리하고 다음 필드들을 로그로 남기세요:
trace_idtenant_id_hashactor_roleagent_nameprompt_versionmodel_routetool_nametool_schema_versionargument_sourcespolicy_decisionapproval_ididempotency_keyresult_statuslatency_msretry_countside_effect_checkfinal_response_hash
명확한 보존 및 액세스 정책(retention and access policy)이 없는 한, 가공되지 않은 비밀값(raw secrets), 개인 정보가 포함된 전체 프롬프트, 또는 마스킹되지 않은 고객 페이로드(customer payloads)를 로그로 남기지 마세요.
아키텍처에서의 위치
AI 도구 계약 테스트(AI tool contract testing)는 프로덕션 AI 스택의 여러 부분을 연결합니다:
- LLM 게이트웨이 (LLM gateway): 모델 경로(model route), 프롬프트 버전, 비용 및 폴백(fallback) 동작을 기록합니다.
- 에이전트 런타임 정책 (Agent runtime policy): 도구 호출 허용 여부를 결정합니다.
- 승인 게이트 (Approval gates): 위험한 쓰기(writes) 작업을 인간의 검토를 위해 일시 중지합니다.
- 샌드박스 (Sandbox): 테스트 중에 쓰기 도구(write tools)를 안전하게 실행합니다.
- 관측성 (Observability): 트레이스(traces)와 리플레이 패킷(replay packets)을 저장합니다.
- 평가 하네스 (Evaluation harness): 시나리오 및 회귀 테스트(regression tests)를 실행합니다.
- 장애 검토 (Incident review): 실패 사례를 새로운 계약(contracts)으로 전환합니다.
1인 SaaS 개발자와 마이크로 SaaS 빌더들의 목표는 기업용 쇼(enterprise theater)를 보여주는 것이 아닙니다. 목표는 조용한 실패(silent failures)를 줄이는 것입니다. 가장 위험도가 높은 3가지 도구부터 시작하여 해당 도구들에 대한 계약(contracts)을 먼저 작성하세요.
피해야 할 일반적인 실수
실수 1: 프롬프트(Prompts)를 정책으로 신뢰하는 것
프롬프트는 행동을 안내할 수 있습니다. 하지만 프롬프트가 유일한 권한 시스템(permission system)이 되어서는 안 됩니다.
만약 특정 도구가 고객 데이터를 변경할 수 있다면, 코드 내에서 권한을 강제하고 계약(contracts)을 통해 이를 테스트해야 합니다.
실수 2: 해피 패스(Happy path)만 테스트하는 것
대부분의 잘못된 도구 호출은 복잡하고 지저분한 대화 과정에서 발생합니다. 모호함(ambiguity), 승인 누락(missing approval), 오래된 컨텍스트(stale context), 그리고 도구 실패(tool failure)에 대한 테스트를 추가하세요.
실수 3: 멱등성(Idempotency)을 무시하는 것
에이전트 시스템에서 재시도(Retries)는 일반적입니다. 하지만 중복된 부작용(side effects)은 일반적이지 않습니다.
모든 쓰기 도구(write tool)는 멱등성 키(idempotency key)를 갖거나 안전한 재시도 설계(safe retry design)를 갖추어야 합니다.
실수 4: 스키마(Schemas)의 드리프트(drift)를 방치하는 것
도구 스키마에 버전을 부여하세요. 백엔드(backend)는 v3를 기대하고 있는데 모델이 여전히 v2 인자(arguments)를 출력한다면, 계약(contract)이 이를 잡아내야 합니다.
실수 5: 도구 테스트와 최종 응답을 분리하는 것
도구 호출(tool call) 자체는 올바를 수 있지만, 최종 답변은 틀릴 수 있습니다. 두 가지 모두를 테스트하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기