Understanding Recipes

Recipes are declarative YAML specifications that define multi-step AI agent workflows. They provide a structured way to orchestrate complex tasks that require multiple sequential or parallel operations, with built-in support for state persistence, error handling, and human approval gates.

What is a Recipe?

A recipe is essentially a blueprint for AI-assisted work. Rather than manually guiding an agent through each step of a complex process, you define the entire workflow upfront in a YAML file. The recipe system then executes each step in order, passing context between steps and handling failures gracefully.

Think of recipes like cooking recipes: they specify ingredients (context), steps (operations), and expected outcomes. Just as a cooking recipe ensures consistent results regardless of who follows it, an agent recipe ensures consistent, reproducible workflows.

Core Characteristics

Declarative: You specify what should happen, not how to do it. The recipe executor handles the mechanics of step execution, context management, and error recovery.

Sequential with State: Each step can access the accumulated context from all previous steps. This creates a natural flow where early steps gather information that later steps act upon.

Resumable: If a recipe is interrupted (network failure, timeout, manual stop), it can be resumed from the last successful checkpoint. No work is lost.

Composable: Recipes can be simple (3-4 steps) or complex (dozens of steps with conditional branching). You build what you need.

When to Use Recipes

Recipes shine in scenarios where manual orchestration would be tedious, error-prone, or inconsistent.

Ideal Use Cases

Code Review Workflows: A recipe can analyze code changes, check for security issues, verify test coverage, and generate a comprehensive review report - all in one automated flow.

Research and Synthesis: Gather information from multiple sources, analyze patterns, synthesize findings, and produce a structured report.

Multi-Stage Deployments: Validate configuration, run tests, build artifacts, deploy to staging, run smoke tests, and optionally promote to production.

Document Generation: Analyze a codebase, extract documentation, generate API references, and compile everything into a cohesive document.

Refactoring Operations: Analyze current code, identify patterns to change, plan modifications, execute changes across files, and verify results.

When NOT to Use Recipes

  • Simple one-shot tasks: If a single agent call suffices, skip the recipe overhead
  • Highly interactive work: Recipes are designed for autonomous execution, not back-and-forth conversation
  • Exploratory tasks: When you don't know the steps upfront, manual guidance is better

Recipe Structure

A recipe file has a clear structure with metadata, context, and steps.

Basic Anatomy

# Recipe metadata
name: code-review
description: Comprehensive code review workflow
version: "1.0.0"

# Input context (flat dictionary of variables)
context:
  file_path: ""           # Must be provided at execution time
  focus_areas: "general"  # Has a default value

# The workflow steps
steps:
  - id: analyze-structure
    agent: foundation:explorer
    prompt: |
      Analyze the structure of {{file_path}}.
      Identify key components, dependencies, and patterns.
    output: "structure_analysis"

  - id: security-check
    agent: foundation:security-guardian
    prompt: |
      Review {{file_path}} for security vulnerabilities.
      Previous analysis: {{structure_analysis}}
    output: "security_findings"

  - id: generate-report
    agent: foundation:file-ops
    prompt: |
      Generate a code review report based on:
      - Structure analysis: {{structure_analysis}}
      - Security findings: {{security_findings}}

Key Elements

name: A unique identifier for the recipe. Used for execution and logging.

description: Human-readable explanation of what the recipe does.

context: A flat dictionary of input variables. Variables with empty string values must be provided at execution time; variables with non-empty values serve as defaults.

steps: The ordered list of operations to execute. Each step has an id, an agent to invoke, and a prompt template. Steps use the output field to store their result in a named variable for later steps.

Steps and Context

Steps are the building blocks of recipes. Each step represents a discrete unit of work performed by an agent.

Step Definition

steps:
  - id: step-identifier         # Unique id within recipe
    agent: foundation:explorer   # Agent to invoke
    prompt: |                    # Instructions for the agent
      Your task description here.
      You can reference context: {{variable_name}}
    output: "step_result"        # Store result for later steps

Step Types

Not all steps require an agent. The recipe system supports multiple step types, each suited to a different kind of work.

Agent steps are the default. They delegate work to an Amplifier agent, which processes the prompt and returns a result. Most workflow logic lives in agent steps.

Bash steps execute shell commands directly, without invoking an agent. They are useful for build commands, test runners, file operations, and any task where a deterministic shell command is more appropriate than an AI agent. A bash step captures stdout and stderr as its output, making results available to subsequent steps just like agent output.

- id: run-tests
  type: bash
  command: "pytest tests/ --tb=short -q"
  cwd: "{{project_root}}"
  timeout: 120
  output: "test_output"

Recipe steps call another recipe as a sub-recipe, enabling composition of complex workflows from simpler building blocks. See the Recipe Composition section below.

