AI Agent Tool 계약: 호출 형식이 맞아도 작업은 실패할 수 있다

AI 깊이 이해하기 · 16편 | 입력·출력 스키마, 사전 조건, 실행 결과 검증

문서 A의 변경 보고서를 만들려는데, 비교 도구에 문서 B의 스냅샷이 들어갔다. 식별자는 모두 문자열이고 필수 항목도 빠짐없다. JSON 검사는 통과한다. 그대로 비교하면 두 문서의 차이가 문서 A의 변경 사항으로 보고될 수 있다.

이번 편은 공개 문서의 이전·현재 스냅샷을 비교해 로컬 보고서를 만드는 교육용 Agent의 도구 계약을 설계한다. 실제 서비스에서 관측한 장애나 성능 실험은 아니다. Tool Calling 입문 글에서 호출과 실행을 구분했다면, 여기서는 실행을 허용할 조건과 결과를 받아들일 조건을 구체화한다. 13편의 작업 상태 관리에 넘길 증거가 개별 도구에서 어떻게 만들어지는지 살펴보는 단계다.

1. 스키마가 검사하는 것과 외부에서 확인할 것

비교 도구의 입력을 먼저 작게 정의하자. source_id는 비교 대상 문서, 나머지 두 값은 비교할 스냅샷의 식별자다. 아래는 이 글에서 만든 JSON Schema 예시이며 특정 모델 제공사의 API 요청 전체가 아니다.

{
  "type": "object",
  "properties": {
    "source_id": { "type": "string", "minLength": 1 },
    "before_snapshot_id": { "type": "string", "minLength": 1 },
    "after_snapshot_id": { "type": "string", "minLength": 1 }
  },
  "required": [
    "source_id",
    "before_snapshot_id",
    "after_snapshot_id"
  ],
  "additionalProperties": false
}

required는 지정한 속성의 존재를, additionalProperties: false는 이 구조에서 정의하지 않은 속성의 배제를 요구한다. 문자열의 빈 값은 minLength로 제한했다. 필수 속성이라고 적는 것만으로 빈 문자열까지 막히지는 않는다. 이 구분은 JSON Schema의 객체와 문자열 문서에서 확인할 수 있다.

이 스키마를 통과한 식별자가 실제 저장소에 존재하는지는 아직 모른다. 두 스냅샷이 같은 문서에 속하는지, 사용자가 요청한 비교 기준인지도 별도로 확인해야 한다. 식별자의 모양과 그 식별자가 가리키는 대상 사이에 검증할 관계가 남아 있다.

스키마가 단순한 자료형 검사만 할 수 있다는 뜻은 아니다. JSON Schema는 조건부 스키마처럼 더 풍부한 제약도 표현한다. 다만 호출 입력에 없는 현재 권한이나 저장소의 실제 내용을 스키마 선언만으로 알 수는 없다. 어떤 JSON Schema 버전과 키워드를 지원하고 어느 단계에서 강제하는지는 사용하는 API·SDK·검증기 문서에서 확인해야 한다. 한 제품의 엄격한 출력 형식 보장을 모든 도구 호출 환경의 성질로 확대하면 안 된다.

2. 도구 계약에 실행 전후의 약속을 넣기

도구 계약은 이 글에서 입력 형식, 입력의 의미, 실행 허용 조건, 결과의 의미를 함께 기록하는 명세를 가리킨다. 설명문에만 적고 끝내지 않고, 실제로 조건을 확인할 실행 코드와 연결한다. 예제의 compare_document_snapshots 계약은 다음처럼 정할 수 있다.

