agent() Agents Build agents with tools, child agents, runtime profiles, and context controls. typescript subsystems subsystems/agent website/content-src/templates/subsystem-agent.md subsystems agent() Agents

agent() Agents

Use agent() to build an RLM agent with a typed final response.

TypeScript
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.

Agent tree

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

text
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

TypeScript
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.

TypeScript
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(...).

TypeScript
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

TypeScript
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'] });
TypeScript
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.

TypeScript
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.

TypeScript
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.

Docs