10 KiB
DeerFlow Architecture
This document is the top-level architecture overview for DeerFlow. It explains the "big picture" — how the services, layers, and cross-cutting subsystems fit together — and points to the module-level guides that own the depth:
- Backend depth →
backend/AGENTS.mdandbackend/docs/ARCHITECTURE.md - Frontend depth →
frontend/AGENTS.md
DeerFlow 2.0 is a ground-up rewrite of the original Deep Research framework (see
README.md); it shares no code with v1.
1. What DeerFlow Is
DeerFlow (Deep Exploration and Efficient Research Flow) is an open-source super-agent harness built on LangGraph. A single "lead agent" orchestrates sub-agents, persistent memory, sandboxed code execution, and extensible skills/tools — all isolated per conversation thread. The frontend is a Next.js chat UI; external IM platforms (Feishu, Slack, Telegram, Discord, DingTalk) bridge into the same agent through the Gateway.
2. Service Topology
A single make dev (or Docker stack) runs four cooperating services; Nginx is the only
public entry point.
| Service | Port | Role |
|---|---|---|
| Nginx | 2026 |
Unified reverse proxy — open this in the browser |
| Gateway API | 8001 |
FastAPI REST API + embedded LangGraph-compatible agent runtime |
| Frontend | 3000 |
Next.js web interface |
| Provisioner | 8002 |
Optional — only when sandbox is in provisioner/K8s mode |
Nginx routing (the key entry-point contract):
/api/langgraph/*→ Gateway's LangGraph-compatible runtime (rewritten to native/api/*)/api/*(other) → Gateway REST routers/*(non-API) → Frontend
This lets standard LangGraph SDK clients talk to DeerFlow without a separate LangGraph
server. Both compose files publish nginx as "${BIND_HOST:-127.0.0.1}:${PORT:-2026}:2026"
— loopback by default; the Gateway's 8001 is never published. Any new published port
must carry an explicit bind address (backend/tests/test_compose_default_bind_host.py
pins this for every service in both compose files).
3. Backend: Harness / App Split
The backend is two layers with a strict one-way dependency:
- Harness (
backend/packages/harness/deerflow/, import prefixdeerflow.*) — the publishable agent framework: orchestration, tools, sandbox, models, MCP, skills, memory, config. Everything needed to build and run agents. - App (
backend/app/, import prefixapp.*) — unpublished application code: the FastAPI Gateway and IM channel integrations.
Rule: App imports deerflow, but deerflow never imports app. This boundary is
enforced in CI by backend/tests/test_harness_boundary.py. A thin third package,
deerflow-extension-api (backend/packages/extension-api/), defines the host-independent
extension contract that plugins implement.
There is also an embedded Python client (deerflow.client.DeerFlowClient) used by
scheduled tasks and tests to drive the same run lifecycle programmatically.
Agent runtime path
All run modes (local make dev, Docker, prod) execute the agent through the Gateway via
RunManager + run_agent() + StreamBridge (packages/harness/deerflow/runtime/). The
agent is assembled by make_lead_agent() and wrapped in a middleware chain that runs
before the model call:
- ThreadDataMiddleware — set up
workspace/uploads/outputspaths - UploadsMiddleware — inject uploaded file list
- SandboxMiddleware — acquire sandbox
- SummarizationMiddleware — context reduction (if enabled)
- TitleMiddleware — auto-generate conversation title
- TodoListMiddleware — task tracking (plan mode)
- ViewImageMiddleware — vision-model image handling
- ClarificationMiddleware — handle
ask_clarification
SSE streaming carries both per-chunk messages and bounded values snapshots; with
stream_subgraphs, delegated subagents publish namespaced SSE events (values|<ns>,
LangGraph Platform style) rather than impersonating root frames, so SDK clients don't lose
the parent thread view.
State, tools, sandbox
ThreadStateextends LangGraph'sAgentStatewithsandbox,artifacts,thread_data,title,todos,viewed_images. Each thread gets isolated data dirs underbackend/.deer-flow/threads/{thread_id}/.- Tools come from three sources, merged by
get_available_tools(): built-ins (present_files,ask_clarification,view_image,review_skill_package), configured tools (bash,read_file,write_file,str_replace,ls, web search/fetch), and MCP tools. - Sandbox is an abstract
SandboxProviderwithLocalSandboxProvider(dev, direct execution) andAioSandboxProvider(Docker, production isolation). Agent code executes inside sandbox boundaries with virtual path mapping (/mnt/user-data/...).
4. Frontend: Stateful Chat over LangGraph SDK
Next.js 16 / React 19 / TypeScript / Tailwind v4. Stack: LangGraph SDK (@langchain/langgraph-sdk)
for orchestration + streaming, TanStack Query for server state. Requires Node 22+ and pnpm
10.26.2+.
The frontend is a stateful chat app: users create threads (conversations), send
messages, set thread-scoped /goal completion conditions, and receive streamed responses.
The backend may produce artifacts (files/code), todos, and goal-state updates.
Source layout (frontend/src/):
app/— App Router routes:/workspace/chats/[thread_id](authenticated chat),/workspace/agents/[agent_name](custom agents),/showcase/[thread_id](allowlisted public read-only demos),/api/*route handlers,(auth)/{login,setup,auth/callback}.core/— the business-logic heart. Domains:threads/(creation, streaming, state),api/(LangGraph client singleton),agents/,auth/,artifacts/,channels/,integrations/,memory/,skills/,mcp/,models/,tasks/,todos/,tools/,workspace-changes/,config/,i18n/(en-US, zh-CN), and more.components/—workspace/(chat),landing/,docs/;ui/andai-elements/are registry-generated (Shadcn / Vercel AI SDK) and must not be hand-edited.hooks/,lib/(cn()),content/(MDX),styles/.
Streaming data flow: core/threads/ subscribes to the LangGraph run stream via the
core/api/ client singleton, normalizes SSE events (messages, values, task_*,
artifact deltas) into TanStack-Query-managed thread state that components render. Subtask
progress rides root-namespace task_* custom events (the web frontend does not request
subgraph streaming).
By default the frontend connects through nginx: NEXT_PUBLIC_LANGGRAPH_BASE_URL=/api/langgraph
and NEXT_PUBLIC_BACKEND_BASE_URL= (empty). Leave these unset for the standard make dev
/ Docker flow.
5. Cross-Cutting Subsystems
These span both layers and require reading multiple files to understand:
- Config system — lives at repo root:
config.yaml(models, tools, sandbox, summarization, scheduler) andextensions_config.json(MCP servers + skills). Both are gitignored, generated from the*.example.*templates, and editable at runtime via the Gateway API. Operator-controlled third-partyplugins:live only inconfig.yaml(never the API-writableextensions_config.json) because that list causes code import. - Skills —
skills/public/(committed) andskills/custom/(gitignored); managed integration packs are global at.deer-flow/integrations/skills/{provider}/. Skills are discovered/loaded lazily by the harness;skills/public/skill-reviewer/is a read-only quality reviewer using the harnessreview_skill_packagetool. - Sub-agents — background delegation via
SubagentExecutor(server-sideexecution_id) correlated to providertool_call_idforToolMessage/SSE/lifecycle/persistence. Scheduled tasks reuse the same Gateway run lifecycle (scheduler decides when, not how). - Scheduled tasks — workspace page
/workspace/scheduled-tasks+ a background scheduler gated byconfig.yaml → scheduler.enabled; non-interactive runs dropask_clarificationand client-suppliednon_interactive. - Long-running MCP — a durable
McpTaskService(leased rows, DB as source of truth) keeps remote task IDs/polling out of the agent loop. - Version sources — a release version must match in
backend/pyproject.toml,frontend/package.json, anddeploy/helm/deer-flow/Chart.yaml(version+appVersion); pushing av*tag triggers CI that runsscripts/verify_versions.shand blocks all publishing on drift. SeeRELEASING.md.
6. Security & Isolation Model
- Thread isolation: each conversation has separate data dirs; uploads are validated
against path traversal and staged as
.upload-*.partbefore atomic replace. - Sandbox isolation: production should use the Docker
AioSandboxProvider; local sandbox is dev-only direct execution. - MCP isolation: each MCP server runs in its own process with runtime env-var resolution; servers toggle independently.
- Loopback-by-default ingress: nginx is the only published surface; the Gateway's
8001is container-internal and never published. A bare"${PORT}:2026"bind (0.0.0.0) is rejected by convention and CI. See the Security Notice inREADME.mdbefore any non-loopback deployment.
7. Where to Go Next
- System topology & component depth →
backend/docs/ARCHITECTURE.md - Backend commands, TDD, harness/app boundary, config reload →
backend/AGENTS.md - Frontend commands, source layout, streaming data flow →
frontend/AGENTS.md - Setup & install →
Install.md,CONTRIBUTING.md - Release process →
RELEASING.md - User-facing features & deployment sizing →
README.md