7dab4112ff
Phase 5: - agent/: model + system prompt + toolboxes composition; bounded tool-dispatch loop (default 10 steps); panic-proof tool execution; unknown-tool and duplicate-name handling; history continuation; step observers; partial results on ErrMaxSteps/errors (ADR-0012) - llm.SchemaFor[T]: strict-compatible JSON schemas from Go types (nullable pointers, description/enum tags, recursion rejected) - majordomo.Generate[T]: typed structured output with fence-stripping decode and model-naming errors - README agents/structured-output sections + matrix synced Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
54 lines
2.8 KiB
Markdown
54 lines
2.8 KiB
Markdown
# ADR-0012: Agent run loop
|
|
|
|
**Status:** Accepted — 2026-06-10
|
|
|
|
## Context
|
|
|
|
Agents are the consumer-facing composition: Model + system prompt +
|
|
toolboxes (+ skills), run as a tool-dispatch loop. mort's agents need
|
|
multi-step tool use, bounded loops, per-step observation (tracing/usage
|
|
recording), conversation continuation, and a loop that survives every tool
|
|
failure.
|
|
|
|
## Decision
|
|
|
|
- `agent.New(model, system, opts...)` with options WithToolbox, WithTools,
|
|
WithSkill, WithMaxSteps (default 10), WithRequestOptions (per-step
|
|
generation knobs), WithStepObserver. `AddSkill`/`AddToolbox` extend an
|
|
agent after construction (configure-then-share concurrency contract).
|
|
- `Run(ctx, input, opts...)`: history seeds via WithHistory (a previous
|
|
`Result.Messages` round-trips), per-run request options, per-run OnStep
|
|
callbacks. The loop: Generate with the merged toolset → no tool calls =
|
|
final answer → otherwise execute calls sequentially, append the
|
|
assistant turn + one RoleTool message, repeat.
|
|
- **Never panics, never stalls on tool failure:** handlers run through
|
|
`llm.ExecuteTool` (panic-recovering, errors → IsError results); unknown
|
|
tool names come back as IsError results the model can react to; observer
|
|
panics are swallowed. Only model errors (already retried/failed-over by
|
|
the chain) and ctx cancellation abort the run — and even then the
|
|
partial `Result` (transcript, steps, usage) is returned alongside the
|
|
error, as it is on ErrMaxSteps.
|
|
- **Duplicate tool names across toolboxes/skills fail loudly at Run** —
|
|
a silently shadowed tool is a debugging trap.
|
|
- Skills compose additively (the `agent.Skill` interface lives in this
|
|
package so agent does not import skill): `Instructions()` append to the
|
|
system prompt in attachment order; `Tools()` join the merged toolset.
|
|
- **Intermediate-step streaming = step observers** (agent-level and
|
|
per-run), invoked synchronously after every step with the response and
|
|
tool results. Token-level streaming inside the loop was deliberately
|
|
deferred: tool-heavy steps end on buffered tool calls anyway, and mort's
|
|
observers (trace/usage recorders) want completed steps. `Model.Stream`
|
|
remains available for direct streaming outside the loop.
|
|
- `Generate[T]` + `llm.SchemaFor[T]` give typed structured output:
|
|
reflect-derived schemas (strict-compatible: all properties required,
|
|
additionalProperties:false, pointers → nullable anyOf), markdown-fence
|
|
stripping as a decode fallback, errors that name the serving model.
|
|
|
|
## Consequences
|
|
|
|
- An agent over a failover chain inherits retry/backoff/media
|
|
normalization transparently — no agent-level error handling for
|
|
transient provider trouble.
|
|
- Sequential tool execution is deterministic and trace-friendly; parallel
|
|
dispatch can be added later behind an option without API change.
|