~22 min · tool-calling, function-calling, structured-output
Level 0호기심 많은 독자
0 XP0/48 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete
Breakthrough 는 너무 조용해서 놓치기 쉬워. 2023 년쯤, provider 들이 inference API 에 새 조각을 추가했어: 모델 호출할 때, 대화 옆에 tool 정의 리스트 를 같이 건네줄 수 있게 됐어. 모델이 tool 이 필요하다고 결정하면, 응답에 더 이상 영어와 JSON 이 섞이지 않아. 대신 응답이 별도의, 구조화된 tool-call object — name, arguments — 를 들고 와. SDK 는 그걸 typed field 로 노출해.
모양은 작은 변화지만, 신뢰는 어마어마한 변화야. Tool call 은 모델이 적은 텍스트가 아니야. Inference engine 이 기계 읽기용으로 예약된 자리에 토해낸 결정이야. Serializer 가 모양을 보장해. 네가 준 schema 가 필드를 보장해. Agentic loop 는 이렇게 돼:
유저 메시지와 tool 리스트로 모델 호출.
모델은 일반 텍스트 답변이나 tool-call object 중 하나를 반환 — 절대 둘이 prose 에 섞이지 않아.
Tool 이 호출됐으면 실행하고 결과를 새 메시지로 다시 넣어줌.
모델 다시 호출. 또 tool 부르거나, 텍스트로 끝냄.
그게 loop 의 전부야. 모든 modern agent — Claude Code, Cursor, browsing 켠 ChatGPT, MCP 받쳐주는 custom assistant — 는 저 네 step 의 어떤 변형이야. Tool calling 은 모델을 더 똑똑하게 만든 게 아니야. 모델한테 행동할 깔끔한 구조화된 문을 줬고, 너한테는 거기에 코드를 붙일 깔끔한 구조화된 손잡이를 준 거야.
여기서부터 quest 가 구체적으로 들어가: 네가 적는 schema, 네가 돌리는 loop, provider 별로 tool 사투리가 어떻게 다른지, 그리고 MCP 가 같은 idea 를 어떻게 scaling 해서 한 server 가 지구상의 모든 tool-calling client 한테 재사용되게 만드는지.
Code
Tool calling, 현대의 모양 (Anthropic)·python
# 모델의 tool call 은 'tool_use' content block 으로 구조화돼서 돌아옴 —
# 모델이 적은 텍스트 아님.
import anthropic, json
client = anthropic.Anthropic()
tools = [{
"name": "get_weather",
"description": "Look up the current weather for a city.",
"input_schema": {
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"],
},
}]
resp = client.messages.create(
model="claude-opus-4-7",
max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": "Weather in Seoul?"}],
)
for block in resp.content:
if block.type == "tool_use":
print(block.name, block.input)
# 'get_weather' {'location': 'Seoul'}
4-step agentic loop·python
# 모든 modern agent 를 굴리는 loop 의 pseudocode.
def agent_loop(user_msg, tools, executors):
messages = [{"role": "user", "content": user_msg}]
while True:
resp = call_model(messages, tools=tools)
if resp.stop_reason == "end_turn":
return resp.text
for tc in resp.tool_calls:
result = executors[tc.name](**tc.args)
messages.append({"role": "assistant", "tool_calls": [tc]})
messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})
아무 provider SDK 골라서 30 줄짜리 스크립트 작성: tool 하나 정의, 모델한테 건네주고, 호출되면 실행, 결과 다시 넣음. 한 번 round-trip 후 stop. 핵심은 prompt-and-parse 시대에 비해 loop 가 얼마나 짧아졌는지 느끼는 거야.
Progress
Progress is local-only — sign in to sync across devices.