feat: agent run loop, Generate[T], reflect-derived schemas
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>
This commit is contained in:
@@ -200,13 +200,46 @@ resp, _ := m.Generate(ctx, req, majordomo.WithSchema(schemaJSON, "answer"))
|
||||
```
|
||||
|
||||
Maps to OpenAI `response_format: json_schema`, Anthropic
|
||||
`output_config.format`, and Ollama `format`. A generic `Generate[T]` helper
|
||||
(schema from your struct, unmarshal into it) lands with the agent phase.
|
||||
`output_config.format`, Ollama `format`, and Google `responseJsonSchema`.
|
||||
|
||||
## Agents & skills *(pending — Phases 5–6)*
|
||||
The typed helper derives the schema from your struct (all fields required,
|
||||
`additionalProperties:false`, pointers nullable; `description:"..."` and
|
||||
`enum:"a,b,c"` tags supported) and unmarshals the result:
|
||||
|
||||
Agents = model + system prompt + toolboxes, running a tool-dispatch loop;
|
||||
skills = reusable instruction+tool bundles attachable to any agent.
|
||||
```go
|
||||
type Verdict struct {
|
||||
Guilty bool `json:"guilty"`
|
||||
Why string `json:"why" description:"one-sentence rationale"`
|
||||
}
|
||||
v, err := majordomo.Generate[Verdict](ctx, m, req)
|
||||
```
|
||||
|
||||
## Agents
|
||||
|
||||
An agent is a model + system prompt + toolboxes, run as a tool-dispatch
|
||||
loop until the model answers (or `MaxSteps`):
|
||||
|
||||
```go
|
||||
import "gitea.stevedudenhoeffer.com/steve/majordomo/agent"
|
||||
|
||||
a := agent.New(m, "You are a research assistant.",
|
||||
agent.WithToolbox(searchTools),
|
||||
agent.WithMaxSteps(8),
|
||||
agent.WithStepObserver(func(s agent.Step) { log.Printf("step %d", s.Index) }),
|
||||
)
|
||||
res, err := a.Run(ctx, "What changed in Go 1.26?")
|
||||
// res.Output, res.Steps, res.Usage; res.Messages round-trips via
|
||||
// agent.WithHistory for conversation continuation.
|
||||
```
|
||||
|
||||
The loop never panics: tool handler errors and panics become error results
|
||||
the model can react to; unknown tools likewise; duplicate tool names across
|
||||
toolboxes fail loudly. On `agent.ErrMaxSteps` (and on model errors) the
|
||||
partial result with the full transcript is still returned.
|
||||
|
||||
## Skills *(pending — Phase 6)*
|
||||
|
||||
Skills = reusable instruction+tool bundles attachable to any agent.
|
||||
|
||||
## Feature/provider support matrix
|
||||
|
||||
@@ -229,8 +262,8 @@ Notes: Ollama has no native tool_choice — `"none"` drops the tools;
|
||||
|
||||
Cross-cutting: Parse grammar ✅ · aliases/tiers ✅ · failover chains ✅ ·
|
||||
health tracking/backoff ✅ · LLM_* env DSNs ✅ · media pipeline ✅
|
||||
(per-target normalization in chains) · agent loop pending · skills pending
|
||||
· `Generate[T]` pending.
|
||||
(per-target normalization in chains) · agent loop ✅ · `Generate[T]` +
|
||||
schema derivation ✅ · skills pending.
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
Reference in New Issue
Block a user