- id: lint-check
  type: recipe
  recipe: "./lint-recipe.yaml"
  context:
    target: "{{file_path}}"
  output: "lint_results"

Context Flow

Context flows through recipes in a predictable way:

  1. Initial Context: Variables provided at recipe execution
  2. Step Outputs: Each step with an output field stores its result as a named variable
  3. Accumulated State: Later steps have access to all prior output variables
# Step 1 receives initial context
- id: gather-info
  prompt: "Analyze {{target_directory}}"
  output: "gathered_info"

# Step 2 receives initial context + step 1 output
- id: process-info
  prompt: |
    Based on: {{gathered_info}}
    Process the information...
  output: "processed_info"

# Step 3 receives everything
- id: synthesize
  prompt: |
    Original target: {{target_directory}}
    Gathered info: {{gathered_info}}
    Processed info: {{processed_info}}

Template Syntax

Recipes use double-brace templating for dynamic content:

  • {{variable}}: Insert a context variable or a previous step's named output

To make a step's result available to later steps, add an output field to the step. The value becomes the variable name you reference downstream.

Step Options

Steps support additional configuration:

- id: critical-operation
  agent: foundation:modular-builder
  prompt: "Implement the feature..."
  output: "implementation"

  # Error handling
  on_error: continue    # Options: fail (default), continue, skip_remaining

  # Timeouts
  timeout: 300          # Seconds before timeout

  # Conditional execution
  condition: "{{validation_passed}} == 'true'"

Approval Gates

For workflows that require human oversight, recipes support approval gates. These pause execution at critical points, allowing humans to review progress before continuing.

Staged Recipes

Approval gates are implemented through staged recipes -- recipes divided into stages that require explicit approval between them. Each stage groups related steps together, and an approval block on the stage controls whether execution pauses after that stage completes.

name: production-deployment
description: Deploy to production with approval gates
version: "1.0.0"

stages:
  - name: preparation
    steps:
      - id: validate-config
        agent: foundation:explorer
        prompt: "Validate deployment configuration..."
        output: "config_validation"

      - id: run-tests
        agent: foundation:test-coverage
        prompt: "Execute full test suite..."
        output: "test_results"

  - name: staging-deployment
    approval:
      required: true
      prompt: |
        Preparation complete. Test results:
        {{test_results}}

        Approve to deploy to staging?
      timeout: 3600
      default: "deny"
    steps:
      - id: deploy-staging
        agent: foundation:modular-builder
        prompt: "Deploy to staging environment..."
        output: "staging_result"

  - name: production-release
    approval:
      required: true
      prompt: |
        Staging deployment successful.
        Approve production release?
      timeout: 3600
      default: "deny"
    steps:
      - id: deploy-production
        agent: foundation:modular-builder
        prompt: "Deploy to production..."

Approval Workflow

  1. Recipe executes until it hits a stage with approval: required: true
  2. Execution pauses and the approval prompt is displayed
  3. Human reviews the state and either approves or denies
  4. On approval: execution continues to the next stage. The approver can include a message (e.g., "merge" or "pr"), which becomes available in subsequent steps as {{_approval_message}}
  5. On denial: execution stops with the provided reason

Managing Approvals

# List pending approvals across all sessions
amp recipes approvals

# Approve a pending stage
amp recipes approve --session-id <id> --stage-name staging-deployment

# Deny with reason
amp recipes deny --session-id <id> --stage-name production-release \
  --reason "Test coverage below threshold"

Use Cases for Approval Gates

  • Production deployments: Human sign-off before going live
  • Data migrations: Review migration plan before execution
  • Bulk operations: Approve after seeing what will be affected
  • Compliance workflows: Required human verification for audit trails

Advanced Features

Foreach Loops

Execute a step once for each item in a collection. The foreach field takes a variable reference containing a list. Use as to name the loop variable and parallel to control concurrency.

- id: process-files
  foreach: "{{discovered_files}}"
  as: current_file
  parallel: 3
  agent: foundation:explorer
  prompt: "Analyze {{current_file}} for code quality issues."
  collect: "file_analyses"

When parallel is omitted (or set to false), iterations run sequentially. Setting it to an integer allows that many iterations to execute concurrently, which is useful for independent tasks like reviewing a list of files. Setting it to true runs all iterations simultaneously.

While/Convergence Loops

Some workflows need to iterate until a quality threshold is met rather than running a fixed number of times. The while_condition field causes a step to repeat as long as the condition evaluates to true. Combined with update_context (to mutate variables each iteration) and break_when (for early exit), this enables convergence patterns.

- id: refine-draft
  agent: foundation:zen-architect
  prompt: |
    Current draft quality score: {{quality_score}}
    Improve the draft and return a revised version with a new score.
    Return JSON: {"draft": "...", "sufficient": "true" or "false"}
  output: "refinement"
  parse_json: true
  while_condition: "{{quality_sufficient}} != 'true'"
  break_when: "{{iteration_limit_reached}} == 'true'"
  max_while_iterations: 10
  update_context:
    quality_sufficient: "{{refinement.sufficient}}"