계약 항목이 예제에서 정할 내용
입력문서 식별자와 고정된 두 스냅샷 식별자. 비교 방향은 before에서 after로 한다.
권한현재 실행 주체가 해당 스냅샷을 읽을 수 있어야 한다. 이 도구는 원문 수정이나 외부 전송 권한을 사용하지 않는다.
사전 조건두 스냅샷이 존재하고 같은 source_id에 속하며, 비교 가능한 추출 형식을 갖춰야 한다.
사후 조건실제로 비교한 입력 식별자, 비교 범위, 누락 범위, 변경 근거를 반환한다. 불완전한 비교를 complete로 표시하지 않는다.
실행 효과스냅샷 내용을 읽고 비교 결과를 반환한다. 이 도구 자체는 보고서 파일을 저장하지 않는다.
오류입력 형식 오류, 대상 불일치, 읽기 거부, 불완전한 원문을 구분해 알린다.

사전 조건(precondition)은 작업을 시작해도 되는 조건이다. 사후 조건(postcondition)은 성공했다고 반환하려면 실행 뒤에 성립해야 하는 조건이다. “두 문서의 차이를 알려준다”는 설명에 비해 길지만, 실패했을 때 어느 약속을 어겼는지 찾을 수 있다.

권한은 모델이 입력에 넣은 authorized: true 같은 값으로 증명하지 않는다. 호스트 애플리케이션과 도구 서버가 신뢰할 수 있는 실행 주체와 정책을 기준으로 확인한다. 이 편에서는 검사의 위치까지만 정한다. 계정별 접근 제어나 권한 위임의 자세한 설계는 뒤의 권한 편에서 다룬다.

검사와 실행 사이에 대상이 바뀔 가능성도 계약에 반영해야 한다. 이 예제는 내용이 고정된 스냅샷을 읽는다고 가정한다. 실제 시스템에서 식별자가 최신 문서로 다시 연결될 수 있다면 그 가정은 성립하지 않는다. 구현은 버전이 고정된 읽기나 저장소가 제공하는 일관성 수단으로 가정을 뒷받침해야 한다.

3. 합성 사례: 정상 JSON을 실행 전에 거부하기

교육용 저장소에 다음 세 스냅샷이 있다고 하자. 아래 이름과 내용은 모두 설명을 위해 만든 값이다.

  • snap-a-7: 문서 A의 이전 스냅샷. 보관 기간은 30일이다.
  • snap-a-8: 문서 A의 다음 스냅샷. 보관 기간이 60일로 바뀌었다.
  • snap-b-8: 문서 B의 스냅샷. 문서 A의 개정 이력에 속하지 않는다.

모델이 다음 호출을 제안했다.

{
  "source_id": "document-a",
  "before_snapshot_id": "snap-a-7",
  "after_snapshot_id": "snap-b-8"
}

1절의 입력 스키마에는 맞는다. 그러나 도구가 저장소에서 메타데이터를 읽으면 마지막 스냅샷의 source_id가 document-b임을 확인할 수 있다. 계약에 따르면 비교를 시작하기 전에 거부해야 한다. 이때 필요한 답은 막연한 “오류 발생”보다 구체적이다.

{
  "status": "rejected",
  "code": "SOURCE_MISMATCH",
  "field": "after_snapshot_id",
  "expected_source_id": "document-a",
  "observed_source_id": "document-b",
  "comparison_performed": false
}

이 오류 객체는 예제 애플리케이션이 정한 형식이다. MCP나 모든 API에 공통으로 정해진 필드가 아니다. 또한 실제 권한 확인을 통과한 범위 안에서만 대상 정보를 돌려줘야 한다.

그다음은 snap-b-8의 글자 하나를 추측으로 바꾸는 일이 아니다. 호스트가 조회한 문서 A의 스냅샷 목록과 사용자의 비교 기준을 대조해 snap-a-8이 맞는지 확인한다. 대상이 확인된 호출에 대해 도구가 비교를 수행하고, “보관 기간 30일 → 60일”과 두 스냅샷의 근거 위치를 반환하도록 설계할 수 있다.

이 사례의 기대 결과는 세 가지다. 잘못된 대상은 스키마 검사 뒤의 의미 검증에서 거부된다. 올바른 대상에서는 변경 내용과 근거가 함께 나온다. 로컬 보고서 저장은 이 비교 결과를 검증한 다음 별도 도구가 맡는다. 아직 실행 실험으로 입증한 결과는 아니다.

