28 KiB
DeerFlow Backend
DeerFlow is a LangGraph-based AI super agent with sandbox execution, persistent memory, and extensible tool integration. The backend enables AI agents to execute code, browse the web, manage files, delegate tasks to subagents, and retain context across conversations - all in isolated, per-thread environments.
Architecture
┌──────────────────────────────────────┐
│ Nginx (Port 2026) │
│ Unified reverse proxy │
└───────┬──────────────────┬───────────┘
│ │
/api/langgraph/* │ │ /api/* (other)
▼ ▼
┌────────────────────┐ ┌────────────────────────┐
│ LangGraph Server │ │ Gateway API (8001) │
│ (Port 2024) │ │ FastAPI REST │
│ │ │ │
│ ┌────────────────┐ │ │ Models, MCP, Skills, │
│ │ Lead Agent │ │ │ Memory, Uploads, │
│ │ ┌──────────┐ │ │ │ Artifacts │
│ │ │Middleware│ │ │ └────────────────────────┘
│ │ │ Chain │ │ │
│ │ └──────────┘ │ │
│ │ ┌──────────┐ │ │
│ │ │ Tools │ │ │
│ │ └──────────┘ │ │
│ │ ┌──────────┐ │ │
│ │ │Subagents │ │ │
│ │ └──────────┘ │ │
│ └────────────────┘ │
└────────────────────┘
Request Routing (via Nginx):
/api/langgraph/*→ LangGraph Server - agent interactions, threads, streaming/api/*(other) → Gateway API - models, MCP, skills, memory, artifacts, uploads, thread-local cleanup/(non-API) → Frontend - Next.js web interface
Core Components
Lead Agent
The single LangGraph agent (lead_agent) is the runtime entry point, created via make_lead_agent(config). It combines:
- Dynamic model selection with thinking and vision support
- Middleware chain for cross-cutting concerns (9 middlewares)
- Tool system with sandbox, MCP, community, and built-in tools
- Subagent delegation for parallel task execution
- System prompt with skills injection, memory context, and working directory guidance
Middleware Chain
Middlewares execute in strict order, each handling a specific concern:
| # | Middleware | Purpose |
|---|---|---|
| 1 | ThreadDataMiddleware | Creates per-thread isolated directories (workspace, uploads, outputs) |
| 2 | UploadsMiddleware | Injects newly uploaded files into conversation context |
| 3 | SandboxMiddleware | Acquires sandbox environment for code execution |
| 4 | SummarizationMiddleware | Reduces context when approaching token limits (optional) |
| 5 | TodoListMiddleware | Tracks multi-step tasks in plan mode (optional) |
| 6 | TitleMiddleware | Auto-generates conversation titles after first exchange |
| 7 | MemoryMiddleware | Queues conversations for async memory extraction |
| 8 | ViewImageMiddleware | Injects image data for vision-capable models (conditional) |
| 9 | ClarificationMiddleware | Intercepts clarification requests and interrupts execution (must be last) |
Sandbox System
Per-thread isolated execution with virtual path translation:
- Abstract interface:
execute_command,read_file,write_file,list_dir - Providers:
LocalSandboxProvider(filesystem) andAioSandboxProvider(Docker, in community/) - Virtual paths:
/mnt/user-data/{workspace,uploads,outputs}→ thread-specific physical directories - Skills path:
/mnt/skills→deer-flow/skills/directory - Skills loading: Recursively discovers nested
SKILL.mdfiles underskills/{public,custom}and preserves nested container paths - WeKnora retrieval skill:
skills/custom/weknora-knowledge-searchpairs a concise Skill with a stdio MCP server. It retrieves tenant-scoped WeKnora document/FAQ passages through the public API, groups multi-KB calls by embedding model, optionally searches Wiki pages, and can use the local reranker. It does not reject a KB solely because the legacy list API reportschunk_count=0; hybrid-search remains the source of truth. For WeKnora's explicit DeerFlow sharing mode, configureWEKNORA_DEERFLOW_SHARE_TOKEN_FILE: the MCP uses the restricted/api/v1/deerflow/*capability API and can only list/search knowledge bases whose owner enabled sharing; it never receives a user bearer token. Shared document results now contain a credential-freeurlpointer; the source card's “查看原文” dialog calls DeerFlow's authenticated/api/weknora/source-contextproxy, which retrieves the hit plus two adjacent chunks while keeping the share token server-side. Shared mode currently excludes Wiki pages. Otherwise useWEKNORA_API_KEY_FILEfor scoped-key deployments orWEKNORA_BEARER_TOKEN_FILEfor legacy deployments; never put any credential inSKILL.mdor a chat prompt. - File-write safety:
str_replaceserializes read-modify-write per(sandbox.id, path)so isolated sandboxes keep concurrency even when virtual paths match - Tools:
bash,ls,read_file,write_file,str_replace(bashis disabled by default when usingLocalSandboxProvider; useAioSandboxProviderfor isolated shell access)
Subagent System
Async task delegation with concurrent execution:
- Built-in agents:
general-purpose(full toolset) andbash(command specialist, exposed only when shell access is available) - Concurrency: Max 3 subagents per turn, 15-minute timeout
- Execution: Background thread pools with status tracking and SSE events
- Flow: Agent calls
task()tool → executor runs subagent in background → polls for completion → returns result
Memory System
LLM-powered persistent context retention across conversations:
- Automatic extraction: Analyzes conversations for user context, facts, and preferences
- Structured storage: User context (work, personal, top-of-mind), history, and confidence-scored facts
- Debounced updates: Batches updates to minimize LLM calls (configurable wait time)
- System prompt injection: Top facts + context injected into agent prompts
- Storage: JSON file with mtime-based cache invalidation
Tool Ecosystem
| Category | Tools |
|---|---|
| Sandbox | bash, ls, read_file, write_file, str_replace |
| Built-in | present_files, ask_clarification, view_image, task (subagent) |
| Community | Tavily (web search), Jina AI (web fetch), Firecrawl (scraping), DuckDuckGo (image search) |
| MCP | Any Model Context Protocol server (stdio, SSE, HTTP transports) |
| Skills | Domain-specific workflows injected via system prompt |
Gateway API
FastAPI application providing REST endpoints for frontend integration:
| Route | Purpose |
|---|---|
GET /api/models |
List available LLM models |
GET/PUT /api/mcp/config |
Manage MCP server configurations |
GET/PUT /api/skills |
List and manage skills |
POST /api/skills/install |
Install skill from .skill archive |
GET/POST /api/agents |
List visible agents or create a user-owned agent |
GET/PUT/DELETE /api/agents/{id} |
Read, update, or delete an id-addressed agent |
GET/POST/PUT/DELETE /api/roundtable-chains |
Manage user-owned roundtable business chains; each seat may optionally carry position_id for the separate position-collaboration workspace (the original roundtable ignores it) |
GET/PUT /api/business-mapping |
Read global business mappings. Updates to 3Q/6BF/7BF require an administrator; the 8BF public business-chain mapping is maintained exclusively by the trusted lqq account (derived from the authenticated email prefix). |
GET/POST/PUT /api/position-roundtable/sessions |
Standalone position-collaboration sessions: freeze task/intent/chain snapshots, index one direct-agent node per seat, build upstream handoff context (including bounded UTF-8 excerpts of text artifacts), and make archived sessions read-only without invoking the original roundtable coordinator or jobs. Built-in summary/action-plan completion mirrors visible report text into a Markdown artifact when a model omits its final write_file call. POST /sessions/{id}/nodes/{nodeKey}/reject accepts an optional reason, marks the rejected delivery for rework, and invalidates affected downstream deliveries so they are regenerated in order. The same router is also mounted at /api/multi-agent/position-roundtable/* for intranet gateways that only forward the established multi-agent namespace; the compatibility mount is intentionally omitted from OpenAPI. |
GET /api/memory |
Retrieve memory data |
POST /api/memory/reload |
Force memory reload |
GET /api/memory/config |
Memory configuration |
GET /api/memory/status |
Combined config + data |
POST /api/threads/{id}/uploads |
Upload files (auto-converts PDF/PPT/Excel/Word to Markdown, rejects directory paths) |
GET /api/threads/{id}/uploads/list |
List uploaded files |
DELETE /api/threads/{id} |
Delete DeerFlow-managed local thread data after LangGraph thread deletion; unexpected failures are logged server-side and return a generic 500 detail |
GET /api/threads/{id}/artifacts/{path} |
Serve generated artifacts |
POST /api/ai-writing/sample/extract |
Extract text from Word/PDF/Markdown/TXT samples for AI-writing imitation mode |
POST /cop/saveSuperiorTask |
TaskCOP compatibility API (DeerFlow Bearer/session authentication required): create a situation-overview task with a sequential four-digit id (0001, 0002, ...) and return the legacy {state, msg, data} envelope |
POST /api/taskcop/import/tasks |
TaskCOP compatibility API (authenticated): import tasks from a JSON array or tasks/records envelope; every row's supplied id/taskId is authoritative and a duplicate id completely replaces the stored task |
DELETE /cop/tasks/{task_id} |
TaskCOP compatibility API (authenticated): delete one task and its task-scoped situation-report document |
GET /taskAnalyseSearch/cop-task-three-list |
TaskCOP compatibility API (authenticated): paginated task list for the situation-overview sentiment page (content, taskStatus, taskDirection, startTime, endTime) |
GET/POST/PUT /api/task-reports/by-task/{task_id} |
Shared task-scoped situation-report detail. Query, first save, and full update all use the legacy-compatible {state, msg, data:[{id, taskId, createTime, updateTime, sessionId, contentJson, categoryType}]} shape; contentJson is preserved as a JSON string. Saving a non-empty report automatically moves the matching TaskCOP task to status 25 (the legacy list's “已完成” state). |
POST /api/task-reports/import/by-task/{task_id} |
TaskCOP detail import (authenticated): accept a sq-report-mock.json-compatible data/records array and atomically replace the selected task's full report. The path task id overrides task ids inside the file. |
The separately deployed TaskCOP Vue app uses POST /api/v1/auth/login/username directly (its username, userId, or yUserId URL parameter identifies the user), so this compatibility flow does not depend on the retired Consumer login service. It calls the Gateway address configured as b1ConsumerUrl directly; it does not use a Vite /api reverse proxy. An empty TaskCOP store remains empty; task data is introduced through normal creation or the JSON import API. The Gateway CORS middleware always merges the local TaskCOP Vite origins http://localhost:8080 and http://127.0.0.1:8080, including when GATEWAY_CORS_ORIGINS is set for another frontend. scripts/env.sh also includes them in its startup default. For a deployment, set GATEWAY_CORS_ORIGINS to the exact additional frontend origins that are allowed to call the Gateway.
Position-roundtable history supports two modes. A non-empty external_task_id from the frontend route's taskId creates a row with task_scoped = true, so all sessions, nodes and persisted conversation snapshots for that task are shared across users; user_id is then creator audit metadata only. When the field is absent or empty, the original per-user personal-session behavior remains unchanged. Alembic revision 20260808_01 adds task_scoped with the safe default false for historical rows plus the task/update-time index.
AI writing supports a sample-imitation mode: the frontend can upload or paste a sample article, the Gateway extracts text for document files, and the ai_writing graph runs a sample_analyzer node before normal intent/research/outline/draft stages. The resulting style profile is injected into outline and draft prompts with explicit guardrails against copying source sentences, facts, names, or data.
Custom Agents
Custom agents are indexed in the agents database table. The id column is the stable runtime identifier and filesystem directory name (.deer-flow/agents/{id}/SOUL.md); name is display-only, may be Chinese, and may be duplicated. Rows with user_id = NULL are built-in agents, user rows are private unless published = true, and users can only update or delete agents they created.
AI Writing Intranet Retrieval
AI 写作的「知识库检索」会直连 ai_writing.intranet_search_url 内网 ES/知识库接口,并默认跳过代理环境变量和 HTTPS 证书校验,适配自签名或内部 CA 证书的内网服务。需要强校验时可在 config.yaml 里设置 ai_writing.intranet_verify_ssl: true。通用检索里的非内置技能在 ai_writing.researcher_skill_agent: true 时会用真实 agent 运行时执行技能:以 ai-writing-researcher 身份读取该技能的 SKILL.md,并按技能要求通过 bash 运行脚本,弱模型也会在本轮任务里收到明确的读文件和脚本执行指令。
IM Channels
The IM bridge supports Feishu, Slack, and Telegram. Slack and Telegram still use the final runs.wait() response path, while Feishu now streams through runs.stream(["messages-tuple", "values"]) and updates a single in-thread card in place.
For Feishu card updates, DeerFlow stores the running card's message_id per inbound message and patches that same card until the run finishes, preserving the existing OK / DONE reaction flow.
Quick Start
Prerequisites
- Python 3.12+
- uv package manager
- API keys for your chosen LLM provider
Installation
cd deer-flow
# Copy configuration files
cp config.example.yaml config.yaml
# Install backend dependencies
cd backend
make install
Configuration
Edit config.yaml in the project root:
models:
- name: gpt-4o
display_name: GPT-4o
use: langchain_openai:ChatOpenAI
model: gpt-4o
api_key: $OPENAI_API_KEY
supports_thinking: false
supports_vision: true
- name: gpt-5-responses
display_name: GPT-5 (Responses API)
use: langchain_openai:ChatOpenAI
model: gpt-5
api_key: $OPENAI_API_KEY
use_responses_api: true
output_version: responses/v1
supports_vision: true
Set your API keys:
export OPENAI_API_KEY="your-api-key-here"
Running
Full Application (from project root):
make dev # Starts LangGraph + Gateway + Frontend + Nginx
Access at: http://localhost:2026
Backend Only (from backend directory):
# Terminal 1: LangGraph server
make dev
# Terminal 2: Gateway API
make gateway
Direct access: LangGraph at http://localhost:2024, Gateway at http://localhost:8001
Project Structure
backend/
├── src/
│ ├── agents/ # Agent system
│ │ ├── lead_agent/ # Main agent (factory, prompts)
│ │ ├── middlewares/ # 9 middleware components
│ │ ├── memory/ # Memory extraction & storage
│ │ └── thread_state.py # ThreadState schema
│ ├── gateway/ # FastAPI Gateway API
│ │ ├── app.py # Application setup
│ │ └── routers/ # 6 route modules
│ ├── sandbox/ # Sandbox execution
│ │ ├── local/ # Local filesystem provider
│ │ ├── sandbox.py # Abstract interface
│ │ ├── tools.py # bash, ls, read/write/str_replace
│ │ └── middleware.py # Sandbox lifecycle
│ ├── subagents/ # Subagent delegation
│ │ ├── builtins/ # general-purpose, bash agents
│ │ ├── executor.py # Background execution engine
│ │ └── registry.py # Agent registry
│ ├── tools/builtins/ # Built-in tools
│ ├── mcp/ # MCP protocol integration
│ ├── models/ # Model factory
│ ├── skills/ # Skill discovery & loading
│ ├── config/ # Configuration system
│ ├── community/ # Community tools & providers
│ ├── reflection/ # Dynamic module loading
│ └── utils/ # Utilities
├── docs/ # Documentation
├── tests/ # Test suite
├── langgraph.json # LangGraph server configuration
├── pyproject.toml # Python dependencies
├── Makefile # Development commands
└── Dockerfile # Container build
Configuration
Main Configuration (config.yaml)
Place in project root. Config values starting with $ resolve as environment variables.
Key sections:
models- LLM configurations with class paths, API keys, thinking/vision flagstools- Tool definitions with module paths and groupstool_groups- Logical tool groupingssandbox- Execution environment providerskills- Skills directory pathstitle- Auto-title generation settingssummarization- Context summarization settingssubagents- Subagent system (enabled/disabled)memory- Memory system settings (enabled, storage, debounce, facts limits)
Provider note:
models[*].usereferences provider classes by module path (for examplelangchain_openai:ChatOpenAI).- If a provider module is missing, DeerFlow now returns an actionable error with install guidance (for example
uv add langchain-google-genai).
Browser CORS
The Gateway permits the local TaskCOP/Vite development origins
http://localhost:8080 and http://127.0.0.1:8080 with credentials. For a
separate deployed frontend, set GATEWAY_CORS_ORIGINS to its exact origin
(including scheme and port), for example http://47.88.25.99:7010. Explicit
origins are used in addition to the local TaskCOP development origins.
Extensions Configuration (extensions_config.json)
MCP servers and skill states in a single file:
{
"mcpServers": {
"github": {
"enabled": true,
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {"GITHUB_TOKEN": "$GITHUB_TOKEN"}
},
"secure-http": {
"enabled": true,
"type": "http",
"url": "https://api.example.com/mcp",
"oauth": {
"enabled": true,
"token_url": "https://auth.example.com/oauth/token",
"grant_type": "client_credentials",
"client_id": "$MCP_OAUTH_CLIENT_ID",
"client_secret": "$MCP_OAUTH_CLIENT_SECRET"
}
}
},
"skills": {
"pdf-processing": {"enabled": true}
}
}
Environment Variables
DEER_FLOW_CONFIG_PATH- Override config.yaml locationDEER_FLOW_EXTENSIONS_CONFIG_PATH- Override extensions_config.json location- Model API keys:
OPENAI_API_KEY,ANTHROPIC_API_KEY,DEEPSEEK_API_KEY, etc. - Tool API keys:
TAVILY_API_KEY,GITHUB_TOKEN, etc.
LangSmith Tracing
DeerFlow has built-in LangSmith integration for observability. When enabled, all LLM calls, agent runs, tool executions, and middleware processing are traced and visible in the LangSmith dashboard.
Setup:
- Sign up at smith.langchain.com and create a project.
- Add the following to your
.envfile in the project root:
LANGSMITH_TRACING=true
LANGSMITH_ENDPOINT=https://api.smith.langchain.com
LANGSMITH_API_KEY=lsv2_pt_xxxxxxxxxxxxxxxx
LANGSMITH_PROJECT=xxx
Legacy variables: The LANGCHAIN_TRACING_V2, LANGCHAIN_API_KEY, LANGCHAIN_PROJECT, and LANGCHAIN_ENDPOINT variables are also supported for backward compatibility. LANGSMITH_* variables take precedence when both are set.
Langfuse Tracing
DeerFlow also supports Langfuse observability for LangChain-compatible runs.
Add the following to your .env file:
LANGFUSE_TRACING=true
LANGFUSE_PUBLIC_KEY=pk-lf-xxxxxxxxxxxxxxxx
LANGFUSE_SECRET_KEY=sk-lf-xxxxxxxxxxxxxxxx
LANGFUSE_BASE_URL=https://cloud.langfuse.com
If you are using a self-hosted Langfuse deployment, set LANGFUSE_BASE_URL to your Langfuse host.
Dual Provider Behavior
If both LangSmith and Langfuse are enabled, DeerFlow initializes and attaches both callbacks so the same run data is reported to both systems.
If a provider is explicitly enabled but required credentials are missing, or the provider callback cannot be initialized, DeerFlow raises an error when tracing is initialized during model creation instead of silently disabling tracing.
Docker: In docker-compose.yaml, tracing is disabled by default (LANGSMITH_TRACING=false). Set LANGSMITH_TRACING=true and/or LANGFUSE_TRACING=true in your .env, together with the required credentials, to enable tracing in containerized deployments.
Development
Commands
make install # Install dependencies
make dev # Run LangGraph server (port 2024)
make gateway # Run Gateway API (port 8001)
make lint # Run linter (ruff)
make format # Format code (ruff)
Code Style
- Linter/Formatter:
ruff - Line length: 240 characters
- Python: 3.12+ with type hints
- Quotes: Double quotes
- Indentation: 4 spaces
Testing
uv run pytest
Runtime appearance themes
The admin appearance settings are persisted in .deer-flow/system_settings.json.
Besides the built-in themes, an administrator can create up to 12 runtime
themes through PUT /api/system-settings/appearance. Each theme stores a
safe six-digit primary colour, an optional primary-button text_color,
light/dark mode, name, enabled flag and sort order. The frontend derives
the rest of the visual tokens; the optional overrides object only exposes
ten interaction colours (button_hover, navigation_gradient_start,
navigation_gradient_end, selected_background, border, muted,
muted_text, destructive, brand_title, primary_foreground) for
exceptional brand requirements. The two navigation values override the
automatically derived top-bar gradient. A custom theme id
must use the custom-theme- prefix. Disabled or removed custom themes cannot
remain the system default and automatically fall back to light.
Technology Stack
- LangGraph (1.0.6+) - Agent framework and multi-agent orchestration
- LangChain (1.2.3+) - LLM abstractions and tool system
- FastAPI (0.115.0+) - Gateway REST API
- langchain-mcp-adapters - Model Context Protocol support
- agent-sandbox - Sandboxed code execution
- markitdown - Multi-format document conversion
- tavily-python / firecrawl-py - Web search and scraping
Documentation
- Configuration Guide
- Architecture Details
- API Reference
- Offline Docker wheelhouse deployment (ZH)
- File Upload
- Path Examples
- Context Summarization
- Plan Mode
- Backend Run/Stop Guide (ZH)
- Setup Guide
License
See the LICENSE file in the project root.
Contributing
See CONTRIBUTING.md for contribution guidelines.
Position roundtable durable history
The position-roundtable workflow stores database-owned recovery snapshots for
Step-1 intent chat, every position-node chat, the summary/action-plan chats,
unsent composer drafts, and generated text deliverables. LangGraph checkpoints
remain the execution source, but SQL history continues to render after a
checkpoint or sandbox-output volume is replaced. Apply Alembic revision
20260723_05 (or simply upgrade to head) before deploying this version.
Its live intent conversation still uses the shared /api/intent/init and
/api/intent/stream endpoints used by the original multi-agent roundtable.
/api/intent/init creates its empty thread directly through the in-process
thread store/checkpointer, matching multi-agent initialization and avoiding a
deployment-sensitive 127.0.0.1:<gateway-port> loopback. If initial checkpoint
creation fails or is cancelled, bootstrap removes both the partial checkpoint and
its thread metadata row. Thread metadata creation issues no post-commit ORM
read-back on any code path: all returned fields are assigned before commit, and
skipping the extra read keeps a MySQL read/write-splitting endpoint from sending
it to a lagging replica (which fails the request with
Could not refresh instance). Only the
recovery snapshot layer is position-specific. Deployments must expose
either /api/position-roundtable/* or the compatibility mirror
/api/multi-agent/position-roundtable/*; updated clients probe both and only
retry when the first response is the framework/proxy-level 404 Not Found.
Position-roundtable role directory
The position-roundtable work views are persisted separately from organisation
positions/RBAC in position_roles. Gateway startup idempotently inserts the
four built-in ids (testing analysis, organisation test, solution design and
comprehensive test), but only when an id is missing: administrator changes to
the name, order, enabled state or role type are never overwritten.
GET /api/position-roles is consumed by the workbench and PUT /api/position-roles is administrator-only. Exactly one enabled intent role
is required. It is the initial intent-identification workspace and owns the
summary/action-plan closing agents. Built-in rows retain stable ids so
historical chain nodes remain readable; they can be edited or disabled but not
deleted. Custom roles may be freely added or removed.
Roundtable artifact submission status
The task-scoped roundtable workbench exposes a lightweight submission-status API for generated deliverables:
GET /api/roundtable/tasks/{taskId}/artifacts?submitted=truereturns the submitted artifact references for a task (oritems: []).PUT /api/roundtable/tasks/{taskId}/artifacts/submissionrecordssubmitted: trueor cancels it withsubmitted: false.
Both routes use the normal Gateway Authorization: Bearer <token> middleware.
The record is shared by taskId; the authenticated user is retained only in
audit columns. Its server-owned idempotency key is
taskId + sourceKey + path + revision. Repeating the same submitted payload
returns the existing record; cancellation updates its state and timestamp
without deleting the audit row. DeerFlow stores only the artifact reference
(threadId + sandbox path, plus the optional session/node/name/MIME fields),
never the Markdown body and never a task-system delivery. Continue to preview
the file through the existing thread-artifact endpoint.