跪拜 Guibai
← All articles
Frontend · Backend · Interview

DeepSeek Harness Under the Hood: An Agent Runtime Built on Event Sourcing and Plugin Trees

By kymjs张涛 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Most agent frameworks treat session history as a mutable chat array, which breaks on crashes and complicates tool-result attribution. DeepSeek Harness's append-only event log and deriveMessages() projection give developers a recoverable, auditable execution trace—and the plugin tree means every capability, from the LLM adapter to the sandbox, can be swapped without touching the agent loop.

Summary

The runtime centers on a ReactLoopAgent spine that drives a turn-step loop: user input enters an Inbox, a pre-step waterfall decides what the model sees, and each step streams a request through the LLM, executes tool calls, and appends every chunk and result to an append-only session log. That log is the system's event-sourced backbone—deriveMessages() projects the model's visible history from it, not from a separate message list, which makes sessions resumable, forkable, and replayable.

Capabilities like shell, file I/O, and MCP tools follow a three-role Capability Seam pattern: a Service definition, a pluggable Provider, and a Consumer that exposes the capability to the model as a tool. The Cordis framework wires everything together through a shared context (ctx) and a waterfall event chain, letting plugins intercept and rewrite agent pre-steps, tool execution, and request building.

Assembly is handled by Profiles, Bundles, and declarative patch files that insert or replace plugins in the tree, resolving conflicts through a bottom-up load order and top-down priority. The design trades simplicity for extreme configurability: swapping a local bash executor for a sandboxed one requires only a patch, not a rewrite of the tool protocol.

Takeaways
Session is an append-only event log, not a chat history; the model's Message[] is derived from it via deriveMessages() on each step.
The agent loop (ReactLoopAgent) follows a turn-step structure: Inbox → pre-step waterfall → step (LLM stream + tool execution) → turn end.
A pre-step waterfall lets listeners rewrite or reject the messages the model sees before each step, controlling context injection and skill loading.
Capability Seam splits every capability into a Service definition, a Provider implementation, and a Consumer that exposes it to the model, so swapping backends (e.g., local vs. sandboxed bash) doesn't change the tool interface.
Cordis is the DI and event framework underneath: plugins register services on a shared ctx, communicate via waterfall/emit/parallel/serial events, and can be inserted or replaced through declarative patch files.
Profiles bundle multiple Bundles; each Bundle carries a cordis.patch.yml that inserts or replaces plugins in the tree, with bottom-up loading and top-down priority for conflict resolution.
Tool execution runs through a full pipeline: pre-execute waterfall → allow/deny → execute waterfall → tool body → post-execute waterfall → result emit to session, logger, and metrics.
Session persistence supports JSONL/Zstd and SQLite backends, storing each event as a separate line or row so a crash mid-execution doesn't corrupt the entire log.
Conclusions

Making the session log the single source of truth—rather than a parallel message array—solves a class of bugs that plague agent frameworks: tool results attributed to the wrong turn, lost context on crash recovery, and unreplayable trajectories.

The Capability Seam pattern (Definition/Provider/Consumer) is a formalization of what many agent projects do ad-hoc; baking it into the framework means the model never needs to relearn tool names when the backend changes.

Waterfall events are the runtime's primary extension point, not hooks or callbacks. This turns every decision—what the model sees, whether a tool runs, how a request is built—into an overridable chain, which is powerful but demands discipline to avoid ordering surprises.

The Profile/Bundle/Patch assembly layer mirrors how Android and frontend ecosystems handle feature modules, suggesting agent runtimes are converging on the same composition patterns as application frameworks.

DeepSeek Harness optimizes for runtime architecture, not for a specific use case like coding or research; this makes it a building block rather than a product, and the cost is the conceptual overhead required to assemble a working Profile.

Concepts & terms
Agent Loop (ReactLoopAgent)
The core execution driver that runs a turn-step cycle: user input enters an Inbox, a pre-step waterfall decides what the model sees, a step streams an LLM request and executes tool calls, and the loop repeats until the task ends or a policy interrupts.
Session Log (Event Sourcing)
An append-only event log that is the single source of truth for the agent's entire interaction history. The model's Message[] for each request is derived from this log via deriveMessages(), not maintained as a separate chat array.
Capability Seam
A three-role pattern for replaceable capabilities: a Service definition (abstract class owning a ctx key), a Provider (concrete implementation), and a Consumer (typically a tool that exposes the capability to the model). Swapping a Provider does not change the tool interface the model sees.
Cordis
A dependency injection and plugin framework that underpins DeepSeek Harness. Plugins register services on a shared context (ctx) and communicate through event patterns—waterfall (chain of responsibility), emit (broadcast), parallel, and serial.
Waterfall Event
A chain-of-responsibility event pattern where each listener receives the previous listener's output, processes it, and optionally calls next() to pass it on. If a listener does not call next(), the chain short-circuits. Used extensively for agent/pre-step, tools/pre-execute, and agent/request.
Profile / Bundle / Patch
The assembly layer: a Profile is a complete runnable agent configuration; Bundles are distributable configuration layers that group plugins; Patches are declarative YAML files that insert or replace plugins in the Cordis tree, with bottom-up loading and top-down priority for conflict resolution.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