4. 출력에도 형식과 의미가 있다

입력 스키마만 정하면 호출자가 무엇을 넣을지는 알지만, 돌아온 결과를 어떻게 해석해야 하는지는 비어 있을 수 있다. MCP 2025-11-25 도구 명세에는 선택적인 outputSchema가 있다. 이를 제공한 서버는 스키마에 맞는 구조화 결과를 반환해야 하고, 클라이언트에는 그 결과를 검증하도록 권고한다. 선언과 실제 검증 실행은 확인할 대상이 다르다.

예제 도구의 출력에는 아래 항목을 두자. 성공과 오류의 결과 형태도 구별해 정의한다.

  • status: 완전 비교, 부분 비교, 거부를 구분하는 값
  • compared_inputs: 실제 사용한 문서와 두 스냅샷 식별자
  • compared_sections, missing_sections: 비교한 범위와 읽지 못한 범위
  • changes: 변경 항목과 이전·이후 근거 위치

스키마는 changes가 배열인지, 각 항목에 필요한 속성이 있는지 검사할 수 있다. 하지만 그 배열이 비어 있다는 사실은 읽지 못한 구간까지 변화가 없었다는 증거가 되지 않는다. 원문 추출이 실패했는데 결과가 빈 배열이라면, 그대로 “변경 없음” 보고서를 만들면 안 된다.

이 예제에서는 “변경 없음”을 인정하려면 실제로 비교한 두 스냅샷이 요청한 before/after 식별자와 각각 일치하고, 계약상 필요한 구간을 모두 비교했으며, 비교 결과가 비어 있어야 한다고 정한다. 의미 있는 내용 차이를 요약하는 기능이라면 비교 규칙의 한계도 적어야 한다. 공백 차이를 무시할지, 표의 셀 이동을 변경으로 볼지에 따라 같은 문서의 결과가 달라질 수 있다.

반환된 근거 위치가 실제로 존재하는지, 인용한 내용이 원문과 맞는지는 실행 코드로 일부 확인할 수 있다. “이 변경으로 이용자 의무가 강화됐다” 같은 해석은 원문에 대한 별도 검토가 필요하다. 출력 스키마를 통과했다는 사실로 해석의 정확성까지 확정하지 않는다.

5. 오류 응답과 실행 효과를 따로 기록하기

도구 실패에는 서로 다른 상황이 들어 있다. 호출 형식이 잘못돼 도구까지 도달하지 못했을 수 있다. 도구가 입력을 읽은 뒤 업무 규칙에 따라 거부했을 수도 있다. 작업은 끝났는데 응답을 받는 통신만 끊겼을 가능성도 있다.

MCP의 오류 처리 명세는 프로토콜 오류와 isError: true로 표현하는 도구 실행 오류를 구분한다. 이 구분은 복구 방향을 정하는 데 도움이 되지만, 오류 표시 하나가 모든 외부 효과의 부재까지 증명하는 것은 아니다. 효과에 대한 보장은 해당 도구 계약에 따로 있어야 한다.

앞의 비교 도구는 보고서를 쓰지 않는다고 정했다. 반면 후속 save_local_report 도구에는 파일 생성 효과가 있다. 저장 요청 뒤 응답이 끊겼다면 호스트가 아는 사실은 “응답을 확인하지 못했다”까지다. 파일이 없는지, 이미 생성됐는지는 추가 관측이 필요하다.

확인된 상황남길 결과다음 판단에 필요한 것
사전 조건 검사에서 거부거부 이유와 실행하지 않은 범위수정 가능한 입력인지, 사용자 판단이 필요한지
비교 범위 일부 누락부분 결과와 누락된 구간누락 구간을 확보할 수 있는지
저장 요청 후 응답 불명확실행 효과 미확정저장 대상 조회와 기존 요청의 결과

