로드 중

엄격한 도구 사용: 자신의 호출을 거부하지 마세요

강의 210분
로드 중

엄격한 도구 사용: 자신의 호출을 거부하지 마세요

요약대본

백엔드가 Claude 스스로 생성한 도구 호출을 거부할 때, 엄격한 도구 사용이 그 해결책입니다. 이 영상에서는 가상의 엔지니어링 컨설팅 회사인 Meridian의 도구 하나에 대해 이 기능을 켜고, 동일한 100건의 요청을 끈 상태와 켠 상태로 각각 실행합니다.

이 영상의 데모는 2026년 9월에 녹화되었습니다. Claude Haiku 4.5(claude-haiku-4-5)에서 실행되었습니다. 최신 모델에서는 결과가 다를 수 있습니다.

Claude는 일반적으로 도구의 input_schema를 따르지만, 기본적으로 이를 강제하는 장치는 없습니다. 그래서 호출의 입력에 다음 중 하나가 있으면 백엔드가 Claude가 생성한 도구 호출을 거부하는 경우가 있습니다:

  • 정수 2 대신 문자열 "2"
  • 누락된 필수 필드

엄격한 도구 사용(strict tool use)은 이를 해결합니다. 플래그 하나, 즉 strict: true만으로 Claude가 작성하는 입력이 스키마와 일치하게 됩니다.

Meridian에서는 어시스턴트가 queue_stamp_request 도구를 사용하여 면허를 보유한 엔지니어의 날인을 위해 도면 세트를 대기열에 추가합니다. Meridian의 검증기(validator)는 모든 도구 호출을 확인하는 코드입니다. strict를 끄면 100건의 요청 중 79건을 거부합니다. strict를 켜고 검증기에서 생성한 스키마를 사용하면 하나도 거부하지 않습니다.

엄격한 도구 사용의 작동 방식

strict: true를 사용하면 API는 먼저 JSON 스키마를 문법(grammar)으로 컴파일합니다. 문법이란 입력의 각 지점에서 다음에 올 수 있는 토큰을 규정하는 규칙의 집합입니다. 그런 다음 Claude가 도구 호출의 다음 토큰을 선택할 때마다, 문법이 허용하는 토큰만 선택할 수 있습니다. 이를 문법 제약 샘플링(grammar-constrained sampling)이라고 합니다.

스키마는 revision이 정수라고 명시하며, Claude는 "revision": 까지 작성했습니다. strict가 없으면 3, "3", three, 3.0, null 중 다섯 가지 다음 토큰을 선택할 수 있습니다. strict: true를 사용하면 3만 선택할 수 있고 나머지 네 개는 제외됩니다.

strict: true를 사용하면 스키마가 허용하는 토큰만 선택할 수 있으므로, 정수 필드에는 정수가 들어갑니다.

다음은 Meridian의 도구 정의를 축약한 것입니다. strict에는 이 중 두 줄, 즉 플래그와 그 플래그가 요구하는 설정이 필요합니다.

json
{
  "name": "queue_stamp_request",
  "description": "Queues a drawing set for a professional engineer's stamp.",
  "strict": true,
  "input_schema": {
    "type": "object",
    "properties": {
      "drawing_set_id": {"type": "string"},
      "revision": {"type": "integer"}
    },
    "required": ["drawing_set_id", "revision"],
    "additionalProperties": false
  }
}
  • "strict": true는 플래그입니다. 이는 스키마 바깥, 즉 최상위 레벨에서 name, description, input_schema와 나란히 위치합니다.
  • "additionalProperties": false는 설정입니다. 이는 입력이 스키마에 명시되지 않은 필드를 가질 수 없음을 의미하며, strict는 모든 객체에 이 설정을 요구합니다.

검증기에서 스키마 만들기

엄격한 도구 사용은 Claude가 작성하는 입력이 스키마와 일치하도록 만들므로, 스키마는 검증기가 말하는 내용과 일치해야 합니다. 스키마를 직접 작성하면 둘은 서로 어긋나게 됩니다. 그러면 도구 호출이 스키마와 일치하더라도 거부될 수 있습니다. Meridian에서는 손으로 작성한 스키마가 면허 번호를 패턴이 없는 단순 문자열로 만들었습니다. 그 결과 "PE 46196"은 스키마는 통과했지만, PE-와 숫자 다섯 자리를 요구하는 검증기는 통과하지 못했습니다.

규칙을 검증기에 한 번만 작성하고, 거기서 스키마를 생성하세요. Python에서는 검증기가 보통 Pydantic 클래스입니다. TypeScript에서는 보통 Zod로 작성합니다.

아래는 Meridian의 Pydantic 클래스인 StampRequest와 이를 사용하는 코드, 그리고 StampRequest.model_json_schema()가 반환하는 스키마입니다. 각 # ...는 표시되지 않은 코드를 나타내며, 스키마는 면허 번호만 보여줍니다.

