examples
Start with a working agent. Make it yours.
Choose a small agent, a guided tour, or an API-shaped application. Each template runs as written, and the test suite executes all three on every change.
minimal
Ask an agent where an order is, and let it call a tool to find out.
psych new demo && cd demo && python main.pyYou get: Prints the answer, then the token totals and the terminal state. No API key, no database, no Docker.
tour
The same agent, plus the five features you reach for next: an approval, a skill loaded on demand, a fact remembered across Runs, a workflow, and the report for all of it.
psych new demo --template tour && cd demo && python main.pyYou get: Stops at issue_refund and prints what it is waiting on, then resumes, answers, and prints usage, cost and wall-clock latency.
fastapi
Put an agent behind your own HTTP API, with the Worker in a process of its own.
psych new api --template fastapiYou get: Writes six routes and a separate worker.py that share a Store and nothing else. You run them; the CLI never does.
psych skills install # 26 guides for your AI coding agent
psych doctor # what is installed and configured; opens no socket
psych --help # and that is the whole command line
That is all of it. psych writes files, copies files and reports what is installed. It has no command that starts a Worker or runs an agent: you own the process that does that.
the tour, running
The tour, stopping for approval.
Output from --template tour against the fake model. The Run stops before the destructive call and waits for a decision.
$ psych new demo --template tour && cd demo && python main.py
No OPENAI_API_KEY set, so this tour uses the fake model.
====================================================================
1-3. Tool call, approval, and a skill loaded on demand
====================================================================
waiting on approval for: issue_refund
answer: Refunded 4200 on order A1. I will email you from now on.
work: 4 turns, 4 tool calls
approvals granted: 1
tool lookup_order({'order_id': 'A1'}) -> ok
tool load_skill({'name': 'refund-policy'}) -> ok
tool issue_refund({'order_id': 'A1', 'cents': 4200}) -> ok
tool remember({'content': 'prefers email over SMS'}) -> ok
====================================================================
6. What all of it cost
====================================================================
state: completed
usage: input=0 output=0 cache_read=0 cache_write=0 cache_write_1h=0 reasoning=0
cost: None (None means no price is known, never zero)
unpriced: 5 model calls
latency: 0.51s wall clock
status = await psych_runtime.status(s.store, run_id)
if status.pending_approval is not None:
print(f"waiting on approval for: {status.pending_approval.tool}")
await psych_runtime.resume(s.store, run_id, approved=True, by="manager-7")
next
Two more shapes.
Every feature has a guide with working code.
spec = psych_runtime.AgentSpec(
name="researcher",
instructions="Answer from the connected sources.",
model=psych_runtime.ModelRef(model="gpt-4o"),
mcp_servers=(
psych_runtime.McpServer(
name="docs",
url="https://mcp.example.com/mcp",
credential="docs-token", # resolved per Scope by your SecretResolver
allow=("search", "read_page"),
),
),
)
The credential is a name your resolver reads per Scope. The connection pools by scope, server and credential, never by URL. MCP guide
flow = psych_runtime.WorkflowSpec(
name="close-ticket",
steps=(
psych_runtime.ToolStep(name="verify", tool="lookup_order", arguments={"order_id": "A1"}),
psych_runtime.AgentStep(name="write-up", spec=spec), # a nested agent
),
tools=(psych_runtime.CodeTool(name="lookup_order"),),
)
Step names are the memoisation key. After a crash it resumes from the last completed step. Workflows guide