How to
Build an Agent with Tools
Define a typed tool, hand it to an agent, and read the run result
Read this when
- Giving a model the ability to act
- Wiring your first agent loop
Build the tool first, then pass it to the agent.
Define a tool
type inventoryQuery struct { SKU string `json:"sku"`}
type inventoryStatus struct { SKU string `json:"sku"` InStock bool `json:"in_stock"` Count int `json:"count"`}
lookupInventory := ai.DefineTool( "lookup_inventory", "Look up current stock for one SKU.", func(ctx context.Context, in inventoryQuery) (inventoryStatus, error) { return inventoryStatus{SKU: in.SKU, InStock: true, Count: 7}, nil },)ai.DefineTool infers the input and output types from the function. It also
builds the tool schemas. The JSON tags name the fields the model sees.
Create the agent
a, err := pi.Agent( "openai-completions/gpt-5-mini", pi.WithSystemPrompt("Answer with current inventory data."), pi.WithTools(lookupInventory), pi.WithMaxTurns(4),)defer a.Close()pi.Agent wraps the model in the default agent loop. WithMaxTurns caps the tool
loop.
Run it and read the answer
answer, err := agent.Prompt(ctx, a, "Do we have SKU mug-12?")
fmt.Println(answer.JoinText(""))Use agent.Prompt for the blocking path. Use Run and Stream.Events when a UI
needs events.
Add the built-in tools
fsys := sandbox.New(".", sandbox.Strict())
a, err := pi.Agent( "openai-completions/gpt-5-mini", pi.WithSystemPrompt("Work only inside the configured filesystem."), pi.WithTools( ai.DefineTool(read.ToolName, read.Description, read.New(read.FS{FS: fsys})), ai.DefineParallelTool(grep.ToolName, grep.Description, grep.New(grep.FS{FS: fsys})), ai.DefineTool(edit.ToolName, edit.Description, edit.New(edit.FS{FS: fsys})), // ... bash, write, find, todowrite ), pi.WithMaxTurns(8),)sandbox.Strict confines the file tools to the root directory. Add more tool
packages only when the agent needs them.
Next: Stream a Run into a UI.