python
class StampRequest(BaseModel):
    model_config = {"extra": "forbid"}
    # ...
    pe_license_number: str | None = Field(pattern=r"^PE-\d{5}$")
    # ...
stamp_tool = {
    # ...
    "input_schema": StampRequest.model_json_schema(),
    "strict": True,
}
stamp_request = StampRequest.model_validate(call.input)
json
{
  "additionalProperties": false,
  "properties": {
    "pe_license_number": {
      "anyOf": [
        {"pattern": "^PE-\\d{5}$", "type": "string"},
        {"type": "null"}
      ],
      "title": "Pe License Number"
    }
  }
}
  • "extra": "forbid"는 strict가 요구하는 설정인 "additionalProperties": false가 됩니다.
  • str | None은 anyOf가 되어, 면허 번호는 해당 pattern을 가진 문자열이거나 null이 됩니다.
  • model_validate()는 Claude가 작성한 입력인 call.input을 동일한 클래스와 대조하여 확인합니다.

strict를 끈 경우와 켠 경우 비교하기

Meridian의 테스트는 queue_stamp_request 도구를 사용해 Claude Haiku 4.5에 일상 언어로 작성된 날인 요청 100건을 보내며, StampRequest가 모든 도구 호출을 검증합니다. 동일한 100건의 요청을 두 번 실행하는데, 먼저 손으로 작성한 스키마와 strict를 끈 상태로, 그다음 생성된 스키마와 strict를 켠 상태로 실행합니다.

아래에서 두 실행을 모두 다시 재생해 보세요.

로드 중

스키마의 pattern은 허용되는 형식을 지정하며, strict는 Claude가 이를 지키도록 만듭니다.

strict는 입력의 형태를 제어할 뿐, 요청에서 전혀 제공되지 않은 사실을 채워 넣을 수는 없습니다. 100건의 요청 중 28건은 면허를 전혀 언급하지 않았습니다. 생성된 스키마는 면허 번호에 null을 허용하며, 28건 모두 null로 반환되었습니다.

엄격한 도구 사용의 한계

2026년 10월 기준으로 알아야 할 여섯 가지 사항입니다:

  1. 보호 대상 건강 정보(PHI)를 제외하세요. 컴파일된 스키마는 프롬프트 및 응답과 동일한 PHI 보호를 받지 않으므로, 속성 이름, enum 값, const 값, pattern 표현식에서 PHI를 제외하세요.
  2. 범위(bounds) 제한은 지원되지 않습니다. 스키마에 minimum, maximum, minLength, maxLength를 사용하는 요청은 400 오류를 반환합니다. 범위는 Pydantic의 @field_validator와 같은 검증 함수에서 확인하고, 필드의 설명에 명시하세요.
  3. 한도는 도구 전체에 걸쳐 합산됩니다. 하나의 요청은 최대 엄격한 도구 20개까지 포함할 수 있습니다. 해당 도구들 전체에 걸쳐 선택적 매개변수는 최대 24개까지 포함할 수 있습니다. 또한 anyOf 또는 타입 배열을 사용하는 매개변수는 최대 16개까지 포함할 수 있으며, 이는 str | None과 같은 nullable 필드가 생성하는 형태입니다.
  4. 입력이 스키마와 일치하지 않을 수도 있습니다. 이는 거부(refusal) 이후, 응답이 max_tokens에서 잘린 경우, 또는 enum이나 const 문자열이 다른 대소문자로 반환되는 경우에 발생합니다.
  5. 일부 모델은 강제 도구 호출을 거부합니다. tool_choice를 any 또는 tool로 설정하면 도구 호출이 강제됩니다. 이는 Claude Opus 5.5, Claude Sonnet 5.5, Claude Fable 5.1, Claude Mythos 5.1에서 400 오류를 반환합니다. 이러한 모델에서는 tool_choice를 기본값인 auto로 유지하세요.
  6. 프로그래밍 방식 도구 호출(programmatic tool calling)은 엄격한 도구를 사용할 수 없습니다. 프로그래밍 방식 도구 호출에서는 Claude가 여러분의 도구를 호출하는 코드를 작성합니다. 이 내용은 8강에서 다룹니다.

핵심 요약

  • strict: true는 거부나 잘린 응답 이후, 또는 enum이나 const 문자열의 대소문자가 바뀌는 경우를 제외하고 도구 입력이 스키마와 일치하도록 만듭니다.
  • Pydantic 클래스와 같은 검증기에서 스키마를 생성하세요.
  • 범위 제한은 스키마에서 제외하고, 도구, 선택적 매개변수, nullable 매개변수는 전체 요청에 걸쳐 합산해서 세세요.