“Context” means two distinct things, and the Go SDK keeps them separate by design:
RunContextWhere Python parameterizes everything on a generic RunContextWrapper[T], Go splits the two concerns idiomatically:
context.Context passed to Run (and through to every tool, guardrail and hook).RunContext.Context, an any value you supply via RunOptions.Context. The SDK never inspects it.type AppContext struct {
UserID string
DB *sql.DB
}
res, err := agents.Run(ctx, agent, input, agents.RunOptions{
ModelProvider: provider,
Context: &AppContext{UserID: "u_123", DB: db},
})
Every tool, guardrail, hook and dynamic-instructions function receives the same *agents.RunContext; type-assert your value back:
tool := agents.NewFunctionTool("whoami", "Return the current user.",
func(ctx context.Context, tc *agents.ToolContext, _ struct{}) (string, error) {
app := tc.RunContext.Context.(*AppContext)
return app.UserID, nil
})
RunContext also carries run-scoped state the SDK maintains for you:
| Field | Meaning |
|---|---|
Context |
Your value, verbatim |
Usage |
Token usage accumulated so far (Usage) |
Approvals |
Recorded human-in-the-loop decisions (Human-in-the-loop) |
ToolContext embeds *RunContext and adds the call’s ToolName, ToolCallID and raw ToolArguments.
Tools run concurrently within a turn. If tools mutate your context value, make it goroutine-safe.
The LLM only sees the conversation history. To expose data to it you have the same options as Python:
Run.