Chapter 17 · Real systems
Framework case studies
Three real agent systems, read feature by feature as answers to the design questions from the earlier chapters.
How to read a framework
Take one feature. Deep Agents moves most large tool results, over about 80,000 characters, into a file. The model sees only a short preview and the file's path. You could learn that as a setting to configure. But it is also an idea from chapter 4. Keep large data outside the context, and let the model fetch what it needs. This chapter reads real systems the second way, so each feature links back to the concept it implements.
Three systems get full profiles, because they make very different choices around the same loop. Shorter profiles of other systems follow, and chapter 17b puts everything side by side.
Versions and evidence date
The facts on this page come from research notes checked on 2026-10-19. They hold for these versions:
- Hermes Agent v0.22.2, released 2026-10-15.
- Deep Agents: the
deepagentsPython SDK 0.7.28, released 2026-10-16, with code read at commit 12f70f2. - Pi: the package
@earendil-works/pi-coding-agent1.3.0, released 2026-10-15, with code read at main commit 73f8de8.
These projects change quickly, and docs lag behind code. For example, the Hermes loop docs still give a turn limit of 500, but the v0.22.2 code sets no step limit within a turn. Chapter 17b links the exact source behind each claim.
Support levels
- Built in Part of the system as shipped.
- Optional Part of the system, but off until you turn it on.
- Extension or custom Not in the core. An example extension, a plugin, or your own code adds it.
- Not built in Absent. The project's answer is something else, such as plain files or an outside container.
- Unknown The research did not establish it. Unknown does not mean unsupported.
| Idea | System | What to look at |
|---|---|---|
| Context shaped by the prompt cache | Hermes | A system prompt frozen for the whole session |
| Large outputs moved to files | Deep Agents | Big tool results and old history saved to files the model can read back |
| State as an append-only log | Pi | One JSONL file per session, shaped as a tree |
| Memory with a write policy | Hermes | Two capped files, written by the agent and by a review step after turns |
| Planning as a measured option | Deep Agents | The todo tool left the standard defaults after a test showed no gain |
| Isolated context, shared state | Deep Agents | Child agents start fresh but share the parent's files |
| Choosing no subagents | Pi | Left out of the core on purpose |
| Skills loaded on demand | All three | Skill names in the prompt, full text read when needed |
| Approval decided by a model | Hermes | A second model reviews risky shell commands by default |
| Security left to a container | Pi | No permission system; the docs recommend a container or VM |
| Instructions versus enforced gates | Claude Code | CLAUDE.md is advice; hooks run every time |
| Durable execution as a layer | LangGraph | Saved steps, a cross-thread store, and pauses, under Deep Agents |
Hermes Agent: an assistant that edits its own notes
Suppose you tell Hermes, in a Telegram chat, that you prefer short answers. The agent can save that preference to a small file, USER.md, right away. But the new line will not appear in the system prompt until your next session. Hermes builds the system prompt once per session, and changes it only when it compacts (summarizes) old history. So every request starts with the same text, and the provider's prompt cache can reuse its work (chapter 4).
Hermes Agent v0.22.2 is an open-source (MIT) application from Nous Research. The same agent core runs behind a CLI, a desktop app, an editor protocol, and an API server. A messaging gateway also connects it to 20+ chat platforms. You change most of its behavior in a configuration file, config.yaml.
CLI, desktop, chat apps (20+), API server
|
v
One agent loop per user message
(system prompt frozen per session)
| | |
v v v
Tools state.db Profile folder
shell on 7 sessions, MEMORY.md,
backends, messages, USER.md,
MCP search skills/
^
Review step after turns ---------+
rewrites memory and skills
The fourteen questions
- Core mental model
- A personal assistant that lives on a server. One tool loop serves every chat front door, and between conversations the agent rewrites a notebook of memory, skills, and transcripts. Its design guide keeps the core small: new abilities should arrive as skills, plugins, or MCP servers.
- What it calls an agent
- A profile is the stored home of one assistant: its config, keys, memory, skills, and sessions. An agent is the running assistant that uses a profile. Subagents and the review step run the same agent class.
- Loop Built in
- Each user message starts one turn. Hermes calls the model, runs the tool calls it asks for, adds the results, and repeats until a reply contains no tool calls. Since v0.20.5 there is no step limit within a turn by default, because limits "caused silent mid-task truncation". Other limits remain. A watchdog stops a turn after 600 seconds without progress, and per-turn caps limit web searches and subagent spawns.
- State Built in
- During a turn, the state is a list of messages. Each profile also has one SQLite database that stores every session and message, with keyword search. Memory, skills, and the agent's identity are plain text files in the profile folder.
- Context construction Built in
The system prompt has three parts, ordered from most to least stable. Identity and tool guidance come first, then project files such as
AGENTS.md, then the skills list and memory. Anything that changes per call goes into the user message instead, so the start of the prompt stays the same. When a conversation grows long, a second model summarizes the middle of the history. The old messages stay in the database, marked as compacted.Default in v0.22.2: compaction starts at 50% of the window, or at least 75% for windows under 512K tokens.
- Tools Built in
- Tools register themselves, and Hermes hides any tool whose key or binary is missing. Rarely used tools sit behind a search tool that loads their definitions on demand. In v0.22.0 that cut the desktop tool definitions from 13,367 to 6,883 tokens. The shell tool runs on 7 backends, from the local machine to Docker and remote sandbox services such as Modal.
- Planning Built in
- There is no separate planner. A
todo_listtool keeps a task list and puts it back into the context after compaction. In v0.22.2 the model has to find it through tool search first. The/goalcommand adds an outer loop. After each turn, another model answers done, continue, or blocked. On continue, Hermes keeps going, for up to 20 turns by default. - Subagents Built in
delegate_taskstarts child agents with a fresh conversation. A child does not see the parent's history or memory. It gets the goal and context the parent writes, the workspace path, and the project's instruction files. It also gets fewer tools, for example no memory and no way to ask the user. By default, children run in the background, up to 10 at once and one level deep, and both limits can be changed. Each result comes back later as a new message, with a summary of at most 24,000 characters.- Memory Built in
- Two small files are always in the prompt: notes about the environment (2,200 characters) and about the user (1,375 characters). The agent writes them with a
memorytool. A write over the limit fails, so the agent has to merge entries. Older conversations are found by keyword search over the session database, with no model call. - Skills Built in
- A skill is a folder with a
SKILL.mdfile. The prompt lists only skill names and descriptions, and the model loads a full skill when it needs one. The agent also writes and edits skills itself, and a curator archives unused agent-written skills after 30 days. - Permissions Built in
- Hermes checks shell commands against 110 patterns for dangerous commands. By default, a second model reviews each match and approves it, denies it, or escalates it to you. A short hardcoded blocklist and your own deny rules apply even when approvals are off. In a container backend the approval check is skipped, because the container is the boundary.
- Persistence Built in
- Hermes saves every message during the turn, so a session survives crashes and restarts. File rollback is optional: Hermes can snapshot the working folder into a hidden git store before edits. Cron jobs run as fresh agents on a schedule.
- Distinctive ideas
- It improves itself through text, not training: a review step after turns edits the memory and skill files. It also offers a ladder of autonomy: one turn, a judge-driven
/goalloop, a cron schedule, or a Kanban board. The board is a shared task queue in SQLite that several agent processes work from. - Hidden complexity
- Hermes hides provider differences, cache management, compaction, and chat-platform plumbing. For example, if you switch models mid-conversation, Hermes converts the history to the new provider's format. Its docs warn that the next request then gets no cache hits. It also hides its own upkeep, because counters trigger the review step without being asked.
What it teaches
- A frozen prompt makes calls cheaper, but new facts reach the model later. Decide which facts can wait for the next session (chapter 4).
- If a model approves actions, keep hard limits that no reviewer can override, as Hermes does (chapter 11).
Deep Agents: a standard loop wrapped in layers
Suppose a custom search tool returns 200,000 characters. Deep Agents writes that result to a file. Only its first and last 5 lines, plus the file path, go into the conversation. The model can read more with its file tools if it needs to. The model did not ask for this. Middleware did it, meaning code that runs around every model call. Most of what Deep Agents adds works this way.
Deep Agents is LangChain's agent library. This profile covers the Python SDK, deepagents 0.7.28, which runs on langchain 1.4.6. Underneath is LangGraph, a LangChain library that stores the state of each run and can pause it. You call Deep Agents from your own code. A JavaScript version and a terminal coding agent, Deep Agents Code (dcode), ship as separate packages.
create_deep_agent(model, tools, options)
|
v
LangChain loop, run as a LangGraph graph:
model call <---> tools (ends when a reply
has no tool calls)
Middleware wraps every call, in order:
files, subagents, summarization,
your middleware, prompt caching,
plus skills, memory, and approval
if you configure them
| |
v v
One file interface LangGraph underneath:
(state, store, disk, saves steps with a
or a command backend) checkpointer; pauses
for approval if set up
The fourteen questions
- Core mental model
- A standard tool loop wrapped in ordered middleware. A virtual file system serves as working memory, and subagents are throwaway context windows. It adds no new runtime of its own.
- What it calls an agent
- The agent is the compiled LangGraph graph that
create_deep_agent()returns: a model, tools, and middleware, run as a loop. Subagents are agents too, and any LangGraph graph with a message list can serve as one. - Loop Built in
- LangChain runs it: call the model, run every requested tool, and end when a reply contains no tool calls. Tool calls from one reply run in the same step. Deep Agents sets no turn limit of its own. It only sets LangGraph's recursion limit, to 9,999 steps.
- State Built in
- State is a typed dictionary that LangGraph keeps per conversation thread. Besides the messages, it can hold the files themselves. By default, files live in the state, not on disk.
- Context construction Built in
Since 0.7.0 the SDK ships no general system prompt. The prompt is your own text, plus extra sections from a per-model profile for some models. If you configure skills and memory files, middleware adds the skills list and those files on each call. Large tool results go to files, as in the example above. When the history grows long, Deep Agents archives the oldest messages to a file and replaces them with a summary. The model sees the summary plus recent messages, while the state keeps every original message.
Defaults in 0.7.28: tool results above about 80,000 characters (20,000 tokens at 4 characters each) are offloaded. The built-in file tools are exempt. Summarization starts at 85% of the input limit in the model's profile and keeps the newest 10%. Without a profile, it starts at 170,000 tokens and keeps 6 messages.
- Tools Built in
- It ships file tools (
ls,read_file,write_file,edit_file,glob,grep,delete), a shell tool (execute), andtaskfor subagents.executeappears only when the storage backend can run commands. The default backend cannot, so the default setup has no shell at all. A backend that can run commands is not automatically isolated: the local shell backend runs them on your machine. You add your own tools as plain functions, and MCP tools through LangChain. - Planning Optional
- Since 0.7.0,
write_todosis no longer in the standard default stack, but the built-in OpenAI Codex profiles still add it. LangChain removed it after testing three models. The tool gave no significant accuracy gain, and cost went up on two of them. Even when it is on, nothing checks that the agent follows the list. - Subagents Built in
- The
tasktool runs a child agent while the parent waits. The child starts with only the task description and returns only its final message. But with the default storage, the child reads and writes the same files as the parent. So its context is isolated, while its state is shared. The parent runs children in parallel by callingtaskseveral times in one reply. - Memory Optional
- Short-term memory is the thread's saved state. Long-term memory is files stored outside any one thread. You route a folder such as
/memories/to a LangGraph store, scoped per user or per assistant. If you list anAGENTS.mdfile as memory, it goes into the prompt on every call. The agent edits it with the normal file tools. - Skills Optional
- Skills use the Agent Skills format: a folder with a
SKILL.mdfile. When you pass skill folders, the prompt lists each skill's name, description, and file path. The model reads the file withread_filewhen a task matches. - Permissions Optional
- You name the tools that need approval with
interrupt_on. The run pauses, which needs a checkpointer, and a person approves, edits, or rejects each gated call. You can also set allow, deny, or ask rules on file paths. They cover only the built-in file tools, not the shell. The README puts it this way: "Enforce boundaries at the tool/sandbox level, not by expecting the model to self-police." - Persistence Optional
- Persistence comes from LangGraph. A checkpointer is a store that saves the state after every step. If you pass one, a run can resume after a crash without redoing finished steps. It can also wait for an approval as long as needed. Files can live in the state, in a LangGraph store, on real disk, or in a sandbox.
- Distinctive ideas
- One file interface serves file tools, skills, memory, offloaded results, and archived history. Changing the storage changes all of them at once. Its defaults also follow experiments, as with the todo tool above.
- Hidden complexity
- It hides the order of about a dozen middleware, token counting, and history archiving. For example, if a run stops while a tool call is pending, the next run adds an error result for that call. So the provider never sees a call without a result. LangGraph hides threads, checkpoints, and pausing.
What it teaches
Pi: a small core you extend
Ask Pi to plan a large refactor. It has no plan mode and no to-do tool, so the plan goes into a file such as PLAN.md. You can read that file, edit it, and commit it with your code. Pi's creator prefers it that way, because file plans "can be shared across sessions, and can be versioned with your code".
Pi calls itself "a minimal, extensible agent harness that you can make your own". This profile covers the package @earendil-works/pi-coding-agent 1.3.0. Pi is both a CLI, pi, and a set of packages you can import. One package handles model access across providers, one runs the loop, and one is the coding agent. Mario Zechner created it, and Earendil acquired the project on 2026-04-08.
Terminal UI, print, JSON, RPC, SDK
|
v
pi-coding-agent short prompt, 4 tools,
compaction, skills list
|
v
pi-agent-core the loop
|
v
pi-ai one API, many providers
|
v
Model any supported provider
Extensions add tools, gates, subagents,
and plan mode.
Session: an append-only JSONL tree
The fourteen questions
- Core mental model
- A tiny agent core with a large plugin system. The goal is control over context: its creator wrote that "Exactly controlling what goes into the model's context yields better outputs". So opinionated features such as plan mode and subagents stay out of the core. Pi can write its own extensions and load them with
/reload. - What it calls an agent
- An
Agentobject holds the model, tools, and messages, and anAgentSessionwraps it with queued messages, compaction, and extensions. Pi's words differ from this course. A Pi "turn" is one model reply plus its tool calls, which chapter 1 calls a step. - Loop Built in
- Pi streams a reply, runs its tool calls in parallel, records the results, and repeats while tool calls or queued user messages remain. You can steer mid-run: a message you type while Pi works is delivered after the current tool calls finish. A search of the source found no step limit.
- State Built in
- Each session is one append-only JSONL file. Every entry points to its parent entry, so the entries form a tree, and you can branch from any point. Even changes to the system prompt and the tool list are entries, so any branch can be replayed exactly.
- Context construction Built in
The system prompt is a few short sections: one line per tool, brief rules, pointers to Pi's docs, project instructions, and skills. Project instruction files (
AGENTS.md) load from a global folder and every parent directory. Prompt and tool changes are appended as system messages. On models that accept them mid-conversation, the cached prefix survives.Compaction is separate. When the context nears the window limit, a model call summarizes older entries. Pi keeps the most recent ones word for word, and the original entries stay in the file.
Defaults in 1.3.0: compaction starts when the context passes the window minus 16,384 reserved tokens. About 20,000 recent tokens stay word for word. The prompt with tool definitions is roughly 1,000 to 1,500 tokens, by a character-count estimate.
- Tools Built in
- Four tools by default:
read,bash,edit, andwrite. The creator's reason is that models already know bash and "have been trained on the read, write, and edit tools with similar input schemas".readandbashoutput stops at 2,000 lines or 50 KB.readcan page through a long file, andbashsaves its full output to a temp file. MCP is built in since 0.99.0. Its tools stay hidden until the model searches for them or calls them from a script. - Planning Extension or custom
- No plan mode and no to-do tool, by design. The creator wrote that "to-do lists generally confuse models more than they help". Plans live in files, and a plan-mode extension ships as an example.
- Subagents Extension or custom
- Not built in. Its creator argues that a built-in subagent hides its work from you. He suggests asking Pi to run another copy of itself through bash, where you can watch it. An example extension runs each subagent as a separate
piprocess with its own model, tools, and prompt. - Memory Not built in
- There is no memory system. What carries over between sessions is ordinary: session files you can resume,
AGENTS.mdfiles, files the agent writes, and skills. - Skills Built in
- Pi follows the Agent Skills format. The prompt lists each skill's name, description, and file path. When a task matches, the model reads the
SKILL.mdfile with the normalreadtool. Skills in the project's.pi/skills/folder load only after you trust the project. - Permissions Extension or custom
- Pi has no built-in permission system and does not ask before tool calls, by design. Its docs say that watching the transcript, project trust, and reviewing changes do not create a security boundary. They recommend a container or VM instead. The only built-in prompt asks whether to trust a project's own settings and extensions. Extensions can block or change any tool call, and if such a handler crashes, Pi blocks the call.
- Persistence Built in
- Pi saves every session automatically, and you can resume it, branch it, or fork it into a new file. Branching rewinds the conversation but not the files on disk, so an example extension uses git to restore them. Resuming a turn that crashed is the goal of Pi Durable, an experimental separate package.
- Distinctive ideas
- The agent extends itself: extensions are TypeScript files that load without a build step, and Pi writes many of them. Codemode lets the model write a JavaScript script that calls tools. The script runs in QuickJS, a small JavaScript engine with no direct file, network, or Node APIs. Only its output enters the context. The tools it calls still have their normal effects, such as writing files.
- Hidden complexity, and what it leaves to you
- Pi hides provider differences, compaction, session-tree bookkeeping, and parallel tool scheduling. For example, when you switch to another provider's model mid-session, Pi passes text, tool calls, and results through unchanged. It drops the old model's thinking blocks, because signed reasoning only works with the model that wrote it. It leaves the rest to you: the sandbox, approval rules, planning, subagents, and memory.
What it teaches
- If users can branch a conversation, decide what happens to the files on disk. Pi rewinds only the conversation (chapter 3).
- A minimal harness moves safety work to you. Plan the container, the network limits, and any approval gates first (chapter 10).
Other systems, briefly
Claude Code: advice versus enforcement
Claude Code is Anthropic's coding agent. Its project file, CLAUDE.md, loads every session, but the docs call those instructions advisory, while hooks are deterministic. A hook is a script that the runtime runs at a fixed point. For example, a Stop hook can keep a turn from ending until a check passes. That is the difference between an instruction and an enforced gate from chapter 10.
Its sandbox shows what containment buys. Claude Code users approve 93% of permission prompts, which Anthropic calls "approval fatigue". Anthropic added an OS sandbox, using bubblewrap on Linux and Seatbelt on macOS. A proxy outside it lets traffic reach only approved domains. In internal use, this cut permission prompts by 84%, because a command that cannot reach much needs less review.
Sources: the Claude Code best practices docs page and Anthropic's sandboxing post (2025-10-20), both checked 2026-10-19.
LangGraph: the runtime under a harness
Deep Agents leaves saving and pausing to LangGraph. LangChain describes its stack in three layers. LangGraph is the runtime, create_agent is the framework, and a harness such as Deep Agents sits on top. Its list of harnesses also names the Claude Agent SDK. The split is worth copying: durable execution (chapter 12) lives in one layer, and agent defaults live in another.
Sources: LangChain's products page and LangGraph persistence docs, checked 2026-10-19.
Shared formats and protocols
Some pieces now work across systems. AGENTS.md is an open format for project instructions, "used by over 60k open-source projects". All three profiled systems can read it. In the A2A project's words, "MCP standardizes agent-to-tool communication; A2A standardizes agent-to-agent communication." Neither is an agent loop. The MCP spec also says it "cannot enforce these security principles at the protocol level", so the host program has to enforce them.
Sources: agents.md, the MCP specification (version 2026-07-28), and the A2A specification (v1.0), checked 2026-10-19.
OpenHands, the OpenAI Agents SDK, PydanticAI, and smolagents build on the same primitives: a loop, tools, state, and per-call context. The course's research did not verify their details, so read them yourself with the same fourteen questions.
Running example: Patch on each system
| Patch needs | Hermes | Deep Agents | Pi |
|---|---|---|---|
| Search, read, edit, run | Built-in file tools and shell | File tools; the shell needs a command-capable backend | read, edit, write, bash |
| Project memory | AGENTS.md, plus a 2,200-character notes file | An AGENTS.md memory file in a store | The AGENTS.md chain and notes in files |
| Skills: add-db-migration, deploy-staging | Skill folders; the agent may edit them | Skill folders you pass in | Skill folders in the project |
| Read-only explorer, write-free reviewer | delegate_task; a write-free reviewer is unverified, because children get the parent's tools minus a fixed list | Declared subagents, each with its own tool list | The example subagent extension, one file per role |
| Container sandbox | The Docker shell backend | An isolating sandbox backend (not the local shell), with secrets kept outside it | Run Pi in a container or VM |
| Approval for push, migrations, deploys | Model or human review of risky commands, skipped inside a container | interrupt_on for the gated tools | A gate you write as an extension |
| Nightly runs from a queue | Cron jobs or the Kanban board | Your queue, one saved thread per issue | Your queue; Pi Durable is experimental |
Every system covers Patch's coding loop, but the gaps are in control. Hermes skips its approval check inside a container, so Patch's push gate belongs in branch protection on the Git host. On all three, "one PR per issue" stays Patch's own logic, because no framework deduplicates side effects for you.
Check yourself
Hermes saves a memory entry in the middle of a session. When does the entry first appear in the system prompt, and why? (Q7)
Answer
At the start of the next session. The memory files go into the system prompt once per session, and changing the prompt mid-session would break the prompt cache. The file on disk changes right away.
A Deep Agents subagent runs with the default settings. What does it share with its parent? (Q12)
Answer
The child's conversation starts with only the task description. But with the default storage, parent and child read and write the same files, and the child's changes stay after it ends.
You build Patch on Pi. Where does the security boundary come from? (Q23)
Answer
Pi has no permission system, and its docs say that watching the transcript does not create a boundary. An extension gate only covers the tool calls it intercepts, so containment has to come from outside.
Takeaways
- All three run the same loop, and they load skills and compact history in similar ways.
- They differ most in defaults. Hermes builds in memory, subagents, and approvals. Deep Agents builds in subagents and makes memory and approvals options. Pi leaves all three to extensions, plain files, and outside containers.
- Framework facts expire quickly, so note the version and trust the code over the docs.