# Deep Agents harness structure

Cloned repo: `research/deepagents`

Snapshot inspected: `df8db8af`

## One sentence

Deep Agents is a batteries-included agent harness: it does not replace LangGraph or LangChain, it assembles a reusable agent loop with default middleware, filesystem and shell backends, subagents, skills, memory, profiles, and eval/deployment support.

## Mental model

```txt
Deep Agents      opinionated harness: defaults, middleware, backends, profiles
LangChain        agent abstraction: model + tools + middleware -> agent loop
LangGraph        runtime: state, checkpoints, streaming, interrupts
```

The core trick is that generality comes from composable surfaces, not from one giant prompt. `create_deep_agent()` resolves a model, chooses a backend, assembles middleware, injects a system prompt, wires subagents, and returns a LangGraph-backed agent.

## Diagram-first view

### Layer Cake

```mermaid
flowchart TB
    App["Your app / example agent"] --> DeepAgents["Deep Agents harness"]
    DeepAgents --> LangChain["LangChain create_agent loop"]
    LangChain --> LangGraph["LangGraph runtime"]

    DeepAgents --> Middleware["Middleware stack"]
    DeepAgents --> Backends["Backends"]
    DeepAgents --> Profiles["Harness profiles"]
    DeepAgents --> Subagents["Subagents"]
    DeepAgents --> SkillsMemory["Skills + memory"]

    Middleware --> PromptToolsState["Prompt, tools, state, permissions, context"]
    Backends --> FilesShellMemory["Files, shell, virtual mounts, stores"]
    Profiles --> ModelTuning["Provider/model-specific tuning"]
    Subagents --> Delegation["Task delegation with isolated context"]
```

### Construction Flow

```mermaid
flowchart TD
    A["Caller invokes create_deep_agent(...)"] --> B["Resolve model"]
    B --> C["Resolve provider/model harness profile"]
    C --> D["Resolve backend or use StateBackend"]
    D --> E["Normalize caller tools"]
    E --> F["Process caller subagents"]
    F --> G{"Default general-purpose subagent enabled?"}
    G -->|Yes| H["Add general-purpose subagent"]
    G -->|No| I["Skip default subagent"]
    H --> J["Assemble main middleware stack"]
    I --> J
    J --> K["Insert caller middleware"]
    K --> L["Apply profile middleware and tool exclusions"]
    L --> M["Add memory, prompt caching, human approval if configured"]
    M --> N["Compose final system prompt"]
    N --> O["Call LangChain create_agent(...)"]
    O --> P["Return LangGraph CompiledStateGraph"]
```

### Runtime Flow

```mermaid
flowchart TD
    A["agent.invoke({ messages })"] --> B["LangGraph loads state"]
    B --> C["Middleware prepares model request"]
    C --> C1["Inject system prompt fragments"]
    C --> C2["Add/filter tools"]
    C --> C3["Compact or summarize context"]
    C --> C4["Attach state-backed capabilities"]
    C1 --> D["Model call"]
    C2 --> D
    C3 --> D
    C4 --> D
    D --> E{"Model response"}
    E -->|Final answer| F["Return output and checkpoint state"]
    E -->|Tool calls| G["Tool execution path"]
    G --> H["Middleware wraps or validates tool call"]
    H --> I{"Tool type"}
    I -->|Filesystem tool| J["FilesystemMiddleware"]
    I -->|Task tool| K["SubAgentMiddleware"]
    I -->|Async task| L["AsyncSubAgentMiddleware"]
    I -->|User tool| M["Caller-provided tool"]
    J --> N["BackendProtocol / SandboxBackendProtocol"]
    K --> O["Run isolated subagent graph"]
    L --> P["Remote/background LangGraph task"]
    M --> Q["Function result"]
    N --> R["Tool result appended to messages"]
    O --> R
    P --> R
    Q --> R
    R --> B
```

### Middleware Stack Flow

```mermaid
flowchart LR
    A["TodoListMiddleware"] --> B["SkillsMiddleware optional"]
    B --> C["FilesystemMiddleware"]
    C --> D["SubAgentMiddleware optional"]
    D --> E["SummarizationMiddleware"]
    E --> F["PatchToolCallsMiddleware"]
    F --> G["AsyncSubAgentMiddleware optional"]
    G --> H["Caller middleware"]
    H --> I["Profile extra middleware"]
    I --> J["Tool exclusion middleware"]
    J --> K["Prompt caching"]
    K --> L["MemoryMiddleware optional"]
    L --> M["HumanInTheLoopMiddleware optional"]
```

### Backend Flow

