Skip to content

Guide: Multi-agent pipelines

A pipeline is an ordered list of steps, each running an agent over a templated input. It is the division-of-labour primitive: every step is a separately bound agent, so a pipeline can deliberately run one step on a cheap model and another on a strong one — or on two different models, which is the point of the shipped example. Pipelines run non-interactively, so tool access resolves by policy with no approval prompts.

muaz ships one: code-review. Run it from the UI Pipelines page (live per-step timeline with tokens, cost, and duration), or the CLI:

Terminal window
muaz pipeline run code-review "the staged changes"

It is an adversarial review in three steps. review-proposer reads the change and reports every defect it can find. review-disputer then attacks that list, reading the code itself to refute what doesn’t hold. review-arbiter decides which findings survive and writes the review you actually read.

The dispute step exists because a single model reviewing code reliably produces plausible findings — things that look like defects to a reader who hasn’t checked the guard three lines up. Each false positive sends a developer into code that was fine, and a few of those are enough that people stop reading reviews.

The dispute step works best when the disputer does not share the proposer’s blind spots, which means running them on different models. Because every step is a named agent, that is just a binding in ~/.muaz/models.yaml:

agents:
"@preset/review-proposer": { profile: anthropic-main }
"@preset/review-disputer": { profile: openai-main } # different model, on purpose
"@preset/review-arbiter": { profile: anthropic-main }

Nothing about the pipeline file changes. This is why steps name an agent rather than carrying an inline prompt: a binding needs something to key on, and an agent’s identity is that key.

review-proposer and friends are marked entrypoint: false — they are built to be called by a pipeline, not chatted with, so they are kept out of the pickers that start a new conversation.

They are not lesser agents. They resolve by name, they appear in muaz agents list, on the UI Agents page, and in the pipeline editor’s agent picker, and they are bound to models exactly like any other agent. You can still open a chat with one directly from the Agents page, which is the easiest way to iterate on its prompt. The flag governs one surface: the new-chat picker, where a handful of step agents would bury the two you actually start from.

Set it on your own agents with entrypoint: false in the YAML, or the Pipeline step only checkbox in the UI agent editor.

name: code-review
budget:
max_seconds: 1800 # pipeline-wide
steps:
- name: propose
agent: review-proposer
input: |
Review this change and report every defect you find.
Review target:
${input}
budget: { max_seconds: 600 }
retry: { max: 1, backoff_secs: 5 }
- name: dispute
agent: review-disputer
input: |
Attack the findings below.
Review target:
${input}
Proposed findings:
${steps.propose.output}

Key pieces:

  • Templated input over ${input}, ${vars.*}, ${steps.<name>.output}, and ${steps.<name>.<field>} (unknown placeholders are hard errors).
  • Routing is structured output, not free-text scanning: give a step an output_schema with a route field, then gate later steps with when: (equals / not_equals / contains).
  • Recovery per step: retry, on_error: fail|continue|fallback.
  • Budgets (max_cost_usd / max_tokens / max_seconds) on each step and the pipeline — time budgets hard-timeout an in-flight step.
  • parallel: groups run several agents concurrently.

Create and edit pipelines on the UI Pipelines page (a form-based step-card editor), or drop a YAML file in ~/.muaz/plugins/local/pipelines/. Plugins can ship pipelines too — they appear namespaced as @<plugin>/<name>. Full reference: Pipelines.