도구 사용 — 모델이 함수를 부른다
Article
Course A 완결. 11강까지 만든 CLI 어시스턴트는 이미 말을 잘한다. 구조화된 지시를 따르고, 예시를 보고 배우고, JSON 으로 검증까지 마친 출력을 낸다. 그런데 아직 못 하는 게 하나 있다. 말밖에 못 한다.
당장 이렇게 물어보면 어떻게 될까.
Claude 는 오늘이 며칠인지 모른다
이번 강의부터는 ai-lab-b 저장소다. 새 폴더 my-ai-assistant 를 만들고 1강과 똑같이 세팅한다.
mkdir my-ai-assistant
cd my-ai-assistant
npm init -y
npm i @anthropic-ai/sdk
npm i -D typescript tsx @types/node
package.json 에 "type": "module" 을 추가하고, .env 에 키를 넣는 것도 1강과 같다. 이제 no-tools.ts 를 만든다.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-haiku-4-5",
max_tokens: 300,
messages: [{ role: "user", content: "오늘이 며칠이야? 짧게 답해줘." }],
});
console.log(`stop_reason: ${response.stop_reason}`);
console.log(`content[0].type: ${response.content[0].type}`);
console.log("---");
console.log(response.content[0].type === "text" ? response.content[0].text : "");
npx tsx --env-file=.env no-tools.ts
- stop_reason: end_turn
- content[0].type: text
- ---
- 저는 실시간 정보에 접근할 수 없어서 오늘 날짜를 알 수 없습니다. 기기의 달력이나 시계를 확인해주세요.
당연하다. Claude 는 학습된 시점의 지식으로 답을 생성할 뿐이다. 지금 몇 시인지, 오늘 환율이 얼마인지, 우리 회사 DB 에 뭐가 들어있는지는 원래 알 방법이 없다. 지금까지 11강 동안 우리가 만든 건 전부 "말만 하는" 비서였던 이유가 이거다.
함수 하나를 정의해서 건네준다
messages.create 에 tools 라는 파라미터를 추가하면 된다. 함수 하나를 이렇게 "설명"한다. 이름, 무슨 일을 하는지, 어떤 입력을 받는지.
- name함수 이름 — get_current_time
- description언제 이 함수를 써야 하는지 설명 (Claude 가 이걸 읽고 판단한다)
- input_schema이 함수가 받는 인자의 JSON 스키마
index.ts 를 만든다.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const tools: Anthropic.Tool[] = [
{
name: "get_current_time",
description: "지금 이 순간의 날짜와 시간을 KST(한국 표준시) 기준으로 돌려준다.",
input_schema: {
type: "object",
properties: {},
},
},
];
async function ask(question: string) {
const response = await client.messages.create({
model: "claude-haiku-4-5",
max_tokens: 300,
tools,
messages: [{ role: "user", content: question }],
});
console.log(`\n질문: "${question}"`);
console.log(`stop_reason: ${response.stop_reason}`);
for (const block of response.content) {
if (block.type === "text") {
console.log(`[text] ${block.text}`);
} else if (block.type === "tool_use") {
console.log(`[tool_use] id=${block.id} name=${block.name} input=${JSON.stringify(block.input)}`);
}
}
console.log(`usage: input=${response.usage.input_tokens} output=${response.usage.output_tokens}`);
}
await ask("오늘이 며칠이야? 짧게 답해줘.");
await ask("피보나치 수열이 뭔지 두 줄로 설명해줘.");
함수 자체는 텅 비어 있다. get_current_time 이라는 진짜 함수를 만들지도 않았다. Claude 에게 "이런 함수가 있다"는 설명서만 보여준 것이다. 이것만으로 뭐가 달라지는지 실행해서 본다.
npx tsx --env-file=.env index.ts
- 질문: "오늘이 며칠이야? 짧게 답해줘."
- stop_reason: tool_use
- [tool_use] id=toolu_01GHjX7TFfEr3ipkxDpbnbJC name=get_current_time input={}
- usage: input=683 output=39
- 질문: "피보나치 수열이 뭔지 두 줄로 설명해줘."
- stop_reason: end_turn
- [text] 피보나치 수열은 각 항이 앞의 두 항의 합으로 이루어진 수열입니다. 0, 1로 시작하여 0+1=1, 1+1=2, 1+2=3, 2+3=5... 처럼 계속 이어져 나갑니다.
- 이 수열은 자연계의 나선형 패턴(해바라기 씨, 앵무조개 껍질 등)이나 건축, 예술 등 다양한 분야에서 나타나는 신비한 규칙성 때문에 수학에서 중요하게 여겨집니다.
- usage: input=688 output=198
두 질문에서 완전히 다른 일이 벌어졌다. 날짜 질문은 stop_reason 이 tool_use 로 나왔고, content 안에도 텍스트 대신 tool_use 블록이 들어 있다. Claude 가 "나는 이 질문에 바로 답 못 하니, get_current_time 을 인자 없이({}) 불러달라"고 요청한 것이다. 반면 피보나치 질문은 도구가 필요 없으니 여느 때처럼 end_turn 으로 끝나고 텍스트가 바로 왔다.
같은 tools 를 건넸는데도 Claude 가 알아서 골랐다. 우리가 "이 질문엔 도구 써" 라고 지시한 적이 없다. description 을 읽고 스스로 판단한 것이다.
지금까지 만든 것
- my-ai-assistant
- no-tools.ts
- index.ts
- .env
- package.json
정리하면
tools 는 함수를 실제로 실행하는 게 아니라, Claude 에게 "이런 함수가 있다"고 알려주는 설명서다. Claude 는 필요하다고 판단하면 tool_use 블록으로 "이 함수를 이 인자로 불러달라"고 요청하고 거기서 멈춘다.
- 01
tools 없이 물어봐서 한계를 확인했다
Claude 는 오늘 날짜를 모른다고 솔직히 답했다. stop_reason 은 평소처럼 end_turn.
npx tsx --env-file=.env no-tools.ts - 02
get_current_time 도구를 정의해 건네줬다
name · description · input_schema 세 가지로 함수를 "설명"만 했다. 진짜 함수는 만들지 않았다.
- 03
같은 질문에 tool_use 블록이 오는 것을 실제로 봤다
stop_reason 이 tool_use 로 바뀌고, id·name·input 이 담긴 요청만 왔다. 도구가 필요 없는 질문엔 평소처럼 텍스트로 답했다.
npx tsx --env-file=.env index.ts
다음 강의에서는 도구 하나를 제대로 설계한다. 지금의 빈 껍데기 get_current_time 대신, 입력을 받고 진짜로 계산하는 계산기 도구를 만들고, input_schema 를 촘촘히 써서 Claude 가 정확한 인자를 뽑아내게 만든다.