Migrate an agent
Start from one agent and port only what your real inputs use. When the two diverge, fix the first point of difference rather than adding prompt text.
You need: the CLI set up (Create a recipe) and a few real inputs the old agent handles today.
1. Scaffold the target
introspection init support-agent2. Map each part
| In your agent | In the recipe |
|---|---|
| System prompt | SYSTEM.md, or system_instructions in the agent YAML (Define agents) |
| Reusable judgment, prompt helpers | Skills (Skills & prompts) |
Custom tools (@tool, tool(), function_tool) | An extension tool (Add a tool) or an MCP server (Connect an MCP server) |
| Model choice | ai.model: <provider>/<model-id> |
| API keys in environment variables | Bindings per environment (Connect services and secrets) |
| Threads, sessions, stored message arrays | One task per thread. Send later turns to the same task. |
| Supervisor graph, several assistants | One agent by default; subagents when the split is real (Multi-agent) |
3. Map framework-specific features
LangGraph, OpenAI Agents SDK, Claude Agent SDK, Vercel AI SDK, Mastra, Temporal
| Source | Maps to |
|---|---|
LangGraph interrupt and checkpointer | askUserApproval from a tool, then tasks resume (Ask the user); only the root agent pauses. |
| LangGraph assistant config | Per-task metadata, read in the sandbox from INTROSPECTION_TASK_METADATA_JSON |
| OpenAI Agents SDK handoffs | Subagents. The lead agent stays in charge; the conversation is not transferred. |
| OpenAI Agents SDK guardrails | A tool_call hook (Block a tool call), or validation in a submit tool (Get structured output) |
| OpenAI Agents SDK sessions | The task’s conversation id |
| Claude Agent SDK | SKILL.md files go to skills/; custom tools become extensions or an MCP server. |
Vercel AI SDK tool() | An extension tool. No useChat adapter: render the task stream (Add a web chat). |
| Mastra suspend and resume | Interrupts (askUserQuestion, askUserApproval) |
| Temporal workflows | Keep Temporal. Start the task from an activity, then stream or poll it. |
4. Port your eval cases
LangSmith and Evalite
| Source | Maps to |
|---|---|
| LangSmith dataset | JSONL rows with schema_version: 2, name, input.prompt, and optional expected (Add cases to an eval) |
| LangSmith LLM evaluators | judges/*.yaml (Calibrate a judge) |
| LangSmith code evaluators | A createJudge code judge returning { score } (Run and compare evals) |
Evalite data | JSONL rows, one case each |
| Evalite scorers | judges/*.yaml or createJudge. A case with only expect() and no score counts as incomplete. |
5. Prove parity
Run each real input through the old agent (when safe) and the recipe:
introspection check
introspection local -p "<the same input>"Then put the inputs in an eval suite so the check repeats on every change (Run and compare evals).
Already a Pi package?
Add agents/agent.yaml with name and ai.model, and a runtime manifest
.introspection/<slug>.yaml at the repository root with name and the recipe
path. Then run introspection check. If pi.agents is an explicit list, add
the new file to it.
Verify
introspection check passes, and every parity input gives the same decision
from both agents, or a difference you chose on purpose.
Limits
| Not supported | Do this instead |
|---|---|
| Importing LangSmith or Evalite data automatically | Write the JSONL rows with a short script. |
Or ask your agent: “Migrate the support agent in src/agent to an Introspection recipe and show me parity on our real inputs.”