Documentation
¶
Overview ¶
Package agents is a small, provider-agnostic foundation for building AI tool-calling agents in Go.
It has four parts:
- Engine: the boundary to a language model. Complete takes a Request (the transcript plus the tools the model may call) and returns the model's reply, which is either final content or a set of tool calls. An OpenAI-compatible implementation lives in engine/openai.
- Tool: a function the model may call, described by a JSON Schema. A tool marked Mutates changes state, so the host can gate it.
- Harness: a named agent, being a system prompt, a model, a Registry of tools, and a cap on tool rounds per turn.
- Runner: drives a Harness against an Engine, running the tool loop until the model returns a final answer, with optional hooks to authorize a mutating tool and to pause for human approval before tools run.
The library carries no web framework, auth system, or storage. The host supplies authorization through Runner.Authorize and persists a Conversation however it likes; a Conversation is just a slice of Messages.
Index ¶
Examples ¶
Constants ¶
const ( RoleSystem = "system" RoleUser = "user" RoleAssistant = "assistant" RoleTool = "tool" )
Message roles in a transcript.
Variables ¶
var ErrAwaitingApproval = errors.New("agents: awaiting tool approval")
ErrAwaitingApproval is returned by Turn when the Approve hook withheld approval for the model's requested tools. The pending calls are recorded as the last assistant turn; call Resume once the host has approval.
Functions ¶
This section is empty.
Types ¶
type Conversation ¶
type Conversation struct {
Messages []Message
}
Conversation is the running transcript. Persist it however you like; it is just a slice of Messages.
func NewConversation ¶
func NewConversation(systemPrompt string) *Conversation
NewConversation starts a conversation, seeding the system prompt when set.
type Engine ¶
Engine is the model-agnostic boundary. Implementations translate a Request to a provider's wire format and back. See engine/openai for one.
type Harness ¶
type Harness struct {
Name string
SystemPrompt string
Model string
MaxSteps int
Tools *Registry
// StepLimitMessage, when set, replaces the default assistant message a turn
// returns after it exhausts MaxSteps without a final answer.
StepLimitMessage string
}
Harness is a named agent: a system prompt, a model, a tool set, and a cap on how many tool rounds a single turn may take before it must answer.
type Message ¶
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"`
Name string `json:"name,omitempty"`
// Meta is host-owned metadata, ignored by the engine and never sent to the
// model. It round-trips through JSON so a host can persist the transcript as
// []Message and keep its own per-message state (presentation flags, a UI
// label, an author id) without a parallel message type.
Meta map[string]any `json:"meta,omitempty"`
}
Message is one turn in the transcript. An assistant message may carry ToolCalls; a tool message carries the result of one call, keyed by ToolCallID.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry is the ordered set of tools available to a harness, keyed by name. It is not safe for concurrent modification; build it once, then use it.
func NewRegistry ¶
NewRegistry builds a registry from the given tools.
func (*Registry) Register ¶
Register adds or replaces a tool. Insertion order is preserved for the schema list the model sees.
func (*Registry) Schemas ¶
func (r *Registry) Schemas() []ToolSchema
Schemas returns every tool as advertised to the model, in registration order.
type Request ¶
type Request struct {
Model string
Messages []Message
Tools []ToolSchema
MaxTokens int
Temperature float64
}
Request is one call to the model.
type Response ¶
Response is the model's reply. When ToolCalls is non-empty the model wants those run before it continues; otherwise Content is the final answer.
type Runner ¶
type Runner struct {
Engine Engine
Harness Harness
// Authorize, when set, runs before a Mutates tool. A non-nil error stops
// that one tool; its text becomes the tool result the model sees, so the
// model can adapt rather than the whole run failing.
Authorize func(ctx context.Context, tool Tool, call ToolCall) error
// Approve, when set, runs when the model requests tools. Returning false
// pauses the turn (human-in-the-loop): Turn returns ErrAwaitingApproval and
// the pending calls are the last assistant turn. When nil, tools run
// automatically.
Approve func(ctx context.Context, calls []ToolCall) (bool, error)
}
Runner drives a Harness against an Engine.
func (Runner) Advance ¶ added in v0.4.0
Advance drives the tool loop from the conversation's current state, without adding a user message: it calls the model, runs any requested tools (or pauses via Approve), and repeats until a final answer or the step budget runs out. A host that records its own richer user turn appends it, then calls Advance.
func (Runner) Resume ¶
func (r Runner) Resume(ctx context.Context, conv *Conversation, decisions map[string]bool) (string, error)
Resume runs the tools of a turn paused by Approve, then advances. It errors if nothing is pending.
decisions maps a pending call's ID to whether the operator approved it. When decisions is non-nil, a call runs only if its ID maps to true; every other pending call is declined and its result records the refusal, so the model continues without it. A nil map approves every pending call.
func (Runner) Turn ¶
Turn appends a user message and advances until the model gives a final answer or the step budget runs out, returning the final content.
Example ¶
ExampleRunner_Turn drives a tool loop against a fake engine: the model asks for a tool, the runner runs it, and the model gives a final answer.
eng := &scriptedEngine{responses: []Response{
{ToolCalls: []ToolCall{{ID: "c1", Name: "echo", Arguments: `{"text":"world"}`}}},
{Content: "The tool said echo:world"},
}}
runner := Runner{Engine: eng, Harness: Harness{Model: "demo", Tools: NewRegistry(echoTool())}}
conv := NewConversation("You are a demo.")
answer, err := runner.Turn(context.Background(), conv, "echo world")
if err != nil {
panic(err)
}
fmt.Println(answer)
Output: The tool said echo:world
type Tool ¶
type Tool struct {
Name string
Description string
Parameters map[string]any
Mutates bool
Handler func(ctx context.Context, args json.RawMessage) (string, error)
}
Tool is a function the model may call. Handler runs it and returns the result text that is fed back to the model. Parameters is a JSON Schema for the arguments. Mutates marks a state-changing tool, which the host can gate through Runner.Authorize.
type ToolCall ¶
type ToolCall struct {
ID string `json:"id"`
Name string `json:"name"`
Arguments string `json:"arguments"`
}
ToolCall is one tool invocation the model requested. Arguments is a JSON object string, as produced by the model.
Directories
¶
| Path | Synopsis |
|---|---|
|
engine
|
|
|
openai
Package openai is an Engine that speaks the OpenAI chat-completions wire format, which OpenAI, Azure OpenAI, Ollama, vLLM, LM Studio, and most gateways accept.
|
Package openai is an Engine that speaks the OpenAI chat-completions wire format, which OpenAI, Azure OpenAI, Ollama, vLLM, LM Studio, and most gateways accept. |