```mermaid
flowchart TD
    A["Filesystem tool call"] --> B["FilesystemMiddleware"]
    B --> C{"Permission rule?"}
    C -->|Allow| D["Resolve backend"]
    C -->|Deny| E["Return permission error"]
    C -->|Interrupt| F["Pause for human approval"]
    F -->|Approved| D
    D --> G{"Backend type"}
    G --> H["StateBackend: thread-scoped virtual files"]
    G --> I["FilesystemBackend: real filesystem root"]
    G --> J["StoreBackend: persistent store"]
    G --> K["CompositeBackend: path-prefix routing"]
    G --> L["LocalShellBackend / SandboxBackend: files plus execute"]
    H --> M["Return file result"]
    I --> M
    J --> M
    K --> M
    L --> M
```

### Better-Harness Optimization Flow

```mermaid
flowchart TD
    A["Experiment config"] --> B["Define editable surfaces"]
    B --> C["Build baseline variant"]
    C --> D["Run train evals"]
    C --> E["Run holdout evals"]
    D --> F["Create proposer workspace"]
    F --> G["Outer Deep Agent reads failures and surfaces"]
    G --> H["Outer agent edits /current surfaces"]
    H --> I["Materialize candidate variant"]
    I --> J["Run train evals on candidate"]
    I --> K["Run holdout evals on candidate"]
    J --> L{"Combined pass count improved?"}
    K --> L
    L -->|Yes| M["Accept candidate as current"]
    L -->|No| N["Discard candidate"]
    M --> O{"More iterations?"}
    N --> O
    O -->|Yes| F
    O -->|No| P["Run optional scorecard and write report"]
```

## Repository map

```txt
libs/deepagents/        core SDK: create_deep_agent, middleware, backends, profiles
libs/cli/               deployment CLI
libs/code/              prebuilt coding agent / terminal UI
libs/evals/             eval suite and Harbor integration
libs/acp/               Agent Client Protocol integration
libs/talon/             local runtime host for long-running agents
libs/partners/          sandbox/provider integrations
examples/               worked patterns and deployable agents
examples/better-harness eval-driven harness optimizer
```

## Core SDK structure

### `libs/deepagents/deepagents/graph.py`

This is the assembly point. The public entrypoint is `create_deep_agent()`.

Key responsibilities:

- resolve the requested model and matching harness profile
- set a default backend when none is provided
- process caller-provided subagents
- auto-add the `general-purpose` subagent unless disabled by profile
- assemble the main middleware stack
- merge caller middleware into that stack
- add profile middleware, tool exclusions, memory, prompt caching, and human approval
- compose the final system prompt
- call LangChain `create_agent(...)`

Default main-agent stack, in order:

```txt
TodoListMiddleware
SkillsMiddleware, when skills are configured
FilesystemMiddleware
SubAgentMiddleware, when synchronous subagents exist
SummarizationMiddleware
PatchToolCallsMiddleware
AsyncSubAgentMiddleware, when async subagents exist
caller middleware
profile extra middleware
tool-exclusion middleware
AnthropicPromptCachingMiddleware
MemoryMiddleware, when memory is configured
HumanInTheLoopMiddleware, when interrupts or interrupt permissions exist
```

Protected scaffolding:

- `FilesystemMiddleware` backs file tools and permissions
- `SubAgentMiddleware` backs the `task` tool

Profiles are allowed to hide tools or add model-specific behavior, but these two pieces cannot be removed as middleware because they are structural.

### `libs/deepagents/deepagents/middleware/`

Middleware is where most of the harness lives. The important distinction is:

- plain tools are invoked only after the model chooses them
- middleware can change the model request before each call

That means middleware can inject prompt text, add or filter tools, compact context, persist state, enforce permissions, and wrap tool execution.

Important modules:

- `filesystem.py`: file tools, optional `execute`, permissions, large-result offloading, dynamic filesystem prompt
- `subagents.py`: synchronous `task` tool, default general-purpose subagent, compiled subagents
- `async_subagents.py`: remote/background subagents on LangGraph servers
- `skills.py`: loads skill instructions from backend paths and injects relevant skill guidance
- `memory.py`: loads memory files into system prompt and exposes memory-update behavior
- `summarization.py`: context management and compacting
- `permissions.py` and `_fs_interrupt.py`: filesystem allow/deny/interrupt behavior
- `_tool_exclusion.py`: hides excluded tools from model requests
- `rubric.py`: grader/evaluation middleware

### `libs/deepagents/deepagents/backends/`

Backends define where files, memory, skills, and shell execution live.

Core protocol:

- `BackendProtocol`: `ls`, `read`, `write`, `edit`, `grep`, `glob`, uploads/downloads
- `SandboxBackendProtocol`: extends the file backend with `execute`

Implementations:

- `StateBackend`: default, thread-scoped virtual files
- `FilesystemBackend`: maps backend paths onto a real filesystem root
- `StoreBackend`: persistent store-backed files
- `CompositeBackend`: routes path prefixes to different backends
- `LocalShellBackend`: filesystem plus local shell execution
- `BaseSandbox` and partner integrations: sandbox execution implementations
- `LangSmith`, `ContextHub`, partner packages: remote/service-backed backends

