도구 호출 (Tool Calling)
도구 호출 (Tool Calling)
모델이 스스로 계산기를 쓰거나 날씨를 조회하도록 만들고 싶다면 도구 호출(tool calling)을 써요. 모델은 애플리케이션이 정의한 함수를 요청하고, 애플리케이션이 그 함수를 실행한 결과를 다시 모델에 돌려주는 구조예요.
지원 모델
| 모델 | tool_choice |
스트릭트 모드 | 병렬 호출 | 멀티턴 호출 | 가용성 |
|---|---|---|---|---|---|
qwen-3.8-27b |
none, auto, required, 이름 지정 함수 |
예 | 예 | 예 | 공개 공유 티어 |
kimi-k2.7-code |
none, auto, required, 이름 지정 함수 |
예 | 예 | 예 | 고객 트라이얼 전용 |
gpt-oss-120b |
none, auto, required, 이름 지정 함수 |
예 | 예 | 예 | 공개 공유 티어 |
gemma-4-31b |
none, auto, required, 이름 지정 함수 |
예 | 예 | 예 | 전용 엔드포인트 |
어떻게 동작하나요
도구 호출은 크게 다섯 단계로 진행돼요.
- 도구 정의 — 모델이 접근할 각 도구의 이름, 설명, 입력 파라미터를 제공해요.
- 요청 전송 — API 호출에 사용 가능한 도구 정의와 함께 프롬프트를 보내요.
- 도구 선택 — 모델이 해당 요청에 도구가 도움이 될지 판단하고, 필요하다면 도구 이름과 인자를 반환해요.
- 도구 실행 — 클라이언트 애플리케이션이 모델의 도구 호출 요청을 받아 지정된 도구(예: 외부 API 호출)를 실행하고 결과를 가져와요.
- 최종 응답 생성 — 도구 결과를 모델에 보내 대화를 이어가요.
기본 도구 호출
먼저 Cerebras 클라이언트를 초기화해요. API 키가 아직 없다면 Quickstart를 먼저 완료해 주세요.
import os
import json
import re
from cerebras.cloud.sdk import Cerebras
# Initialize Cerebras client
client = Cerebras(
api_key=os.environ.get("CEREBRAS_API_KEY"),
)
다음으로 애플리케이션이 실행할 함수를 정의해요. 아래는 기본 사칙연산을 수행하는 계산기 예시예요.
def calculate(expression):
expression = re.sub(r'[^0-9+\-*/().]', '', expression)
try:
result = eval(expression)
return str(result)
except (SyntaxError, ZeroDivisionError, NameError, TypeError, OverflowError):
return "Error: Invalid expression"
도구 이름·설명·파라미터를 담은 스키마를 정의해요. 지원되는 JSON Schema 부분집합에서는 strict: true가 제한 디코딩(constrained decoding)을 써서 도구 호출 인자가 스키마를 따르는 것을 보장해요.
tools = [
{
"type": "function",
"function": {
"name": "calculate",
"strict": True,
"description": "A calculator tool that can perform basic arithmetic operations. Use this when you need to compute mathematical expressions or solve numerical problems.",
"parameters": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "The mathematical expression to evaluate"
}
},
"required": ["expression"],
"additionalProperties": False
}
}
}
]
메시지와 도구 스키마를 함께 보내요. 응답에 도구 호출이 포함될 수 있어요.
messages = [
{"role": "system", "content": "You are a helpful assistant with access to a calculator. Use the calculator tool to compute mathematical expressions when needed."},
{"role": "user", "content": "What's the result of 15 multiplied by 7?"},
]
response = client.chat.completions.create(
model="qwen-3.8-27b",
messages=messages,
tools=tools,
parallel_tool_calls=False,
)
응답에서 도구 호출이 있는지 확인하고, 있으면 요청한 함수를 실행한 뒤 결과를 모델에 돌려줘요.
choice = response.choices[0].message
if choice.tool_calls:
function_call = choice.tool_calls[0].function
if function_call.name == "calculate":
# Logging that the model is executing a function named "calculate".
print(f"Model executing function '{function_call.name}' with arguments {function_call.arguments}")
# Parse the arguments from JSON format and perform the requested calculation.
arguments = json.loads(function_call.arguments)
result = calculate(arguments["expression"])
# Note: This is the result of executing the model's request (the tool call), not the model's own output.
print(f"Calculation result sent to model: {result}")
# Send the result back to the model to fulfill the request.
messages.append({
"role": "tool",
"content": json.dumps(result),
"tool_call_id": choice.tool_calls[0].id
})
# Request the final response from the model, now that it has the calculation result.
final_response = client.chat.completions.create(
model="qwen-3.8-27b",
messages=messages,
)
# Handle and display the model's final response.
if final_response:
print("Final model output:", final_response.choices[0].message.content)
else:
print("No final response received")
else:
# Handle cases where the model's response does not include expected tool calls.
print("Unexpected response from the model")
이 예시는 대략 아래와 같은 출력을 만들어요.
Model executing function 'calculate' with arguments {"expression": "15 * 7"}
Calculation result sent to model: 105
Final model output: 15 * 7 = 105
스트릭트 모드
지원되는 JSON Schema 부분집합을 쓰는 스키마에서, 스트릭트 모드는 도구 호출 인자가 스키마를 따르는 것을 보장해요.
스트릭트 모드가 없으면 도구 호출에 이런 문제가 생길 수 있어요.
2대신"2"같은 잘못된 파라미터 타입- 누락된 필수 파라미터
- 예상치 못한 파라미터
- 잘못된 형태의 인자 JSON
스트릭트 모드는 이런 스키마 위반을 막아줘요. 활성화는 도구 정의의 function 객체 안에서 strict를 true로 설정하면 돼요.
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"strict": True, # Enable constrained decoding
"description": "Get the current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City and country, such as San Francisco, USA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location", "unit"],
"additionalProperties": False
}
}
}
]
스키마 요구사항
스트릭트 모드를 쓸 때는 스키마의 모든 객체에 additionalProperties: false를 설정해야 해요. 스키마 제한에 대한 자세한 내용은 Structured Outputs의 Strict Mode 제한을 참고하세요.
모델별로 주의할 점이 있어요. kimi-k2.7-code는 요청의 모든 함수에 같은 strict 값을 주거나, 모든 함수에 생략해야 해요. qwen-3.8-27b의 스트릭트 도구 스키마에서는 pattern, minLength, maxLength를 쓰지 말아야 해요.
멀티턴 도구 호출
실제 워크플로는 대부분 도구를 한 번만 부르지 않아요. 멀티턴 도구 호출은 모델이 도구를 호출하고, 그 출력을 반영한 뒤, 같은 대화 안에서 또 다른 도구를 호출할지 스스로 결정하게 해요. messages에 각 도구 결과를 추가하고 모델에게 계속 진행하라고 요청하는 방식으로, client.chat.completions.create()를 도구 호출이 사라질 때까지 반복 호출하면 돼요. 도구 결과는 {"role": "tool", "tool_call_id": ..., "content": ...} 형태로 돌려주고, 응답의 tool_calls가 없어지면 대화를 끝내면 돼요.
병렬 도구 호출
병렬 도구 호출은 모델이 하나의 응답에서 여러 개의 독립적인 도구 호출을 요청하게 해서 지연을 줄여줘요. 예를 들어 "토론토가 몬트리올보다 따뜻한가요?"라는 질문에 두 도시의 날씨를 각각 확인해야 한다면, 두 요청을 한 번에 처리할 수 있어요. 이런 병렬 호출은 다음 경우에 유용해요.
- 하나의 요청이 서로 다른 도시의 날씨처럼 여러 독립적인 데이터 포인트를 필요로 할 때
- 도구 호출이 다른 도구 호출의 결과에 의존하지 않을 때
parallel_tool_calls 파라미터로 이 동작을 명시적으로 제어할 수 있어요. True로 설정하면 병렬 실행(기본값), False로 설정하면 순차 실행을 강제해요.
response = client.chat.completions.create(
model="qwen-3.8-27b",
messages=messages,
tools=tools,
parallel_tool_calls=True, # Enable parallel calling (default)
)
병렬 호출에서는 응답의 tool_calls 배열의 모든 항목을 순회하고, 각 도구 결과를 messages에 tool_call_id와 함께 추가한 뒤, 모든 도구를 처리하고 나서 최종 응답을 다시 요청하면 돼요.