Defining tools
A tool is an operation the model may request, and Golato splits it in two. tool.Definition describes the operation to
the model. tool.Tool adds the Go function that performs it. A Generator works with definitions alone, while an Agent
registers tools and executes them inside its own loop.
Describing the operation
A definition carries a name, a description, and a self-contained
JSON Schema Draft 2020-12 object. Golato compiles it strictly
in that dialect and rejects $id, $recursiveRef, $recursiveAnchor, a $schema other than Draft 2020-12, and
references outside the document. The schema
OpenAI uses for tool calling is a constrained subset of
Draft 2020-12, so a schema written for that dialect without the rejected keywords compiles unchanged. A schema that
carries a draft-07 $schema or $id is rejected at agent.New.
lookup := tool.Definition{
Name: "lookup_customer",
Description: "Return the current plan for one customer ID.",
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "Customer ID such as C-100",
"minLength": 1
}
},
"required": ["customer_id"],
"additionalProperties": false
}`),
}
The description tells the model when to use the operation, and the schema describes the JSON object a call may carry. Golato does not apply JSON Schema defaults or coerce values: what the model sends is what the tool receives.
Sending definitions with a Generator
When the host application runs the calls itself, the request carries the definitions.
stream, err := generator.Stream(ctx, generation.Request{
Model: model,
Messages: []message.Message{message.NewUserMessage("Which plan does C-100 use?")},
Tools: []tool.Definition{lookup},
})
Read calls from answer.ToolCalls() only when Result returned err == nil. Failed or aborted responses may retain
partial calls, and a length stop can truncate arguments. Do not execute those calls. A Generator never invokes a Go
function.
Attaching the implementation
Add Execute to make the definition runnable by an Agent. The function decodes the raw JSON arguments and returns
model-visible content.
type lookupArgs struct {
CustomerID string `json:"customer_id"`
}
lookupTool := tool.Tool{
Name: lookup.Name,
Description: lookup.Description,
InputSchema: lookup.InputSchema,
Execute: func(ctx context.Context, call tool.Call, update tool.Update) (tool.Result, error) {
args, err := tool.DecodeArguments[lookupArgs](call.Arguments)
if err != nil {
return tool.Result{}, err
}
return tool.TextResult("Customer " + args.CustomerID + " is on the Gold plan."), nil
},
}
Register the tool on the Agent.
assistant, err := agent.New(agent.Config{
Generator: generator,
Model: model,
Tools: []tool.Tool{lookupTool},
})
Registration makes a tool available. The model still decides whether to call it. generation.Options.ToolChoice offers
auto, none, required, and a named function choice.
Validating arguments and results
Arguments stay json.RawMessage until code decodes them, and the helpers cover each step:
tool.ValidateArgumentschecks only that the JSON value is an object.tool.DecodeArguments[T]unmarshals the object into a Go type.tool.ValidateResultchecks the content returned to the model.tool.Validatechecks names, duplicates, the execute function, schema shape, and execution mode.agent.Newcompiles each schema and rejects unsupported schema documents before a run starts.
Schemas may use same-document $ref and $dynamicRef. External references, $id, $recursiveRef, and
$recursiveAnchor are rejected.
At execution time the Agent checks the structural keywords, meaning types, required fields, lengths, and
additionalProperties. format and the content* keywords are annotations only, so a field declared with
"format": "email" accepts a value like "not-an-email" without an error. Defaults are not applied, so a missing
optional field stays missing, and values are not coerced, so "123" remains a string.
A Generator only forwards definitions to its adapter. It compiles no schema and never validates calls, because tool execution happens on the Agent path.
Returning text, images, or progress
The result constructors cover model-visible output.
tool.TextResult("done")
tool.ImageResult(pngBytes, "image/png")
tool.TextAndImageResult("rendered chart", pngBytes, "image/png")
For a slow operation, call update with provisional results while it runs. Updates only create events. They never enter
conversation state, and calls after Execute returns are ignored. A provisional Terminate flag is ignored too.
Set Terminate on the final tool.Result when the Agent should stop after the current batch ends. Every call in that
batch must return a successful terminating result. One error or ordinary sibling keeps the loop running.
Respecting cancellation
The function receives the run context. Check it around expensive work and pass it to downstream operations. Golato cannot stop a function that ignores cancellation, and side effects may already exist when cancellation arrives.
API reference
toolDefinitionToolCallResult- Source:
tool/tool.go