This is why the same agent harness can run locally, in a sandbox, against store-backed memory, or with routed virtual mounts.

### `libs/deepagents/deepagents/profiles/`

Profiles are model/provider tuning layers. They are separate from model construction.

`HarnessProfile` can tune:

- base system prompt
- system prompt suffix
- tool description overrides
- excluded tools
- excluded middleware
- extra middleware
- default general-purpose subagent behavior

The lookup order is:

```txt
exact provider:model profile
provider-wide profile
empty default profile
```

This is how the same harness adapts to OpenAI, Anthropic, or another tool-calling model without every app callsite changing.

## Execution flow

```txt
user calls create_deep_agent(...)
  -> resolve model
  -> resolve harness profile
  -> resolve backend
  -> prepare subagents
  -> assemble middleware
  -> compose system prompt
  -> call LangChain create_agent(...)
  -> LangGraph runs the agent loop
       -> middleware prepares prompt/tools/state
       -> model decides response or tool calls
       -> tools execute through middleware/backend
       -> results append to graph state
       -> loop continues until final response
```

## Why it is general purpose

Deep Agents is general purpose because it standardizes the agent operating system rather than the task domain:

- planning is represented by todo middleware
- working memory is represented by graph state and optional persistent stores
- external work happens through pluggable tools
- file/workspace interaction happens through backend protocols
- long-context survival happens through summarization and offloading
- delegation happens through subagents
- domain-specific behavior is loaded through skills, custom tools, custom middleware, and prompts
- model-specific differences are isolated in profiles
- production concerns ride on LangGraph and LangSmith: streaming, persistence, checkpoints, evals, tracing, deployment

The result is a harness shape that can become a research assistant, coding agent, text-to-SQL agent, content agent, WhatsApp agent, or optimizer, mostly by changing configuration and surfaces rather than rebuilding the loop.

## The `better-harness` example

`examples/better-harness` is a research artifact for improving a harness with eval feedback.

Its loop:

1. define editable surfaces
2. run baseline evals
3. create a proposer workspace
4. run an outer Deep Agent that edits only the exposed surfaces
5. materialize a candidate variant
6. run train and holdout evals
7. keep the candidate only if combined pass count improves
8. optionally run scorecard evals for baseline and final

Editable surfaces can be:

- prompt text
- tool files
- skill files
- middleware implementation files
- middleware registration / agent setup files

This example is important because it exposes the harness philosophy directly: a harness is made of surfaces that can be inspected, edited, evaluated, and rolled forward or discarded.

## Deep Agents vs Codex harness

### Short version

Deep Agents is a developer-facing SDK harness for building many kinds of agents. Codex is a product/runtime harness specialized for coding work in a user workspace.

Deep Agents gives you code-level knobs:

- model
- tools
- middleware
- backend
- memory
- skills
- subagents
- profiles
- checkpointers and stores

Codex gives the agent an operating environment:

- workspace and filesystem sandbox
- shell execution policy
- approvals and escalation
- patch/file editing tools
- git safety rules
- skill/plugin/MCP discovery
- user-update conventions
- review and implementation behavior
- UI/app integration

### Shape Comparison

```mermaid
flowchart TD
    A["Developer code"] --> B["Deep Agents SDK"]
    B --> C["create_deep_agent(...)"]
    C --> D["LangChain agent loop"]
    D --> E["LangGraph runtime"]
    B --> F["Developer-provided tools, middleware, backends, profiles"]

    G["User request in Codex"] --> H["Codex product harness"]
    H --> I["System/developer policy"]
    H --> J["Sandbox and approval layer"]
    H --> K["Built-in tools: shell, patch, browser, connectors"]
    H --> L["Skills, plugins, MCP"]
    I --> M["Coding agent behavior"]
    J --> M
    K --> M
    L --> M
```

### Request Flow Difference

```mermaid
flowchart LR
    subgraph DeepAgents["Deep Agents app you build"]
        A1["Your Python code"] --> A2["create_deep_agent"]
        A2 --> A3["Middleware stack"]
        A3 --> A4["Backend"]
        A3 --> A5["Model"]
    end

    subgraph Codex["Codex session"]
        B1["User in Codex UI"] --> B2["Codex harness instructions"]
        B2 --> B3["Tool sandbox and approvals"]
        B3 --> B4["Workspace tools"]
        B2 --> B5["Model"]
    end
```

### Important Nuance

The Deep Agents repo includes `libs/deepagents/deepagents/profiles/harness/_openai_codex.py`. That file registers a Codex model profile for Deep Agents. It appends Codex-style behavior guidance such as autonomous senior-engineer behavior, persistence, parallel tool use, and TODO hygiene.

That means:

