Playbook Codegen Rules (@ax-llm/ax)
Use this skill to generate context-playbook code. A playbook grows an evolving body of task knowledge and renders it into a program's context. The evolution engine (ACE — Agentic Context Engineering) is hidden behind playbook(...), exactly as optimize(...) hides its optimizer. Prefer the playbook(...) concept; only reach for AxACE directly when the user explicitly wants the low-level engine.
Use These Defaults
- Create with
playbook(program, { studentAI, teacherAI? }); it returns an AxPlaybook handle.
- Grow offline with
await pb.evolve(examples, metric) — returns { bestScore, playbook }.
- Grow online with
await pb.update({ example, prediction, feedback }) — no metric needed.
- Apply with
pb.applyTo(program) (defaults to the bound program).
- Persist with
pb.toJSON() and restore with playbook(program, opts).load(snapshot).
- Inspect with
pb.render() (markdown) and pb.getState() ({ playbook, artifact }).
- For agents use
agent.playbook({ target: 'actor' | 'responder' }); default target is 'actor'.
- Use a cheaper
studentAI to run the program and an optional stronger teacherAI to reflect/curate.
- Prefer
ai(), ax(), and agent() for new code.
Critical Rules
playbook(...) binds to an AxGen program; evolve/update need that program's signature.
evolve() returns only { bestScore, playbook }. There is no Pareto front and no optimizedProgram — that is optimize(...)'s shape, not a playbook's.
update({ example, prediction, feedback }) requires the full { example, prediction }; example must match the program's input fields (plus any expected output). Do not pass bare input fields at the top level.
update() works without a prior evolve()/load() — the handle hydrates lazily on first use.
applyTo() injects a ## Context Playbook block into the program description; calling it repeatedly recomposes from the original base (no stacking).
- Keep the offline
metric deterministic and cheap, like a GEPA metric.
- A playbook is plain JSON. Persist
pb.toJSON() and load(...) it into a fresh program for production.
- The playbook engine, construction-time agent attachment, failure harvesting,
and verified agent evolution are available in TypeScript and the generated
Python, Java, C++, Go, and Rust packages. Use each package's native casing and
callback types.
Offline Pattern (evolve)
import { type AxMetricFn, ai, ax, playbook } from '@ax-llm/ax';
const program = ax('review:string -> sentiment:class "positive, negative"');
const studentAI = ai({ name: 'openai', apiKey: process.env.OPENAI_APIKEY! });
const metric: AxMetricFn = ({ prediction, example }) =>
(prediction as any).sentiment === (example as any).sentiment ? 1 : 0;
const pb = playbook(program, { studentAI, maxEpochs: 2 });
const { bestScore } = await pb.evolve(train, metric);
pb.applyTo(program);
Online Pattern (update)
// After a real run, feed the outcome back so the playbook keeps learning.
await pb.update({
example: { review: 'Five stars, would buy again.' },
prediction: { sentiment: 'negative' },
feedback: 'WRONG: enthusiastic praise is positive.',
});
pb.applyTo(program);
Persist And Restore
const snapshot = pb.toJSON(); // { playbook, artifact } — plain JSON
// later, in another process / a production program instance:
playbook(prodProgram, { studentAI }).load(snapshot).applyTo(prodProgram);
Agents
a.playbook({ target }) returns an agent-aware AxAgentPlaybook (the stage AxPlaybook handle plus an agent-level evolve). The one playbook the agent renders into its prompt grows three ways:
- Continuous (trust): the construction-time
playbook option (see ax-agent) harvests each run's failures automatically — no dataset.
- On-demand (trust):
apb.update({ example, prediction, feedback }).
- Batch verified (proof):
apb.evolve(dataset, options) runs the full agent over a task set, mines failure clusters, and proposes one playbook bullet per weakness; with verify (default on) it keeps a bullet only if held-in improves AND the validation held-out set does not regress, else exact rollback. verify: false = trust-batch. Bullets-only.
const a = agent('ticket:string -> reply:string', { ai });
const apb = a.playbook({ target: 'actor' }); // agent-aware handle; 'actor' (default) or 'responder'
await apb.update({ example, prediction, feedback }); // online: injected into the live stage prompt
const result = await apb.evolve(
{ train, validation }, // AxAgentEvalDataset
{ metric, runsPerTask: 2 }, // verify:true by default
);
The agent-level evolve(dataset, options) is distinct from the program-level pb.evolve(examples, metric) above: it takes an AxAgentEvalDataset plus options, runs the whole pipeline, and returns baseline/final held-in & held-out with per-bullet outcomes (no { bestScore }). For full-pipeline tuning of agent instructions and demos (not the playbook) use agent.optimize(...) (GEPA).
Generated packages expose that same agent-bound loop with language-shaped APIs:
| Language |
Agent-bound evolve call |
| Python |
agent.playbook().evolve(dataset, options) |
| Java |
agent.playbook(null).evolve(dataset, options) |
| C++ |
agent.get_playbook()->evolve(dataset, options) |
| Go |
agent.GetPlaybook().EvolveAgent(ctx, dataset, options) |
| Rust |
playbook.evolve_agent(&mut agent, client, dataset, options) |
All five generated packages thread structured failureSignals through agent
evaluation predictions. The default verify gate accepts a proposed bullet only
when held-in score improves and held-out score stays within epsilon; rejection
restores the exact prior snapshot. Scoring is host-shaped: TypeScript uses its
metric, Python/Java/Go can accept a metric callback, and all generated ports can
use task score/scores values plus the agent evaluation result.
Playbook vs optimize()
playbook(...) — accumulate reusable, evolving task knowledge; the only path that also learns online via update(...).
optimize(...) / agent.optimize(...) — tune instruction text and few-shot demos offline to a best/Pareto result.
- They are complementary; a project can use both.
Troubleshooting
- "Cannot convert undefined or null to object" from
update() → you passed input fields at the top level; wrap them in example: { ... }.
- Empty playbook after
evolve() → the model already scored well, so nothing was curated; use harder/ambiguous examples or a weaker studentAI to surface lessons.
- Playbook not affecting an agent's behavior → ensure
apply is not false and you used agent.playbook(...) (not a bare playbook() on an internal program).
See Also
ax-gepa - optimize(...) and AxGEPA for instruction/demo tuning.
ax-agent-context - choosing between contextMap, contextPolicy, agent.playbook(...), and recall.
ax-agent-optimize - agent.optimize(...) GEPA tuning for agents.
1---2name: ax-playbook3description: This skill helps an LLM generate correct playbook code using @ax-llm/ax. Use when the user asks about playbook(), AxPlaybook, context playbooks, evolving context, ACE / Agentic Context Engineering, agent.playbook(), or growing/applying task knowledge offline and online with evolve() and update().4---56# Playbook Codegen Rules (@ax-llm/ax)78Use this skill to generate context-playbook code. A playbook grows an evolving body of task knowledge and renders it into a program's context. The evolution engine (ACE — Agentic Context Engineering) is hidden behind `playbook(...)`, exactly as `optimize(...)` hides its optimizer. Prefer the `playbook(...)` concept; only reach for `AxACE` directly when the user explicitly wants the low-level engine.910## Use These Defaults1112- Create with `playbook(program, { studentAI, teacherAI? })`; it returns an `AxPlaybook` handle.13- Grow offline with `await pb.evolve(examples, metric)` — returns `{ bestScore, playbook }`.14- Grow online with `await pb.update({ example, prediction, feedback })` — no metric needed.15- Apply with `pb.applyTo(program)` (defaults to the bound program).16- Persist with `pb.toJSON()` and restore with `playbook(program, opts).load(snapshot)`.17- Inspect with `pb.render()` (markdown) and `pb.getState()` (`{ playbook, artifact }`).18- For agents use `agent.playbook({ target: 'actor' | 'responder' })`; default target is `'actor'`.19- Use a cheaper `studentAI` to run the program and an optional stronger `teacherAI` to reflect/curate.20- Prefer `ai()`, `ax()`, and `agent()` for new code.2122## Critical Rules2324- `playbook(...)` binds to an `AxGen` program; `evolve`/`update` need that program's signature.25- `evolve()` returns only `{ bestScore, playbook }`. There is no Pareto front and no `optimizedProgram` — that is `optimize(...)`'s shape, not a playbook's.26- `update({ example, prediction, feedback })` requires the full `{ example, prediction }`; `example` must match the program's input fields (plus any expected output). Do not pass bare input fields at the top level.27- `update()` works without a prior `evolve()`/`load()` — the handle hydrates lazily on first use.28- `applyTo()` injects a `## Context Playbook` block into the program description; calling it repeatedly recomposes from the original base (no stacking).29- Keep the offline `metric` deterministic and cheap, like a GEPA metric.30- A playbook is plain JSON. Persist `pb.toJSON()` and `load(...)` it into a fresh program for production.31- The playbook engine, construction-time agent attachment, failure harvesting,32 and verified agent evolution are available in TypeScript and the generated33 Python, Java, C++, Go, and Rust packages. Use each package's native casing and34 callback types.3536## Offline Pattern (evolve)3738```typescript39import { type AxMetricFn, ai, ax, playbook } from '@ax-llm/ax';4041const program = ax('review:string -> sentiment:class "positive, negative"');42const studentAI = ai({ name: 'openai', apiKey: process.env.OPENAI_APIKEY! });43const metric: AxMetricFn = ({ prediction, example }) =>44 (prediction as any).sentiment === (example as any).sentiment ? 1 : 0;4546const pb = playbook(program, { studentAI, maxEpochs: 2 });47const { bestScore } = await pb.evolve(train, metric);48pb.applyTo(program);49```5051## Online Pattern (update)5253```typescript54// After a real run, feed the outcome back so the playbook keeps learning.55await pb.update({56 example: { review: 'Five stars, would buy again.' },57 prediction: { sentiment: 'negative' },58 feedback: 'WRONG: enthusiastic praise is positive.',59});60pb.applyTo(program);61```6263## Persist And Restore6465```typescript66const snapshot = pb.toJSON(); // { playbook, artifact } — plain JSON67// later, in another process / a production program instance:68playbook(prodProgram, { studentAI }).load(snapshot).applyTo(prodProgram);69```7071## Agents7273`a.playbook({ target })` returns an agent-aware `AxAgentPlaybook` (the stage `AxPlaybook` handle plus an agent-level `evolve`). The one playbook the agent renders into its prompt grows three ways:7475- Continuous (trust): the construction-time `playbook` option (see `ax-agent`) harvests each run's failures automatically — no dataset.76- On-demand (trust): `apb.update({ example, prediction, feedback })`.77- Batch verified (proof): `apb.evolve(dataset, options)` runs the full agent over a task set, mines failure clusters, and proposes one playbook bullet per weakness; with `verify` (default on) it keeps a bullet only if held-in improves AND the `validation` held-out set does not regress, else exact rollback. `verify: false` = trust-batch. Bullets-only.7879```typescript80const a = agent('ticket:string -> reply:string', { ai });81const apb = a.playbook({ target: 'actor' }); // agent-aware handle; 'actor' (default) or 'responder'82await apb.update({ example, prediction, feedback }); // online: injected into the live stage prompt83const result = await apb.evolve(84 { train, validation }, // AxAgentEvalDataset85 { metric, runsPerTask: 2 }, // verify:true by default86);87```8889The agent-level `evolve(dataset, options)` is distinct from the program-level `pb.evolve(examples, metric)` above: it takes an `AxAgentEvalDataset` plus options, runs the whole pipeline, and returns baseline/final held-in & held-out with per-bullet outcomes (no `{ bestScore }`). For full-pipeline tuning of agent instructions and demos (not the playbook) use `agent.optimize(...)` (GEPA).9091Generated packages expose that same agent-bound loop with language-shaped APIs:9293| Language | Agent-bound evolve call |94|---|---|95| Python | `agent.playbook().evolve(dataset, options)` |96| Java | `agent.playbook(null).evolve(dataset, options)` |97| C++ | `agent.get_playbook()->evolve(dataset, options)` |98| Go | `agent.GetPlaybook().EvolveAgent(ctx, dataset, options)` |99| Rust | `playbook.evolve_agent(&mut agent, client, dataset, options)` |100101All five generated packages thread structured `failureSignals` through agent102evaluation predictions. The default verify gate accepts a proposed bullet only103when held-in score improves and held-out score stays within `epsilon`; rejection104restores the exact prior snapshot. Scoring is host-shaped: TypeScript uses its105metric, Python/Java/Go can accept a metric callback, and all generated ports can106use task `score`/`scores` values plus the agent evaluation result.107108## Playbook vs optimize()109110- `playbook(...)` — accumulate reusable, evolving task knowledge; the only path that also learns online via `update(...)`.111- `optimize(...)` / `agent.optimize(...)` — tune instruction text and few-shot demos offline to a best/Pareto result.112- They are complementary; a project can use both.113114## Troubleshooting115116- "Cannot convert undefined or null to object" from `update()` → you passed input fields at the top level; wrap them in `example: { ... }`.117- Empty playbook after `evolve()` → the model already scored well, so nothing was curated; use harder/ambiguous examples or a weaker `studentAI` to surface lessons.118- Playbook not affecting an agent's behavior → ensure `apply` is not `false` and you used `agent.playbook(...)` (not a bare `playbook()` on an internal program).119120## See Also121122- `ax-gepa` - `optimize(...)` and `AxGEPA` for instruction/demo tuning.123- `ax-agent-context` - choosing between contextMap, contextPolicy, `agent.playbook(...)`, and recall.124- `ax-agent-optimize` - `agent.optimize(...)` GEPA tuning for agents.