Skip to Content
Platform

Recipes

A recipe is your agent’s behavior, versioned in git: a precise pointer to the exact commit that defines how the agent thinks and acts.

What a recipe is

A recipe is the packaged, versioned behavior of your agent. It is not a copy of your code; it is a precise pointer to one exact version of it in git. Everything the agent needs to run lives there: its instructions, profiles, skills, and judges. When you point Introspection at a recipe, you are handing it one immutable snapshot of “how this agent behaves.”

Authoring recipes. Build a recipe locally with the pi-recipes CLI (beta): scaffold one with recipes create, validate it with recipes check, run it with pi --recipe <name>, and publish it to git. Browse pi.recipes  for complete recipes you can inspect or customize. Introspection pins the resulting commit as the recipe it deploys.

Inside a recipe

A recipe is a Pi package: a folder whose package.json carries a pi block declaring the recipe’s resources — agents, skills, prompts, optional extensions, and the MCP servers the recipe is allowed to reach. The system prompt lives in SYSTEM.md, and each agent is a YAML file (the default is agents/agent.yaml; named variants are just more agents).

A small customer-support recipe might look like this:

customer-support/ ├── package.json ├── SYSTEM.md ├── agents/ │ ├── agent.yaml │ ├── triage.yaml │ └── escalation.yaml ├── skills/ │ └── ticket-triage/SKILL.md ├── judges/ │ └── useful-support-resolution.yaml └── .introspection/ └── customer-support-agent.yaml

The files have different jobs: package.json declares what the recipe contains, the agent profile selects behavior and capabilities, a skill teaches a reusable workflow, and the .introspection manifest connects the package to a managed runtime.

package.json
{ "name": "@acme/customer-support", "version": "0.1.0", "description": "Triage support requests, draft grounded replies, and escalate sensitive cases.", "type": "module", "scripts": { "check": "recipes check . --profile ci" }, "pi": { "agents": ["agents/*.yaml"], "skills": ["skills/**/SKILL.md"], "mcp": { "servers": [ { "id": "slack", "required": false, "tools": { "include": ["slack_read_thread", "slack_send_message_draft"] } } ] } } }

The package-level MCP list is the recipe’s outer boundary. Each agent can narrow that list, but cannot grant itself a server or tool the package did not declare. Keeping risky actions draft-only in the instructions is useful guidance; excluding the send tool is the stronger capability boundary.

An agent YAML declares, with strict validation (unknown keys are errors, not silently ignored):

KeyWhat it declares
fromAnother agent to inherit from. Objects (model, mcp, extensions) merge by key; lists (tools, skills, subagents) replace.
modelThe model spec: name (<provider>/<model_id>), thinking_level (offxhigh), temperature, max_tokens, cache_retention, timeout/retry settings, and per-provider options such as OpenRouter routing or Anthropic betas.
toolsAn exact allowlist of the built-in and extension tools the agent may use — always enforced.
mcpPer-server MCP tool selection (see MCP). Omit it and the agent gets no MCP tools.
skills / subagentsThe skills the agent loads and the other recipe agents it may delegate to.
system_instructionsContent applied to the system prompt in append or replace mode.

What you write is exactly what runs: the runtime adds nothing to the prompt beyond SYSTEM.md and the agent’s system_instructions — no implicit capability notices, paths, or tool hints — and the agent’s tool and MCP selections are enforced as written, failing closed if the policy is invalid.

Immutable by design

A recipe is immutable. Once it exists, the version it points at never changes. Iterating on your agent does not edit a recipe in place; it produces a new version, recorded as a new recipe.

Only the operator-facing name and description can be edited after the fact. Pointing at different content always means creating a new recipe.

This single property is what the rest of the platform leans on:

  • Every run is reproducible. A task that ran on a given recipe can be reconstructed exactly, because the behavior was pinned to a commit that can’t drift underneath you.
  • Any two versions are comparable. Because each iteration is its own immutable row, you can line up version A against version B and trust that the only thing that changed is what’s in the diff.

Think of a recipe the way you think of a git commit: a fixed point you can always return to. “Editing the recipe” is a contradiction. You make a new commit, and that’s a new recipe.

How recipes are used

A recipe on its own does not run. It becomes runnable when it is bound into a runtime: the deployable version of your agent that joins a recipe to Introspection’s execution harness. One recipe can back a runtime; iterating the recipe produces a new runtime version in the same lineage.

Recipes are also referenced directly by experiments: an experiment arm names a runtime version, and through it the recipe that arm is testing. Comparing two arms is, at bottom, comparing two recipes on the same live traffic.

  • Runtimes: the deployable version that binds a recipe to the execution harness.
  • Experiments: A/B testing between recipe-backed runtime versions.
  • Judges: quality rubrics authored inside the recipe’s git repository.
  • Core Concepts: how recipes fit the rest of the model.
Last updated on