- name
- ax-agent
- description
- This skill helps an LLM generate correct core AxAgent code using @ax-llm/ax. Use when the user asks about agent(), child agents, namespaced functions, discovery mode, clarification, bubbleErrors, host-side final/clarification protocol, or ordinary agent runtime behavior. For RLM/code-runtime work use ax-agent-rlm; for callbacks and telemetry use ax-agent-observability; for recall/memory/skill loading use ax-agent-memory-skills; for agent.optimize(...) use ax-agent-optimize.
- version
- 23.0.0
# AxAgent Codegen Rules (@ax-llm/ax)
Use this skill to generate small, correct `AxAgent` code. Prefer modern factory-style APIs and copyable patterns. Do not write tutorial prose unless the user explicitly asks for explanation.
Your job is to choose the smallest correct `AxAgent` shape for the user's needs:
- If the user wants a normal tool-using assistant, keep the config minimal.
- If the user wants long-running code execution, use the `ax-agent-rlm` skill.
- If the user wants callbacks, logs, tracing, or usage data, use the `ax-agent-observability` skill.
- If the user wants dynamic memory retrieval or skill-guide loading, use the `ax-agent-memory-skills` skill.
- If the user wants tuning or eval with `agent.optimize(...)`, use the `ax-agent-optimize` skill.
## Use These Defaults
- Use `agent(...)`, not `new AxAgent(...)`.
- Prefer string signatures or `f()` signatures over hand-written signature objects.
- Put `ai`, `judgeAI`, and `agentIdentity` on the `agent(...)` config when you want instance defaults or child-agent metadata.
- Prefer `fn(...)` for host-side function definitions instead of hand-writing JSON Schema objects.
- Prefer namespaced functions such as `utils.search(...)` or `kb.find(...)`.
- Pass child agents directly in `functions: [...]`. They land under their `agentIdentity.namespace` (or `utils` if unset), exactly like a `fn()` tool.
- If discovery is enabled, call `discover(...)` before using callables whose docs are not already in the prompt.
- Use explicit child agents in `functions: [...]` for specialist delegation; do not model that as recursive `llmQuery(...)`.
- Add `bubbleErrors` only for fatal infrastructure errors that should abort `.forward()`.
## Decision Guide
Map user intent to agent shape before writing code:
- "Use tools and answer" -> plain `agent(...)` with local functions, no extra observability.
- "Need child agents with distinct responsibilities" -> add child agents to the parent's `functions: [...]` list and set each child's `agentIdentity.namespace` when you want a specific runtime call site such as `team.writer(...)`.
- "Need tool discovery because names/schemas are not stable" -> enable discovery and generate discovery-first actor code.
- "Need certain errors to escape the agent loop" -> add `bubbleErrors` with error classes; those errors propagate through function handlers, actor code, and `llmQuery(...)` sub-queries to `.forward()`.
- "Inspect large context with code", "RLM", or "`llmQuery(...)`" -> use `ax-agent-rlm`.
- "Need debugging, traces, progress updates, tool-call logs, chat logs, or usage" -> use `ax-agent-observability`.
- "Need memories, recall, dynamic skill guides, `discover({ skills })`, or loaded/used tracking" -> use `ax-agent-memory-skills`.
## Critical Rules
- Use `agent(...)` factory syntax for new code.
- Add child agents to the parent's `functions: [...]` list. Each child's `agentIdentity.namespace` (or `utils`, the default) determines the runtime call site, e.g. `await team.writer({...})`.
- If discovery is enabled, call `discover(...)` before using callables whose docs are not already in the prompt.
- `autoUpgrade` is ON by default: large tool catalogs auto-enable discovery, and oversized undeclared input values are auto-kept runtime-only with a truncated prompt preview. Explicit `functionDiscovery` and declared `contextFields` always win; set `autoUpgrade: false` to opt out.
- `directResponse` is ON by default (`'auto'`): when a task needs no user-provided functions, the distiller ends the run with `respond(task, evidence)` and the executor stage is skipped (zero executor model calls). Function-less agents run respond-only every time; agents with functions offer `respond` under a conservative covenant (no live/fresh-state asks, no side effects, nothing a listed function/module domain covers). Set `directResponse: 'off'` to always run the executor.
- If a host-side `AxAgentFunction` needs to end the current actor turn, use `extra.protocol.final(...)` or `extra.protocol.askClarification(...)`.
- In public `forward()` and `streamingForward()` flows, `askClarification(...)` throws `AxAgentClarificationError`; it does not go through the responder.
- When resuming after clarification, prefer `error.getState()` from the thrown `AxAgentClarificationError`, then call `agent.setState(savedState)` before the next `forward(...)`.
- Errors listed in `bubbleErrors` bypass actor-loop catch blocks and propagate directly to the caller of `.forward()`.
- Child agents receive only the arguments the actor passes. Pass parent fields explicitly via `inputs.<field>` or use `inputUpdateCallback` when many calls need the same value.
- Audio input fields are transcribed before agent planner/executor/responder stages by default; internal agent stages receive text transcripts, not base64 audio.
## Canonical Pattern
```typescript
import { agent, ai, f } from '@ax-llm/ax';
const llm = ai({
name: 'openai',
apiKey: process.env.OPENAI_APIKEY!,
});
const assistant = agent(
f()
.input('query', f.string())
.output('answer', f.string())
.build(),
{
agentIdentity: {
name: 'Assistant',
description: 'Answers user questions',
},
contextFields: [],
}
);
const result = await assistant.forward(llm, { query: 'What is TypeScript?' });
console.log(result.answer);
```
## Audio Inputs And Speech Outputs
Agents can accept audio inputs and return scripted speech artifacts. The runtime transcribes audio input fields before internal stages run, then synthesizes `:audio` outputs after the final structured response is selected.
```typescript
const voiceAgent = agent(
'recording:audio, question:string -> speech:audio, summary:string',
{
agentIdentity: {
name: 'Voice Assistant',
description: 'Answers spoken requests',
},
contextFields: [],
}
);
const result = await voiceAgent.forward(
llm,
{
recording: { data: base64Wav, format: 'wav' },
question: 'What should I do next?',
},
{
speech: {
transcribe: { model: 'gpt-4o-mini-transcribe' },
speak: { voice: 'alloy', format: 'mp3' },
},
}
);
console.log(result.summary);
console.log(result.speech.data);
```
Use direct `ax(...)` or `.chat()` if the model should receive native audio instead of a transcript-first agent pipeline.
## Child Agents As Tools
Child agents are passed in the parent's `functions` list. There is no separate `agents` option for new code. Each child agent's `agentIdentity.namespace` (or `utils`, the default) determines where it lands in the actor runtime. With `AxJSRuntime`, that produces JavaScript call sites such as `team.writer(...)`:
```typescript
const writer = agent('draft:string -> revision:string', {
agentIdentity: {
name: 'Writer',
description: 'Polishes drafts',
namespace: 'team',
},
contextFields: [],
});
const coordinator = agent('query:string -> answer:string', {
functions: [writer],
contextFields: [],
});
```
Generated runtime call:
```javascript
const result = await team.writer({ draft: '...' });
```
Without `agentIdentity.namespace`, the child lands under `utils.<name>` like any other tool:
```javascript
const result = await utils.writer({ draft: '...' });
```
Rules:
- Add child agents to `functions: [...]`, the same array as `fn(...)` tools.
- Set `agentIdentity.namespace` on the child to control its runtime call site.
- `onFunctionCall` observers receive `kind: 'internal'` for agent-derived calls and `kind: 'external'` for user-registered tools.
### Reserved namespace names
The agent runtime injects a fixed set of globals into the runtime session. These names cannot be used as `agentIdentity.namespace` values or as agent-function namespaces.
```text
inputs
llmQuery
final
askClarification
reportSuccess
reportFailure
inspectRuntime
discover
recall
```
Pick any other lowercase identifier such as `utils`, `kb`, `tools`, `team`, or `db`.
## Tool Functions And Namespaces
```typescript
import { agent, f, fn } from '@ax-llm/ax';
const findSnippets = fn('findSnippets')
.description('Find handbook snippets by topic')
.namespace('kb')
.arg('topic', f.string('Topic keyword'))
.returns(f.string('Matching snippet').array())
.example({
title: 'Find severity guidance',
code: 'await kb.findSnippets({ topic: "severity" });',
})
.handler(async ({ topic }) => [])
.build();
const analyst = agent('query:string -> answer:string', {
functions: [findSnippets],
contextFields: [],
});
```
Generated runtime call:
```javascript
const snippets = await kb.findSnippets({ topic: 'severity' });
```
Rules:
- Prefer namespaced functions.
- Default function namespace is `utils` when no namespace is set.
- With `AxJSRuntime`, use the runtime call shape `await <namespace>.<name>({...})`. Custom runtimes should expose equivalent namespaced calls through their own `formatCallable()` guidance.
- `.arg()` and `.returns()` can use Ax field helpers or any Standard Schema v1 validator directly.
## Grouped Function Modules
For discovery mode, group functions into modules using the `AxAgentFunctionGroup` shape when you want a clean namespace tree such as `kb.find(...)` or `metrics.score(...)` without setting `namespace` on every individual `fn(...)`:
```typescript
const parent = agent('query:string -> answer:string', {
functions: [
{
namespace: 'kb',
title: 'Knowledge Base',
selectionCriteria: 'Use for handbook and documentation lookups.',
description: 'Knowledge base lookups',
functions: [findSnippetsFn, searchPagesFn],
},
{
namespace: 'workflow',
title: 'Workflow Controls',
description: 'Small control functions the actor should always see',
alwaysInclude: true,
functions: [completeFn],
},
],
functionDiscovery: true,
contextFields: [],
});
```
MCP clients and other `toFunction()` providers can be placed directly inside a group after initialization:
```typescript
await mcpClient.init();
const parent = agent('query:string -> answer:string', {
functions: [
{
namespace: 'memory',
title: 'Memory MCP',
description: 'Memory server tools',
selectionCriteria: 'Use for persistent memory lookup and updates.',
functions: [mcpClient],
},
],
functionDiscovery: true,
contextFields: [],
});
```
Rules:
- A group is `{ namespace, title, description, functions: [...] }`.
- `selectionCriteria` is optional but useful in discovery mode; it tells the actor when to choose that module.
- The group's `namespace`, `title`, `selectionCriteria`, and `description` show up in `discover(...)` module docs.
- `relevanceRanking` (default ON — set `false` to opt out): a deterministic local ranker that injects an advisory `### Likely Relevant` shortlist into the executor turn (dynamic, non-cached field — the cached prompt stays byte-stable). Enabled by default after its A/B gate passed on both small and frontier models and implemented in the generated language ports through AxIR Core. Details in `ax-agent-memory-skills`; outcomes observable via the `relevance_ranking` context event (`ax-agent-observability`).
- Add `alwaysInclude: true` to a group when discovery mode is on but the actor should always see that group's full callable definitions inline in the prompt.
- Keep `functions: [...]` either flat or grouped. Runtime validation rejects mixed plain function entries and group objects.
- In flat mode, pass `fn(...)` tools, child agents, and `toFunction()` providers directly.
- In grouped mode, put callable entries and `toFunction()` providers inside groups. To expose a child agent inside a group, use `childAgent.getFunction()`.
## Host-Side Completion From Functions
Use this pattern when the actor should call a namespaced function, but the host-side function implementation should decide to end the turn:
```typescript
import { f, fn } from '@ax-llm/ax';
const finishReply = fn('finishReply')
.description('Complete the actor turn with the final reply text')
.namespace('workflow')
.arg('reply', f.string('Final reply text'))
.returns(f.string('Final reply text'))
.handler(async ({ reply }, extra) => {
extra?.protocol?.final(reply);
return reply;
})
.build();
const askForOrderId = fn('askForOrderId')
.description('Complete the actor turn by requesting clarification')
.namespace('workflow')
.arg('question', f.string('Clarification question'))
.returns(f.string('Clarification question'))
.handler(async ({ question }, extra) => {
extra?.protocol?.askClarification(question);
return question;
})
.build();
```
Rules:
- `extra.protocol` is only available when the function call comes from an active AxAgent actor runtime session.
- Use `extra.protocol.final(...)`, `extra.protocol.askClarification(...)`, or `extra.protocol.guideAgent(...)` only inside host-side function handlers.
- Inside actor-authored runtime code, use the runtime globals `final(...)` and `askClarification(...)` with the syntax documented by the active runtime.
- `extra.protocol.guideAgent(...)` is handler-only internal control flow. It stops the current actor turn and appends trusted guidance to `guidanceLog` for the next iteration.
- `askClarification(...)` accepts either a simple string or a structured object with `question` plus optional UI hints such as `type: 'date' | 'number' | 'single_choice' | 'multiple_choice'` and `choices`.
## Clarification And Resume State
Use this pattern when the actor should pause for user input and continue later from the same runtime state.
```typescript
import {
AxAgentClarificationError,
AxJSRuntime,
agent,
ai,
Auf GitHub ansehen