Skip to content
skyl

Tool Calling

Letting the model run your code, and getting the results back safely.

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.

Read more →

Schemas#

Read Tool Schemas — three adapters pass your schema through verbatim and one rebuilds it, dropping two things.

Read more →

The loop#

goCompiles
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.

Read more →

Tool choice#

Read Tool Choice for the four modes, all of which are mapped on all four adapters.

Read more →

Errors#

Read Reporting Tool ErrorsIsError reaches the model faithfully on exactly one adapter.

Read more →

Parallel calls#

Read Parallel Tool Calls for running several concurrently, and why Gemini makes pairing harder.

Read more →

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.

Edit this page on GitHub