특히 마지막 상황에 일반적인 “다시 시도” 버튼을 곧바로 연결하면 이미 수행된 작업을 반복할 수 있다. HTTP의 RFC 9110 §9.2.2도 비멱등 요청의 자동 재시도를 허용할 조건을 제한한다. 여기서는 불명확한 효과를 표현할 자리를 마련하는 데 집중한다. 중복 실행을 막는 식별자와 재시도 설계는 17편에서 이어간다.

6. 모델이 제안하고 실행 코드가 확인하는 경계

사용자 정의 도구에서는 모델이 도구 이름과 인자를 제안하고, 이를 받은 애플리케이션이 실제 코드를 호출한다. Anthropic의 Tool use 문서도 애플리케이션에서 실행하는 클라이언트 도구와 제공사 인프라에서 실행하는 서버 도구를 구별한다. 모든 도구가 사용자의 서버에서 실행된다는 뜻은 아니다. 어느 실행 주체가 어떤 검사를 책임지는지 확인해야 한다.

이 글의 호스트 애플리케이션에는 다음 경계를 둔다. 모델이 낸 인자를 파싱한 뒤 스키마를 검증한다. 신뢰할 수 있는 권한과 실제 대상 정보를 확인한다. 사전 조건을 만족할 때만 실행하고, 반환된 출력의 형식과 사후 조건을 검사한다. 확인한 결과와 남은 불확실성을 모델에 전달한다. 모델이 결과를 유창하게 설명했다는 이유로 중간 검사를 생략하지 않는다.

바로 준비할 수 있는 것은 도구별 계약 검사 목록이다. 예제 비교 도구와 보고서 저장 도구에 다음 사례를 넣어보자.

  1. 필수 입력 누락: 실행 전에 거부되는가. 어떤 항목이 부족한지 알 수 있는가.
  2. 모양은 맞는 다른 문서: 스키마 검사를 통과해도 문서 식별자 관계에서 거부되는가.
  3. 권한 없는 스냅샷: 읽기를 차단하고 허용되지 않은 내용도 응답에 섞지 않는가.
  4. 부분 추출과 빈 변경 배열: 비교 누락을 남기며 “변경 없음”을 확정하지 않는가.
  5. 잘못된 출력: 응답 속성이 빠졌거나 요청과 다른 스냅샷이 반환됐을 때 정상 결과로 사용하지 않는가.
  6. 저장 응답 유실: 실행 효과를 미확정으로 남기고, 확인 없이 저장을 반복하지 않는가.

이 목록은 검증 계획이다. 실제 스키마 검증기, 스냅샷 저장소, 도구 구현을 연결해 실행해야 통과 여부를 말할 수 있다. 단위 테스트가 통과해도 실제 LLM이 적절한 도구와 대상을 고르는 능력, 네트워크 장애 복구, 요약의 사실성까지 함께 검증된 것은 아니다.

처음의 잘못된 비교를 막는 데 필요한 것은 문서 A의 이름을 프롬프트에 한 번 더 쓰는 것만이 아니었다. 호출 속 식별자와 저장소 속 문서의 관계를 실행 직전에 확인하는 절차가 필요했다. 다음 도구를 추가할 때도 정상 호출 예시 옆에 한 가지를 더 적어보자. 형식은 맞지만 실행하면 안 되는 입력은 무엇이며, 그 입력을 누가 어떤 근거로 거부할 것인가.

이어 읽기

자료 확인: 2026-10-05. 표준과 제품 문서는 본문의 해당 주장에 연결했다. 도구 이름, 입력·오류 객체, 문서와 스냅샷, 검사 목록은 이 글의 교육용 설계다. 실제 배포, 모델 성능 측정 또는 장애 시험 결과를 보고하는 글이 아니다.


다른 글 보기 · 주제 탐색 · 작성자와 편집 기준 · 문의·정정 요청

RUDA DIRECTOR에서 더 알아보기

지금 구독하여 계속 읽고 전체 아카이브에 액세스하세요.

계속 읽기