1. Architecture Overview
DeerFlow is a full-stack AI agent platform built on LangGraph, FastAPI, and Next.js. It implements a lead agent orchestration pattern where a central agent delegates to sub-agents, tools, and skills to complete complex multi-step tasks. The platform supports pluggable LLM providers, sandboxed code execution, persistent memory, MCP server integration, a skills framework, and multi-channel IM integrations.
1.1 High-Level Architecture
┌──────────────────────────────────────────────────────────────────┐
│ nginx (:2026) │
│ Reverse proxy — routes /api/* to gateway, LangGraph to :2024 │
├─────────────┬──────────────────┬─────────────────────────────────┤
│ Frontend │ Gateway (:8001) │ LangGraph Server (:2024) │
│ Next.js │ FastAPI custom │ LangGraph runtime (lead_agent) │
│ :3000 │ endpoints │ checkpointer + store │
├─────────────┴──────────────────┴─────────────────────────────────┤
│ deerflow-harness (Python package) │
│ Config │ Models │ Sandbox │ Skills │ Memory │ MCP │ Guardrails │
├──────────────────────────────────────────────────────────────────┤
│ External Services & Infrastructure │
│ LLM APIs │ K8s Provisioner │ IM Channels │ Tracing (LangSmith) │
└──────────────────────────────────────────────────────────────────┘
1.2 Component Inventory
| Component | Technology | Port | Purpose |
|---|---|---|---|
| Frontend | Next.js 16, React 19, pnpm 10 | 3000 | Web UI with chat, artifacts, settings |
| Gateway | FastAPI (uvicorn) | 8001 | Custom API: models, MCP, memory, skills, uploads, agents, channels, runs |
| LangGraph Server | langgraph dev |
2024 | Agent graph execution, checkpointing, state persistence |
| nginx | nginx:alpine | 2026 | Reverse proxy, CORS, unified entry point |
| Provisioner | FastAPI | 8002 | Kubernetes sandbox Pod lifecycle (optional) |
1.3 Agent Architecture
lead_agent (LangGraph StateGraph)
├── Tool nodes (web_search, web_fetch, file ops, bash, image_search)
├── Sub-agents
│ ├── general-purpose — multi-step research and analysis
│ └── bash — shell command execution
├── Skills (15+ built-in, user-extensible)
├── MCP tool nodes (stdio/sse/streamable_http servers)
├── ACP agents (Claude Code, Codex CLI via subprocess)
└── Middleware
├── TitleMiddleware — auto-generate conversation titles
├── SummarizationMiddleware — compress long context
└── MemoryMiddleware — persistent fact extraction
The lead agent is registered in backend/langgraph.json as deerflow.agents:make_lead_agent. All custom agents route through make_lead_agent with an agent_name injected into configurable, which loads a per-agent SOUL.md and config.yaml from the agents/<name>/ directory.
2. System Requirements
2.1 Backend Requirements
| Requirement | Version |
|---|---|
| Python | >= 3.12 |
| Package manager | uv (recommended) |
| OS | Linux, macOS, Windows (WSL recommended for Docker) |
| Node.js | >= 18 (for frontend) |
| pnpm | 10.26.2 |
2.2 Python Dependencies (Core)
From pyproject.toml (deerflow-harness):
| Package | Version Constraint | Purpose |
|---|---|---|
langgraph |
>= 1.0.6, < 1.0.10 | Agent graph runtime |
langgraph-api |
>= 0.7.0, < 0.8.0 | LangGraph Platform compatibility |
langgraph-runtime-inmem |
>= 0.22.1 | In-memory runtime |
langchain |
>= 1.2.3 | LLM framework |
langchain-openai |
>= 1.1.7 | OpenAI provider |
langchain-anthropic |
>= 1.3.4 | Anthropic provider |
langchain-deepseek |
>= 1.0.1 | DeepSeek provider |
langchain-google-genai |
>= 4.2.1 | Google Gemini provider |
pydantic |
>= 2.12.5 | Config models |
kubernetes |
>= 30.0.0 | K8s provisioner client |
agent-client-protocol |
>= 0.4.0 | ACP agent integration |
agent-sandbox |
>= 0.0.19 | Sandbox abstraction |
langchain-mcp-adapters |
>= 0.1.0 | MCP server integration |
langgraph-checkpoint-sqlite |
>= 3.0.3 | SQLite checkpointer |
Optional extras:
| Extra | Package | Purpose |
|---|---|---|
ollama |
langchain-ollama >= 0.3.0 |
Ollama native API support |
pymupdf |
pymupdf4llm >= 0.0.17 |
Better PDF-to-Markdown conversion |
2.3 Frontend Dependencies (Key)
| Package | Version | Purpose |
|---|---|---|
next |
16 | React framework with App Router |
react |
19 | UI library |
@langchain/langgraph-sdk |
latest | LangGraph client SDK |
@xyflow/react |
latest | Flow diagram rendering |
better-auth |
latest | Authentication |
nextra |
latest | Documentation site |
streamdown |
latest | Streaming markdown renderer |
codemirror |
latest | Code editor |
3. Configuration Reference
DeerFlow uses two configuration files plus environment variables:
| File | Format | Purpose |
|---|---|---|
config.yaml |
YAML | Primary configuration: models, tools, sandbox, memory, channels |
extensions_config.json |
JSON | MCP server definitions, skill state overrides |
.env |
dotenv | API keys and secrets |
Important
Copy
config.example.yamltoconfig.yamlbefore first run. The setup wizard (make setuporpython scripts/setup_wizard.py) generates a minimal working config interactively.
3.1 Configuration Resolution
DEER_FLOW_CONFIG_PATHenvironment variable (highest priority)config.yamlin the current working directoryconfig.yamlat the repository root
For extensions: DEER_FLOW_EXTENSIONS_CONFIG_PATH or extensions_config.json in the same search order.
3.2 Config Version
config_version: 7 # Schema version — bump triggers make config-upgrade
Run make config-upgrade after upgrading DeerFlow to merge new fields into your local config.
3.3 Models Configuration
Each model entry defines an LLM provider:
models:
- name: gpt-4o # Internal identifier
display_name: GPT-4o # Shown in UI
use: langchain_openai:ChatOpenAI # Python class path
model: gpt-4o # Provider model name
api_key: $OPENAI_API_KEY # $VAR resolves from env
base_url: https://api.openai.com/v1 # Optional override
request_timeout: 600.0
max_retries: 2
max_tokens: 4096
temperature: 0.7
supports_vision: true
supports_thinking: true
supports_reasoning_effort: true
when_thinking_enabled: # Applied when thinking toggle is on
extra_body:
thinking:
type: enabled
when_thinking_disabled:
extra_body:
thinking:
type: disabled
Supported providers and their use class paths:
| Provider | use Path |
Notes |
|---|---|---|
| OpenAI | langchain_openai:ChatOpenAI |
Standard; also works for OpenRouter |
| Anthropic | langchain_anthropic:ChatAnthropic |
Requires max_tokens |
| DeepSeek | deerflow.models.patched_deepseek:PatchedChatDeepSeek |
Patched for thinking |
| Google Gemini (native) | langchain_google_genai:ChatGoogleGenerativeAI |
Uses gemini_api_key |
| Gemini (OpenAI-compat) | deerflow.models.patched_openai:PatchedChatOpenAI |
Preserves thought_signature |
| Ollama (native) | langchain_ollama:ChatOllama |
Install extra: uv pip install 'deerflow-harness[ollama]' |
| vLLM | deerflow.models.vllm_provider:VllmChatModel |
Self-hosted; Qwen-style reasoning toggle |
| MiniMax | deerflow.models.patched_minimax:PatchedChatMiniMax |
Temperature must be in (0.0, 1.0] |
| Claude Code OAuth | deerflow.models.claude_provider:ClaudeChatModel |
Uses local ~/.claude credentials |
| Codex CLI | deerflow.models.openai_codex_provider:CodexChatModel |
Uses ~/.codex/auth.json |
Warning
For Ollama models, use
langchain_ollama:ChatOllama(native API), not the OpenAI-compatible endpoint. The/v1/chat/completionsendpoint drops or flattens reasoning content.
3.4 Tools Configuration
Tools are organized into groups for access control:
tool_groups:
- name: web
- name: file:read
- name: file:write
- name: bash
tools:
- name: web_search
group: web
use: deerflow.community.ddg_search.tools:web_search_tool # No API key needed
max_results: 5
- name: web_fetch
group: web
use: deerflow.community.jina_ai.tools:web_fetch_tool
timeout: 10
- name: bash
group: bash
use: deerflow.sandbox.tools:bash_tool
Search providers: DuckDuckGo (free, default), Tavily, InfoQuest, Exa, Firecrawl.
Web fetch providers: Jina AI (free, default), Exa, InfoQuest, Firecrawl.
File operation tools: ls, read_file, glob, grep, write_file, str_replace.
Tip
Only one
web_searchand oneweb_fetchprovider can be active at a time. Comment out alternatives before enabling a new one.
3.5 Sandbox Configuration
Three sandbox modes are available:
| Mode | use Path |
Isolation | Requirements |
|---|---|---|---|
| Local (default) | deerflow.sandbox.local:LocalSandboxProvider |
None (host process) | None |
| AIO Container | deerflow.community.aio_sandbox:AioSandboxProvider |
Docker / Apple Container | Docker or Apple Container runtime |
| Provisioner (K8s) | deerflow.community.aio_sandbox:AioSandboxProvider + provisioner_url |
Kubernetes Pod per sandbox | K8s cluster + provisioner service |
# Local sandbox (default)
sandbox:
use: deerflow.sandbox.local:LocalSandboxProvider
allow_host_bash: false # Set true only for trusted single-user
bash_output_max_chars: 20000
read_file_output_max_chars: 50000
ls_output_max_chars: 20000
# mounts:
# - host_path: /home/user/project
# container_path: /mnt/project
# read_only: true
Caution
allow_host_bash: truegives the agent unrestricted shell access on the host. Only enable for fully trusted, single-user local workflows. For multi-user or production use, deploy the AIO Container or Kubernetes sandbox.
3.6 Subagents Configuration
subagents:
timeout_seconds: 900 # Default 15 min
# max_turns: 120 # Global max-turn override
agents:
general-purpose:
timeout_seconds: 1800 # 30 min for complex tasks
max_turns: 160
# model: qwen3:32b # Override model
bash:
timeout_seconds: 300 # 5 min
max_turns: 80
Subagents inherit the lead agent's model by default. Set model to use a cheaper/faster model for specific agents.
3.7 Memory Configuration
memory:
enabled: true
storage_path: memory.json # Relative to backend/
debounce_seconds: 30 # Queue delay before processing
model_name: null # null = default model
max_facts: 100
fact_confidence_threshold: 0.7
injection_enabled: true # Inject into system prompt
max_injection_tokens: 2000
Memory uses a debounced queue: fact extraction requests are batched and processed after debounce_seconds of inactivity. Facts below fact_confidence_threshold are discarded.
3.8 Summarization Configuration
Automatic context compression when conversations approach token limits:
summarization:
enabled: true
model_name: null # Use a lightweight model for cost savings
trigger:
- type: tokens
value: 15564 # Summarize when token count reached
# - type: messages
# value: 50
# - type: fraction
# value: 0.8 # 80% of model max input
keep:
type: messages
value: 10 # Retain 10 most recent messages
trim_tokens_to_summarize: 15564
3.9 Checkpointer Configuration
State persistence for multi-turn conversations:
| Type | Persistence | Use Case |
|---|---|---|
memory |
In-process only (default) | Development, ephemeral |
sqlite |
File-based | Single-process, survives restarts |
postgres |
PostgreSQL | Multi-process, production |
checkpointer:
type: sqlite
connection_string: checkpoints.db
# PostgreSQL:
# checkpointer:
# type: postgres
# connection_string: postgresql://user:password@localhost:5432/deerflow
Note
The checkpointer setting applies to the embedded
DeerFlowClient. The LangGraph Server manages its own persistence separately via server infrastructure.
3.10 IM Channels Configuration
All channels use outbound connections (WebSocket or polling) and require no public IP:
channels:
langgraph_url: http://localhost:2024
gateway_url: http://localhost:8001
session: # Default session settings
assistant_id: lead_agent
context:
thinking_enabled: true
is_plan_mode: false
feishu:
enabled: false
app_id: $FEISHU_APP_ID
app_secret: $FEISHU_APP_SECRET
slack:
enabled: false
bot_token: $SLACK_BOT_TOKEN
app_token: $SLACK_APP_TOKEN # Socket Mode
allowed_users: [] # Empty = allow all
telegram:
enabled: false
bot_token: $TELEGRAM_BOT_TOKEN
wechat:
enabled: false
bot_token: $WECHAT_BOT_TOKEN
ilink_bot_id: $WECHAT_ILINK_BOT_ID
qrcode_login_enabled: true
wecom:
enabled: false
bot_id: $WECOM_BOT_ID
bot_secret: $WECOM_BOT_SECRET
Per-channel and per-user session overrides are supported. Set session.assistant_id to a custom agent name for IM-specific behavior.
3.11 MCP Server Configuration
Define MCP servers in extensions_config.json:
{
"mcpServers": {
"filesystem": {
"enabled": true,
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/files"],
"env": {},
"description": "Filesystem access"
},
"github": {
"enabled": false,
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_TOKEN": "$GITHUB_TOKEN" }
}
}
}
Supported transport types: stdio, sse, streamable_http. Environment variables in env values prefixed with $ resolve from the host environment.
Tool search (deferred loading): When many MCP servers expose large tool sets, enable deferred loading to reduce context:
tool_search:
enabled: true # Tools listed by name only; loaded on demand via tool_search
3.12 Guardrails Configuration
Pre-execution authorization for tool calls:
# Option 1: Built-in allowlist
guardrails:
enabled: true
provider:
use: deerflow.guardrails.builtin:AllowlistProvider
config:
denied_tools: ["bash", "write_file"]
# Option 2: OAP (Open Agent Passport) standard
guardrails:
enabled: true
provider:
use: aport_guardrails.providers.generic:OAPGuardrailProvider
3.13 Circuit Breaker
Prevents retry storms when an LLM provider is down:
circuit_breaker:
failure_threshold: 5 # Consecutive failures before opening
recovery_timeout_sec: 60 # Wait before half-open probe
3.14 Additional Configuration
| Feature | Config Key | Default | Description |
|---|---|---|---|
| Title generation | title.enabled |
true |
Auto-generate conversation titles |
| Title max length | title.max_words / title.max_chars |
6 / 60 | Title constraints |
| Token usage tracking | token_usage.enabled |
false |
Record input/output tokens per call |
| Log level | log_level |
info |
Options: debug, info, warning, error |
| Agents API | agents_api.enabled |
false |
HTTP management of custom agent SOUL/config |
| Skill evolution | skill_evolution.enabled |
false |
Agent can create skills in skills/custom/ |
| Skills path | skills.path |
../skills |
Host path to skills directory |
| Skills container path | skills.container_path |
/mnt/skills |
Sandbox-visible path |
4. Environment Variables Reference
4.1 Backend Environment Variables
| Variable | Default | Purpose |
|---|---|---|
DEER_FLOW_CONFIG_PATH |
(none) | Override config.yaml location |
DEER_FLOW_EXTENSIONS_CONFIG_PATH |
(none) | Override extensions_config.json location |
DEER_FLOW_HOME |
(none) | Base directory for runtime data (.deer-flow/) |
DEER_FLOW_HOST_BASE_DIR |
(none) | Host-side base dir for sandbox volume mounts |
DEER_FLOW_HOST_SKILLS_PATH |
(none) | Host-side skills dir for sandbox mounts |
DEER_FLOW_SANDBOX_HOST |
(none) | Hostname for sandbox access from containers |
GATEWAY_HOST |
0.0.0.0 |
Gateway bind address |
GATEWAY_PORT |
8001 |
Gateway bind port |
CORS_ORIGINS |
http://localhost:3000 |
Comma-separated allowed CORS origins |
4.2 Frontend Environment Variables
| Variable | Default | Purpose |
|---|---|---|
NEXT_PUBLIC_BACKEND_BASE_URL |
(none) | Gateway API URL for client-side requests |
NEXT_PUBLIC_LANGGRAPH_BASE_URL |
(none) | LangGraph server URL |
NEXT_PUBLIC_STATIC_WEBSITE_ONLY |
(none) | Run as static documentation site |
BETTER_AUTH_SECRET |
(none) | Auth signing secret |
BETTER_AUTH_GITHUB_CLIENT_ID |
(none) | GitHub OAuth client ID |
BETTER_AUTH_GITHUB_CLIENT_SECRET |
(none) | GitHub OAuth client secret |
SKIP_ENV_VALIDATION |
(none) | Bypass env validation at build time |
4.3 Provisioner Environment Variables
| Variable | Default | Purpose |
|---|---|---|
K8S_NAMESPACE |
deer-flow |
Kubernetes namespace for sandbox Pods |
SANDBOX_IMAGE |
enterprise-public-cn-beijing.cr.volces.com/... |
Container image for sandbox Pods |
SKILLS_HOST_PATH |
/skills |
Host path for K8s HostPath volume (skills) |
THREADS_HOST_PATH |
/.deer-flow/threads |
Host path for thread data |
SKILLS_PVC_NAME |
(none) | PVC name for skills (replaces hostPath) |
USERDATA_PVC_NAME |
(none) | PVC name for user data (replaces hostPath) |
KUBECONFIG_PATH |
/root/.kube/config |
Path to kubeconfig inside container |
NODE_HOST |
host.docker.internal |
Hostname to reach K8s NodePort services |
K8S_API_SERVER |
(none) | Override K8s API server URL |
4.4 LLM Provider API Keys
| Variable | Provider |
|---|---|
OPENAI_API_KEY |
OpenAI |
ANTHROPIC_API_KEY |
Anthropic |
DEEPSEEK_API_KEY |
DeepSeek |
GEMINI_API_KEY |
Google Gemini |
OPENROUTER_API_KEY |
OpenRouter |
VLLM_API_KEY |
vLLM (self-hosted) |
TAVILY_API_KEY |
Tavily search |
EXA_API_KEY |
Exa search |
FIRECRAWL_API_KEY |
Firecrawl search |
MINIMAX_API_KEY |
MiniMax |
4.5 IM Channel Secrets
| Variable | Channel |
|---|---|
FEISHU_APP_ID / FEISHU_APP_SECRET |
Feishu/Lark |
SLACK_BOT_TOKEN / SLACK_APP_TOKEN |
Slack (Socket Mode) |
TELEGRAM_BOT_TOKEN |
Telegram |
WECHAT_BOT_TOKEN / WECHAT_ILINK_BOT_ID |
|
WECOM_BOT_ID / WECOM_BOT_SECRET |
WeCom |
4.6 Observability
| Variable | Provider |
|---|---|
LANGSMITH_API_KEY (or LANGCHAIN_API_KEY) |
LangSmith tracing |
LANGFUSE_HOST / LANGFUSE_PUBLIC_KEY / LANGFUSE_SECRET_KEY |
Langfuse tracing |
5. Deployment & Scaling
5.1 Local Development (make commands)
# First-time setup
make setup # Interactive wizard: LLM provider, search, sandbox
# Start services
cd backend && uv sync && PYTHONPATH=. uv run uvicorn app.gateway.app:app \
--host 0.0.0.0 --port 8001 --reload
cd backend && uv run langgraph dev --no-browser --host 0.0.0.0 --port 2024
# Frontend
cd frontend && pnpm install && pnpm run dev
5.2 Docker Compose Development
# Set project root for volume mounts
export DEER_FLOW_ROOT=$(pwd)
# Start all services
docker compose -f docker/docker-compose-dev.yaml up --build
# Access at http://localhost:2026
Services started:
| Service | Container Name | Exposed Port | Health Check |
|---|---|---|---|
| nginx | deer-flow-nginx | 2026 | — |
| frontend | deer-flow-frontend | (internal 3000) | — |
| gateway | deer-flow-gateway | (internal 8001) | — |
| langgraph | deer-flow-langgraph | (internal 2024) | — |
| provisioner | deer-flow-provisioner | (internal 8002) | curl http://localhost:8002/health |
Docker networking: All services communicate on a bridge network (192.168.200.0/24). For inter-container references, use service names (gateway:8001, langgraph:2024).
Important
On Docker Compose, set
DEER_FLOW_ROOTto the absolute path of the project root before starting. Volume mounts for skills, config, and thread data depend on this variable.
5.3 Docker Volume Strategy
Named volumes preserve Python virtual environments across restarts:
| Volume | Purpose |
|---|---|
gateway-venv |
Gateway .venv (prevents host mount shadowing) |
langgraph-venv |
LangGraph server .venv |
gateway-uv-cache |
uv cache (avoids symlink failures on macOS) |
langgraph-uv-cache |
uv cache for LangGraph |
5.4 Kubernetes Sandbox Provisioner
For production sandbox isolation, the provisioner creates per-sandbox Pods:
┌────────────┐ HTTP ┌─────────────┐ K8s API ┌──────────────┐
│ backend │ ──────▸ │ provisioner │ ────────▸ │ K8s cluster │
│ │ │ :8002 │ │ │
└────────────┘ └─────────────┘ └──────┬───────┘
│ creates
┌─────────────┐ ┌───────▼──────┐
│ backend │ ───────▸ │ sandbox Pods │
│ │ NodePort │ │
└─────────────┘ └──────────────┘
Provisioner API:
| Method | Path | Purpose |
|---|---|---|
POST |
/api/sandboxes |
Create sandbox Pod + Service |
DELETE |
/api/sandboxes/{sandbox_id} |
Destroy sandbox |
GET |
/api/sandboxes/{sandbox_id} |
Get sandbox status & URL |
GET |
/api/sandboxes |
List all sandboxes |
GET |
/health |
Health check |
For production, use PVC-backed storage instead of hostPath:
environment:
- SKILLS_PVC_NAME=deer-flow-skills-pvc
- USERDATA_PVC_NAME=deer-flow-userdata-pvc
5.5 nginx Routing
nginx handles all external traffic on port 2026:
/api/*requests → gateway (:8001)- LangGraph requests → LangGraph server (:2024), controlled by
LANGGRAPH_UPSTREAMandLANGGRAPH_REWRITEenv vars - All other requests → frontend (:3000)
- CORS is handled at the nginx layer (not FastAPI middleware)
6. Operations
6.1 Gateway API Endpoints
The Gateway exposes 13 router groups:
| Router | Mount Path | Tag | Purpose |
|---|---|---|---|
models |
/api/models |
models | Query available LLM models and configs |
mcp |
/api/mcp |
mcp | MCP server configuration management |
memory |
/api/memory |
memory | Global memory data access |
skills |
/api/skills |
skills | Skills listing and enable/disable |
artifacts |
/api/threads/{thread_id}/artifacts |
artifacts | Thread file artifacts |
uploads |
/api/threads/{thread_id}/uploads |
uploads | File upload management |
threads |
/api/threads/{thread_id} |
threads | Thread-local filesystem cleanup |
agents |
/api/agents |
agents | Custom agent CRUD |
suggestions |
/api/threads/{thread_id}/suggestions |
suggestions | Follow-up question generation |
channels |
/api/channels |
channels | IM channel status and management |
assistants_compat |
(LangGraph stub) | assistants-compat | LangGraph Platform assistants compatibility |
thread_runs |
(LangGraph compat) | runs | Thread-scoped run lifecycle |
runs |
(LangGraph compat) | runs | Stateless runs (stream/wait without thread) |
Health endpoint: GET /health returns {"status": "healthy", "service": "deer-flow-gateway"}.
API documentation: Available at /docs (Swagger UI) and /redoc (ReDoc) when the gateway is running.
6.2 Runtime Singletons
The gateway initializes four singletons during startup via the langgraph_runtime context manager:
| Singleton | Class | Purpose |
|---|---|---|
stream_bridge |
StreamBridge |
SSE event distribution to clients |
checkpointer |
(async provider) | State persistence |
store |
(async provider) | Thread metadata persistence |
run_manager |
RunManager |
Run lifecycle, conflict detection |
If any singleton is missing at request time, the corresponding getter raises HTTP 503.
6.3 Run Lifecycle
Client POST /runs/stream (or /threads/{id}/runs/stream)
→ RunManager.create_or_reject() # 409 on conflict
→ resolve_agent_factory() # Always make_lead_agent
→ build_run_config() # Thread ID, agent_name, context overrides
→ asyncio.create_task(run_agent()) # Background execution
→ SSE consumer yields frames # event: data: id: format
→ On disconnect:
cancel mode → abort background task
continue mode → task runs, events discarded
Context overrides in run requests: The context field carries DeerFlow-specific keys:
| Key | Type | Purpose |
|---|---|---|
model_name |
string | Override LLM model for this run |
mode |
string | Agent mode |
thinking_enabled |
bool | Toggle thinking/reasoning |
reasoning_effort |
string | Reasoning effort level |
is_plan_mode |
bool | Enable plan mode with todo lists |
subagent_enabled |
bool | Allow sub-agent delegation |
max_concurrent_subagents |
int | Concurrency limit |
agent_name |
string | Custom agent selection |
is_bootstrap |
bool | Bootstrap mode flag |
6.4 Filesystem Layout
backend/.deer-flow/ # DEER_FLOW_HOME
├── memory.json # Global memory store
├── USER.md # Global user profile
├── agents/ # Custom agents
│ └── {agent-name}/
│ ├── config.yaml # Agent config (name, model, skills)
│ ├── SOUL.md # Agent personality/values
│ └── memory.json # Per-agent memory
├── threads/
│ └── {thread-id}/
│ └── user-data/
│ ├── work/ # Sandbox working directory
│ ├── uploads/ # User-uploaded files
│ └── outputs/ # Agent-generated artifacts
└── checkpoints.db # SQLite checkpointer (if configured)
Thread IDs are validated against pattern ^[A-Za-z0-9_\-]+$. Path traversal is explicitly blocked with a 403 response.
6.5 Observability & Tracing
DeerFlow supports two tracing providers that can run simultaneously:
| Provider | Required Variables | Auto-Detection |
|---|---|---|
| LangSmith | LANGSMITH_API_KEY (or LANGCHAIN_API_KEY) |
Enabled when key is set |
| Langfuse | LANGFUSE_HOST, LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY |
Enabled when all three are set |
Configure via config.yaml:
tracing:
langsmith:
enabled: true
langfuse:
enabled: true
Validation runs at startup — missing keys for enabled providers raise ValueError with specific guidance.
6.6 Logging
log_level: info # debug | info | warning | error
Logs use format: %(asctime)s - %(name)s - %(levelname)s - %(message)s. Gateway and provisioner each configure independent logging.
7. Security
7.1 Sandbox Isolation
| Level | Isolation | Risk |
|---|---|---|
| Local | None — runs as the host process | Agent has full host access when allow_host_bash: true |
| AIO Container | Docker container per sandbox | Container escape = host compromise |
| K8s Provisioner | Dedicated Pod per sandbox | Best isolation; use PVCs and NetworkPolicy |
Caution
LocalSandboxProviderwithallow_host_bash: trueis not a security boundary. The agent can execute arbitrary commands on the host. Use only in fully trusted, single-user environments.
7.2 Path Traversal Protection
The Paths.resolve_virtual_path() method validates all sandbox file access:
- Paths must start with a recognized prefix (
/mnt/user-data/,/mnt/skills/) - Path traversal (
../) is detected and rejected with HTTP 403 - Thread IDs are validated against
^[A-Za-z0-9_\-]+$
7.3 Authentication
- Frontend:
better-authwith optional GitHub OAuth (BETTER_AUTH_GITHUB_CLIENT_ID) - Gateway API: No built-in auth layer — CORS handled by nginx. The
agents_apiis disabled by default - IM Channels: Each channel has its own auth (bot tokens, app secrets).
allowed_userslists restrict access
7.4 Agent API Security
The agents_api.enabled flag (default: false) controls HTTP access to custom agent management. When enabled, it exposes read/write access to SOUL.md, config.yaml, and USER.md for all agents.
Warning
Only enable
agents_apiwhen the gateway is behind a trusted, authenticated admin boundary. The API has no built-in auth — anyone who can reach the gateway can modify agent configurations.
7.5 ACP Agent Permissions
ACP agents (Claude Code, Codex CLI) run as subprocesses. auto_approve_permissions: false (default) denies all permission requests. Set to true only for trusted agents.
7.6 Guardrails
When guardrails.enabled: true, every tool call passes through the configured provider before execution. The built-in AllowlistProvider supports denied_tools to block specific tools (e.g., bash, write_file). External OAP-compliant providers enable policy-based authorization.
8. Custom Agents
8.1 Creating a Custom Agent
Create a directory under agents/ (inside DEER_FLOW_HOME):
agents/my-agent/
├── config.yaml # Required
└── SOUL.md # Optional personality/values prompt
config.yaml:
name: my-agent
description: A specialized agent for data analysis
model: gpt-4o # Override default model (optional)
tool_groups: # Restrict available tool groups (optional)
- web
- file:read
skills: # Restrict loaded skills (optional)
- crawl_and_report
# null or omitted = all skills; [] = no skills
SOUL.md: Injected into the lead agent's system prompt when agent_name matches. Define personality, values, and behavioral guardrails.
8.2 Agent Name Rules
- Pattern:
^[A-Za-z0-9-]+$(letters, digits, hyphens only) - Normalized to lowercase with underscores replaced by hyphens
- Invalid names raise
ValueError
8.3 Per-Channel Agent Routing
IM channels can specify a custom agent via session.assistant_id. Per-user overrides are supported:
channels:
wechat:
session:
assistant_id: mobile-agent
users:
"123456789":
assistant_id: vip-agent
9. Skills System
9.1 Overview
Skills are structured markdown files in skills/ that the lead agent can invoke. They define reusable workflows (e.g., "crawl and generate report", "create podcast script").
- Built-in skills:
skills/public/(15+ included) - Custom skills:
skills/custom/(user-created) - Skill evolution: When
skill_evolution.enabled: true, the agent can autonomously create and improve skills inskills/custom/
9.2 Skill Access Control
Per-agent skill restrictions in agents/<name>/config.yaml:
skills value |
Behavior |
|---|---|
null / omitted |
Load all globally enabled skills |
[] (empty list) |
Disable all skills |
["skill-name"] |
Load only specified skills |
Skill enable/disable state can also be managed via extensions_config.json under the skills key and through the /api/skills gateway endpoint.
10. Troubleshooting
10.1 Common Errors
| Error | Cause | Resolution |
|---|---|---|
Failed to load configuration during gateway startup |
Missing or invalid config.yaml |
Run make setup or copy config.example.yaml |
Config file specified by DEER_FLOW_CONFIG_PATH not found |
DEER_FLOW_CONFIG_PATH points to nonexistent file |
Verify the path; unset to use default resolution |
Model {name} not found in config |
Requested model not in models: list |
Add model entry to config.yaml |
Model {name} does not support thinking |
supports_thinking not set |
Add supports_thinking: true to model config |
Environment variable {VAR} not found for config value |
$VAR in config but not in env |
Set the variable in .env or shell |
Extensions config file is not valid JSON |
Malformed extensions_config.json |
Validate JSON syntax |
LangSmith tracing enabled but API key not set |
Tracing enabled without credentials | Set LANGSMITH_API_KEY or disable tracing |
Stream bridge not available (503) |
Runtime not initialized | Check gateway startup logs for initialization errors |
Run manager not available (503) |
Runtime not initialized | Same as above |
Invalid assistant_id (ValueError) |
Special characters in agent name | Use only letters, digits, and hyphens |
Agent directory not found |
Custom agent path missing | Create agents/<name>/ with config.yaml |
Access denied: path traversal detected (403) |
../ in file path |
Use absolute virtual paths without traversal |
Codex CLI credential not found |
Missing ~/.codex/auth.json |
Run codex auth to authenticate |
10.2 Docker Troubleshooting
| Issue | Resolution |
|---|---|
uv sync fails on startup |
Container recreates .venv and retries once automatically. If persistent, run docker compose down -v to clear named volumes |
| Frontend hot-reload not working | Ensure WATCHPACK_POLLING=true is set (default in compose file) |
| Gateway can't reach LangGraph | Verify both are on deer-flow-dev network; use service name langgraph:2024 |
| Provisioner health check failing | Ensure kubeconfig is mounted and K8s cluster is reachable |
macOS uv symlink failures |
Named volumes (gateway-uv-cache) avoid this — do not bind-mount the uv cache |
10.3 Debugging
Interactive debug mode with breakpoints:
cd backend && PYTHONPATH=. uv run python debug.py
This starts a REPL-style session with the lead agent. Configure model and thread ID in debug.py.
11. Developer Internals
11.1 Module Reference
Package structure:
backend/
├── app/gateway/ # FastAPI gateway application
│ ├── app.py # App factory, lifespan, router mounting
│ ├── config.py # GatewayConfig (host, port, CORS)
│ ├── deps.py # Runtime singletons (StreamBridge, RunManager, etc.)
│ ├── services.py # Run lifecycle, SSE formatting, input normalization
│ ├── path_utils.py # Virtual path resolution
│ └── routers/ # 13 router modules
├── packages/harness/deerflow/
│ ├── agents/ # Lead agent, subagents, checkpointer
│ ├── config/ # 22 config modules (Pydantic models)
│ ├── models/ # LLM provider wrappers
│ │ ├── factory.py # create_chat_model() — model instantiation
│ │ ├── claude_provider.py # ClaudeChatModel (OAuth)
│ │ ├── openai_codex_provider.py # CodexChatModel
│ │ ├── patched_deepseek.py # PatchedChatDeepSeek
│ │ ├── patched_minimax.py # PatchedChatMiniMax
│ │ ├── patched_openai.py # PatchedChatOpenAI (thought_signature)
│ │ ├── vllm_provider.py # VllmChatModel
│ │ └── credential_loader.py # Claude Code + Codex CLI auth loading
│ ├── community/ # Search/fetch provider implementations
│ ├── guardrails/ # AllowlistProvider, guardrail interface
│ ├── mcp/ # MCP tool initialization
│ ├── runtime/ # StreamBridge, RunManager, RunRecord, RunStatus
│ ├── sandbox/ # LocalSandboxProvider, tool implementations
│ └── utils/ # file_conversion, network, readability
├── debug.py # Interactive agent debugger
├── langgraph.json # LangGraph server configuration
└── ruff.toml # Linter config (line-length: 240, py312)
11.2 Key Classes
| Class | Module | Purpose |
|---|---|---|
AppConfig |
config/app_config.py |
Central config — models, tools, sandbox, all subsystems |
ExtensionsConfig |
config/extensions_config.py |
MCP servers, skill state |
GatewayConfig |
gateway/config.py |
Gateway host/port/CORS |
Paths |
config/paths.py |
20+ path resolution methods (thread dirs, virtual paths) |
SandboxConfig |
config/sandbox_config.py |
Sandbox provider + mounts + truncation limits |
SubagentsAppConfig |
config/subagents_config.py |
Per-agent timeout/model/max_turns overrides |
StreamBridge |
runtime/ |
Per-run SSE event distribution |
RunManager |
runtime/ |
Run lifecycle, conflict detection (409 on concurrent) |
RunRecord |
runtime/ |
Run state: pending → running → completed/cancelled |
AgentConfig |
config/agents_config.py |
Custom agent definition (name, model, skills, tool_groups) |
ACPAgentConfig |
config/acp_config.py |
External ACP agent subprocess config |
MemoryConfig |
config/memory_config.py |
Memory system settings |
SummarizationConfig |
config/summarization_config.py |
Context compression triggers and retention |
CircuitBreakerConfig |
config/app_config.py |
LLM call failure circuit breaker |
TracingConfig |
config/tracing_config.py |
LangSmith + Langfuse tracing |
11.3 Configuration Loading Chain
AppConfig.from_file(config_path)
→ resolve_config_path() # DEER_FLOW_CONFIG_PATH → cwd → repo root
→ yaml.safe_load()
→ resolve_env_variables() # Recursive $VAR substitution
→ Load subsystem configs:
load_memory_config_from_dict()
load_summarization_config_from_dict()
load_title_config_from_dict()
load_subagents_config_from_dict()
load_checkpointer_config_from_dict()
load_guardrails_config_from_dict()
load_acp_config_from_dict()
load_agents_api_config_from_dict()
load_stream_bridge_config_from_dict()
load_tool_search_config_from_dict()
Environment variable resolution is recursive: any string value starting with $ is replaced with the corresponding environment variable. Missing variables raise ValueError.
11.4 Model Factory
create_chat_model(name, thinking_enabled) in models/factory.py:
- Looks up model name in
AppConfig.models - Resolves the
useclass path viaresolve_variable() - Applies
when_thinking_enabledorwhen_thinking_disabledoverrides - Instantiates the LangChain
BaseChatModelsubclass
11.5 Credential Loading (Zero-Config Auth)
Two zero-config auth providers use local CLI credentials:
| Provider | Credential Source | Env Override |
|---|---|---|
| Claude Code OAuth | ~/.claude/credentials.json |
CLAUDE_CODE_CREDENTIALS_PATH, CLAUDE_CODE_OAUTH_TOKEN, ANTHROPIC_AUTH_TOKEN |
| Codex CLI | ~/.codex/auth.json |
CODEX_AUTH_PATH |
ClaudeCodeCredential tracks token expiry via is_expired(). Docker containers mount ~/.claude and ~/.codex read-only for auto-auth.
11.6 Code Conventions
| Convention | Value |
|---|---|
| Python version | 3.12+ |
| Line length | 240 (ruff) |
| Lint rules | E, F, I, UP (ruff) |
| Import sorting | isort, first-party: deerflow, app |
| Quote style | Double |
| Indent style | Spaces |
| Config models | Pydantic v2 BaseModel |
| Async | asyncio throughout (gateway, runtime, providers) |
11.7 Testing
- Backend linting:
ruff checkwithruff.tomlconfiguration - Frontend: pnpm-based test and lint commands
- Unit tests: Located alongside source in standard pytest layout
- API tests: Reference in
frontend/tests/unit/core/api/ - Debug mode:
backend/debug.pyfor interactive agent testing with breakpoints
11.8 Known Issues (from GitHub)
| Issue | Description | Status |
|---|---|---|
| #2293 | Error 400 during normal operation | Open |
| #2302 | Event loop closed unexpectedly | Open |
| #2255 | Duplicate tool calls generated | Open |
| #2203 | JSON parse errors in responses | Open |
| #2262 | File upload failures | Open |
| #2303 | Conversation switching interrupts running tasks | Open |
| #2304 | Loop detection triggering incorrectly | Open |
| #2297 | Docker container cannot access host ports | Open |
12. Quick Reference
12.1 Key Files
| File | Purpose |
|---|---|
config.yaml |
Primary config (models, tools, sandbox, memory, channels) |
config.example.yaml |
Full config reference with all options documented |
extensions_config.json |
MCP server definitions |
.env |
API keys and secrets |
backend/langgraph.json |
LangGraph server config (graph entry point, checkpointer) |
backend/ruff.toml |
Python linter configuration |
docker/docker-compose-dev.yaml |
Docker Compose development environment |
docker/nginx/nginx.conf |
nginx reverse proxy configuration (template) |
12.2 Common Commands
| Task | Command |
|---|---|
| First-time setup | make setup |
| Start gateway | cd backend && PYTHONPATH=. uv run uvicorn app.gateway.app:app --host 0.0.0.0 --port 8001 --reload |
| Start LangGraph | cd backend && uv run langgraph dev --no-browser --host 0.0.0.0 --port 2024 |
| Start frontend | cd frontend && pnpm run dev |
| Docker Compose up | docker compose -f docker/docker-compose-dev.yaml up --build |
| Config upgrade | make config-upgrade |
| Debug agent | cd backend && PYTHONPATH=. uv run python debug.py |
| Install Ollama extra | cd backend && uv pip install 'deerflow-harness[ollama]' |
12.3 Port Map
| Port | Service | Protocol |
|---|---|---|
| 2024 | LangGraph Server | HTTP/SSE |
| 2026 | nginx (unified entry) | HTTP |
| 3000 | Frontend (Next.js dev) | HTTP |
| 8001 | Gateway API | HTTP |
| 8002 | Sandbox Provisioner | HTTP |
| 11434 | Ollama (if local) | HTTP |