# Building agents on CAR Two copy-paste prompts that give an LLM (Claude / ChatGPT / Cursor) enough context to produce a working CAR agent on the first try — one for a single agent, one for a multi-agent system that learns from its own traces. Fill in the `TASK:` line and paste the whole block. For the data shapes these reference, see [SPEC.md](./SPEC.md); for runnable versions, see [`examples/`](./examples/). ## Build your first agent Paste everything in the block below into an LLM and fill in the `TASK:` line. ````markdown I want to build an AI agent using **Common Agent Runtime (CAR)**, a Rust-native runtime where models propose actions and the runtime validates + executes them deterministically. TASK: Use the Python binding `car_runtime` (pip install car-runtime — import name is `car_runtime`) unless I tell you otherwise. Keep everything in one file. ## What CAR gives you - `CarRuntime()` — a stateful runtime. Exposes `state_*`, `add_fact`, `register_tool`, `register_policy`, `verify_proposal`, `execute_proposal`, `infer_tracked`, `infer_stream`, plus persistence + memory graph. - **You write the tools.** Tools are Python functions dispatched by a callback. The runtime owns the DAG, state, policies, verification — not the tools. - **Proposals are plans.** A proposal is JSON describing a list of actions and their dependencies. Verify before executing. ## Action / proposal shape ```json { "actions": [ { "id": "a1", "type": "tool_call", "tool": "read_file", "parameters": {"path": "/tmp/foo.txt"}, "dependencies": [] }, { "id": "a2", "type": "tool_call", "tool": "summarize", "parameters": {"text_ref": "$a1.output"}, "dependencies": ["a1"] } ] } ``` Valid `type` values: `tool_call`, `state_write`, `state_read`, `assertion`. ## Tool callback contract ```python def tool_fn(tool: str, params_json: str) -> str: params = json.loads(params_json) if tool == "read_file": return json.dumps({"content": open(params["path"]).read()}) return json.dumps({"error": f"unknown tool: {tool}"}) ``` Return a JSON string. Errors are just a `{"error": "..."}` payload — the runtime handles retries + replans if configured. ## Skeleton to fill in ```python import json import car_runtime def build_agent(): rt = car_runtime.CarRuntime() # 1. Register the tools your agent will use. for tool_name in ["", ""]: rt.register_tool(tool_name) # 2. Add safety policies. rt.register_policy("no_rm", "deny_tool_param", target="shell", key="command", pattern="rm -rf") # 3. Seed facts the agent should know (optional). rt.add_fact("goal", "", "pattern") # 4. Build a proposal (hand-written, or generated by infer_tracked). proposal = {"actions": [ ... ]} # 5. Verify first — cheap, catches bad plans before any tool runs. check = json.loads(rt.verify_proposal(json.dumps(proposal))) if not check["valid"]: raise SystemExit(f"invalid plan: {check['issues']}") # 6. Execute. def tool_fn(tool, params_json): params = json.loads(params_json) return json.dumps({"ok": True}) # IMPLEMENT ME result = json.loads(rt.execute_proposal(json.dumps(proposal), tool_fn)) print(json.dumps(result, indent=2)) if __name__ == "__main__": build_agent() ``` ## Model-driven proposals (optional) If the plan itself should come from an LLM, use `infer_tracked`: ```python out = json.loads(rt.infer_tracked( f"Propose a JSON action plan for: {task}. " f"Return ONLY a JSON object with an `actions` array.", max_tokens=2048, )) proposal = json.loads(out["text"]) ``` ## Rules for the code you generate - **No mocks.** Use real filesystem, real HTTP, real subprocess calls. - **Verify before execute.** Every proposal goes through `verify_proposal` first — show the check in the output. - **One file.** Put everything in a single runnable `.py`. - **Fail loud.** Don't swallow errors. Raise `SystemExit` with a useful message. - **Print the final result as JSON** so it's easy to diff / test. Now write the agent for my TASK above. ```` **TypeScript version:** swap `car_runtime` (Python import) → `car-runtime` (npm); `rt.register_tool` / `register_policy` → `await rt.registerTool` / `registerPolicy`; `rt.execute_proposal(json, fn)` → `await executeProposal(rt, json, fn)`; `rt.infer_tracked` → `await rt.inferTracked`. ## Build a multi-agent system For a pipeline, swarm, or supervisor — or anything that should learn from its own traces — paste this into an LLM with the `TASK:` line filled in. ````markdown I want to build a multi-agent system using **Common Agent Runtime (CAR)** that also learns skills from its own execution traces and evolves them over time. TASK: Use the Python binding `car_runtime` (pip install car-runtime — import name is `car_runtime`). One file. No mocks. ## CAR's multi-agent building blocks Five coordination patterns are exposed as standalone functions: - `run_pipeline(stages_json, task, agent_fn)` — linear chain, each stage feeds the next. Staged refinement. - `run_swarm(mode, agents_json, task, agent_fn, synthesizer_json=None)` — mode is `"parallel"`, `"sequential"`, or `"debate"`. Exploration / multi-perspective. - `run_supervisor(workers_json, supervisor_json, task, max_rounds, agent_fn)` — a supervisor routes subtasks to workers over rounds. Long-horizon planning. - `run_map_reduce(mapper_json, reducer_json, task, items_json, agent_fn)` — map items in parallel, reduce to one answer. Batch work. - `run_vote(agents_json, task, agent_fn, synthesizer_json=None)` — parallel + voted/synthesized result. Higher-confidence answers. Call `register_agent_runner(agent_fn)` once instead of passing `agent_fn` every time; subsequent `run_*` calls use the stored callback. ## AgentSpec + AgentOutput shape ```python spec = { "name": "reviewer", "system_prompt": "You review code for the 3 biggest risks.", "tools": ["grep", "read_file"], "max_turns": 5, "metadata": {"model": "claude-opus-4-8", "temperature": 0.3}, } ``` `agent_fn(spec_json, task)` is YOUR code. It MUST return a JSON string: ```python { "name": spec["name"], "answer": "...final answer text...", "turns": 1, "tool_calls": 0, # integer count — NOT an array "duration_ms": 100.0, "error": None, # or a string if the agent failed } ``` Produce `answer` however you like — Anthropic, OpenAI, local Qwen3 via `rt.infer_tracked`, a deterministic tool chain. ## Learning loop: trace → distill → evolve ```python trace = [ {"kind": "action_succeeded", "action_id": "step1", "tool": "scraper", "data": {"task": task, "domain": "web"}, "reward": 1.0}, {"kind": "action_failed", "action_id": "step2", "tool": "scraper", "data": {"task": task, "domain": "web"}, "reward": 0.0}, ] skills_json = rt.distill_skills(json.dumps(trace)) # requires inference rt.ingest_distilled_skills(skills_json) rt.report_outcome("scrape_and_summarize", "success") rt.report_outcome("scrape_and_summarize", "fail") weak = rt.domains_needing_evolution(threshold=0.6) for domain in weak: rt.evolve_skills(json.dumps(trace), domain) # requires inference repaired = rt.repair_skill("scrape_and_summarize") ``` ## Skeleton to fill in ```python import json import car_runtime def main(): rt = car_runtime.CarRuntime() def agent_fn(spec_json: str, task: str) -> str: spec = json.loads(spec_json) # CALL YOUR LLM HERE. Return the AgentOutput JSON shape. return json.dumps({ "name": spec["name"], "answer": "IMPLEMENT ME", "turns": 1, "tool_calls": 0, "duration_ms": 1.0, }) car_runtime.register_agent_runner(agent_fn) agents = json.dumps([ {"name": "", "system_prompt": "", "tools": [], "max_turns": 5, "metadata": {"domain": ""}}, ]) result = json.loads(car_runtime.run_pipeline(agents, "")) print(json.dumps(result, indent=2)) if __name__ == "__main__": main() ``` ## Rules for the code you generate - **Pick one coordination pattern** and justify it in a comment. - **agent_fn does the LLM work.** Don't try to make CAR call an LLM directly. - **AgentOutput shape is strict:** `name`, `answer`, `turns`, `tool_calls` (integer count — NOT an array), `duration_ms`, `error`. Missing fields break deserialization silently. - **Synthesize traces from pipeline/swarm output** to feed the learning loop. - **Report outcomes** (`rt.report_outcome`) as agents succeed/fail — that drives `domains_needing_evolution`. - **Skip distill_skills / evolve_skills if no inference is configured** — they'll hang waiting for a model. Use hand-coded skills + `ingest_distilled_skills`. - **One file. Print the final result as JSON.** Now write the multi-agent system for my TASK above. ````