Creating Custom Recipes¶
Build multi-step workflows that orchestrate agents.
Overview¶
Recipes let you:
- Orchestrate agents - Chain specialists together
- Create repeatable workflows - Run the same process consistently
- Add approval gates - Human-in-the-loop checkpoints
- Handle complex flows - Loops, conditions, parallel execution
Recipe Structure¶
# my-recipe.yaml
name: my-workflow
description: What this recipe does
version: "1.0.0"
# Input variables (flat dictionary)
context:
input_var: "" # Must be provided at execution time
optional_var: "default" # Has a default value
# Workflow steps
steps:
- id: step-one
prompt: "Do the first thing with {{input_var}}"
output: "first_result"
- id: step-two
agent: foundation:zen-architect
prompt: |
Based on: {{first_result}}
Do the second thing.
Basic Recipe¶
Simple Two-Step Recipe¶
name: explain-and-improve
description: Explain code then suggest improvements
version: "1.0.0"
context:
file: ""
steps:
- id: explain
prompt: "Explain what {{file}} does in plain language"
output: "explanation"
- id: improve
prompt: |
Based on this understanding:
{{explanation}}
Suggest 3 improvements for {{file}}
Run It¶
amp recipes execute explain-and-improve.yaml \
--context '{"file": "src/auth.py"}'
Using Agents¶
Specify Agent for Step¶
steps:
- id: design
agent: foundation:zen-architect
prompt: "Design a solution for {{problem}}"
output: "design_result"
- id: implement
agent: foundation:modular-builder
prompt: |
Implement the design:
{{design_result}}
output: "implementation"
- id: review
agent: foundation:security-guardian
prompt: "Review the implementation for security issues"
Bash Steps¶
Not every step needs an AI agent. Steps with type: bash execute shell commands directly and capture stdout/stderr as the step output. This is ideal for build tools, test runners, linters, and any deterministic operation.
Run Tests and Capture Results¶
name: test-and-report
description: Run tests then summarize results with an agent
version: "1.0.0"
context:
project_dir: ""
steps:
- id: run-tests
type: bash
command: "pytest tests/ --tb=short -q 2>&1"
cwd: "{{project_dir}}"
timeout: 180
output: "test_output"
- id: summarize
prompt: |
Here are the test results:
{{test_output}}
Summarize: how many passed, how many failed, and what
the failures are about. Be concise.
Bash Step Fields¶
| Field | Purpose |
|---|---|
type: bash |
Marks the step as a shell command |
command |
The shell command to execute |
cwd |
Directory to run the command in (optional) |
timeout |
Seconds before the command is killed (optional) |
output |
Variable name to store stdout/stderr (optional) |
The command's combined stdout and stderr become the step output, accessible to later steps via the variable name specified in output.
Build, Lint, and Report¶
steps:
- id: install-deps
type: bash
command: "pip install -r requirements.txt"
cwd: "{{project_dir}}"
timeout: 120
- id: lint
type: bash
command: "ruff check src/ --output-format text"
cwd: "{{project_dir}}"
on_error: continue
output: "lint_output"
- id: typecheck
type: bash
command: "pyright src/"
cwd: "{{project_dir}}"
on_error: continue
output: "typecheck_output"
- id: quality-report
prompt: |
Lint results:
{{lint_output}}
Type check results:
{{typecheck_output}}
Create a prioritized list of issues to fix.
Context Variables¶
Input Variables¶
context:
# Must be provided at execution time (empty default)
target_file: ""
# Has a default value (optional to override)
depth: "standard"
include_tests: "true"
Access Variables¶
prompt: |
Review {{target_file}} with {{depth}} depth.
Step Results¶
Each step with an output field stores its result as a named variable. Later steps reference that variable name:
steps:
- id: analyze
prompt: "Analyze the code"
output: "analysis"
- id: report
prompt: |
Previous analysis: {{analysis}}
Create a summary report.
Approval Gates¶
Approval gates require staged recipes. Stages group related steps together with optional approval checkpoints between them.
Require Human Approval¶
name: migration-workflow
description: Database migration with approval gate
version: "1.0.0"
stages:
- name: planning
steps:
- id: plan
prompt: "Create a migration plan"
output: "migration_plan"
approval:
required: true
prompt: "Review the migration plan before proceeding."
timeout: 3600
default: "deny"
- name: execution
steps:
- id: execute
prompt: "Execute the migration: {{migration_plan}}"
Approval Commands¶
# Check pending approvals
amp recipes approvals
# Approve a pending stage
amp recipes approve --session-id <id> --stage-name planning
# Deny with reason
amp recipes deny --session-id <id> --stage-name planning \
--reason "Need changes"
Advanced Features¶
Foreach Loops¶
Use foreach to repeat a step for each item in a list. The foreach field takes a variable reference, as names the loop variable, and parallel controls concurrency.
steps:
- id: get-files
type: bash
command: "find src/ -name '*.py' -type f"
output: "file_list"
- id: review-each
foreach: "{{file_list}}"
as: current_file
parallel: 3
agent: foundation:explorer
prompt: "Review {{current_file}} for code quality issues"
collect: "file_reviews"
Without parallel (or with parallel: false), iterations run one at a time. Setting it to an integer (as above) allows that many iterations to execute concurrently -- useful when iterations are independent of each other. Setting it to true runs all iterations simultaneously.
Foreach: Complete Working Example¶
name: bulk-file-review
description: Review all Python files in a directory
version: "1.0.0"
context:
src_dir: ""
steps:
- id: discover
type: bash
command: "find {{src_dir}} -name '*.py' -not -path '*/__pycache__/*'"
output: "discovered_files"
- id: review-files
foreach: "{{discovered_files}}"
as: file_path
parallel: 4
agent: foundation:zen-architect
prompt: |
Review {{file_path}} for:
- Complexity issues
- Naming clarity
- Separation of concerns
Return a short summary with a 1-10 quality score.
collect: "file_reviews"
- id: aggregate
prompt: |
Combine all file reviews:
{{file_reviews}}
Create a single prioritized report of issues across the codebase.
Conditional Steps¶
steps:
- id: check
prompt: "Does {{file}} have tests? Return JSON: {\"has_tests\": true/false}"
output: "check_result"
parse_json: true
- id: write-tests
condition: "{{check_result.has_tests}} == 'false'"
prompt: "Write tests for {{file}}"
Error Handling¶
steps:
- id: risky-step
prompt: "Try something that might fail"
on_error: continue # Don't stop on failure
output: "risky_output"
- id: fallback
condition: "{{risky_output}} == ''"
prompt: "The previous step may have failed. Investigate and recover."
While Loops (Convergence)¶
Use while_condition to repeat a step until a condition becomes false. This is the right tool when you need iterative refinement -- keep improving something until it meets a quality bar.
Key fields:
| Field | Purpose |
|---|---|
while_condition |
Expression evaluated before each iteration; loop continues while true |
break_when |
Early exit condition (safety valve to prevent runaway loops) |
update_context |
Variables to update after each iteration |
max_while_iterations |
Hard safety limit (default: 100) |
Note: Condition expressions currently support ==, !=, and, and or operators. Numeric comparisons (<, >, >=, <=), not, and parentheses are planned but not yet implemented. Structure your conditions around string equality checks.
While Loop: Working Example¶
This recipe iterates on a document draft until an evaluator deems it sufficient:
name: iterative-refinement
description: Refine a document until quality threshold is met
version: "1.0.0"
context:
topic: ""
quality_sufficient: "false"
steps:
- id: initial-draft
prompt: |
Write a first draft of a technical document about {{topic}}.
Keep it concise and well-structured.
output: "current_draft"
- id: refine
prompt: |
Current draft:
{{current_draft}}
Evaluate the draft for clarity, accuracy, and completeness.
If it needs improvement, revise it. Return JSON:
{"draft": "<revised text>", "sufficient": "true" or "false"}
output: "refinement"
parse_json: true
while_condition: "{{quality_sufficient}} != 'true'"
max_while_iterations: 5
update_context:
current_draft: "{{refinement.draft}}"
quality_sufficient: "{{refinement.sufficient}}"
- id: final-output
prompt: |
Polish and format the final draft:
{{current_draft}}
The step re-executes after each iteration with updated context. Once quality_sufficient becomes "true", the loop exits. The max_while_iterations field prevents runaway loops if convergence is slow.
While Loop: Retry Until Success¶
Another common pattern is retrying an operation until it succeeds:
steps:
- id: deploy-attempt
type: bash
command: "curl -sf https://api.example.com/deploy --data '{\"version\": \"{{version}}\"}'"
on_error: continue
output: "deploy_output"
while_condition: "{{deploy_succeeded}} != 'true'"
max_while_iterations: 3
Staged Recipes with Approval Gates¶
For workflows that require human checkpoints, staged recipes divide work into stages separated by approval gates. Each stage groups multiple steps into a logical phase, and an approval block on the stage defines whether execution pauses after that stage completes.
Staged Recipe Structure¶
name: release-workflow
description: Build, test, and release with human checkpoints
version: "1.0.0"
context:
version: ""
repo_dir: ""
stages:
- name: build-and-test
steps:
- id: build
type: bash
command: "make build VERSION={{version}}"
cwd: "{{repo_dir}}"
timeout: 300
output: "build_output"
- id: test
type: bash
command: "make test"
cwd: "{{repo_dir}}"
timeout: 600
output: "test_output"
- id: test-summary
prompt: |
Summarize the build and test results:
Build output: {{build_output}}
Test output: {{test_output}}
output: "summary"
- name: staging-deploy
approval:
required: true
prompt: |
Build and tests complete:
{{summary}}
Approve to deploy {{version}} to staging?
timeout: 3600
default: "deny"
steps:
- id: deploy-staging
type: bash
command: "make deploy ENV=staging VERSION={{version}}"
cwd: "{{repo_dir}}"
output: "staging_result"
- id: smoke-test
type: bash
command: "make smoke-test ENV=staging"
cwd: "{{repo_dir}}"
output: "smoke_results"
- name: production-release
approval:
required: true
prompt: |
Staging deployment and smoke tests complete:
{{smoke_results}}
Approve production release of {{version}}?
timeout: 3600
default: "deny"
steps:
- id: deploy-prod
type: bash
command: "make deploy ENV=production VERSION={{version}}"
cwd: "{{repo_dir}}"
output: "prod_result"
- id: verify-prod
prompt: |
Production deployment complete.
Approval decision was: {{_approval_message}}
Verify the deployment is healthy.
Managing Staged Approvals¶
# See what is waiting for approval
amp recipes approvals
# Approve with an optional message (available as {{_approval_message}})
amp recipes approve --session-id <id> --stage-name staging-deploy \
--message "looks good, proceed"
# Deny and halt the recipe
amp recipes deny --session-id <id> --stage-name production-release \
--reason "Smoke tests show elevated error rate"
The _approval_message variable is useful when the approver needs to communicate a decision downstream -- for example, whether to merge directly or create a PR.
Recipe Composition¶
Complex workflows can be assembled from smaller, focused recipes using type: recipe steps. Each sub-recipe runs to completion and its output becomes the calling step's result.
Why Compose?¶
- Reuse: A lint recipe used in both CI and code-review workflows
- Testing: Test each sub-recipe independently
- Clarity: Keep individual recipe files focused on one job
Composition: Working Example¶
Suppose you have two standalone recipes:
lint-recipe.yaml:
name: lint
description: Run linting and format checks
version: "1.0.0"
context:
target: ""
steps:
- id: lint-check
type: bash
command: "ruff check {{target}} --output-format text"
on_error: continue
output: "lint_results"
- id: format-check
type: bash
command: "ruff format --check {{target}}"
on_error: continue
output: "format_results"
- id: lint-report
prompt: |
Lint results: {{lint_results}}
Format results: {{format_results}}
Summarize issues found.
test-recipe.yaml:
name: test
description: Run tests with coverage
version: "1.0.0"
context:
test_dir: ""
steps:
- id: run-tests
type: bash
command: "pytest {{test_dir}} --cov --tb=short -q"
timeout: 300
output: "test_results"
- id: test-report
prompt: |
Test results: {{test_results}}
Summarize pass/fail counts and coverage percentage.
A parent recipe composes them:
code-quality.yaml:
name: code-quality
description: Full code quality check using sub-recipes
version: "1.0.0"
context:
src_dir: ""
test_dir: "tests/"
steps:
- id: lint
type: recipe
recipe: "./lint-recipe.yaml"
context:
target: "{{src_dir}}"
output: "lint_report"
- id: tests
type: recipe
recipe: "./test-recipe.yaml"
context:
test_dir: "{{test_dir}}"
output: "test_report"
- id: combined-report
prompt: |
Combine the quality reports:
## Lint Report
{{lint_report}}
## Test Report
{{test_report}}
Create an overall quality assessment with
a prioritized list of action items.
Run the composed recipe exactly like any other:
amp recipes execute code-quality.yaml \
--context '{"src_dir": "src/", "test_dir": "tests/"}'
Each sub-recipe runs in its own context. The parent recipe only sees the final output from each sub-recipe step, keeping the context clean.
Complete Examples¶
Code Review Recipe¶
name: code-review
description: Comprehensive code review with multiple specialists
version: "1.0.0"
context:
file_path: ""
review_depth: "standard"
steps:
- id: design-review
agent: foundation:zen-architect
prompt: |
Review {{file_path}} for design issues:
- Architecture patterns
- Code organization
- Complexity
Depth: {{review_depth}}
output: "design_findings"
- id: security-review
agent: foundation:security-guardian
prompt: |
Review {{file_path}} for security issues:
- Input validation
- Authentication/authorization
- Data handling
output: "security_findings"
- id: test-review
agent: foundation:test-coverage
prompt: |
Analyze test coverage for {{file_path}}:
- Existing tests
- Missing coverage
- Test quality
output: "test_findings"
- id: final-report
prompt: |
Create a code review report combining:
## Design Review
{{design_findings}}
## Security Review
{{security_findings}}
## Test Coverage
{{test_findings}}
Include:
- Summary of findings
- Priority-ordered action items
- Overall assessment
Deployment Recipe with Approval¶
name: deploy
description: Deploy to staging with approval gate
version: "1.0.0"
context:
environment: "staging"
version: ""
stages:
- name: preparation
steps:
- id: build
prompt: "Build version {{version}}"
output: "build_result"
- id: test
prompt: "Run full test suite"
output: "test_result"
- id: pre-deploy-check
prompt: |
Verify deployment readiness:
- Build: {{build_result}}
- Tests: {{test_result}}
List any blockers.
output: "deploy_readiness"
approval:
required: true
prompt: |
Ready to deploy {{version}} to {{environment}}.
Pre-check results:
{{deploy_readiness}}
timeout: 3600
default: "deny"
- name: deployment
steps:
- id: deploy
prompt: "Deploy {{version}} to {{environment}}"
output: "deploy_result"
- id: verify
prompt: "Verify deployment health"
Recipe Validation¶
Check your recipe before running:
amp recipes validate my-recipe.yaml
Common validation errors:
| Error | Solution |
|---|---|
| Invalid YAML syntax | Check indentation, quotes |
| Unknown agent | Verify agent name |
| Missing context var | Add to context section |
| Circular reference | Check step dependencies |
Recipe Author Agent¶
Get help creating recipes:
> Help me create a recipe for database migration
The recipe-author agent will:
- Ask clarifying questions
- Suggest best structure
- Generate valid YAML
- Add error handling
Try It Yourself¶
Exercise 1: Simple Recipe¶
# hello-recipe.yaml
name: hello
description: Simple greeting recipe
version: "1.0.0"
context:
name: ""
steps:
- id: greet
prompt: "Say hello to {{name}}"
amp recipes execute hello-recipe.yaml --context '{"name": "World"}'
Exercise 2: Multi-Step Recipe¶
Create a recipe that: 1. Reads a file 2. Explains it 3. Suggests improvements
Exercise 3: Add Approval Gate¶
Convert a flat recipe into a staged recipe with an approval gate before making changes.
Best Practices¶
- Clear step IDs - Use descriptive names like
analyze-depsnotstep1 - One responsibility per step - Keep steps focused
- Use appropriate agents - Match agent specialty to task
- Add approval gates - For destructive or important operations (use staged recipes)
- Handle errors - Use
on_errorfor steps that might fail - Name your outputs - Use the
outputfield so results are available downstream - Document context - Make required inputs clear