Tool calling is a loop, not a call. You declare what the model may invoke, it asks, you run it, you send the result back, and you repeat until it stops asking. This chapter covers each step and the places providers differ.
In this chapter
- How to declare a tool the model can call
- What a JSON Schema needs to survive the trip to each provider
- The loop, and why the assistant turn must be appended first
- How to force, forbid, or require a specific tool
- How to tell the model a tool failed — and where that signal is lost
- What happens when the model calls several tools at once
Declaring a tool#
Tools: []skyl.Tool{{
Name: "get_weather",
Description: "Get the current weather for a city. Call this whenever the user asks about weather, temperature, or conditions in a named place.",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"city": map[string]any{"type": "string"},
},
"required": []string{"city"},
},
}}Tools: []skyl.Tool{{
Name: "get_weather",
Description: "Get the current weather for a city. Call this whenever the user asks about weather, temperature, or conditions in a named place.",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"city": map[string]any{"type": "string"},
},
"required": []string{"city"},
},
}}Read Declaring Tools for why the description should describe when to call, not only what it does.
Schemas#
Read Tool Schemas — three adapters pass your schema through verbatim and one rebuilds it, dropping two things.
The loop#
for _, call := range resp.ToolCalls() {
req.Messages = append(req.Messages,
resp.Message, // the assistant's turn FIRST
skyl.ToolResultMessage(call.ID, run(call)), // then your answer
)
}for _, call := range resp.ToolCalls() {
req.Messages = append(req.Messages,
resp.Message, // the assistant's turn FIRST
skyl.ToolResultMessage(call.ID, run(call)), // then your answer
)
}Read The Tool Loop for the bounded version, and why ordering is not optional.
Tool choice#
Read Tool Choice for the four modes, all of which are mapped on all four adapters.
Errors#
Read Reporting Tool Errors — IsError reaches the model faithfully on exactly one adapter.
Parallel calls#
Read Parallel Tool Calls for running several concurrently, and why Gemini makes pairing harder.
What’s next?
Start with Declaring Tools. If you already have a loop and it is failing on one provider but not another, the answer is almost certainly in Tool Schemas.