블로그 목록
AI

Claude tool_choice로 툴 호출 강제하기 — "알아서 부르겠지" 말고 반드시 부르게 만들기

Claude한테 툴을 쥐여줬는데 안 쓸 때가 있다. 특히 "이 문장에서 정보 뽑아서 save_contact 툴로 넘겨"라고 시켰는데, 툴을 호출하는 대신 "네, 이름은 홍길동이고 번호는..." 하고 그냥 텍스트로 답해버린다. 추출 파이프라인 입장에선 이게 제일 곤란하다. 반드시 툴 호출이 와야 뒷단이 돌아가는데, 올 때도 있고 안 올 때도 있으니까.

기본값(tool_choice 생략 또는 auto)은 모델이 툴을 쓸지 말지 스스로 판단한다. 꼭 불러야 하는 상황이면 판단을 맡기지 말고 강제하면 된다.

네 가지 모드

  • auto — 기본값. 모델이 알아서 결정.
  • any — 툴 중 하나는 반드시 호출. 어떤 걸 쓸지는 모델이 고름.
  • tool — 지정한 그 툴을 무조건 호출.
  • none — 이번 턴엔 툴 금지. 텍스트로만 답하게.

추출/라우팅처럼 결과가 꼭 구조화돼서 나와야 하면 tool로 특정 툴을 못 박는다.

const res = await client.messages.create({
  model: 'claude-sonnet-5-5',
  max_tokens: 1024,
  tools: [
    {
      name: 'save_contact',
      description: '연락처 정보를 저장한다',
      input_schema: {
        type: 'object',
        properties: {
          name: { type: 'string' },
          phone: { type: 'string' },
        },
        required: ['name', 'phone'],
      },
    },
  ],
  tool_choice: { type: 'tool', name: 'save_contact' }, // ← 이 툴 강제
  messages: [
    { role: 'user', content: '홍길동, 010-1234-5678 저장해줘' },
  ],
});

const block = res.content.find((b) => b.type === 'tool_use');
console.log(block.input); // { name: '홍길동', phone: '010-1234-5678' }

이제 서두 텍스트 없이 stop_reason: 'tool_use'로 깔끔하게 떨어진다. input_schema가 곧 내 출력 스키마가 되니까, 사실상 구조화 추출을 툴로 구현하는 셈이다. JSON 파싱하다 깨질 걱정도 없다.

함정 두 개

  • any나 tool로 강제하면 확장된 사고(extended thinking)와 같이 못 쓴다. 추론이 필요한 작업이면 auto를 유지해야 한다.
  • 툴을 강제하면 Claude가 여러 툴을 한 번에 부르려 할 수 있다. 딱 하나만 받고 싶으면 disable_parallel_tool_use: true를 같이 넣는다.
tool_choice: { type: 'any', disable_parallel_tool_use: true },

"알아서 판단해"가 필요한 대화형 에이전트는 auto가 맞다. 하지만 "이 입력은 무조건 이 형식으로"가 목적이라면 tool_choice 한 줄로 못 박는 게, 프롬프트에 "꼭 툴 써"라고 사정하는 것보다 훨씬 확실하다.