agent() Agents
Use agent() to build an RLM agent with a typed final response.
import { agent, ai, f, fn } from '@ax-llm/ax';
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_APIKEY! });
const search = fn('search')
.description('Search docs')
.arg('query', f.string('Search query'))
.returns(f.string('Search result text'))
.handler(async ({ query }) => `docs for ${query}`)
.build();
const helper = agent('question:string -> answer:string', { functions: [search] });
const out = await helper.forward(llm, { question: 'How do I tune a program?' });Agents coordinate tools, child agents, runtime sessions, memories, skills, context policies, discovery, recall, shared fields, traces, usage, and final typed responses.
What It Does
agent() creates a structured agent program. The agent planner/executor/responder loop can call tools, delegate to child agents, inspect runtime state, ask for clarification, discover tools or skills, recall memory, and finish with a typed output object.
Core Call Shape
helper = agent(signature, options)
result = helper.forward(aiClient, inputs)Common Patterns
- Start with a signature that names the final answer fields.
- Add
fn()tools for host data and side effects. - Add child agents to the same callable list as tools.
- Use namespaces to keep tool calls readable.
- Enable discovery when available tools are too numerous to include in full.
- Save and restore state around clarification.
- Use context policies for long-running sessions.
Minimal agent
import { agent, ai } from '@ax-llm/ax';
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_APIKEY! });
const assistant = agent('question:string -> answer:string', { contextFields: [] });
const out = await assistant.forward(llm, { question: 'What is RLM?' });
console.log(out.answer);Namespaced tools and discovery
Use a flat functions list for small stable sets: local fn() tools, child agents, MCP clients, and runtime providers can all live beside each other. The actor sees those callables directly.
import { agent, f, fn } from '@ax-llm/ax';
const findPolicy = fn('findPolicy')
.namespace('kb')
.description('Find policy snippets')
.arg('topic', f.string('Policy topic'))
.returns(f.string('Snippets').array())
.handler(async ({ topic }) => ['policy: ' + topic])
.build();
const support = agent('message:string -> reply:string', {
functions: [findPolicy],
functionDiscovery: true,
contextFields: [],
});Use grouped functions when the catalog is large or easier to reason about by domain. Each group gives the actor a namespace plus module-level selection criteria; with functionDiscovery: true, concrete schemas are loaded only after the actor calls discover(...).
const research = agent('task:string -> answer:string', {
functionDiscovery: true,
functions: [
{ namespace: 'kb', title: 'Knowledge Base', selectionCriteria: 'Use for docs lookup.', functions: [findPolicy] },
{ namespace: 'team', title: 'Specialist Agents', functions: [writer.getFunction()] },
],
contextFields: [],
});
// Actor code should call: await discover(['kb']) before using large/unknown modules.
Grouped mode keeps big catalogs out of the prompt until needed. Keep the top-level list either flat or grouped. If a child agent belongs inside a group, pass childAgent.getFunction() inside the group’s functions list.
Memory, skills, and context policy
const assistant = agent('task:string -> answer:string', {
contextFields: [],
onMemoriesSearch: async (searches, alreadyLoaded) => {
const skip = new Set(alreadyLoaded.map((m) => m.id));
return searchMemoryStore(searches).filter((m) => !skip.has(m.id));
},
onSkillsSearch: async (searches) => resolveSkillGuides(searches),
});
// Actor code can use: await recall(['user preferences']);
// Actor code can use: await discover({ skills: ['release-checklist'] });
const longTaskAgent = agent('repo:string, request:string -> answer:string', {
contextFields: ['repo'],
contextPolicy: { preset: 'checkpointed', budget: 'balanced' },
maxRuntimeChars: 3000,
executorModelPolicy: [{ model: 'gpt-5.4', aboveErrorTurns: 2, namespaces: ['kb', 'db'] }],
});Connect MCP servers
MCP clients can be passed as tool providers after initialization. Use the flat form when the server exposes a small, obvious tool set.
import { AxJSRuntime, AxMCPClient, agent } from '@ax-llm/ax';
const mcpClient = new AxMCPClient(transport);
await mcpClient.init();
const assistant = agent('request:string -> response:string', {
functions: [mcpClient],
functionDiscovery: true,
contextFields: [],
runtime: new AxJSRuntime(),
});Use grouped discovery when an MCP server has many tools, prompts, or resources. The group gives the actor a namespace and selection criteria before it asks to see detailed schemas.
const assistant = agent('request:string -> response:string', {
functions: [
{
namespace: 'memory',
title: 'Memory MCP',
description: 'Persistent memory tools',
selectionCriteria: 'Use for memory lookup and updates.',
functions: [mcpClient],
},
],
functionDiscovery: true,
contextFields: [],
});Production Notes
Trace actor turns, tool calls, child-agent calls, clarification, discovery, recall, context growth, token usage, and final typed outputs. Keep host functions narrow and typed. Let fatal infrastructure errors bubble; let task uncertainty become clarification or a typed final answer.
See Tools, agent() API, and MCP.