> ## Documentation Index
> Fetch the complete documentation index at: https://docs.duraton.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# AI agents

> Build AI agents that survive a crash - step.ai turns model calls, tool calls, and agent loops into durable steps, plus the classic agent patterns.

A model call is slow, expensive, and can fail halfway. Wrapping it in a durable step turns it into a
checkpoint: the call happens once, its result is saved, and a crash or retry resumes from after it
rather than paying for it again. That is all `step.ai` is - the [step
API](/core/steps) for model calls, embeddings, and agent loops. See
[AI steps](/reference/sdk/ai-steps) for the full reference, or the
[AI quickstart](/start/ai-quickstart) to run your first one end to end.

## The building block

The unit is a single augmented model call - an LLM with tools and structured output. In Duraton that is
`step.ai.generate`:

```ts theme={null}
const { output } = await ctx.step.ai.generate<Triage>("classify", {
  model: "claude-opus-4-8",
  prompt: `Classify this ticket: ${subject}`,
  output: triageSchema,
});
```

The call is memoized under `"classify"`, so on replay the workflow skips it and reuses the saved
answer. Composing calls means composing checkpoints:

```ts theme={null}
export const triageTicket = workflow<{ subject: string }>({
  name: "ticket.created",
  handler: async (ctx) => {
    const { output } = await ctx.step.ai.generate<{ category: string; priority: string }>("classify", {
      model: "claude-opus-4-8",
      prompt: `Classify this ticket: ${ctx.event.data.subject}`,
      output: {
        type: "object",
        properties: { category: { type: "string" }, priority: { type: "string" } },
        required: ["category", "priority"],
      },
    });

    // A crash here replays the handler from the top: "classify" returns its recorded
    // answer without calling the model, and only "draft-reply" is actually charged.
    const { text } = await ctx.step.ai.generate("draft-reply", {
      model: "claude-opus-4-8",
      prompt: `Write a reply for a ${output?.priority} ${output?.category} ticket.`,
    });

    return { category: output?.category, reply: text };
  },
});
```

## The agent loop

When the model should decide its own next step - call a tool, look at the result, call another - use
`step.ai.loop`. Each turn is a durable step and each tool call is a durable step, so a long-running
agent survives a restart and resumes at the last committed turn instead of starting over.

```ts theme={null}
const agent = await ctx.step.ai.loop("agent", {
  prompt: `Resolve the ticket about: ${subject}`,
  maxIterations: 6,
  tools: {
    "search-kb": { handler: (q) => searchKb(q) },
    "lookup-order": { workflow: "orders.lookup", app: "orders" },
  },
  turn: (ctx, i) => callModel(ctx.prompt, ctx.history, i),
});
```

A tool can be a local `handler` or another **workflow**. A workflow tool becomes a linked child run -
a full durable run with its own retries and steps - so the agent can delegate real work, not just call
a function. That linkage is what makes the orchestrator pattern below durable end to end.

## Agent patterns

Anthropic's [Building effective
agents](https://www.anthropic.com/engineering/building-effective-agents) distinguishes composable
*workflows* (the model follows a fixed structure) from *agents* (the model directs itself). Each maps
onto Duraton primitives, and because every model call is a step, each becomes crash-safe for free.

<Note>
  The pattern names and definitions below are Anthropic's; the mapping to `step.ai` is the durable
  implementation.
</Note>

### Prompt chaining

Decompose a task into a fixed sequence of calls, each consuming the previous one's output. Every call
is its own step, so a failure in the third link resumes from the saved output of the second.

```ts theme={null}
const outline = await ctx.step.ai.generate("outline", { model, prompt: `Outline: ${topic}` });
const draft = await ctx.step.ai.generate("draft", { model, prompt: `Expand:\n${outline.text}` });
const final = await ctx.step.ai.generate("polish", { model, prompt: `Tighten:\n${draft.text}` });
```

### Routing

Classify the input, then send it to a specialized follow-up. Classify with a structured `generate`,
then branch to the workflow that handles that class:

```ts theme={null}
const { output } = await ctx.step.ai.generate<{ queue: string }>("route", {
  model,
  prompt: `Which queue handles: ${subject}`,
  output: { type: "object", properties: { queue: { type: "string" } }, required: ["queue"] },
});
const result = await ctx.step.runWorkflow("handle", { name: `support.${output?.queue}` });
```

### Parallelization

Run independent calls at once and aggregate the results (**sectioning**), or run the same call several
times and combine the answers (**voting**). Fan out with `Promise.all` - each branch is its own durable
step and the run joins when all have committed:

```ts theme={null}
const [safety, topic, sentiment] = await Promise.all([
  ctx.step.ai.generate("safety", { model, prompt: `Unsafe? ${text}` }),
  ctx.step.ai.generate("topic", { model, prompt: `Topic of: ${text}` }),
  ctx.step.ai.generate("sentiment", { model, prompt: `Sentiment of: ${text}` }),
]);
```

### Orchestrator-workers

A central model breaks a task down, delegates to workers, and synthesizes the results. This is
`step.ai.loop` with **workflow tools**: the loop is the orchestrator, each workflow tool is a worker,
and every delegation is a linked child run that retries and checkpoints on its own.

```ts theme={null}
await ctx.step.ai.loop("orchestrate", {
  prompt: `Ship the change described in: ${brief}`,
  maxIterations: 12,
  tools: {
    "search-code": { workflow: "repo.search" },
    "open-pr": { workflow: "repo.open-pr", app: "ci" },
  },
  turn: (ctx, i) => planNextStep(ctx.prompt, ctx.history, i),
});
```

### Evaluator-optimizer

One call generates, another evaluates and feeds back, in a loop until the check passes. For a single
output against a schema, `generate`'s built-in [durable
re-ask](/reference/sdk/ai-steps#structured-output-and-durable-re-ask) is this pattern - `validate` is the
evaluator and each re-ask is a durable step:

```ts theme={null}
const { output } = await ctx.step.ai.generate("summary", {
  model,
  prompt: `Summarize in under 40 words: ${doc}`,
  output: summarySchema,
  validate: (v) => (wordCount(v) <= 40 ? undefined : "too long, tighten it"),
  reask: 3,
});
```

For a free-form generate/critique cycle, run the two calls as steps inside `step.ai.loop` and `stop`
when the evaluator is satisfied.

## Keeping it replay-safe

The model results are memoized, but the code around them - your `turn`, `validate`, and `stop`
functions, and any branching on a result - runs again on every replay. Keep them deterministic (a pure
function of their inputs) so a resumed run takes the same path it took the first time. The
[durable-execution](/core/durable-execution) rules for regular steps apply unchanged.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.