The step re-executes after each iteration with updated context. Once quality_sufficient becomes "true", the loop exits. The break_when guard and max_while_iterations prevent runaway loops if convergence is slow.

Conditional Execution

Steps can be skipped entirely based on a condition. The condition field accepts an expression that evaluates against the current context. If it evaluates to false (or an empty/falsy value), the step is skipped and execution moves to the next step.

- id: security-scan
  condition: "{{security_required}} == 'true'"
  agent: foundation:security-guardian
  prompt: "Perform security scan on {{file_path}}..."

Conditions are evaluated at runtime, so they can depend on the output of earlier steps.

Expression Operators

Recipe expressions in condition, while_condition, and break_when fields support the following operators:

  • Comparison: ==, !=
  • Logical: and, or
condition: "{{severity}} == 'critical' or {{severity}} == 'high'"
while_condition: "{{converged}} != 'true' and {{max_reached}} != 'true'"

Numeric comparisons (<, >, >=, <=), the not operator, and parentheses for grouping are planned but not yet implemented. Structure conditions around string equality checks.

Recipe Composition

A step with type: recipe calls another recipe file as a sub-recipe. This enables building complex workflows from smaller, tested building blocks. The sub-recipe runs to completion, and its final output becomes the step's result.

- id: lint-and-format
  type: recipe
  recipe: "./lint-recipe.yaml"
  context:
    target: "{{file_path}}"
    fix_mode: true
  output: "lint_result"

- id: run-tests
  type: recipe
  recipe: "./test-recipe.yaml"
  context:
    test_dir: "{{test_directory}}"
  output: "test_result"

Composition keeps individual recipes focused and reusable. A deployment recipe might compose a build recipe, a test recipe, and a deploy recipe rather than inlining all those steps.

Step Dependencies

Within a stage, steps normally execute in the order they are listed. The depends_on field lets you declare explicit dependencies between steps, enabling the recipe executor to determine safe execution order. Steps whose dependencies are all satisfied can potentially run in parallel.

steps:
  - id: fetch-data
    agent: foundation:explorer
    prompt: "Fetch data from the API..."
    output: "fetched_data"

  - id: fetch-config
    agent: foundation:explorer
    prompt: "Read configuration files..."
    output: "fetched_config"

  - id: merge-results
    depends_on:
      - fetch-data
      - fetch-config
    agent: foundation:zen-architect
    prompt: |
      Combine data: {{fetched_data}}
      With config: {{fetched_config}}

Rate Limiting

For workflows that interact with external services or APIs, recipe-level rate limiting prevents overwhelming those systems. The rate_limiting field controls how fast LLM calls execute across the entire recipe tree.

name: bulk-api-calls
version: "1.0.0"
description: Workflow with rate limiting

rate_limiting:
  max_concurrent_llm: 3
  min_delay_ms: 500
  backoff:
    enabled: true
    initial_delay_ms: 2000

steps:
  # ...

The max_concurrent_llm field limits concurrent LLM calls globally. The min_delay_ms field adds pacing between calls. The backoff block configures adaptive backoff for handling rate limit (429) errors. This is especially useful in foreach loops where many iterations could otherwise fire simultaneously.

Error Recovery

Configure how failures are handled:

- id: optional-optimization
  agent: foundation:zen-architect
  prompt: "Optimize if possible..."
  on_error: continue  # Don't fail the whole recipe

Key Takeaways

  1. Recipes are declarative workflows: Define what you want, not how to do it. The recipe system handles execution mechanics.

  2. Multiple step types: Agent steps for AI work, bash steps for deterministic commands, recipe steps for composition. Use the right type for each task.

  3. State flows through named outputs: Each step's output field stores its result as a named variable accessible to subsequent steps via {{output_name}}.

  4. Resumability is built-in: Interrupted recipes can be resumed. Checkpointing happens automatically.

  5. Approval gates enable human oversight: Staged recipes pause between stages for human approval. Approvers can pass messages that subsequent steps can act on.

  6. Loops handle iteration: Foreach loops process collections (with optional parallel concurrency). While loops converge on quality thresholds or exit conditions.

  7. Compose, don't monolith: Build complex workflows by composing simple, tested sub-recipes. Keep individual recipes focused and reusable.

  8. Start simple, grow as needed: Begin with linear sequences of steps. Add stages, conditions, loops, and composition only when your workflow demands it.

Recipes transform complex, multi-step processes into repeatable, reliable workflows. They bridge the gap between manual agent interaction and fully automated pipelines, giving you control over the workflow design while letting AI handle the execution.