From Prompt to Run: How DeerFlow Turns Intent into Controlled Work
In DeerFlow, the central engineering question is not how a model generates an answer, but how a user request becomes a run that can be authorized, observed, resumed, and closed correctly. This chapter follows that path through the Gateway, the lead-agent factory, middleware, ThreadState, tools, streaming, and terminal persistence. The result is a control-flow model that makes later capabilities easier to understand: each skill, sandbox, sub-agent, and memory update enters a run at a particular boundary and inherits its ownership rules.
The Gateway makes a request a managed run
The visible application has three main pieces. Nginx is the public entry point; it serves the frontend and routes API traffic. The Next.js frontend provides the conversation surface. The FastAPI Gateway owns the embedded LangGraph-compatible runtime as well as the application APIs for threads, models, skills, tools, files, artifacts, and runs. A provisioner can be added when sandboxes are managed as Kubernetes workloads, but it is not required for the default local or Docker topology.
This arrangement matters because the Gateway is more than a proxy. It is where an incoming request acquires an identity, a thread, an execution policy, and a run record. The public LangGraph-compatible path is translated to the Gateway’s native routes, allowing compatible clients to use the same runtime without requiring a second LangGraph server. The frontend, an embedded Python client, and the terminal workbench can enter through different surfaces, but the work still relies on the same agent and state machinery.
A run begins before the model is called. The Gateway validates the supported request shape and the options that it can honestly implement. Unsupported stream modes or run controls are rejected rather than silently converted into a different behavior. This is a small but important design choice: a compatibility API is useful only when its accepted contract means what the caller thinks it means.
After admission, the run has a lifecycle of its own. A RunManager keeps the active record in memory and can persist serializable run metadata to a backing store. The record carries the thread, assistant, status, model, timestamps, ownership information, cancellation state, and eventual outcome. The worker does not immediately assume that every request can start. It waits for earlier finalization on the same thread, checks cancellation, and crosses the pending-to-running barrier only when the run has a valid place to execute.
The worker then publishes run metadata so a streaming client can associate the work with its run and thread. It installs runtime context containing the thread and run identity, the effective user, configuration, and other server-owned values that middleware and tools need. This context is deliberately different from arbitrary client metadata. A caller cannot safely decide which user bucket, sandbox lease, or internal trace identity the server should use, so those values are resolved or overwritten at the boundary.
The worker also binds the run to its checkpoint and event infrastructure. If a thread uses a persisted checkpointer, the worker verifies that the configured checkpoint mode is compatible with the existing thread before reading or writing state. It can capture a pre-run snapshot for rollback and, where event storage is enabled, a workspace snapshot for later change detection. These checks occur before the model begins changing the thread or its files because recovery is only meaningful if the system knows what the starting point was.
Streaming is separated from execution through a StreamBridge. The agent worker publishes metadata, model messages, tool activity, state snapshots, and custom progress events. An SSE consumer subscribes to those events and can reconnect with a cursor while the bridge retains the relevant history. Heartbeats give the consumer a liveness signal while the run is idle. If a consumer falls behind the retained window, the bridge emits an explicit gap rather than pretending that a partial replay is complete. The client must then reload durable state and continue from the retained tail.
This producer-consumer split solves two different timing problems. The worker should be free to keep executing when a browser briefly disconnects, while, in a configured multi-worker deployment with shared stream infrastructure, a client can observe a run admitted by another Gateway worker. A stream event is therefore a view of execution, not the execution record itself. Checkpoints, run events, and the durable run row have different retention and ownership rules, and the Gateway coordinates them at the end.
The sequence is intentionally recursive. A tool result returns to the model, which may choose another action, so the “model call” is not one step but a loop inside the run. The Gateway’s job is to keep that loop attached to the correct thread, policy, sandbox, stream, and terminal record.
Assembly is a sequence of control gates
Once runtime context is available, DeerFlow assembles the lead agent. The factory resolves the requested model, custom-agent settings, user-scoped skills, tool groups, authorization, deferred tools, memory options, planning mode, and sub-agent limits. Request-level options can override an agent’s defaults where allowed, but a request cannot widen a server-side policy. If a requested model is missing, DeerFlow can fall back to the configured default; if authorization is enabled, the selected model is checked again for actual use and an allowed fallback is sought when necessary.
The same principle applies to tools. DeerFlow first constructs the candidate capability set, then applies authorization before binding the model to those tools. MCP schemas can be held back for deferred discovery, and a routing hint can promote a likely tool without giving it unconditional priority. A skill may later narrow the visible and executable set after activation. The model therefore does not receive an undifferentiated catalog and hope that its prompt will behave. Capability visibility and capability execution are separate decisions.
The system prompt is assembled with the model, but it is not the only source of behavior. The middleware chain surrounds the model call and gives the run its lifecycle. The first layers sanitize user input and remote-content tool results so untrusted text cannot imitate framework-owned tags. Thread-data middleware establishes the workspace and output locations. Upload handling makes user files discoverable. Sandbox middleware acquires or reuses an execution environment. Later layers manage context, memory, skills, tools, receipts, sub-agents, terminal responses, and clarification.
Order matters because each layer changes what the next layer is allowed to see or do. Authorization must be able to remove a tool before the model sees its schema, and the execution check must still run when a call arrives. A sandbox authorization decision must precede acquisition or reuse; otherwise a cached environment could bypass a newly changed role. Durable context must capture delegation and loaded-skill references before summarization can remove the messages that revealed them. The receipt layer sits outside result-changing middleware so a short-circuited or rewritten tool result cannot disappear from the evidence ledger. Clarification stays at the end because asking the user for input must stop sibling tool calls from running first.
This is why describing DeerFlow as “a model plus tools” misses the important behavior. Middleware determines whether a tool call is admitted, how its result is framed, whether its output is truncated or externalized, whether a write is based on a fresh read, whether a loop is capped, and whether an empty final response is retried. These are causal controls. They mediate the model’s decisions before those decisions become state changes or external effects.
ThreadState is more than a message list
The graph’s state extends the ordinary conversation with structured channels. Alongside messages, DeerFlow tracks the sandbox identity, thread-data paths, uploaded files, generated artifacts, todos, goals, viewed-image metadata, promoted deferred tools, delegation entries, loaded skill references, summaries, task notes, and background task projections. These fields are not a dumping ground. Each has a reducer that expresses what should happen when several middleware or tool operations update it in the same graph step.
An artifact list can merge and deduplicate while preserving order. A sandbox update accepts an idempotent repeat but rejects conflicting sandbox identities rather than choosing one silently. A delegation ledger replaces the latest version of the same task while protecting terminal status from being overwritten by an old in-progress update. Loaded skill references are bounded and deduplicated by path, while the body of a skill is not copied into the state channel. These choices turn concurrent updates into explicit lifecycle rules.
The distinction between state and files is equally important. A checkpoint can remember that a sandbox or artifact exists, but the physical workspace is managed by the sandbox and filesystem layer. Files are not automatically rolled back when a conversation is edited or regenerated. Conversely, a branch should not inherit a parent’s live sandbox binding as though the same environment belonged to two threads. The runtime must coordinate these resources separately because a graph state update cannot undo every external side effect.
DeerFlow supports full and delta checkpoint modes for message history. Full mode keeps complete channel values in each checkpoint. Delta mode stores per-step message writes and reconstructs materialized state through a mode-aware accessor, reducing long-run storage growth but imposing compatibility and restart rules. The important operational lesson is not the implementation detail itself: state representation affects what can be read safely, how replay works, and which mutations must be serialized. Persistence is part of the run’s semantics, not merely a backup switch.
Execution, streaming, and finalization are different phases
Inside the graph, the model may produce ordinary text or request a tool. Tool errors are normally converted into structured ToolMessage results so the model can recover instead of the entire graph disappearing on the first exception. Provider failures can become marked assistant fallbacks that the worker maps to an error outcome. A message that merely sounds like an error is not enough to change status; the runtime relies on its own marker rather than trying to infer lifecycle from prose.
The worker streams the visible consequences of each step, but it also records what must survive the stream. RunJournal captures execution events and token information. Sub-agent progress can be written in batches so deep delegated work does not turn every small update into a separate high-cost persistence transaction. Workspace snapshots compare the thread’s relevant directories before and after execution. Internal process-feedback files are excluded so a temporary tool-output cache is not mistaken for a user deliverable.
Artifact delivery is a concrete example of finalization semantics. If a run creates or modifies a regular file under the output area, DeerFlow expects the run to present a matching path through its artifact mechanism. A pre-existing file presented without being produced by the current run does not satisfy that requirement. If changed output is not presented, the run is marked as having incomplete delivery rather than being reported as an uncomplicated success. If the terminal receipt itself cannot be durably recorded, a successful artifact-producing run can also be downgraded because the system can no longer prove its delivery state.
The terminal phase is deliberately ordered. The worker closes the agent stream, flushes journal and sub-agent events, records workspace changes, persists an idempotent delivery receipt, updates completion data, writes any necessary duration metadata, persists the terminal run status, updates thread status, and only then publishes the stream end. The exact cleanup has more edge cases than this summary, especially around cancellation and ownership leases, but the ordering expresses the principle: do not send “finished” while durable evidence and status can still disagree.
Ownership makes the same principle apply across workers. In a multi-worker deployment, a run lease identifies which process may finalize the run. If renewal fails until the lease expires, the local worker cancels execution and stops writing durable completion data; a peer can recover the orphan. A worker that lost ownership must not continue writing receipts, checkpoints, thread status, or completion fields after a peer has become responsible. Remote tool effects already in flight may not be cancellable, which is a boundary rather than a hidden guarantee.
Cancellation therefore has more than one meaning. An interactive user may interrupt a run, ask for rollback, or disconnect under a configured policy. A remote worker may durably request cancellation from the owner. A lease may expire and trigger takeover. These paths converge on a terminal lifecycle, but they do not all produce the same state or restore the same files. In particular, conversation-state rollback cannot promise to undo external API calls or every workspace side effect. DeerFlow keeps those distinctions visible because pretending that “cancel” reverses the world would be more dangerous than returning a partial result.
A DeerFlow run is thus a chain of managed transitions: admission creates ownership, assembly defines capabilities and policy, middleware shapes each model/tool exchange, ThreadState records structured context, the sandbox contains or exposes side effects according to its mode, StreamBridge exposes live progress, and finalization reconciles evidence with durable status. Once that chain is visible, the next architectural question is no longer “what tools does the agent have?” It is “which capability paths can safely enter this lifecycle, and what happens when they outlive the immediate model turn?”