Crux
GuidesAgents

Agent

Bundle a prompt with optional model and tools into a reusable agent primitive for composition utilities.

An agent wraps a prompt with optional execution config — a default model, tools, and handoff targets. Agents are the building blocks for all composition patterns: parallel, pipeline, consensus, and swarm.

agents.ts
import { agent } from '@crux/core/agent'

const reviewer = agent({
  id: 'content-reviewer',
  description: 'Reviews content for quality and accuracy',
  prompt: reviewPrompt,
  model: gpt4mini,        // optional: overrides composition-level model
  tools: [searchTool],    // optional: agent-specific tools
  handoffs: ['editor'],   // optional: agents this agent can route to in a swarm
  swarmTools: ['search'], // optional: tool whitelist for swarm context
})
FieldPurposeDefault
idUnique identifier (used in devtools and swarm routing)Required
descriptionHuman-readable description (used in swarm transfer tool descriptions)undefined
promptThe prompt this agent executesRequired
modelModel override (takes precedence over composition-level model)undefined
toolsAgent-specific toolsundefined
handoffsAgent IDs or { id, when } objects for swarm routing[]
swarmToolsTool name whitelist for swarm contextAll tools

Why agent?

Composition patterns need to know what to execute (prompt), how to execute it (model), and what tools are available. agent() bundles these into a single, frozen, typed object.

Without agent(), you'd pass these separately to every composition call. With it, you define once and reuse across compositions:

composition.ts
import { parallel } from '@crux/ai'

const { results } = await parallel({
  agents: { reviewer, factChecker, seoAnalyzer },
  context: { content: articleDraft },
  model: claude35,  // shared default — reviewer's gpt4mini overrides this
})

results.reviewer.output     // typed from reviewer's output schema
results.factChecker.output  // typed from factChecker's output schema

Per-agent model overrides

When an agent has a model, it takes precedence over the composition's shared model. This lets you use cheap models for simple tasks and expensive models for complex ones:

cost-optimization.ts
const classifier = agent({
  id: 'classifier',
  prompt: classifyPrompt,
  model: gpt4mini,        // cheap model for classification
})

const writer = agent({
  id: 'writer',
  prompt: writePrompt,
  // no model — uses composition-level default
})

await pipeline({
  steps: [
    { agent: classifier, name: 'classify' },
    { agent: writer, name: 'write', input: (ctx) => ({ topic: ctx.classify.output.category }) },
  ],
  model: claude35,  // writer uses this; classifier uses gpt4mini
  context: { text: userMessage },
})

Escape hatch: plain functions

Composition utilities also accept plain async functions alongside agents. Use this for custom logic, external API calls, or wrapping delegates:

escape-hatch.ts
const { results } = await parallel({
  agents: {
    reviewer: reviewerAgent,
    external: async (ctx) => {
      const resp = await fetch('https://api.example.com/review', {
        method: 'POST',
        body: JSON.stringify(ctx),
      })
      return resp.json()
    },
  },
  context: { content: draft },
})

results.reviewer.output  // typed AgentResult
results.external.output  // external API response

Plain functions skip devtools agent tracing but still appear as flow steps.

Type guard

Use isAgent() to distinguish agents from plain functions at runtime:

import { isAgent } from '@crux/core/agent'

if (isAgent(entry)) {
  // entry is Agent — has .id, .prompt, .model, .tools, .handoffs
} else {
  // entry is a plain async function
}

On this page