agents-go

Agents

Agents are the core building block. An agent is an LLM configured with instructions, tools, guardrails and handoffs. Unlike the Python SDK’s dataclass, a Go agent is a plain struct literal — only Name is required, and zero values are sensible defaults.

Basic configuration

type weatherArgs struct {
	City string `json:"city" jsonschema:"the city to look up"`
}

weather := agents.NewFunctionTool("get_weather", "Look up the weather.",
	func(ctx context.Context, tc *agents.ToolContext, args weatherArgs) (string, error) {
		return "Sunny in " + args.City, nil
	})

agent := &agents.Agent{
	Name:         "Haiku agent",
	Instructions: agents.StaticInstructions("Always respond in haiku form."),
	Model:        "gpt-4o-mini",
	Tools:        []agents.Tool{weather},
}

The most common fields:

Field Purpose
Name Identifies the agent (required)
Instructions The system prompt (a.k.a. developer message)
Model / ModelImpl Model name resolved via the run’s provider, or an explicit Model implementation
ModelSettings Temperature, tool_choice, max tokens, … (Models)
Tools Function tools the model may call (Tools)
MCPServers MCP servers whose tools are exposed to the agent (MCP)
Handoffs Agents this agent can delegate to (Handoffs)
InputGuardrails / OutputGuardrails Validation that can stop the run (Guardrails)
OutputType Structured output schema (below)
Hooks Agent-scoped lifecycle callbacks
ToolUseBehavior What happens after tools run (below)

Dynamic instructions

Instructions is an interface, so the system prompt can be computed per run from the context:

agent.Instructions = agents.InstructionsFunc(
	func(ctx context.Context, rc *agents.RunContext, a *agents.Agent) (string, error) {
		user := rc.Context.(*MyAppContext)
		return "The user's name is " + user.Name + ". Help them with their questions.", nil
	})

agents.StaticInstructions("...") wraps the fixed-string case.

Stored prompts

Instead of (or alongside) inline Instructions, an agent can reference an OpenAI stored prompt via Agent.Prompt. The prompt’s id, optional version, and variable substitutions are sent as the Responses API prompt parameter:

agent.Prompt = agents.StaticPrompt(agents.Prompt{
	ID:        "pmpt_abc123",
	Version:   "2",                                   // optional
	Variables: map[string]any{"tone": "concise"},     // optional string substitutions
})

agents.PromptFunc(func(ctx, rc, agent) (*agents.Prompt, error)) computes the prompt per run from the context — the counterpart of Python’s DynamicPromptFunction. Only the OpenAI Responses backend honors Prompt; other backends ignore it. This is distinct from MCP server prompts (server.GetPrompt), which fetch prompt text to use as instructions.

Structured output types

By default agents produce plain text (string). Set OutputType to request a typed result, validated against a reflected JSON schema in strict mode:

type CalendarEvent struct {
	Name         string   `json:"name"`
	Date         string   `json:"date"`
	Participants []string `json:"participants"`
}

agent := &agents.Agent{
	Name:         "Calendar extractor",
	Instructions: agents.StaticInstructions("Extract calendar events from text."),
	OutputType:   agents.OutputType[CalendarEvent](),
}

res, _ := agents.Run(ctx, agent, input, opts)
event, ok := agents.FinalOutputAs[CalendarEvent](res)

Notes:

Tool use behavior

ToolUseBehavior controls what happens after the model calls tools:

agent.ToolUseBehavior = agents.RunLLMAgain{}          // default: feed results back to the model
agent.ToolUseBehavior = agents.StopOnFirstTool{}      // first tool's output is the final output
agent.ToolUseBehavior = agents.StopAtTools{Names: []string{"save_report"}} // stop if this tool ran
agent.ToolUseBehavior = agents.ToolUseBehaviorFunc(   // custom decision
	func(ctx context.Context, rc *agents.RunContext, results []agents.FunctionToolResult) (stop bool, output any, err error) {
		return len(results) > 0, results[0].Output, nil
	})

To prevent infinite tool loops, once an agent has called a tool the runner leaves tool_choice unset on its later turns (so a "required" or specific-tool setting cannot loop forever). Set Agent.DisableToolChoiceReset = true to keep tool_choice as configured on every turn — the inverse of Python’s reset_tool_choice=True default.

Lifecycle hooks

Observe (or veto) an agent’s lifecycle by setting Hooks. Embed BaseAgentHooks and override what you need; any hook returning an error aborts the run:

type myHooks struct{ agents.BaseAgentHooks }

func (myHooks) OnToolStart(ctx context.Context, rc *agents.RunContext, agent *agents.Agent, tool agents.Tool) error {
	log.Printf("%s invoking %s", agent.Name, tool.ToolName())
	return nil
}

agent.Hooks = myHooks{}

Available callbacks: OnStart, OnEnd, OnHandoff (fires on the receiving agent), OnToolStart, OnToolEnd, OnLLMStart, OnLLMEnd. OnLLMStart/OnLLMEnd bracket each model call (with the system prompt and input items, then the response); they do not fire on a HITL-resumed turn, which reuses the interrupted response without calling the model. Run-scoped equivalents exist on RunOptions.Hooks (Running agents).

Cloning

Clone returns a shallow copy — replace (rather than append to) slices when customizing:

pirate := agent.Clone()
pirate.Name = "Pirate"
pirate.Instructions = agents.StaticInstructions("Talk like a pirate.")