Files
probo/contrib/claude/agent.md
Émile Ré c4147e6801 Extract agent and tools from tracker mapping worker into dedicated files
Split tracker_mapping_worker.go: agent construction, prompts, and
structured output type move to tracker_mapping_agent.go; each tool gets
its own *_tool.go file. Update naming conventions (worker, agent, tool
file patterns, AgentResult suffix, RunTyped preference).

Signed-off-by: Émile Ré <emile@probo.com>
2026-05-19 12:06:15 +04:00

3.6 KiB

Agent (pkg/agent)

LLM agent orchestration framework.

Agent construction

agent := agent.NewAgent(
    "agent-name",
    "System instructions here",
    agent.WithTools(tool1, tool2),
    agent.WithHandoffs(otherAgent),
    agent.WithModel(model),
)

Functional options: WithTools, WithHandoffs, WithInstructions, WithModel, WithModelSettings, WithMCPServers, WithInputGuardrails, WithOutputGuardrails, WithApproval, WithSession.

Execution

result, err := agent.Run(ctx, messages)
result.FinalMessage().Text()  // final output
result.LastAgent              // agent that produced the result

Prefer RunTyped[T] over Run + manual unmarshalling when the agent declares a structured output type via WithOutputType. RunTyped validates the response against the JSON Schema and returns the typed value directly:

result, err := agent.RunTyped[TrackerIdentification](ctx, ag, messages)
identification := result.Output  // already typed, no json.Unmarshal needed

Only fall back to agent.Run when the agent produces free-form text with no output schema.

Tool interface

type Tool interface {
    Name() string
    Description() string
    Parameters() jsonschema.Schema
    Execute(ctx context.Context, input json.RawMessage) (string, error)
}

Agent-as-tool

agent.AsTool(name, description) wraps an agent as a tool for composition.

Cancellation semantics

ctx.Done() is a graceful-suspend signal, not a hard abort. When the caller cancels ctx, Run/RunStreamed/Resume/Restore let the in-flight LLM call and tool finish, persist a checkpoint via the configured Checkpointer, and return *SuspendedError. The framework shields downstream calls (LLM, tools, hooks, guardrails, save) from the cancellation so they complete naturally; the cancel is only observed at the next safe boundary.

Use agent.ErrSuspendForCheckpoint as the cancel cause when the intent is graceful suspend — supervisors that distinguish a graceful-stop request from infrastructure-level causes (lease loss, heartbeat failure) inspect context.Cause(ctx) to dispatch.

Implications:

  • A context.WithTimeout becomes a "max wall-clock budget then suspend" — strictly better than the alternative where the deadline kills work outright.
  • There is no in-process hard-abort path. Callers that genuinely need to kill a run terminate the process; stale recovery handles the row.
  • Tools receive a non-cancellable ctx; if a tool needs a hard deadline it must derive its own with context.WithTimeout(ctx, ...) inside the tool body.

The supervisor (pkg/probo/agent_run_handler.go) maps a SIGTERM-driven shutdown broadcast onto a per-run cancelRun(agent.ErrSuspendForCheckpoint), so the same contract drives both the public Go API and the worker infrastructure path.

Prompt templates

Prompt files with placeholders use .txt.tmpl (see general template naming convention in .cursor/rules/template-files.mdc).

Use agent.WithInstructionsFunc to build prompts dynamically at runtime:

//go:embed prompts/tracker_identification.txt.tmpl
var trackerIdentificationPrompt string

func trackerMappingInstructions(_ context.Context, _ *agent.Agent) string {
    categories := coredata.ThirdPartyCategories()
    parts := make([]string, len(categories))
    for i, c := range categories {
        parts[i] = string(c)
    }
    return strings.Replace(trackerIdentificationPrompt, "{{.Categories}}", strings.Join(parts, ", "), 1)
}

Limits

  • Max turns: 10 (default)
  • Max tool depth: 16 (default)
  • Depth tracking prevents infinite recursion in handoffs