Tool calling
Let models call functions that you define, and return the results to the model.
This page explains how to define tools, receive tool calls from a model, and return tool results. It is intended for developers who build agents or connect models to external functions.
How tool calling works
- You send a request that describes the available tools.
- The model either answers directly or returns one or more tool calls with arguments.
- Your application runs the tools and sends the results back in a new request.
- The model uses the results to continue or to produce its final answer.
The model does not run any tools itself. Your application decides whether and how to run each call.
Define tools
On /v1/chat/completions, use the standard OpenAI tools parameter. Each tool has a name, a description, and a JSON Schema for its parameters.
The following example defines a weather tool, handles the tool call, and sends the result back to the model:
import json
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.engy.ai/v1",
api_key=os.environ["ENGY_API_KEY"],
)
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "The city name"},
},
"required": ["city"],
},
},
}
]
def get_weather(city):
# Replace this with a call to a real weather service.
return {"city": city, "temperature_c": 18, "conditions": "cloudy"}
messages = [{"role": "user", "content": "What is the weather in Paris?"}]
response = client.chat.completions.create(
model="glm-5.2",
messages=messages,
tools=tools,
)
message = response.choices[0].message
while message.tool_calls:
messages.append(message)
for call in message.tool_calls:
arguments = json.loads(call.function.arguments)
result = get_weather(**arguments)
messages.append(
{
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
}
)
response = client.chat.completions.create(
model="glm-5.2",
messages=messages,
tools=tools,
)
message = response.choices[0].message
print(message.content)When the model calls a tool, finish_reason is tool_calls, and message.tool_calls contains the calls. Each call has an id, which you return as tool_call_id with the result.
Control tool use
Use the tool_choice parameter to control whether the model calls tools:
| Value | Behavior |
|---|---|
auto | The model decides whether to call a tool. This is the default when you provide tools. |
required | The model must call at least one tool. |
none | The model must not call a tool. |
{"type": "function", "function": {"name": "..."}} | The model must call the named tool. |
Some models can return tool calls even when tool_choice is none. In that case, Engy removes the tool calls from the response and sets finish_reason to stop. The tools stay in the request, so the model does not write tool-call markup into the text instead.
Stream tool calls
When you stream a response, tool calls arrive in delta.tool_calls. The arguments arrive as string fragments. Concatenate the fragments for each call index before you parse the JSON. For details about streaming, see Streaming.
Other endpoint formats
Tool calling also works on the other endpoints, in their native formats:
| Endpoint | Tool definition | Tool call | Tool result |
|---|---|---|---|
/v1/messages | tools with name, description, and input_schema | tool_use content block | tool_result content block in a user message |
/v1/responses | tools with type set to function | function_call output item | function_call_output input item |
On /v1/messages, tool_choice accepts auto, any, none, and tool. The value any corresponds to required.
Best practices
- Write clear tool descriptions and parameter descriptions. The model uses them to decide when and how to call each tool.
- Validate the arguments before you run a tool. The model can produce arguments that do not match the schema.
- Set
max_tokenshigh enough for reasoning models, which can reason before they call a tool. For details, see Reasoning.