Multi-step AI analysis

Chain an analyst agent with a judge that verifies the result.

Use this when you want one agent to do open-ended work (analysis, drafting, classification) and a second agent to check the first's output before the workflow proceeds. The pattern catches the "looks plausible but wrong" class of mistakes that single-shot prompts miss, without paying for full consensus voting.

The workflow#

Two AI nodes chained together. summarize is an ai_agent (free-form response, single forward edge). verify is an ai_judge (validator-driven routing) that reads the summarizer's response via the {{ summarize.response }} template and decides whether the summary holds up.

name: multi-step-analysis
version: 1
description: Summarize then verify, second agent checks the first's work.
initial: summarize
inputs:
  - name: researchDoc
    type: string
    description: The research document to summarize.
states:
  summarize:
    type: ai_agent
    label: Summarize the document
    model: claude-haiku-4-5
    prompt: |
      Summarize the following research document in three bullet points,
      focused on the findings that would change a decision. Be specific
      with numbers; do not editorialise.

      Document: {{ researchDoc }}
    on:
      next: verify

  verify:
    type: ai_judge
    label: Verify the summary
    model: claude-sonnet-5
    prompt: |
      Here is a summary of a research document:

      {{ summarize.response }}

      Original document: {{ researchDoc }}

      Does the summary accurately reflect the document's findings AND cite
      numbers consistent with the source? Answer with the JSON shape
      requested below.
    validator:
      kind: boolean
    maxRetries: 2
    maxIterations: 3
    on:
      'true': accept
      'false': summarize

  accept:
    type: end
    label: Accepted
    outcome: success
    message: Verified summary ready.

researchDoc is declared under inputs: because the prompts read it as a bare {{ researchDoc }}; an undeclared bare reference fails validation even when you plan to supply it with --var. The 'false': summarize edge also makes summarizeverify a cycle, so one node in it needs maxIterations (here, 3 on verify) or the loader rejects the workflow outright. See /docs/recipes/closed-loop-feedback for the full cap and stall-detection rules.

Save as multi-step-analysis.workflow.yaml in ~/.roscoe/workflows/ or <repo>/.roscoe/workflows/.

Running it#

roscoe run multi-step-analysis --var researchDoc='Q3 churn study: ...'

Or interactively in the web UI. With Claude Code via MCP, the run pauses at each AI node and Claude itself supplies the response via advance_run.

Why these node types#

ai_agent suits the summarizer: free-form output, and the single outgoing transition (next) only means "continue when done"; there's no branching decision. Output goes to variables.summarize.response. ai_judge suits the verifier because its validator (kind: boolean) parses the model's JSON envelope into a routing key; this example deliberately uses a stronger model (claude-sonnet-5) for verification, since it's the call that has to catch mistakes.

The 'false': summarize edge re-runs the summarizer when the judge disagrees. It sees the same prompt again, but a fresh model call usually produces a different response. Two separate knobs bound this: maxRetries on the judge caps retries of a single model call after a transient failure, while maxIterations caps how many times the whole summarize → verify cycle can repeat before the run fails.

The {{ nodeId.field }} template#

After every node completes, its output is merged into a shared variables bag and made available to downstream prompts and scripts. The fields by node type:

Node type Fields
ai_agent <id>.response: raw text
ai_judge <id>.result, <id>.reasoning, <id>.score (confidence only)
script <id>.result, <id>.stdout, <id>.exitCode
condition <id>.result: 'true' or 'false'
consensus <id>.outcome, <id>.approvals, <id>.votes[], etc.

Anything passed via --var name=value (or the context field on the REST POST /api/runs) is reachable as {{ name }}, provided it's declared under the workflow's inputs:.

Variations#

For a three-stage version, chain summarize → critique (ai_agent) → judge (ai_judge); the middle critique step gets the summarizer's output, drafts objections, and the judge weighs both before deciding. For a validator with a confidence score, switch the judge's validator to kind: confidence with a threshold, so borderline outputs go to a human review step instead of being silently rejected. To pull data from a script step first, insert a script node before summarize that fetches the document (curl, or reads a file) and reference its stdout in the summarizer's prompt (see /docs/recipes/script-then-ai).

See also#

  • /docs/nodes/ai-agent — single AI completion node
  • /docs/nodes/ai-judge — validator-routed AI node
  • /docs/recipes/consensus-voting — when one verifier isn't enough

View this page as Markdown

Predictable workflows from unpredictable AI