본문으로 건너뛰기
마크다운으로 보기

툴 호출, 처음부터 끝까지

tool_choice를 "auto"로 둔 왕복 한 번입니다. 모델이 툴을 요청하면 코드가 실행하고, 그 결과를 tool 메시지로 돌려보내 최종 답을 받습니다. 이 레시피는 auto만 다룹니다. 툴 호출 여부는 모델이 결정하며, 강제 tool_choice는 다루지 않습니다.

빠른 시작

python
import json, os
from openai import OpenAI

client = OpenAI(base_url="https://api.bricksum.com/v1", api_key=os.environ["KILN_API_KEY"])

tools = [{"type": "function", "function": {
    "name": "get_weather",
    "description": "Current weather for a city",  # required
    "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
}}]
messages = [{"role": "user", "content": "What's the weather in Seoul?"}]

first = client.chat.completions.create(model="qwen3-32b", messages=messages,
                                       tools=tools, tool_choice="auto", max_tokens=800)
choice = first.choices[0]
# The model may answer without calling the tool, so this is a check, not an index.
if not choice.message.tool_calls:
    print(f'no tool call, finish_reason: "{choice.finish_reason}"')
    print("answer:", choice.message.content)
else:
    # One reply can carry several tool calls; answer every one of them.
    messages.append(choice.message)
    for call in choice.message.tool_calls:
        print("tool call:", call.function.name, call.function.arguments)
        messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps({"temp_c": 27})})
    final = client.chat.completions.create(model="qwen3-32b", messages=messages, max_tokens=800)
    print("final:", final.choices[0].message.content)

확인

스크립트를 실행하세요. 첫 번째 호출의 message.tool_calls에는 city를 담은 JSON 인자와 함께 get_weather가 들어 있습니다(항목이 여러 개일 수 있으며 각각에 tool 메시지를 보냅니다). 두 번째 호출의 message.content는 모델이 직접 작성한 문장이라 실행할 때마다 표현이 달라지지만, 전달한 온도(27)를 언급합니다.

expected output
tool call: get_weather {
  "city": "Seoul"
}
final: The current temperature in Seoul is **27 °C**.

설정 참고

  • tool_choice = "auto" — 모델이 툴 호출 여부를 결정합니다. "required"나 함수를 지정하는 강제 선택은 지원하지 않습니다. qwen3-32b에서는 강제 툴 선택이 명확한 에러 없이 실패합니다(2026-09-26 실측). tool_choice를 "required"로 보내면 본문이 평문 "Internal Server Error"인 HTTP 500이 오고, 함수를 지정하면 arguments가 빈 200(finish_reason "stop")이 옵니다. 둘 다 4xx가 아니므로 재시도로 해결되지 않습니다. tool_choice는 "auto"만 보내세요.
  • tools[].function.description = required — 모든 툴에 필요합니다. 없으면 400으로 거부됩니다.
  • parallel_tool_calls = not applied — 모델 페이지에서 병렬 툴 호출이 지원으로 표시된 모델에서만 적용되며, qwen3-32b는 해당하지 않습니다. gpt-oss-120b 실측에서는 200으로 수락되고, 모델이 응답마다 툴을 최대 하나만 호출했습니다. qwen3-32b에서는 parallel_tool_calls 설정이 적용되지 않습니다. 이는 응답에 툴 호출이 하나만 온다는 뜻이 아닙니다. parallel_tool_calls: false를 보내도 한 응답에 툴 호출이 여러 건 올 수 있습니다. tool_calls의 모든 항목에 각각 tool 메시지로 답하세요.
  • max_tokens = 800 — reasoning 토큰도 이 값 안에서 과금됩니다. 값이 작으면 tool_calls가 나오기 전에 finish_reason이 "length"로 끝날 수 있습니다.

제한 사항

  • 강제 tool_choice("required" 또는 특정 함수 지정)는 지원하지 않습니다. 이 레시피는 "auto"만 다룹니다. qwen3-32b에서는 강제 툴 선택이 명확한 에러 없이 실패합니다(2026-09-26 실측). tool_choice를 "required"로 보내면 본문이 평문 "Internal Server Error"인 HTTP 500이 오고, 함수를 지정하면 arguments가 빈 200(finish_reason "stop")이 옵니다. 둘 다 4xx가 아니므로 재시도로 해결되지 않습니다. tool_choice는 "auto"만 보내세요.
  • qwen3-32b에서는 parallel_tool_calls 설정이 적용되지 않습니다. 이는 응답에 툴 호출이 하나만 온다는 뜻이 아닙니다. parallel_tool_calls: false를 보내도 한 응답에 툴 호출이 여러 건 올 수 있습니다. tool_calls의 모든 항목에 각각 tool 메시지로 답하세요.

문제 해결

  • HTTP 500 Internal Server Error — 요청의 tool_choice가 "required"입니다. qwen3-32b에서는 4xx 대신 평문 500으로 실패합니다(2026-09-26 실측). tool_choice를 "auto"로 보내세요. 같은 요청을 재시도해도 같은 500이 옵니다.
  • "arguments": "" — 요청이 tool_choice에서 함수를 지정했습니다. qwen3-32b에서는 arguments가 빈 200(finish_reason "stop")이 와서(2026-09-26 실측) JSON.parse / json.loads가 실패합니다. tool_choice를 "auto"로 보내고 어떤 툴을 기대하는지 프롬프트에 설명하세요. arguments를 파싱하기 전에 tool_calls를 확인하세요.
  • "message": "missing or malformed API key" — KILN_API_KEY가 설정되지 않았거나 만료됐거나, 스크립트를 실행하는 셸에 export되지 않았습니다. 콘솔 → API 키에서 새 키를 발급하고 다시 export하세요.
  • "code": "model_not_found" — model 필드가 내 키로 GET /v1/models를 호출했을 때 반환되는 ID가 아닙니다. 모델 페이지나 GET /v1/models에서 목록을 확인하고, 그 ID 중 하나(예: "qwen3-32b")로 model을 설정하세요.
  • finish_reason: "length" — 모델이 툴 호출 여부를 판단하는 reasoning을 끝내기 전에 max_tokens에 도달했습니다. 퀵스타트가 보내는 800보다 크게, 예를 들어 1600으로 첫 번째 create() 호출의 max_tokens를 올리세요.
참고
이 페이지의 스니펫은 2026-09-21에 openai 3.16.2 (Python 3.14.7), openai 7.20.0 (Node 24.12.0) 환경에서 Kiln API로 직접 실행해 확인했습니다. 확인하지 않은 부분: 이 실행은 gpt-oss-120b에서 했습니다. 페이지는 이제 qwen3-32b을 사용하며, 이 모델로는 아직 실행하지 않아 아래 예상 출력과 다를 수 있습니다. 두 퀵스타트 탭의 툴 미호출 분기는 이 실행 이후에 추가됐으며, 스테이징이 아닌 로컬 스텁 서버로만 확인했습니다. 모든 툴 호출에 답하는 반복문은 그 뒤에 추가됐으며(#760) API로 실행하지 않았습니다.