Documentation
¶
Overview ¶
Package learn is the agent's learning loop: it turns a finished run into durable knowledge the agent can reuse, so experience compounds across runs instead of being discarded when a conversation ends.
This package is the capture half. A Curator takes the outcome of a run, asks a Distiller what reusable lesson it taught, and writes the result to the skill and memory stores, stamped with the run's provenance. Capture is outcome-gated: only a run that actually converged is distilled, so the agent never crystallizes a lesson from work that failed or stalled. That gate, plus the provenance stamp, is what keeps a self-curating skill set from degrading into noise: every stored lesson is traceable to a run that met its goal.
A skill can also carry an executable check. When a Verifier is supplied, the Curator runs that check in a sandbox before crystallizing the skill: a skill whose check runs and fails is proven broken and dropped, never stored; one that passes is tagged verified; one with no runnable check is kept but tagged unverified. A captured procedure is thus trusted in proportion to evidence that it actually works, rather than on the model's say-so alone.
The Distiller is a port. The model-backed implementation (ModelDistiller) asks a language model to summarize the run; a test supplies a scripted one. The Curator itself contains no model dependency, so its gating, provenance, and persistence are deterministic and fully testable.
Index ¶
- Constants
- func Confidence(uses, wins int) float64
- func Decay(ctx context.Context, skills state.SkillStore, scope state.Scope, p DecayPolicy) ([]state.Skill, error)
- func Reinforce(ctx context.Context, skills state.SkillStore, slugs []string, success bool) error
- type Captured
- type Curator
- type DecayPolicy
- type Distiller
- type DistillerOption
- type GovernedDistiller
- type GovernedVerifier
- type Lesson
- type LessonKind
- type ModelDistiller
- type Option
- type Outcome
- type RegradeResult
- type SandboxFactory
- type SandboxVerifier
- type Verdict
- type Verifier
Constants ¶
const DistillAction = "learn.distill"
DistillAction is the dispatch action name a distillation runs under, so the model call that turns a finished run into lessons is admitted, traced, and recorded on the spine under a stable name rather than reaching the model on a side channel.
const VerifyAction = "learn.verify"
VerifyAction is the dispatch action name a skill check runs under, so a verification appears on the event spine and in traces under a stable, greppable name alongside the tools the agent invokes.
Variables ¶
This section is empty.
Functions ¶
func Confidence ¶
Confidence estimates how reliably a skill helps, as the Wilson lower bound of its win rate (wins over uses). The lower bound is the principled small-sample estimate: it is conservative when evidence is thin, so a skill that won its only use does not outrank one that won 50 of 55, and it rises toward the raw win rate as evidence accumulates. It is 0 when the skill has never been used.
Grading a skill by its confirmed outcomes is what makes this learning loop self-correcting where a recency- or usage-only one is not: a skill that keeps appearing in failing runs decays however often it is touched.
func Decay ¶
func Decay(ctx context.Context, skills state.SkillStore, scope state.Scope, p DecayPolicy) ([]state.Skill, error)
Decay archives the skills in scope that the policy judges unhelpful, returning the ones it archived. Archiving is a soft delete (a tombstone), so a retired skill is recoverable and never silently lost, and a skill with little evidence is kept because it has not yet earned retirement.
func Reinforce ¶
Reinforce records one run's outcome against the skills it recalled: each is used once more, and once more a win if the run succeeded. The evidence accrues on the skill so it can be ranked and retired by how it actually performs. A duplicate or empty slug, or one with no live skill, is skipped; the first store error is returned. It is a read-modify-write per skill, which is safe under the agent's single-writer local store.
Types ¶
type Captured ¶
type Captured struct {
Skills []state.Skill
Memories []state.MemoryItem
Dropped []Lesson
}
Captured is what a Curate call persisted, returned for audit and for the caller to surface ("learned 2 skills, 1 memory from this run"). Dropped holds skill lessons rejected because their check ran and failed, so a caller can report what was discarded as broken rather than silently losing it.
type Curator ¶
type Curator struct {
// contains filtered or unexported fields
}
Curator is the capture half of the learning loop: it distills a converged run into lessons and persists them, stamped with provenance. It holds no model dependency of its own; the Distiller supplies that. With a Verifier set, a skill's executable check gates whether it is crystallized.
func NewCurator ¶
func NewCurator(d Distiller, skills state.SkillStore, memories state.MemoryStore, opts ...Option) *Curator
NewCurator builds a Curator over a distiller and the skill and memory stores it writes to.
func (*Curator) Curate ¶
Curate distills o and persists the resulting lessons, returning what it stored. It is outcome-gated: a run that did not converge yields nothing, so a failed or stalled run never crystallizes a lesson. A lesson missing a body is skipped. The first store error aborts and is returned, with whatever was stored before it.
type DecayPolicy ¶
DecayPolicy decides when a skill is retired: once it has at least MinUses of evidence and its confidence is below MinConfidence, it has been tried enough and helped too rarely to keep surfacing.
func DefaultDecay ¶
func DefaultDecay() DecayPolicy
DefaultDecay retires a skill only after a fair number of uses with a poor confirmed win rate, so a still-unproven skill keeps its chance.
type Distiller ¶
Distiller turns a run outcome into zero or more lessons. Returning none is a valid result: not every run teaches something worth keeping.
type DistillerOption ¶
type DistillerOption func(*ModelDistiller)
DistillerOption configures a ModelDistiller.
func WithMaxTokens ¶
func WithMaxTokens(n int) DistillerOption
WithMaxTokens caps the output length requested of the model.
func WithSystem ¶
func WithSystem(s string) DistillerOption
WithSystem overrides the standing instruction framing the distillation.
type GovernedDistiller ¶
type GovernedDistiller struct {
// contains filtered or unexported fields
}
GovernedDistiller routes a Distiller's model call through the dispatch waist, so summarising a finished run into skills and memory is admitted, traced, and recorded like every other action rather than reaching the model on a side channel. It wraps any Distiller; the lessons stay in this closure and never reach dispatch.
func NewGovernedDistiller ¶
func NewGovernedDistiller(inner Distiller, opts ...dispatch.Option) *GovernedDistiller
NewGovernedDistiller wraps inner so its distillation runs through a dispatcher built from opts. Pass the same admitter, event sink, and observability the rest of the run uses; with no options the dispatcher applies standalone defaults and the distillation is recorded but ungoverned.
func (*GovernedDistiller) Distill ¶
Distill governs the distillation as a scoped action and returns the inner lessons. If admission declines the distillation it never runs, so nothing is captured and no error is raised (governance opting out is not a failure); a cancelled context is a hard error. An error from the inner distiller itself propagates, so a broken distiller stays visible rather than silently dropping knowledge.
type GovernedVerifier ¶
type GovernedVerifier struct {
// contains filtered or unexported fields
}
GovernedVerifier routes each skill check through the dispatch waist before it runs, so a verification is admitted, traced, and recorded on the event spine exactly like any tool the agent invokes. Executing a model-proposed command is itself an action with consequences; sending it through the same chokepoint means one capability gate, audit trail, and replay path cover it, rather than letting verification be a side channel that bypasses governance.
func NewGovernedVerifier ¶
func NewGovernedVerifier(inner Verifier, opts ...dispatch.Option) *GovernedVerifier
NewGovernedVerifier wraps inner so its checks run through a dispatcher built from opts. Pass the same admitter, event sink, and observability the rest of the agent uses (dispatch.WithAdmitter / WithEventSink / WithObservability) so verifications share its governance and spine; with no options the dispatcher applies standalone defaults and the check is recorded but ungoverned.
func (*GovernedVerifier) Verify ¶
func (g *GovernedVerifier) Verify(ctx context.Context, l Lesson, scope state.Scope) (Verdict, error)
Verify governs the check as a scoped action and returns the inner verdict. The lesson and verdict stay in this closure; the dispatcher brackets the run without seeing them. A cancelled context is a hard error, matching the inner verifier. Any other failure (admission rejected the check, or the inner verifier failed for a non-cancel reason) means the check did not run to a clean verdict, so the skill is reported unproven rather than broken: governance can decline a verification without that being mistaken for evidence the skill is wrong.
type Lesson ¶
type Lesson struct {
Kind LessonKind
Title string
Body string
Tags []string
// Check is an optional shell command that verifies a skill works: it is run in
// a sandbox and a zero exit code means the skill is sound. Empty means the skill
// has no executable check. Ignored for memory items, which are not executable.
Check string
}
Lesson is one piece of durable knowledge a Distiller proposes from a run. Title names a skill (and seeds its slug); for a memory item it is an optional label. Body is the lesson itself.
type LessonKind ¶
type LessonKind string
LessonKind selects where a distilled lesson is stored: a reusable Skill (a procedure the agent can apply again) or a Memory item (a fact or observation to recall).
const ( // LessonSkill is a reusable procedure, stored as a skill keyed by its slug so a // later run with the same lesson updates rather than duplicates it. LessonSkill LessonKind = "skill" // LessonMemory is a fact or observation, stored as an append-only memory item. LessonMemory LessonKind = "memory" )
type ModelDistiller ¶
type ModelDistiller struct {
// contains filtered or unexported fields
}
ModelDistiller is a Distiller backed by a language model: it summarizes a run into lessons by asking the model for a strict JSON array, then parsing it. It is the production distiller; the Curator stays model-free and testable behind the Distiller port.
func NewModelDistiller ¶
func NewModelDistiller(m llm.Model, opts ...DistillerOption) *ModelDistiller
NewModelDistiller builds a model-backed distiller over m.
func (*ModelDistiller) Distill ¶
Distill asks the model for lessons learned from o and parses its JSON reply. A reply with no JSON array is treated as "nothing to capture" (no error); a reply whose array is malformed is a terminal fault, so a broken distiller is visible rather than silently dropping knowledge.
type Option ¶
type Option func(*Curator)
Option configures a Curator.
func WithVerifier ¶
WithVerifier gates skill capture on an executable check: a skill whose check runs and fails is dropped, one that passes is tagged verified, and one with no runnable check is kept tagged unverified. Without a verifier, skills are kept as captured (untagged by verification).
type Outcome ¶
type Outcome struct {
Objective string
Result string
Transcript []llm.Message
Converged bool
Scope state.Scope
// Source identifies the run this knowledge came from (a session or stream id),
// stamped onto every captured item so a lesson is always traceable to its run.
Source string
}
Outcome is the finished run a Curator learns from: what it set out to do, what it produced, the conversation it took to get there, whether it converged, the scope the knowledge belongs to, and a provenance string identifying the run.
type RegradeResult ¶
type RegradeResult struct {
Checked int // skills with a check that were re-run
Reconfirmed []state.Skill // checks re-ran and still passed
Retired []state.Skill // checks re-ran and now failed, so they were archived
}
RegradeResult summarizes a regrade pass: the skills whose checks were re-run and what became of them.
func Regrade ¶
func Regrade(ctx context.Context, skills state.SkillStore, scope state.Scope, v Verifier) (RegradeResult, error)
Regrade re-runs the verification check of every checkable skill in scope and brings the corpus back in line with current ground truth: a skill whose check still passes is re-confirmed (tagged verified), and one whose check now fails is retired (archived, recoverable). Skills with no check, or whose check could not run, are left untouched.
This is what a write-once skill file cannot do: knowledge captured earlier is re-graded as the environment changes, so a procedure that has quietly stopped working is caught and removed rather than recalled forever. A nil verifier is a no-op; the first store or verifier error aborts and is returned.
type SandboxFactory ¶
SandboxFactory creates the sandbox a single verification runs in. A fresh sandbox per check keeps verifications independent and confines a check the same way the agent's own tools are confined.
type SandboxVerifier ¶
type SandboxVerifier struct {
// contains filtered or unexported fields
}
SandboxVerifier runs a skill's check as a shell command in a sandbox and treats a zero exit code as verification. Running the candidate before keeping it is what separates a procedure that works from one that merely sounds plausible; doing it inside the sandbox is what makes executing model-proposed commands safe.
func NewSandboxVerifier ¶
func NewSandboxVerifier(f SandboxFactory) *SandboxVerifier
NewSandboxVerifier builds a verifier that runs each check in a sandbox from f.
func (*SandboxVerifier) Verify ¶
Verify runs l's check in a fresh sandbox. A skill with no check is unproven (Ran=false), not broken. A sandbox that cannot start the check is unproven too, not an error, so a verification-environment hiccup keeps the skill (tagged unverified) rather than discarding it; only a cancelled context is a hard error.
type Verdict ¶
Verdict is the outcome of verifying a skill's check. Ran reports whether the check actually executed: a check that ran and did not verify is proven broken, while one that could not run (none was supplied, or the sandbox failed to start it) is simply unproven. Detail is a short human-readable reason.
type Verifier ¶
type Verifier interface {
// Verify runs l's check and reports the verdict. scope is the run the lesson
// belongs to: it carries through to governance and the audit trail when the
// check is dispatched as an action (see GovernedVerifier), so a verification is
// attributed to the same scope as the work that proposed it.
Verify(ctx context.Context, l Lesson, scope state.Scope) (Verdict, error)
}
Verifier decides whether a skill lesson is sound by running its check. It is the gate that makes a captured procedure trustworthy in proportion to evidence: the Curator crystallizes a verified skill, drops a proven-broken one, and keeps an unproven one tagged unverified.