- Deep Agents can tune itself for Codex-family models.
- This profile is still inside the Deep Agents SDK.
- It does not recreate the full Codex app harness: sandbox policy, approval UX, patch tooling, plugin lifecycle, and workspace-specific guardrails live outside that profile.

### Practical Difference

Use Deep Agents when you want to build or study an agent harness.

Use Codex when you want a ready coding collaborator operating inside a real workspace with strong editing, shell, git, approval, and UX constraints already provided.

## `openai/codex` concrete comparison

Local clone: `research/codex`

Snapshot inspected: `98845e4`

The `openai/codex` repo is not shaped like a small reusable agent SDK. It is a Rust-first local coding-agent product stack with a CLI/TUI, app-server protocol, thread/session runtime, model client, tool router, sandbox/approval engine, MCP/plugin/skill support, and wrapper packages.

The README describes Codex CLI as a coding agent that runs locally on your computer. The repo structure backs that up:

- `codex-rs/core`: thread, session, model, tool, approval, sandbox, MCP, plugin, skill, and config orchestration
- `codex-rs/tui`: terminal interface
- `codex-rs/app-server` and `app-server-protocol`: desktop/app integration boundary
- `codex-rs/exec`, `execpolicy`, `sandboxing`, `windows-sandbox-rs`: local command execution control
- `codex-rs/apply-patch`: structured code-edit tool
- `codex-rs/protocol`: shared wire/data model for tools, sandbox permissions, events, and config
- `codex-cli`: npm-facing launcher/wrapper
- `sdk`: external programmatic surfaces

### Codex Architecture Flow

```mermaid
flowchart TD
    User["User in CLI, TUI, desktop, or IDE"] --> UI["Codex UI or client"]
    UI --> Server["App server / CLI session boundary"]
    Server --> Thread["ThreadManager / CodexThread"]
    Thread --> Turn["Session run_turn loop"]
    Turn --> Model["Model client / Responses stream"]
    Model --> Decision{"Assistant message or tool call?"}
    Decision -->|assistant message| Store["Record event in thread / rollout state"]
    Decision -->|tool call| Router["ToolRouter + ToolCallRuntime"]
    Router --> Tools["exec_command, apply_patch, MCP, skills, plugins"]
    Tools --> Policy["approval policy + sandbox policy + exec policy"]
    Policy --> Result["tool output"]
    Result --> Turn
```

### Deep Agents Architecture Flow

```mermaid
flowchart TD
    App["Your Python app"] --> Factory["create_deep_agent(...)"]
    Factory --> Defaults["Deep Agents defaults"]
    Factory --> Custom["User-provided model, tools, middleware, backend"]
    Defaults --> Middleware["Middleware stack"]
    Custom --> Middleware
    Middleware --> Agent["LangChain create_agent"]
    Agent --> Runtime["LangGraph runtime"]
    Runtime --> Backend["Backend protocol"]
    Runtime --> State["DeepAgentState / checkpoints / stores"]
    Runtime --> DomainTools["Domain tools and subagents"]
```

### Same Word, Different Level

```mermaid
flowchart LR
    DA["Deep Agents harness"] --> DA2["A framework for assembling agents"]
    DA2 --> DA3["You choose the product, tools, backend, and UX"]

    CX["Codex harness"] --> CX2["A product runtime for coding work"]
    CX2 --> CX3["It already owns workspace, tools, policy, UX, and lifecycle"]
```

### Main Difference

Deep Agents is general-purpose because it lets developers assemble many possible agents from common pieces.

Codex is general-purpose inside the coding domain because it gives an agent a complete operating environment: workspace access, command execution, structured patching, policy enforcement, approvals, persistence, and UI integration.

So the comparison is not:

`Deep Agents agent` vs `Codex agent`

It is closer to:

`agent framework you embed` vs `local coding-agent application you run`

## Files to read first

- `libs/ARCHITECTURE.md`
- `libs/deepagents/deepagents/graph.py`
- `libs/deepagents/deepagents/middleware/__init__.py`
- `libs/deepagents/deepagents/middleware/filesystem.py`
- `libs/deepagents/deepagents/middleware/subagents.py`
- `libs/deepagents/deepagents/backends/protocol.py`
- `libs/deepagents/deepagents/profiles/harness/harness_profiles.py`
- `examples/better-harness/README.md`
- `examples/better-harness/better_harness/core.py`
- `examples/better-harness/better_harness/agent.py`
- `examples/better-harness/examples/deepagents_example.toml`
- `../codex/README.md`
- `../codex/codex-rs/core/src/codex_thread.rs`
- `../codex/codex-rs/core/src/session/turn.rs`
- `../codex/codex-rs/core/src/tools/router.rs`
- `../codex/codex-rs/core/src/exec_policy.rs`
- `../codex/codex-rs/protocol/src/models.rs`
