From 02111ab5b8f8302c32439a9833a4ca596376dded Mon Sep 17 00:00:00 2001 From: fengxiangdebaozi <10845145+fengxiangdebaozi@user.noreply.gitee.com> Date: Wed, 13 May 2026 20:22:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E6=A1=A3=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- offline-backend-20260512/backend/CLAUDE.md | 1136 ++++++++--------- offline-backend-20260512/backend/README.md | 840 ++++++------ .../backend/docs/houduankai.md | 825 ++++++++++++ 3 files changed, 1813 insertions(+), 988 deletions(-) create mode 100644 offline-backend-20260512/backend/docs/houduankai.md diff --git a/offline-backend-20260512/backend/CLAUDE.md b/offline-backend-20260512/backend/CLAUDE.md index 059d594d..c269035e 100644 --- a/offline-backend-20260512/backend/CLAUDE.md +++ b/offline-backend-20260512/backend/CLAUDE.md @@ -1,571 +1,571 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -DeerFlow is a LangGraph-based AI super agent system with a full-stack architecture. The backend provides a "super agent" with sandbox execution, persistent memory, subagent delegation, and extensible tool integration - all operating in per-thread isolated environments. - -**Architecture**: -- **Gateway API** (port 8001): REST API plus embedded LangGraph-compatible agent runtime -- **Frontend** (port 3000): Next.js web interface -- **Nginx** (port 2026): Unified reverse proxy entry point -- **Provisioner** (port 8002, optional in Docker dev): Started only when sandbox is configured for provisioner/Kubernetes mode - -**Runtime**: -- `make dev`, Docker dev, and production all run the agent runtime in Gateway via `RunManager` + `run_agent()` + `StreamBridge` (`packages/harness/deerflow/runtime/`). Nginx exposes that runtime at `/api/langgraph/*` and rewrites it to Gateway's native `/api/*` routers. - -**Project Structure**: -``` -deer-flow/ -├── Makefile # Root commands (check, install, dev, stop) -├── config.yaml # Main application configuration -├── extensions_config.json # MCP servers and skills configuration -├── backend/ # Backend application (this directory) -│ ├── Makefile # Backend-only commands (dev, gateway, lint) -│ ├── langgraph.json # LangGraph Studio graph configuration -│ ├── packages/ -│ │ └── harness/ # deerflow-harness package (import: deerflow.*) -│ │ ├── pyproject.toml -│ │ └── deerflow/ -│ │ ├── agents/ # LangGraph agent system -│ │ │ ├── lead_agent/ # Main agent (factory + system prompt) -│ │ │ ├── middlewares/ # 10 middleware components -│ │ │ ├── memory/ # Memory extraction, queue, prompts -│ │ │ └── thread_state.py # ThreadState schema -│ │ ├── sandbox/ # Sandbox execution system -│ │ │ ├── local/ # Local filesystem provider -│ │ │ ├── sandbox.py # Abstract Sandbox interface -│ │ │ ├── tools.py # bash, ls, read/write/str_replace -│ │ │ └── middleware.py # Sandbox lifecycle management -│ │ ├── subagents/ # Subagent delegation system -│ │ │ ├── builtins/ # general-purpose, bash agents -│ │ │ ├── executor.py # Background execution engine -│ │ │ └── registry.py # Agent registry -│ │ ├── tools/builtins/ # Built-in tools (present_files, ask_clarification, view_image) -│ │ ├── mcp/ # MCP integration (tools, cache, client) -│ │ ├── models/ # Model factory with thinking/vision support -│ │ ├── skills/ # Skills discovery, loading, parsing -│ │ ├── config/ # Configuration system (app, model, sandbox, tool, etc.) -│ │ ├── community/ # Community tools (tavily, jina_ai, firecrawl, image_search, aio_sandbox) -│ │ ├── reflection/ # Dynamic module loading (resolve_variable, resolve_class) -│ │ ├── utils/ # Utilities (network, readability) -│ │ └── client.py # Embedded Python client (DeerFlowClient) -│ ├── app/ # Application layer (import: app.*) -│ │ ├── gateway/ # FastAPI Gateway API -│ │ │ ├── app.py # FastAPI application -│ │ │ └── routers/ # FastAPI route modules (models, mcp, memory, skills, uploads, threads, artifacts, agents, suggestions, channels) -│ │ └── channels/ # IM platform integrations -│ ├── tests/ # Test suite -│ └── docs/ # Documentation -├── frontend/ # Next.js frontend application -└── skills/ # Agent skills directory - ├── public/ # Public skills (committed) - └── custom/ # Custom skills (gitignored) -``` - -## Important Development Guidelines - -### Documentation Update Policy -**CRITICAL: Always update README.md and CLAUDE.md after every code change** - -When making code changes, you MUST update the relevant documentation: -- Update `README.md` for user-facing changes (features, setup, usage instructions) -- Update `CLAUDE.md` for development changes (architecture, commands, workflows, internal systems) -- Keep documentation synchronized with the codebase at all times -- Ensure accuracy and timeliness of all documentation - -## Commands - -**Root directory** (for full application): -```bash -make check # Check system requirements -make install # Install all dependencies (frontend + backend) -make dev # Start all services (Gateway + Frontend + Nginx), with config.yaml preflight -make start # Start production services locally -make stop # Stop all services -``` - -**Backend directory** (for backend development only): -```bash -make install # Install backend dependencies -make dev # Run Gateway API with reload (port 8001) -make gateway # Run Gateway API only (port 8001) -make test # Run all backend tests -make lint # Lint with ruff -make format # Format code with ruff -``` - -Regression tests related to Docker/provisioner behavior: -- `tests/test_docker_sandbox_mode_detection.py` (mode detection from `config.yaml`) -- `tests/test_provisioner_kubeconfig.py` (kubeconfig file/directory handling) - -Boundary check (harness → app import firewall): -- `tests/test_harness_boundary.py` — ensures `packages/harness/deerflow/` never imports from `app.*` - -CI runs these regression tests for every pull request via [.github/workflows/backend-unit-tests.yml](../.github/workflows/backend-unit-tests.yml). - -## Architecture - -### Harness / App Split - -The backend is split into two layers with a strict dependency direction: - -- **Harness** (`packages/harness/deerflow/`): Publishable agent framework package (`deerflow-harness`). Import prefix: `deerflow.*`. Contains agent orchestration, tools, sandbox, models, MCP, skills, config — everything needed to build and run agents. -- **App** (`app/`): Unpublished application code. Import prefix: `app.*`. Contains the FastAPI Gateway API and IM channel integrations (Feishu, Slack, Telegram, DingTalk). - -**Dependency rule**: App imports deerflow, but deerflow never imports app. This boundary is enforced by `tests/test_harness_boundary.py` which runs in CI. - -**Import conventions**: -```python -# Harness internal -from deerflow.agents import make_lead_agent -from deerflow.models import create_chat_model - -# App internal -from app.gateway.app import app -from app.channels.service import start_channel_service - -# App → Harness (allowed) -from deerflow.config import get_app_config - -# Harness → App (FORBIDDEN — enforced by test_harness_boundary.py) -# from app.gateway.routers.uploads import ... # ← will fail CI -``` - -### Agent System - -**Lead Agent** (`packages/harness/deerflow/agents/lead_agent/agent.py`): -- Entry point: `make_lead_agent(config: RunnableConfig)` registered in `langgraph.json` -- Dynamic model selection via `create_chat_model()` with thinking/vision support -- Tools loaded via `get_available_tools()` - combines sandbox, built-in, MCP, community, and subagent tools -- System prompt generated by `apply_prompt_template()` with skills, memory, and subagent instructions - -**ThreadState** (`packages/harness/deerflow/agents/thread_state.py`): -- Extends `AgentState` with: `sandbox`, `thread_data`, `title`, `artifacts`, `todos`, `uploaded_files`, `viewed_images` -- Uses custom reducers: `merge_artifacts` (deduplicate), `merge_viewed_images` (merge/clear) - -**Runtime Configuration** (via `config.configurable`): -- `thinking_enabled` - Enable model's extended thinking -- `model_name` - Select specific LLM model -- `is_plan_mode` - Enable TodoList middleware -- `subagent_enabled` - Enable task delegation tool - -### Middleware Chain - -Lead-agent middlewares are assembled in strict append order across `packages/harness/deerflow/agents/middlewares/tool_error_handling_middleware.py` (`build_lead_runtime_middlewares`) and `packages/harness/deerflow/agents/lead_agent/agent.py` (`_build_middlewares`): - -1. **ThreadDataMiddleware** - Creates per-thread directories under the user's isolation scope (`backend/.deer-flow/users/{user_id}/threads/{thread_id}/user-data/{workspace,uploads,outputs}`); resolves `user_id` via `get_effective_user_id()` (falls back to `"default"` in no-auth mode); Web UI thread deletion now follows LangGraph thread removal with Gateway cleanup of the local thread directory -2. **UploadsMiddleware** - Tracks and injects newly uploaded files into conversation -3. **SandboxMiddleware** - Acquires sandbox, stores `sandbox_id` in state -4. **DanglingToolCallMiddleware** - Injects placeholder ToolMessages for AIMessage tool_calls that lack responses (e.g., due to user interruption), including raw provider tool-call payloads preserved only in `additional_kwargs["tool_calls"]` -5. **LLMErrorHandlingMiddleware** - Normalizes provider/model invocation failures into recoverable assistant-facing errors before later middleware/tool stages run -6. **GuardrailMiddleware** - Pre-tool-call authorization via pluggable `GuardrailProvider` protocol (optional, if `guardrails.enabled` in config). Evaluates each tool call and returns error ToolMessage on deny. Three provider options: built-in `AllowlistProvider` (zero deps), OAP policy providers (e.g. `aport-agent-guardrails`), or custom providers. See [docs/GUARDRAILS.md](docs/GUARDRAILS.md) for setup, usage, and how to implement a provider. -7. **SandboxAuditMiddleware** - Audits sandboxed shell/file operations for security logging before tool execution continues -8. **ToolErrorHandlingMiddleware** - Converts tool exceptions into error `ToolMessage`s so the run can continue instead of aborting -9. **SummarizationMiddleware** - Context reduction when approaching token limits (optional, if enabled) -10. **TodoListMiddleware** - Task tracking with `write_todos` tool (optional, if plan_mode) -11. **TokenUsageMiddleware** - Records token usage metrics when token tracking is enabled (optional) -12. **TitleMiddleware** - Auto-generates thread title after first complete exchange and normalizes structured message content before prompting the title model -13. **MemoryMiddleware** - Queues conversations for async memory update (filters to user + final AI responses) -14. **ViewImageMiddleware** - Injects base64 image data before LLM call (conditional on vision support) -15. **DeferredToolFilterMiddleware** - Hides deferred tool schemas from the bound model until tool search is enabled (optional) -16. **SubagentLimitMiddleware** - Truncates excess `task` tool calls from model response to enforce `MAX_CONCURRENT_SUBAGENTS` limit (optional, if `subagent_enabled`) -17. **LoopDetectionMiddleware** - Detects repeated tool-call loops; hard-stop responses clear both structured `tool_calls` and raw provider tool-call metadata before forcing a final text answer -18. **ClarificationMiddleware** - Intercepts `ask_clarification` tool calls, interrupts via `Command(goto=END)` (must be last) - -### Configuration System - -**Main Configuration** (`config.yaml`): - -Setup: Copy `config.example.yaml` to `config.yaml` in the **project root** directory. - -**Config Versioning**: `config.example.yaml` has a `config_version` field. On startup, `AppConfig.from_file()` compares user version vs example version and emits a warning if outdated. Missing `config_version` = version 0. Run `make config-upgrade` to auto-merge missing fields. When changing the config schema, bump `config_version` in `config.example.yaml`. - -**Config Caching**: `get_app_config()` caches the parsed config, but automatically reloads it when the resolved config path changes or the file's mtime increases. This keeps Gateway and LangGraph reads aligned with `config.yaml` edits without requiring a manual process restart. - -Configuration priority: -1. Explicit `config_path` argument -2. `DEER_FLOW_CONFIG_PATH` environment variable -3. `config.yaml` in current directory (backend/) -4. `config.yaml` in parent directory (project root - **recommended location**) - -Config values starting with `$` are resolved as environment variables (e.g., `$OPENAI_API_KEY`). -`ModelConfig` also declares `use_responses_api` and `output_version` so OpenAI `/v1/responses` can be enabled explicitly while still using `langchain_openai:ChatOpenAI`. - -**Extensions Configuration** (`extensions_config.json`): - -MCP servers and skills are configured together in `extensions_config.json` in project root: - -Configuration priority: -1. Explicit `config_path` argument -2. `DEER_FLOW_EXTENSIONS_CONFIG_PATH` environment variable -3. `extensions_config.json` in current directory (backend/) -4. `extensions_config.json` in parent directory (project root - **recommended location**) - -### Gateway API (`app/gateway/`) - -FastAPI application on port 8001 with health check at `GET /health`. Set `GATEWAY_ENABLE_DOCS=false` to disable `/docs`, `/redoc`, and `/openapi.json` in production (default: enabled). - -**Routers**: - -| Router | Endpoints | -|--------|-----------| -| **Models** (`/api/models`) | `GET /` - list models; `GET /{name}` - model details | -| **MCP** (`/api/mcp`) | `GET /config` - get config; `PUT /config` - update config (saves to extensions_config.json) | -| **Skills** (`/api/skills`) | `GET /` - list skills; `GET /{name}` - details; `PUT /{name}` - update enabled; `POST /install` - install from .skill archive (accepts standard optional frontmatter like `version`, `author`, `compatibility`) | -| **Memory** (`/api/memory`) | `GET /` - memory data; `POST /reload` - force reload; `GET /config` - config; `GET /status` - config + data | -| **Agents** (`/api/agents`) | `GET /` - list built-in, owned, and published agents; `POST /` - create user-owned agent; `GET/PUT/DELETE /{id}` - read/update/delete by stable agent id; `GET /check` - validate non-empty display name (duplicates allowed) | -| **Uploads** (`/api/threads/{id}/uploads`) | `POST /` - upload files (auto-converts PDF/PPT/Excel/Word); `GET /list` - list; `DELETE /{filename}` - delete | -| **Threads** (`/api/threads/{id}`) | `DELETE /` - remove DeerFlow-managed local thread data after LangGraph thread deletion; unexpected failures are logged server-side and return a generic 500 detail | -| **Artifacts** (`/api/threads/{id}/artifacts`) | `GET /{path}` - serve artifacts; active content types (`text/html`, `application/xhtml+xml`, `image/svg+xml`) are always forced as download attachments to reduce XSS risk; `?download=true` still forces download for other file types | -| **Suggestions** (`/api/threads/{id}/suggestions`) | `POST /` - generate follow-up questions; rich list/block model content is normalized before JSON parsing | -| **Thread Runs** (`/api/threads/{id}/runs`) | `POST /` - create background run; `POST /stream` - create + SSE stream; `POST /wait` - create + block; `GET /` - list runs; `GET /{rid}` - run details; `POST /{rid}/cancel` - cancel; `GET /{rid}/join` - join SSE; `GET /{rid}/messages` - paginated messages `{data, has_more}`; `GET /{rid}/events` - full event stream; `GET /../messages` - thread messages with feedback; `GET /../token-usage` - aggregate tokens | -| **Feedback** (`/api/threads/{id}/runs/{rid}/feedback`) | `PUT /` - upsert feedback; `DELETE /` - delete user feedback; `POST /` - create feedback; `GET /` - list feedback; `GET /stats` - aggregate stats; `DELETE /{fid}` - delete specific | -| **Runs** (`/api/runs`) | `POST /stream` - stateless run + SSE; `POST /wait` - stateless run + block; `GET /{rid}/messages` - paginated messages by run_id `{data, has_more}` (cursor: `after_seq`/`before_seq`); `GET /{rid}/feedback` - list feedback by run_id | - -Proxied through nginx: `/api/langgraph/*` → LangGraph, all other `/api/*` → Gateway. - -### Custom Agent Persistence - -Custom agent metadata lives in the `agents` ORM table (`deerflow.persistence.agents`). `agents.id` is the stable runtime id and the filesystem directory name under `.deer-flow/agents/{id}/`; `agents.name` is display-only, can contain Chinese characters, and is not unique. `user_id IS NULL` marks built-in agents, user-owned agents are visible only to their owner unless `published = true`, and update/delete operations require ownership. The runtime accepts `agent_id` in context/configurable, and non-default `assistant_id` values are treated as agent ids after visibility checks. - -### Sandbox System (`packages/harness/deerflow/sandbox/`) - -**Interface**: Abstract `Sandbox` with `execute_command`, `read_file`, `write_file`, `list_dir` -**Provider Pattern**: `SandboxProvider` with `acquire`, `get`, `release` lifecycle -**Implementations**: -- `LocalSandboxProvider` - Singleton local filesystem execution with path mappings -- `AioSandboxProvider` (`packages/harness/deerflow/community/`) - Docker-based isolation - -**Virtual Path System**: -- Agent sees: `/mnt/user-data/{workspace,uploads,outputs}`, `/mnt/skills` -- Physical: `backend/.deer-flow/users/{user_id}/threads/{thread_id}/user-data/...`, `deer-flow/skills/` -- Translation: `replace_virtual_path()` / `replace_virtual_paths_in_command()` -- Detection: `is_local_sandbox()` checks `sandbox_id == "local"` - -**Sandbox Tools** (in `packages/harness/deerflow/sandbox/tools.py`): -- `bash` - Execute commands with path translation and error handling -- `ls` - Directory listing (tree format, max 2 levels) -- `read_file` - Read file contents with optional line range -- `write_file` - Write/append to files, creates directories -- `str_replace` - Substring replacement (single or all occurrences); same-path serialization is scoped to `(sandbox.id, path)` so isolated sandboxes do not contend on identical virtual paths inside one process - -### Subagent System (`packages/harness/deerflow/subagents/`) - -**Built-in Agents**: `general-purpose` (all tools except `task`) and `bash` (command specialist) -**Execution**: Dual thread pool - `_scheduler_pool` (3 workers) + `_execution_pool` (3 workers) -**Concurrency**: `MAX_CONCURRENT_SUBAGENTS = 3` enforced by `SubagentLimitMiddleware` (truncates excess tool calls in `after_model`), 15-minute timeout -**Flow**: `task()` tool → `SubagentExecutor` → background thread → poll 5s → SSE events → result -**Events**: `task_started`, `task_running`, `task_completed`/`task_failed`/`task_timed_out` - -### Tool System (`packages/harness/deerflow/tools/`) - -`get_available_tools(groups, include_mcp, model_name, subagent_enabled)` assembles: -1. **Config-defined tools** - Resolved from `config.yaml` via `resolve_variable()` -2. **MCP tools** - From enabled MCP servers (lazy initialized, cached with mtime invalidation) -3. **Built-in tools**: - - `present_files` - Make output files visible to user (only `/mnt/user-data/outputs`) - - `ask_clarification` - Request clarification (intercepted by ClarificationMiddleware → interrupts) - - `view_image` - Read image as base64 (added only if model supports vision) -4. **Subagent tool** (if enabled): - - `task` - Delegate to subagent (description, prompt, subagent_type, max_turns) - -**Community tools** (`packages/harness/deerflow/community/`): -- `tavily/` - Web search (5 results default) and web fetch (4KB limit) -- `jina_ai/` - Web fetch via Jina reader API with readability extraction -- `firecrawl/` - Web scraping via Firecrawl API - -**ACP agent tools**: -- `invoke_acp_agent` - Invokes external ACP-compatible agents from `config.yaml` -- ACP launchers must be real ACP adapters. The standard `codex` CLI is not ACP-compatible by itself; configure a wrapper such as `npx -y @zed-industries/codex-acp` or an installed `codex-acp` binary -- Missing ACP executables now return an actionable error message instead of a raw `[Errno 2]` -- Each ACP agent uses a per-thread workspace at `{base_dir}/users/{user_id}/threads/{thread_id}/acp-workspace/`. The workspace is accessible to the lead agent via the virtual path `/mnt/acp-workspace/` (read-only). In docker sandbox mode, the directory is volume-mounted into the container at `/mnt/acp-workspace` (read-only); in local sandbox mode, path translation is handled by `tools.py` -- `image_search/` - Image search via DuckDuckGo - -### MCP System (`packages/harness/deerflow/mcp/`) - -- Uses `langchain-mcp-adapters` `MultiServerMCPClient` for multi-server management -- **Lazy initialization**: Tools loaded on first use via `get_cached_mcp_tools()` -- **Cache invalidation**: Detects config file changes via mtime comparison -- **Transports**: stdio (command-based), SSE, HTTP -- **OAuth (HTTP/SSE)**: Supports token endpoint flows (`client_credentials`, `refresh_token`) with automatic token refresh + Authorization header injection -- **Runtime updates**: Gateway API saves to extensions_config.json; LangGraph detects via mtime - -### Skills System (`packages/harness/deerflow/skills/`) - -- **Location**: `deer-flow/skills/{public,custom}/` -- **Format**: Directory with `SKILL.md` (YAML frontmatter: name, description, license, allowed-tools) -- **Loading**: `load_skills()` recursively scans `skills/{public,custom}` for `SKILL.md`, parses metadata, and reads enabled state from extensions_config.json -- **Injection**: Enabled skills listed in agent system prompt with container paths -- **Installation**: `POST /api/skills/install` extracts .skill ZIP archive to custom/ directory - -### Model Factory (`packages/harness/deerflow/models/factory.py`) - -- `create_chat_model(name, thinking_enabled)` instantiates LLM from config via reflection -- Supports `thinking_enabled` flag with per-model `when_thinking_enabled` overrides -- Supports vLLM-style thinking toggles via `when_thinking_enabled.extra_body.chat_template_kwargs.enable_thinking` for Qwen reasoning models, while normalizing legacy `thinking` configs for backward compatibility -- Supports `supports_vision` flag for image understanding models -- Config values starting with `$` resolved as environment variables -- Missing provider modules surface actionable install hints from reflection resolvers (for example `uv add langchain-google-genai`) - -### vLLM Provider (`packages/harness/deerflow/models/vllm_provider.py`) - -- `VllmChatModel` subclasses `langchain_openai:ChatOpenAI` for vLLM 0.19.0 OpenAI-compatible endpoints -- Preserves vLLM's non-standard assistant `reasoning` field on full responses, streaming deltas, and follow-up tool-call turns -- Designed for configs that enable thinking through `extra_body.chat_template_kwargs.enable_thinking` on vLLM 0.19.0 Qwen reasoning models, while accepting the older `thinking` alias - -### IM Channels System (`app/channels/`) - -Bridges external messaging platforms (Feishu, Slack, Telegram, DingTalk) to the DeerFlow agent via the LangGraph Server. - - -**Architecture**: Channels communicate with Gateway through the `langgraph-sdk` HTTP client (same as the frontend), ensuring threads are created and managed server-side. The internal SDK client injects process-local internal auth plus a matching CSRF cookie/header pair so Gateway accepts state-changing thread/run requests from channel workers without relying on browser session cookies. - -**Components**: -- `message_bus.py` - Async pub/sub hub (`InboundMessage` → queue → dispatcher; `OutboundMessage` → callbacks → channels) -- `store.py` - JSON-file persistence mapping `channel_name:chat_id[:topic_id]` → `thread_id` (keys are `channel:chat` for root conversations and `channel:chat:topic` for threaded conversations) -- `manager.py` - Core dispatcher: creates threads via `client.threads.create()`, routes commands, keeps Slack/Telegram on `client.runs.wait()`, and uses `client.runs.stream(["messages-tuple", "values"])` for Feishu incremental outbound updates -- `base.py` - Abstract `Channel` base class (start/stop/send lifecycle) -- `service.py` - Manages lifecycle of all configured channels from `config.yaml` -- `slack.py` / `feishu.py` / `telegram.py` / `dingtalk.py` - Platform-specific implementations (`feishu.py` tracks the running card `message_id` in memory and patches the same card in place; `dingtalk.py` optionally uses AI Card streaming for in-place updates when `card_template_id` is configured) - -**Message Flow**: -1. External platform -> Channel impl -> `MessageBus.publish_inbound()` -2. `ChannelManager._dispatch_loop()` consumes from queue -3. For chat: look up/create thread through Gateway's LangGraph-compatible API -4. Feishu chat: `runs.stream()` → accumulate AI text → publish multiple outbound updates (`is_final=False`) → publish final outbound (`is_final=True`) -5. Slack/Telegram chat: `runs.wait()` → extract final response → publish outbound -6. Feishu channel sends one running reply card up front, then patches the same card for each outbound update (card JSON sets `config.update_multi=true` for Feishu's patch API requirement) -7. DingTalk AI Card mode (when `card_template_id` configured): `runs.stream()` → create card with initial text → stream updates via `PUT /v1.0/card/streaming` → finalize on `is_final=True`. Falls back to `sampleMarkdown` if card creation or streaming fails -8. For commands (`/new`, `/status`, `/models`, `/memory`, `/help`): handle locally or query Gateway API -9. Outbound → channel callbacks → platform reply - -**Configuration** (`config.yaml` -> `channels`): -- `langgraph_url` - LangGraph-compatible Gateway API base URL (default: `http://localhost:8001/api`) -- `gateway_url` - Gateway API URL for auxiliary commands (default: `http://localhost:8001`) -- In Docker Compose, IM channels run inside the `gateway` container, so `localhost` points back to that container. Use `http://gateway:8001/api` for `langgraph_url` and `http://gateway:8001` for `gateway_url`, or set `DEER_FLOW_CHANNELS_LANGGRAPH_URL` / `DEER_FLOW_CHANNELS_GATEWAY_URL`. -- Per-channel configs: `feishu` (app_id, app_secret), `slack` (bot_token, app_token), `telegram` (bot_token), `dingtalk` (client_id, client_secret, optional `card_template_id` for AI Card streaming) - - -### Memory System (`packages/harness/deerflow/agents/memory/`) - -**Components**: -- `updater.py` - LLM-based memory updates with fact extraction, whitespace-normalized fact deduplication (trims leading/trailing whitespace before comparing), and atomic file I/O -- `queue.py` - Debounced update queue (per-thread deduplication, configurable wait time); captures `user_id` at enqueue time so it survives the `threading.Timer` boundary -- `prompt.py` - Prompt templates for memory updates -- `storage.py` - File-based storage with per-user isolation; cache keyed by `(user_id, agent_name)` tuple - -**Per-User Isolation**: -- Memory is stored per-user at `{base_dir}/users/{user_id}/memory.json` -- Per-agent per-user memory at `{base_dir}/users/{user_id}/agents/{agent_name}/memory.json` -- `user_id` is resolved via `get_effective_user_id()` from `deerflow.runtime.user_context` -- In no-auth mode, `user_id` defaults to `"default"` (constant `DEFAULT_USER_ID`) -- Absolute `storage_path` in config opts out of per-user isolation -- **Migration**: Run `PYTHONPATH=. python scripts/migrate_user_isolation.py` to move legacy `memory.json` and `threads/` into per-user layout; supports `--dry-run` - -**Data Structure** (stored in `{base_dir}/users/{user_id}/memory.json`): -- **User Context**: `workContext`, `personalContext`, `topOfMind` (1-3 sentence summaries) -- **History**: `recentMonths`, `earlierContext`, `longTermBackground` -- **Facts**: Discrete facts with `id`, `content`, `category` (preference/knowledge/context/behavior/goal), `confidence` (0-1), `createdAt`, `source` - -**Workflow**: -1. `MemoryMiddleware` filters messages (user inputs + final AI responses), captures `user_id` via `get_effective_user_id()`, and queues conversation with the captured `user_id` -2. Queue debounces (30s default), batches updates, deduplicates per-thread -3. Background thread invokes LLM to extract context updates and facts, using the stored `user_id` (not the contextvar, which is unavailable on timer threads) -4. Applies updates atomically (temp file + rename) with cache invalidation, skipping duplicate fact content before append -5. Next interaction injects top 15 facts + context into `` tags in system prompt - -Focused regression coverage for the updater lives in `backend/tests/test_memory_updater.py`. - -**Configuration** (`config.yaml` → `memory`): -- `enabled` / `injection_enabled` - Master switches -- `storage_path` - Path to memory.json (absolute path opts out of per-user isolation) -- `debounce_seconds` - Wait time before processing (default: 30) -- `model_name` - LLM for updates (null = default model) -- `max_facts` / `fact_confidence_threshold` - Fact storage limits (100 / 0.7) -- `max_injection_tokens` - Token limit for prompt injection (2000) - -### Reflection System (`packages/harness/deerflow/reflection/`) - -- `resolve_variable(path)` - Import module and return variable (e.g., `module.path:variable_name`) -- `resolve_class(path, base_class)` - Import and validate class against base class - -### Config Schema - -**`config.yaml`** key sections: -- `models[]` - LLM configs with `use` class path, `supports_thinking`, `supports_vision`, provider-specific fields -- vLLM reasoning models should use `deerflow.models.vllm_provider:VllmChatModel`; for Qwen-style parsers prefer `when_thinking_enabled.extra_body.chat_template_kwargs.enable_thinking`, and DeerFlow will also normalize the older `thinking` alias -- `tools[]` - Tool configs with `use` variable path and `group` -- `tool_groups[]` - Logical groupings for tools -- `sandbox.use` - Sandbox provider class path -- `skills.path` / `skills.container_path` - Host and container paths to skills directory -- `title` - Auto-title generation (enabled, max_words, max_chars, prompt_template) -- `summarization` - Context summarization (enabled, trigger conditions, keep policy) -- `subagents.enabled` - Master switch for subagent delegation -- `memory` - Memory system (enabled, storage_path, debounce_seconds, model_name, max_facts, fact_confidence_threshold, injection_enabled, max_injection_tokens) - -**`extensions_config.json`**: -- `mcpServers` - Map of server name → config (enabled, type, command, args, env, url, headers, oauth, description) -- `skills` - Map of skill name → state (enabled) - -Both can be modified at runtime via Gateway API endpoints or `DeerFlowClient` methods. - -### Embedded Client (`packages/harness/deerflow/client.py`) - -`DeerFlowClient` provides direct in-process access to all DeerFlow capabilities without HTTP services. All return types align with the Gateway API response schemas, so consumer code works identically in HTTP and embedded modes. - -**Architecture**: Imports the same `deerflow` modules that Gateway API uses. Shares the same config files and data directories. No FastAPI dependency. - -**Agent Conversation**: -- `chat(message, thread_id)` — synchronous, accumulates streaming deltas per message-id and returns the final AI text -- `stream(message, thread_id)` — subscribes to LangGraph `stream_mode=["values", "messages", "custom"]` and yields `StreamEvent`: - - `"values"` — full state snapshot (title, messages, artifacts); AI text already delivered via `messages` mode is **not** re-synthesized here to avoid duplicate deliveries - - `"messages-tuple"` — per-chunk update: for AI text this is a **delta** (concat per `id` to rebuild the full message); tool calls and tool results are emitted once each - - `"custom"` — forwarded from `StreamWriter` - - `"end"` — stream finished (carries cumulative `usage` counted once per message id) -- Agent created lazily via `create_agent()` + `_build_middlewares()`, same as `make_lead_agent` -- Supports `checkpointer` parameter for state persistence across turns -- `reset_agent()` forces agent recreation (e.g. after memory or skill changes) -- See [docs/STREAMING.md](docs/STREAMING.md) for the full design: why Gateway and DeerFlowClient are parallel paths, LangGraph's `stream_mode` semantics, the per-id dedup invariants, and regression testing strategy - -**Gateway Equivalent Methods** (replaces Gateway API): - -| Category | Methods | Return format | -|----------|---------|---------------| -| Models | `list_models()`, `get_model(name)` | `{"models": [...]}`, `{name, display_name, ...}` | -| MCP | `get_mcp_config()`, `update_mcp_config(servers)` | `{"mcp_servers": {...}}` | -| Skills | `list_skills()`, `get_skill(name)`, `update_skill(name, enabled)`, `install_skill(path)` | `{"skills": [...]}` | -| Memory | `get_memory()`, `reload_memory()`, `get_memory_config()`, `get_memory_status()` | dict | -| Uploads | `upload_files(thread_id, files)`, `list_uploads(thread_id)`, `delete_upload(thread_id, filename)` | `{"success": true, "files": [...]}`, `{"files": [...], "count": N}` | -| Artifacts | `get_artifact(thread_id, path)` → `(bytes, mime_type)` | tuple | - -**Key difference from Gateway**: Upload accepts local `Path` objects instead of HTTP `UploadFile`, rejects directory paths before copying, and reuses a single worker when document conversion must run inside an active event loop. Artifact returns `(bytes, mime_type)` instead of HTTP Response. The new Gateway-only thread cleanup route deletes `.deer-flow/threads/{thread_id}` after LangGraph thread deletion; there is no matching `DeerFlowClient` method yet. `update_mcp_config()` and `update_skill()` automatically invalidate the cached agent. - -**Tests**: `tests/test_client.py` (77 unit tests including `TestGatewayConformance`), `tests/test_client_live.py` (live integration tests, requires config.yaml) - -**Gateway Conformance Tests** (`TestGatewayConformance`): Validate that every dict-returning client method conforms to the corresponding Gateway Pydantic response model. Each test parses the client output through the Gateway model — if Gateway adds a required field that the client doesn't provide, Pydantic raises `ValidationError` and CI catches the drift. Covers: `ModelsListResponse`, `ModelResponse`, `SkillsListResponse`, `SkillResponse`, `SkillInstallResponse`, `McpConfigResponse`, `UploadResponse`, `MemoryConfigResponse`, `MemoryStatusResponse`. - -## Development Workflow - -### Test-Driven Development (TDD) — MANDATORY - -**Every new feature or bug fix MUST be accompanied by unit tests. No exceptions.** - -- Write tests in `backend/tests/` following the existing naming convention `test_.py` -- Run the full suite before and after your change: `make test` -- Tests must pass before a feature is considered complete -- For lightweight config/utility modules, prefer pure unit tests with no external dependencies -- If a module causes circular import issues in tests, add a `sys.modules` mock in `tests/conftest.py` (see existing example for `deerflow.subagents.executor`) - -```bash -# Run all tests -make test - -# Run a specific test file -PYTHONPATH=. uv run pytest tests/test_.py -v -``` - -### Running the Full Application - -From the **project root** directory: -```bash -make dev -``` - -This starts all services and makes the application available at `http://localhost:2026`. - -**All startup modes:** - -| | **Local Foreground** | **Local Daemon** | **Docker Dev** | **Docker Prod** | -|---|---|---|---|---| -| **Dev** | `./scripts/serve.sh --dev`
`make dev` | `./scripts/serve.sh --dev --daemon`
`make dev-daemon` | `./scripts/docker.sh start`
`make docker-start` | — | -| **Prod** | `./scripts/serve.sh --prod`
`make start` | `./scripts/serve.sh --prod --daemon`
`make start-daemon` | — | `./scripts/deploy.sh`
`make up` | - -| Action | Local | Docker Dev | Docker Prod | -|---|---|---|---| -| **Stop** | `./scripts/serve.sh --stop`
`make stop` | `./scripts/docker.sh stop`
`make docker-stop` | `./scripts/deploy.sh down`
`make down` | -| **Restart** | `./scripts/serve.sh --restart [flags]` | `./scripts/docker.sh restart` | — | - -**Nginx routing**: -- `/api/langgraph/*` → Gateway embedded runtime (8001), rewritten to `/api/*` -- `/api/*` (other) → Gateway API (8001) -- `/` (non-API) → Frontend (3000) - -### Running Backend Services Separately - -From the **backend** directory: - -```bash -# Gateway API -make gateway -``` - -Direct access (without nginx): -- Gateway: `http://localhost:8001` - -### Frontend Configuration - -The frontend uses environment variables to connect to backend services: -- `NEXT_PUBLIC_LANGGRAPH_BASE_URL` - Defaults to `/api/langgraph` (through nginx) -- `NEXT_PUBLIC_BACKEND_BASE_URL` - Defaults to empty string (through nginx) - -When using `make dev` from root, the frontend automatically connects through nginx. - -## Key Features - -### File Upload - -Multi-file upload with automatic document conversion: -- Endpoint: `POST /api/threads/{thread_id}/uploads` -- Supports: PDF, PPT, Excel, Word documents (converted via `markitdown`) -- Rejects directory inputs before copying so uploads stay all-or-nothing -- Reuses one conversion worker per request when called from an active event loop -- Files stored in thread-isolated directories -- Agent receives uploaded file list via `UploadsMiddleware` - -See [docs/FILE_UPLOAD.md](docs/FILE_UPLOAD.md) for details. - -### Plan Mode - -TodoList middleware for complex multi-step tasks: -- Controlled via runtime config: `config.configurable.is_plan_mode = True` -- Provides `write_todos` tool for task tracking -- One task in_progress at a time, real-time updates - -See [docs/plan_mode_usage.md](docs/plan_mode_usage.md) for details. - -### Context Summarization - -Automatic conversation summarization when approaching token limits: -- Configured in `config.yaml` under `summarization` key -- Trigger types: tokens, messages, or fraction of max input -- Keeps recent messages while summarizing older ones - -See [docs/summarization.md](docs/summarization.md) for details. - -### Vision Support - -For models with `supports_vision: true`: -- `ViewImageMiddleware` processes images in conversation -- `view_image_tool` added to agent's toolset -- Images automatically converted to base64 and injected into state - -## Code Style - -- Uses `ruff` for linting and formatting -- Line length: 240 characters -- Python 3.12+ with type hints -- Double quotes, space indentation - -## Documentation - -See `docs/` directory for detailed documentation: -- [CONFIGURATION.md](docs/CONFIGURATION.md) - Configuration options -- [ARCHITECTURE.md](docs/ARCHITECTURE.md) - Architecture details -- [API.md](docs/API.md) - API reference -- [SETUP.md](docs/SETUP.md) - Setup guide -- [FILE_UPLOAD.md](docs/FILE_UPLOAD.md) - File upload feature +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +DeerFlow is a LangGraph-based AI super agent system with a full-stack architecture. The backend provides a "super agent" with sandbox execution, persistent memory, subagent delegation, and extensible tool integration - all operating in per-thread isolated environments. + +**Architecture**: +- **Gateway API** (port 8001): REST API plus embedded LangGraph-compatible agent runtime +- **Frontend** (port 3000): Next.js web interface +- **Nginx** (port 2026): Unified reverse proxy entry point +- **Provisioner** (port 8002, optional in Docker dev): Started only when sandbox is configured for provisioner/Kubernetes mode + +**Runtime**: +- `make dev`, Docker dev, and production all run the agent runtime in Gateway via `RunManager` + `run_agent()` + `StreamBridge` (`packages/harness/deerflow/runtime/`). Nginx exposes that runtime at `/api/langgraph/*` and rewrites it to Gateway's native `/api/*` routers. + +**Project Structure**: +``` +deer-flow/ +├── Makefile # Root commands (check, install, dev, stop) +├── config.yaml # Main application configuration +├── extensions_config.json # MCP servers and skills configuration +├── backend/ # Backend application (this directory) +│ ├── Makefile # Backend-only commands (dev, gateway, lint) +│ ├── langgraph.json # LangGraph Studio graph configuration +│ ├── packages/ +│ │ └── harness/ # deerflow-harness package (import: deerflow.*) +│ │ ├── pyproject.toml +│ │ └── deerflow/ +│ │ ├── agents/ # LangGraph agent system +│ │ │ ├── lead_agent/ # Main agent (factory + system prompt) +│ │ │ ├── middlewares/ # 10 middleware components +│ │ │ ├── memory/ # Memory extraction, queue, prompts +│ │ │ └── thread_state.py # ThreadState schema +│ │ ├── sandbox/ # Sandbox execution system +│ │ │ ├── local/ # Local filesystem provider +│ │ │ ├── sandbox.py # Abstract Sandbox interface +│ │ │ ├── tools.py # bash, ls, read/write/str_replace +│ │ │ └── middleware.py # Sandbox lifecycle management +│ │ ├── subagents/ # Subagent delegation system +│ │ │ ├── builtins/ # general-purpose, bash agents +│ │ │ ├── executor.py # Background execution engine +│ │ │ └── registry.py # Agent registry +│ │ ├── tools/builtins/ # Built-in tools (present_files, ask_clarification, view_image) +│ │ ├── mcp/ # MCP integration (tools, cache, client) +│ │ ├── models/ # Model factory with thinking/vision support +│ │ ├── skills/ # Skills discovery, loading, parsing +│ │ ├── config/ # Configuration system (app, model, sandbox, tool, etc.) +│ │ ├── community/ # Community tools (tavily, jina_ai, firecrawl, image_search, aio_sandbox) +│ │ ├── reflection/ # Dynamic module loading (resolve_variable, resolve_class) +│ │ ├── utils/ # Utilities (network, readability) +│ │ └── client.py # Embedded Python client (DeerFlowClient) +│ ├── app/ # Application layer (import: app.*) +│ │ ├── gateway/ # FastAPI Gateway API +│ │ │ ├── app.py # FastAPI application +│ │ │ └── routers/ # FastAPI route modules (models, mcp, memory, skills, uploads, threads, artifacts, agents, suggestions, channels) +│ │ └── channels/ # IM platform integrations +│ ├── tests/ # Test suite +│ └── docs/ # Documentation +├── frontend/ # Next.js frontend application +└── skills/ # Agent skills directory + ├── public/ # Public skills (committed) + └── custom/ # Custom skills (gitignored) +``` + +## Important Development Guidelines + +### Documentation Update Policy +**CRITICAL: Always update README.md and CLAUDE.md after every code change** + +When making code changes, you MUST update the relevant documentation: +- Update `README.md` for user-facing changes (features, setup, usage instructions) +- Update `CLAUDE.md` for development changes (architecture, commands, workflows, internal systems) +- Keep documentation synchronized with the codebase at all times +- Ensure accuracy and timeliness of all documentation + +## Commands + +**Root directory** (for full application): +```bash +make check # Check system requirements +make install # Install all dependencies (frontend + backend) +make dev # Start all services (Gateway + Frontend + Nginx), with config.yaml preflight +make start # Start production services locally +make stop # Stop all services +``` + +**Backend directory** (for backend development only): +```bash +make install # Install backend dependencies +make dev # Run Gateway API with reload (port 8001) +make gateway # Run Gateway API only (port 8001) +make test # Run all backend tests +make lint # Lint with ruff +make format # Format code with ruff +``` + +Regression tests related to Docker/provisioner behavior: +- `tests/test_docker_sandbox_mode_detection.py` (mode detection from `config.yaml`) +- `tests/test_provisioner_kubeconfig.py` (kubeconfig file/directory handling) + +Boundary check (harness → app import firewall): +- `tests/test_harness_boundary.py` — ensures `packages/harness/deerflow/` never imports from `app.*` + +CI runs these regression tests for every pull request via [.github/workflows/backend-unit-tests.yml](../.github/workflows/backend-unit-tests.yml). + +## Architecture + +### Harness / App Split + +The backend is split into two layers with a strict dependency direction: + +- **Harness** (`packages/harness/deerflow/`): Publishable agent framework package (`deerflow-harness`). Import prefix: `deerflow.*`. Contains agent orchestration, tools, sandbox, models, MCP, skills, config — everything needed to build and run agents. +- **App** (`app/`): Unpublished application code. Import prefix: `app.*`. Contains the FastAPI Gateway API and IM channel integrations (Feishu, Slack, Telegram, DingTalk). + +**Dependency rule**: App imports deerflow, but deerflow never imports app. This boundary is enforced by `tests/test_harness_boundary.py` which runs in CI. + +**Import conventions**: +```python +# Harness internal +from deerflow.agents import make_lead_agent +from deerflow.models import create_chat_model + +# App internal +from app.gateway.app import app +from app.channels.service import start_channel_service + +# App → Harness (allowed) +from deerflow.config import get_app_config + +# Harness → App (FORBIDDEN — enforced by test_harness_boundary.py) +# from app.gateway.routers.uploads import ... # ← will fail CI +``` + +### Agent System + +**Lead Agent** (`packages/harness/deerflow/agents/lead_agent/agent.py`): +- Entry point: `make_lead_agent(config: RunnableConfig)` registered in `langgraph.json` +- Dynamic model selection via `create_chat_model()` with thinking/vision support +- Tools loaded via `get_available_tools()` - combines sandbox, built-in, MCP, community, and subagent tools +- System prompt generated by `apply_prompt_template()` with skills, memory, and subagent instructions + +**ThreadState** (`packages/harness/deerflow/agents/thread_state.py`): +- Extends `AgentState` with: `sandbox`, `thread_data`, `title`, `artifacts`, `todos`, `uploaded_files`, `viewed_images` +- Uses custom reducers: `merge_artifacts` (deduplicate), `merge_viewed_images` (merge/clear) + +**Runtime Configuration** (via `config.configurable`): +- `thinking_enabled` - Enable model's extended thinking +- `model_name` - Select specific LLM model +- `is_plan_mode` - Enable TodoList middleware +- `subagent_enabled` - Enable task delegation tool + +### Middleware Chain + +Lead-agent middlewares are assembled in strict append order across `packages/harness/deerflow/agents/middlewares/tool_error_handling_middleware.py` (`build_lead_runtime_middlewares`) and `packages/harness/deerflow/agents/lead_agent/agent.py` (`_build_middlewares`): + +1. **ThreadDataMiddleware** - Creates per-thread directories under the user's isolation scope (`backend/.deer-flow/users/{user_id}/threads/{thread_id}/user-data/{workspace,uploads,outputs}`); resolves `user_id` via `get_effective_user_id()` (falls back to `"default"` in no-auth mode); Web UI thread deletion now follows LangGraph thread removal with Gateway cleanup of the local thread directory +2. **UploadsMiddleware** - Tracks and injects newly uploaded files into conversation +3. **SandboxMiddleware** - Acquires sandbox, stores `sandbox_id` in state +4. **DanglingToolCallMiddleware** - Injects placeholder ToolMessages for AIMessage tool_calls that lack responses (e.g., due to user interruption), including raw provider tool-call payloads preserved only in `additional_kwargs["tool_calls"]` +5. **LLMErrorHandlingMiddleware** - Normalizes provider/model invocation failures into recoverable assistant-facing errors before later middleware/tool stages run +6. **GuardrailMiddleware** - Pre-tool-call authorization via pluggable `GuardrailProvider` protocol (optional, if `guardrails.enabled` in config). Evaluates each tool call and returns error ToolMessage on deny. Three provider options: built-in `AllowlistProvider` (zero deps), OAP policy providers (e.g. `aport-agent-guardrails`), or custom providers. See [docs/GUARDRAILS.md](docs/GUARDRAILS.md) for setup, usage, and how to implement a provider. +7. **SandboxAuditMiddleware** - Audits sandboxed shell/file operations for security logging before tool execution continues +8. **ToolErrorHandlingMiddleware** - Converts tool exceptions into error `ToolMessage`s so the run can continue instead of aborting +9. **SummarizationMiddleware** - Context reduction when approaching token limits (optional, if enabled) +10. **TodoListMiddleware** - Task tracking with `write_todos` tool (optional, if plan_mode) +11. **TokenUsageMiddleware** - Records token usage metrics when token tracking is enabled (optional) +12. **TitleMiddleware** - Auto-generates thread title after first complete exchange and normalizes structured message content before prompting the title model +13. **MemoryMiddleware** - Queues conversations for async memory update (filters to user + final AI responses) +14. **ViewImageMiddleware** - Injects base64 image data before LLM call (conditional on vision support) +15. **DeferredToolFilterMiddleware** - Hides deferred tool schemas from the bound model until tool search is enabled (optional) +16. **SubagentLimitMiddleware** - Truncates excess `task` tool calls from model response to enforce `MAX_CONCURRENT_SUBAGENTS` limit (optional, if `subagent_enabled`) +17. **LoopDetectionMiddleware** - Detects repeated tool-call loops; hard-stop responses clear both structured `tool_calls` and raw provider tool-call metadata before forcing a final text answer +18. **ClarificationMiddleware** - Intercepts `ask_clarification` tool calls, interrupts via `Command(goto=END)` (must be last) + +### Configuration System + +**Main Configuration** (`config.yaml`): + +Setup: Copy `config.example.yaml` to `config.yaml` in the **project root** directory. + +**Config Versioning**: `config.example.yaml` has a `config_version` field. On startup, `AppConfig.from_file()` compares user version vs example version and emits a warning if outdated. Missing `config_version` = version 0. Run `make config-upgrade` to auto-merge missing fields. When changing the config schema, bump `config_version` in `config.example.yaml`. + +**Config Caching**: `get_app_config()` caches the parsed config, but automatically reloads it when the resolved config path changes or the file's mtime increases. This keeps Gateway and LangGraph reads aligned with `config.yaml` edits without requiring a manual process restart. + +Configuration priority: +1. Explicit `config_path` argument +2. `DEER_FLOW_CONFIG_PATH` environment variable +3. `config.yaml` in current directory (backend/) +4. `config.yaml` in parent directory (project root - **recommended location**) + +Config values starting with `$` are resolved as environment variables (e.g., `$OPENAI_API_KEY`). +`ModelConfig` also declares `use_responses_api` and `output_version` so OpenAI `/v1/responses` can be enabled explicitly while still using `langchain_openai:ChatOpenAI`. + +**Extensions Configuration** (`extensions_config.json`): + +MCP servers and skills are configured together in `extensions_config.json` in project root: + +Configuration priority: +1. Explicit `config_path` argument +2. `DEER_FLOW_EXTENSIONS_CONFIG_PATH` environment variable +3. `extensions_config.json` in current directory (backend/) +4. `extensions_config.json` in parent directory (project root - **recommended location**) + +### Gateway API (`app/gateway/`) + +FastAPI application on port 8001 with health check at `GET /health`. Set `GATEWAY_ENABLE_DOCS=false` to disable `/docs`, `/redoc`, and `/openapi.json` in production (default: enabled). + +**Routers**: + +| Router | Endpoints | +|--------|-----------| +| **Models** (`/api/models`) | `GET /` - list models; `GET /{name}` - model details | +| **MCP** (`/api/mcp`) | `GET /config` - get config; `PUT /config` - update config (saves to extensions_config.json) | +| **Skills** (`/api/skills`) | `GET /` - list skills; `GET /{name}` - details; `PUT /{name}` - update enabled; `POST /install` - install from .skill archive (accepts standard optional frontmatter like `version`, `author`, `compatibility`) | +| **Memory** (`/api/memory`) | `GET /` - memory data; `POST /reload` - force reload; `GET /config` - config; `GET /status` - config + data | +| **Agents** (`/api/agents`) | `GET /` - list built-in, owned, and published agents; `POST /` - create user-owned agent; `GET/PUT/DELETE /{id}` - read/update/delete by stable agent id; `GET /check` - validate non-empty display name (duplicates allowed) | +| **Uploads** (`/api/threads/{id}/uploads`) | `POST /` - upload files (auto-converts PDF/PPT/Excel/Word); `GET /list` - list; `DELETE /{filename}` - delete | +| **Threads** (`/api/threads/{id}`) | `DELETE /` - remove DeerFlow-managed local thread data after LangGraph thread deletion; unexpected failures are logged server-side and return a generic 500 detail | +| **Artifacts** (`/api/threads/{id}/artifacts`) | `GET /{path}` - serve artifacts; active content types (`text/html`, `application/xhtml+xml`, `image/svg+xml`) are always forced as download attachments to reduce XSS risk; `?download=true` still forces download for other file types | +| **Suggestions** (`/api/threads/{id}/suggestions`) | `POST /` - generate follow-up questions; rich list/block model content is normalized before JSON parsing | +| **Thread Runs** (`/api/threads/{id}/runs`) | `POST /` - create background run; `POST /stream` - create + SSE stream; `POST /wait` - create + block; `GET /` - list runs; `GET /{rid}` - run details; `POST /{rid}/cancel` - cancel; `GET /{rid}/join` - join SSE; `GET /{rid}/messages` - paginated messages `{data, has_more}`; `GET /{rid}/events` - full event stream; `GET /../messages` - thread messages with feedback; `GET /../token-usage` - aggregate tokens | +| **Feedback** (`/api/threads/{id}/runs/{rid}/feedback`) | `PUT /` - upsert feedback; `DELETE /` - delete user feedback; `POST /` - create feedback; `GET /` - list feedback; `GET /stats` - aggregate stats; `DELETE /{fid}` - delete specific | +| **Runs** (`/api/runs`) | `POST /stream` - stateless run + SSE; `POST /wait` - stateless run + block; `GET /{rid}/messages` - paginated messages by run_id `{data, has_more}` (cursor: `after_seq`/`before_seq`); `GET /{rid}/feedback` - list feedback by run_id | + +Proxied through nginx: `/api/langgraph/*` → LangGraph, all other `/api/*` → Gateway. + +### Custom Agent Persistence + +Custom agent metadata lives in the `agents` ORM table (`deerflow.persistence.agents`). `agents.id` is the stable runtime id and the filesystem directory name under `.deer-flow/agents/{id}/`; `agents.name` is display-only, can contain Chinese characters, and is not unique. `user_id IS NULL` marks built-in agents, user-owned agents are visible only to their owner unless `published = true`, and update/delete operations require ownership. The runtime accepts `agent_id` in context/configurable, and non-default `assistant_id` values are treated as agent ids after visibility checks. + +### Sandbox System (`packages/harness/deerflow/sandbox/`) + +**Interface**: Abstract `Sandbox` with `execute_command`, `read_file`, `write_file`, `list_dir` +**Provider Pattern**: `SandboxProvider` with `acquire`, `get`, `release` lifecycle +**Implementations**: +- `LocalSandboxProvider` - Singleton local filesystem execution with path mappings +- `AioSandboxProvider` (`packages/harness/deerflow/community/`) - Docker-based isolation + +**Virtual Path System**: +- Agent sees: `/mnt/user-data/{workspace,uploads,outputs}`, `/mnt/skills` +- Physical: `backend/.deer-flow/users/{user_id}/threads/{thread_id}/user-data/...`, `deer-flow/skills/` +- Translation: `replace_virtual_path()` / `replace_virtual_paths_in_command()` +- Detection: `is_local_sandbox()` checks `sandbox_id == "local"` + +**Sandbox Tools** (in `packages/harness/deerflow/sandbox/tools.py`): +- `bash` - Execute commands with path translation and error handling +- `ls` - Directory listing (tree format, max 2 levels) +- `read_file` - Read file contents with optional line range +- `write_file` - Write/append to files, creates directories +- `str_replace` - Substring replacement (single or all occurrences); same-path serialization is scoped to `(sandbox.id, path)` so isolated sandboxes do not contend on identical virtual paths inside one process + +### Subagent System (`packages/harness/deerflow/subagents/`) + +**Built-in Agents**: `general-purpose` (all tools except `task`) and `bash` (command specialist) +**Execution**: Dual thread pool - `_scheduler_pool` (3 workers) + `_execution_pool` (3 workers) +**Concurrency**: `MAX_CONCURRENT_SUBAGENTS = 3` enforced by `SubagentLimitMiddleware` (truncates excess tool calls in `after_model`), 15-minute timeout +**Flow**: `task()` tool → `SubagentExecutor` → background thread → poll 5s → SSE events → result +**Events**: `task_started`, `task_running`, `task_completed`/`task_failed`/`task_timed_out` + +### Tool System (`packages/harness/deerflow/tools/`) + +`get_available_tools(groups, include_mcp, model_name, subagent_enabled)` assembles: +1. **Config-defined tools** - Resolved from `config.yaml` via `resolve_variable()` +2. **MCP tools** - From enabled MCP servers (lazy initialized, cached with mtime invalidation) +3. **Built-in tools**: + - `present_files` - Make output files visible to user (only `/mnt/user-data/outputs`) + - `ask_clarification` - Request clarification (intercepted by ClarificationMiddleware → interrupts) + - `view_image` - Read image as base64 (added only if model supports vision) +4. **Subagent tool** (if enabled): + - `task` - Delegate to subagent (description, prompt, subagent_type, max_turns) + +**Community tools** (`packages/harness/deerflow/community/`): +- `tavily/` - Web search (5 results default) and web fetch (4KB limit) +- `jina_ai/` - Web fetch via Jina reader API with readability extraction +- `firecrawl/` - Web scraping via Firecrawl API + +**ACP agent tools**: +- `invoke_acp_agent` - Invokes external ACP-compatible agents from `config.yaml` +- ACP launchers must be real ACP adapters. The standard `codex` CLI is not ACP-compatible by itself; configure a wrapper such as `npx -y @zed-industries/codex-acp` or an installed `codex-acp` binary +- Missing ACP executables now return an actionable error message instead of a raw `[Errno 2]` +- Each ACP agent uses a per-thread workspace at `{base_dir}/users/{user_id}/threads/{thread_id}/acp-workspace/`. The workspace is accessible to the lead agent via the virtual path `/mnt/acp-workspace/` (read-only). In docker sandbox mode, the directory is volume-mounted into the container at `/mnt/acp-workspace` (read-only); in local sandbox mode, path translation is handled by `tools.py` +- `image_search/` - Image search via DuckDuckGo + +### MCP System (`packages/harness/deerflow/mcp/`) + +- Uses `langchain-mcp-adapters` `MultiServerMCPClient` for multi-server management +- **Lazy initialization**: Tools loaded on first use via `get_cached_mcp_tools()` +- **Cache invalidation**: Detects config file changes via mtime comparison +- **Transports**: stdio (command-based), SSE, HTTP +- **OAuth (HTTP/SSE)**: Supports token endpoint flows (`client_credentials`, `refresh_token`) with automatic token refresh + Authorization header injection +- **Runtime updates**: Gateway API saves to extensions_config.json; LangGraph detects via mtime + +### Skills System (`packages/harness/deerflow/skills/`) + +- **Location**: `deer-flow/skills/{public,custom}/` +- **Format**: Directory with `SKILL.md` (YAML frontmatter: name, description, license, allowed-tools) +- **Loading**: `load_skills()` recursively scans `skills/{public,custom}` for `SKILL.md`, parses metadata, and reads enabled state from extensions_config.json +- **Injection**: Enabled skills listed in agent system prompt with container paths +- **Installation**: `POST /api/skills/install` extracts .skill ZIP archive to custom/ directory + +### Model Factory (`packages/harness/deerflow/models/factory.py`) + +- `create_chat_model(name, thinking_enabled)` instantiates LLM from config via reflection +- Supports `thinking_enabled` flag with per-model `when_thinking_enabled` overrides +- Supports vLLM-style thinking toggles via `when_thinking_enabled.extra_body.chat_template_kwargs.enable_thinking` for Qwen reasoning models, while normalizing legacy `thinking` configs for backward compatibility +- Supports `supports_vision` flag for image understanding models +- Config values starting with `$` resolved as environment variables +- Missing provider modules surface actionable install hints from reflection resolvers (for example `uv add langchain-google-genai`) + +### vLLM Provider (`packages/harness/deerflow/models/vllm_provider.py`) + +- `VllmChatModel` subclasses `langchain_openai:ChatOpenAI` for vLLM 0.19.0 OpenAI-compatible endpoints +- Preserves vLLM's non-standard assistant `reasoning` field on full responses, streaming deltas, and follow-up tool-call turns +- Designed for configs that enable thinking through `extra_body.chat_template_kwargs.enable_thinking` on vLLM 0.19.0 Qwen reasoning models, while accepting the older `thinking` alias + +### IM Channels System (`app/channels/`) + +Bridges external messaging platforms (Feishu, Slack, Telegram, DingTalk) to the DeerFlow agent via the LangGraph Server. + + +**Architecture**: Channels communicate with Gateway through the `langgraph-sdk` HTTP client (same as the frontend), ensuring threads are created and managed server-side. The internal SDK client injects process-local internal auth plus a matching CSRF cookie/header pair so Gateway accepts state-changing thread/run requests from channel workers without relying on browser session cookies. + +**Components**: +- `message_bus.py` - Async pub/sub hub (`InboundMessage` → queue → dispatcher; `OutboundMessage` → callbacks → channels) +- `store.py` - JSON-file persistence mapping `channel_name:chat_id[:topic_id]` → `thread_id` (keys are `channel:chat` for root conversations and `channel:chat:topic` for threaded conversations) +- `manager.py` - Core dispatcher: creates threads via `client.threads.create()`, routes commands, keeps Slack/Telegram on `client.runs.wait()`, and uses `client.runs.stream(["messages-tuple", "values"])` for Feishu incremental outbound updates +- `base.py` - Abstract `Channel` base class (start/stop/send lifecycle) +- `service.py` - Manages lifecycle of all configured channels from `config.yaml` +- `slack.py` / `feishu.py` / `telegram.py` / `dingtalk.py` - Platform-specific implementations (`feishu.py` tracks the running card `message_id` in memory and patches the same card in place; `dingtalk.py` optionally uses AI Card streaming for in-place updates when `card_template_id` is configured) + +**Message Flow**: +1. External platform -> Channel impl -> `MessageBus.publish_inbound()` +2. `ChannelManager._dispatch_loop()` consumes from queue +3. For chat: look up/create thread through Gateway's LangGraph-compatible API +4. Feishu chat: `runs.stream()` → accumulate AI text → publish multiple outbound updates (`is_final=False`) → publish final outbound (`is_final=True`) +5. Slack/Telegram chat: `runs.wait()` → extract final response → publish outbound +6. Feishu channel sends one running reply card up front, then patches the same card for each outbound update (card JSON sets `config.update_multi=true` for Feishu's patch API requirement) +7. DingTalk AI Card mode (when `card_template_id` configured): `runs.stream()` → create card with initial text → stream updates via `PUT /v1.0/card/streaming` → finalize on `is_final=True`. Falls back to `sampleMarkdown` if card creation or streaming fails +8. For commands (`/new`, `/status`, `/models`, `/memory`, `/help`): handle locally or query Gateway API +9. Outbound → channel callbacks → platform reply + +**Configuration** (`config.yaml` -> `channels`): +- `langgraph_url` - LangGraph-compatible Gateway API base URL (default: `http://localhost:8001/api`) +- `gateway_url` - Gateway API URL for auxiliary commands (default: `http://localhost:8001`) +- In Docker Compose, IM channels run inside the `gateway` container, so `localhost` points back to that container. Use `http://gateway:8001/api` for `langgraph_url` and `http://gateway:8001` for `gateway_url`, or set `DEER_FLOW_CHANNELS_LANGGRAPH_URL` / `DEER_FLOW_CHANNELS_GATEWAY_URL`. +- Per-channel configs: `feishu` (app_id, app_secret), `slack` (bot_token, app_token), `telegram` (bot_token), `dingtalk` (client_id, client_secret, optional `card_template_id` for AI Card streaming) + + +### Memory System (`packages/harness/deerflow/agents/memory/`) + +**Components**: +- `updater.py` - LLM-based memory updates with fact extraction, whitespace-normalized fact deduplication (trims leading/trailing whitespace before comparing), and atomic file I/O +- `queue.py` - Debounced update queue (per-thread deduplication, configurable wait time); captures `user_id` at enqueue time so it survives the `threading.Timer` boundary +- `prompt.py` - Prompt templates for memory updates +- `storage.py` - File-based storage with per-user isolation; cache keyed by `(user_id, agent_name)` tuple + +**Per-User Isolation**: +- Memory is stored per-user at `{base_dir}/users/{user_id}/memory.json` +- Per-agent per-user memory at `{base_dir}/users/{user_id}/agents/{agent_name}/memory.json` +- `user_id` is resolved via `get_effective_user_id()` from `deerflow.runtime.user_context` +- In no-auth mode, `user_id` defaults to `"default"` (constant `DEFAULT_USER_ID`) +- Absolute `storage_path` in config opts out of per-user isolation +- **Migration**: Run `PYTHONPATH=. python scripts/migrate_user_isolation.py` to move legacy `memory.json` and `threads/` into per-user layout; supports `--dry-run` + +**Data Structure** (stored in `{base_dir}/users/{user_id}/memory.json`): +- **User Context**: `workContext`, `personalContext`, `topOfMind` (1-3 sentence summaries) +- **History**: `recentMonths`, `earlierContext`, `longTermBackground` +- **Facts**: Discrete facts with `id`, `content`, `category` (preference/knowledge/context/behavior/goal), `confidence` (0-1), `createdAt`, `source` + +**Workflow**: +1. `MemoryMiddleware` filters messages (user inputs + final AI responses), captures `user_id` via `get_effective_user_id()`, and queues conversation with the captured `user_id` +2. Queue debounces (30s default), batches updates, deduplicates per-thread +3. Background thread invokes LLM to extract context updates and facts, using the stored `user_id` (not the contextvar, which is unavailable on timer threads) +4. Applies updates atomically (temp file + rename) with cache invalidation, skipping duplicate fact content before append +5. Next interaction injects top 15 facts + context into `` tags in system prompt + +Focused regression coverage for the updater lives in `backend/tests/test_memory_updater.py`. + +**Configuration** (`config.yaml` → `memory`): +- `enabled` / `injection_enabled` - Master switches +- `storage_path` - Path to memory.json (absolute path opts out of per-user isolation) +- `debounce_seconds` - Wait time before processing (default: 30) +- `model_name` - LLM for updates (null = default model) +- `max_facts` / `fact_confidence_threshold` - Fact storage limits (100 / 0.7) +- `max_injection_tokens` - Token limit for prompt injection (2000) + +### Reflection System (`packages/harness/deerflow/reflection/`) + +- `resolve_variable(path)` - Import module and return variable (e.g., `module.path:variable_name`) +- `resolve_class(path, base_class)` - Import and validate class against base class + +### Config Schema + +**`config.yaml`** key sections: +- `models[]` - LLM configs with `use` class path, `supports_thinking`, `supports_vision`, provider-specific fields +- vLLM reasoning models should use `deerflow.models.vllm_provider:VllmChatModel`; for Qwen-style parsers prefer `when_thinking_enabled.extra_body.chat_template_kwargs.enable_thinking`, and DeerFlow will also normalize the older `thinking` alias +- `tools[]` - Tool configs with `use` variable path and `group` +- `tool_groups[]` - Logical groupings for tools +- `sandbox.use` - Sandbox provider class path +- `skills.path` / `skills.container_path` - Host and container paths to skills directory +- `title` - Auto-title generation (enabled, max_words, max_chars, prompt_template) +- `summarization` - Context summarization (enabled, trigger conditions, keep policy) +- `subagents.enabled` - Master switch for subagent delegation +- `memory` - Memory system (enabled, storage_path, debounce_seconds, model_name, max_facts, fact_confidence_threshold, injection_enabled, max_injection_tokens) + +**`extensions_config.json`**: +- `mcpServers` - Map of server name → config (enabled, type, command, args, env, url, headers, oauth, description) +- `skills` - Map of skill name → state (enabled) + +Both can be modified at runtime via Gateway API endpoints or `DeerFlowClient` methods. + +### Embedded Client (`packages/harness/deerflow/client.py`) + +`DeerFlowClient` provides direct in-process access to all DeerFlow capabilities without HTTP services. All return types align with the Gateway API response schemas, so consumer code works identically in HTTP and embedded modes. + +**Architecture**: Imports the same `deerflow` modules that Gateway API uses. Shares the same config files and data directories. No FastAPI dependency. + +**Agent Conversation**: +- `chat(message, thread_id)` — synchronous, accumulates streaming deltas per message-id and returns the final AI text +- `stream(message, thread_id)` — subscribes to LangGraph `stream_mode=["values", "messages", "custom"]` and yields `StreamEvent`: + - `"values"` — full state snapshot (title, messages, artifacts); AI text already delivered via `messages` mode is **not** re-synthesized here to avoid duplicate deliveries + - `"messages-tuple"` — per-chunk update: for AI text this is a **delta** (concat per `id` to rebuild the full message); tool calls and tool results are emitted once each + - `"custom"` — forwarded from `StreamWriter` + - `"end"` — stream finished (carries cumulative `usage` counted once per message id) +- Agent created lazily via `create_agent()` + `_build_middlewares()`, same as `make_lead_agent` +- Supports `checkpointer` parameter for state persistence across turns +- `reset_agent()` forces agent recreation (e.g. after memory or skill changes) +- See [docs/STREAMING.md](docs/STREAMING.md) for the full design: why Gateway and DeerFlowClient are parallel paths, LangGraph's `stream_mode` semantics, the per-id dedup invariants, and regression testing strategy + +**Gateway Equivalent Methods** (replaces Gateway API): + +| Category | Methods | Return format | +|----------|---------|---------------| +| Models | `list_models()`, `get_model(name)` | `{"models": [...]}`, `{name, display_name, ...}` | +| MCP | `get_mcp_config()`, `update_mcp_config(servers)` | `{"mcp_servers": {...}}` | +| Skills | `list_skills()`, `get_skill(name)`, `update_skill(name, enabled)`, `install_skill(path)` | `{"skills": [...]}` | +| Memory | `get_memory()`, `reload_memory()`, `get_memory_config()`, `get_memory_status()` | dict | +| Uploads | `upload_files(thread_id, files)`, `list_uploads(thread_id)`, `delete_upload(thread_id, filename)` | `{"success": true, "files": [...]}`, `{"files": [...], "count": N}` | +| Artifacts | `get_artifact(thread_id, path)` → `(bytes, mime_type)` | tuple | + +**Key difference from Gateway**: Upload accepts local `Path` objects instead of HTTP `UploadFile`, rejects directory paths before copying, and reuses a single worker when document conversion must run inside an active event loop. Artifact returns `(bytes, mime_type)` instead of HTTP Response. The new Gateway-only thread cleanup route deletes `.deer-flow/threads/{thread_id}` after LangGraph thread deletion; there is no matching `DeerFlowClient` method yet. `update_mcp_config()` and `update_skill()` automatically invalidate the cached agent. + +**Tests**: `tests/test_client.py` (77 unit tests including `TestGatewayConformance`), `tests/test_client_live.py` (live integration tests, requires config.yaml) + +**Gateway Conformance Tests** (`TestGatewayConformance`): Validate that every dict-returning client method conforms to the corresponding Gateway Pydantic response model. Each test parses the client output through the Gateway model — if Gateway adds a required field that the client doesn't provide, Pydantic raises `ValidationError` and CI catches the drift. Covers: `ModelsListResponse`, `ModelResponse`, `SkillsListResponse`, `SkillResponse`, `SkillInstallResponse`, `McpConfigResponse`, `UploadResponse`, `MemoryConfigResponse`, `MemoryStatusResponse`. + +## Development Workflow + +### Test-Driven Development (TDD) — MANDATORY + +**Every new feature or bug fix MUST be accompanied by unit tests. No exceptions.** + +- Write tests in `backend/tests/` following the existing naming convention `test_.py` +- Run the full suite before and after your change: `make test` +- Tests must pass before a feature is considered complete +- For lightweight config/utility modules, prefer pure unit tests with no external dependencies +- If a module causes circular import issues in tests, add a `sys.modules` mock in `tests/conftest.py` (see existing example for `deerflow.subagents.executor`) + +```bash +# Run all tests +make test + +# Run a specific test file +PYTHONPATH=. uv run pytest tests/test_.py -v +``` + +### Running the Full Application + +From the **project root** directory: +```bash +make dev +``` + +This starts all services and makes the application available at `http://localhost:2026`. + +**All startup modes:** + +| | **Local Foreground** | **Local Daemon** | **Docker Dev** | **Docker Prod** | +|---|---|---|---|---| +| **Dev** | `./scripts/serve.sh --dev`
`make dev` | `./scripts/serve.sh --dev --daemon`
`make dev-daemon` | `./scripts/docker.sh start`
`make docker-start` | — | +| **Prod** | `./scripts/serve.sh --prod`
`make start` | `./scripts/serve.sh --prod --daemon`
`make start-daemon` | — | `./scripts/deploy.sh`
`make up` | + +| Action | Local | Docker Dev | Docker Prod | +|---|---|---|---| +| **Stop** | `./scripts/serve.sh --stop`
`make stop` | `./scripts/docker.sh stop`
`make docker-stop` | `./scripts/deploy.sh down`
`make down` | +| **Restart** | `./scripts/serve.sh --restart [flags]` | `./scripts/docker.sh restart` | — | + +**Nginx routing**: +- `/api/langgraph/*` → Gateway embedded runtime (8001), rewritten to `/api/*` +- `/api/*` (other) → Gateway API (8001) +- `/` (non-API) → Frontend (3000) + +### Running Backend Services Separately + +From the **backend** directory: + +```bash +# Gateway API +make gateway +``` + +Direct access (without nginx): +- Gateway: `http://localhost:8001` + +### Frontend Configuration + +The frontend uses environment variables to connect to backend services: +- `NEXT_PUBLIC_LANGGRAPH_BASE_URL` - Defaults to `/api/langgraph` (through nginx) +- `NEXT_PUBLIC_BACKEND_BASE_URL` - Defaults to empty string (through nginx) + +When using `make dev` from root, the frontend automatically connects through nginx. + +## Key Features + +### File Upload + +Multi-file upload with automatic document conversion: +- Endpoint: `POST /api/threads/{thread_id}/uploads` +- Supports: PDF, PPT, Excel, Word documents (converted via `markitdown`) +- Rejects directory inputs before copying so uploads stay all-or-nothing +- Reuses one conversion worker per request when called from an active event loop +- Files stored in thread-isolated directories +- Agent receives uploaded file list via `UploadsMiddleware` + +See [docs/FILE_UPLOAD.md](docs/FILE_UPLOAD.md) for details. + +### Plan Mode + +TodoList middleware for complex multi-step tasks: +- Controlled via runtime config: `config.configurable.is_plan_mode = True` +- Provides `write_todos` tool for task tracking +- One task in_progress at a time, real-time updates + +See [docs/plan_mode_usage.md](docs/plan_mode_usage.md) for details. + +### Context Summarization + +Automatic conversation summarization when approaching token limits: +- Configured in `config.yaml` under `summarization` key +- Trigger types: tokens, messages, or fraction of max input +- Keeps recent messages while summarizing older ones + +See [docs/summarization.md](docs/summarization.md) for details. + +### Vision Support + +For models with `supports_vision: true`: +- `ViewImageMiddleware` processes images in conversation +- `view_image_tool` added to agent's toolset +- Images automatically converted to base64 and injected into state + +## Code Style + +- Uses `ruff` for linting and formatting +- Line length: 240 characters +- Python 3.12+ with type hints +- Double quotes, space indentation + +## Documentation + +See `docs/` directory for detailed documentation: +- [CONFIGURATION.md](docs/CONFIGURATION.md) - Configuration options +- [ARCHITECTURE.md](docs/ARCHITECTURE.md) - Architecture details +- [API.md](docs/API.md) - API reference +- [SETUP.md](docs/SETUP.md) - Setup guide +- [FILE_UPLOAD.md](docs/FILE_UPLOAD.md) - File upload feature - [PATH_EXAMPLES.md](docs/PATH_EXAMPLES.md) - Path types and usage - [summarization.md](docs/summarization.md) - Context summarization - [plan_mode_usage.md](docs/plan_mode_usage.md) - Plan mode with TodoList diff --git a/offline-backend-20260512/backend/README.md b/offline-backend-20260512/backend/README.md index c8119794..40e7e03b 100644 --- a/offline-backend-20260512/backend/README.md +++ b/offline-backend-20260512/backend/README.md @@ -1,425 +1,425 @@ -# 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) and `AioSandboxProvider` (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.md` files under `skills/{public,custom}` and preserves nested container paths -- **File-write safety**: `str_replace` serializes 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` (`bash` is disabled by default when using `LocalSandboxProvider`; use `AioSandboxProvider` for isolated shell access) - -### Subagent System - -Async task delegation with concurrent execution: - -- **Built-in agents**: `general-purpose` (full toolset) and `bash` (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 /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 | - -### 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. - -### 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](https://docs.astral.sh/uv/) package manager -- API keys for your chosen LLM provider - -### Installation - -```bash -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: - -```yaml -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: - -```bash -export OPENAI_API_KEY="your-api-key-here" -``` - -### Running - -**Full Application** (from project root): - -```bash -make dev # Starts LangGraph + Gateway + Frontend + Nginx -``` - -Access at: http://localhost:2026 - -**Backend Only** (from backend directory): - -```bash -# 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 flags -- `tools` - Tool definitions with module paths and groups -- `tool_groups` - Logical tool groupings -- `sandbox` - Execution environment provider -- `skills` - Skills directory paths -- `title` - Auto-title generation settings -- `summarization` - Context summarization settings -- `subagents` - Subagent system (enabled/disabled) -- `memory` - Memory system settings (enabled, storage, debounce, facts limits) - -Provider note: -- `models[*].use` references provider classes by module path (for example `langchain_openai:ChatOpenAI`). -- If a provider module is missing, DeerFlow now returns an actionable error with install guidance (for example `uv add langchain-google-genai`). - -### Extensions Configuration (`extensions_config.json`) - -MCP servers and skill states in a single file: - -```json -{ - "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 location -- `DEER_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](https://smith.langchain.com) integration for observability. When enabled, all LLM calls, agent runs, tool executions, and middleware processing are traced and visible in the LangSmith dashboard. - -**Setup:** - -1. Sign up at [smith.langchain.com](https://smith.langchain.com) and create a project. -2. Add the following to your `.env` file in the project root: - -```bash -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](https://langfuse.com) observability for LangChain-compatible runs. - -Add the following to your `.env` file: - -```bash -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 - -```bash -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 - -```bash -uv run pytest -``` - ---- - -## 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](docs/CONFIGURATION.md) -- [Architecture Details](docs/ARCHITECTURE.md) -- [API Reference](docs/API.md) -- [File Upload](docs/FILE_UPLOAD.md) +# 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) and `AioSandboxProvider` (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.md` files under `skills/{public,custom}` and preserves nested container paths +- **File-write safety**: `str_replace` serializes 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` (`bash` is disabled by default when using `LocalSandboxProvider`; use `AioSandboxProvider` for isolated shell access) + +### Subagent System + +Async task delegation with concurrent execution: + +- **Built-in agents**: `general-purpose` (full toolset) and `bash` (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 /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 | + +### 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. + +### 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](https://docs.astral.sh/uv/) package manager +- API keys for your chosen LLM provider + +### Installation + +```bash +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: + +```yaml +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: + +```bash +export OPENAI_API_KEY="your-api-key-here" +``` + +### Running + +**Full Application** (from project root): + +```bash +make dev # Starts LangGraph + Gateway + Frontend + Nginx +``` + +Access at: http://localhost:2026 + +**Backend Only** (from backend directory): + +```bash +# 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 flags +- `tools` - Tool definitions with module paths and groups +- `tool_groups` - Logical tool groupings +- `sandbox` - Execution environment provider +- `skills` - Skills directory paths +- `title` - Auto-title generation settings +- `summarization` - Context summarization settings +- `subagents` - Subagent system (enabled/disabled) +- `memory` - Memory system settings (enabled, storage, debounce, facts limits) + +Provider note: +- `models[*].use` references provider classes by module path (for example `langchain_openai:ChatOpenAI`). +- If a provider module is missing, DeerFlow now returns an actionable error with install guidance (for example `uv add langchain-google-genai`). + +### Extensions Configuration (`extensions_config.json`) + +MCP servers and skill states in a single file: + +```json +{ + "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 location +- `DEER_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](https://smith.langchain.com) integration for observability. When enabled, all LLM calls, agent runs, tool executions, and middleware processing are traced and visible in the LangSmith dashboard. + +**Setup:** + +1. Sign up at [smith.langchain.com](https://smith.langchain.com) and create a project. +2. Add the following to your `.env` file in the project root: + +```bash +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](https://langfuse.com) observability for LangChain-compatible runs. + +Add the following to your `.env` file: + +```bash +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 + +```bash +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 + +```bash +uv run pytest +``` + +--- + +## 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](docs/CONFIGURATION.md) +- [Architecture Details](docs/ARCHITECTURE.md) +- [API Reference](docs/API.md) +- [File Upload](docs/FILE_UPLOAD.md) - [Path Examples](docs/PATH_EXAMPLES.md) - [Context Summarization](docs/summarization.md) - [Plan Mode](docs/plan_mode_usage.md) - [Backend Run/Stop Guide (ZH)](docs/BACKEND_RUN_STOP_ZH.md) - [Setup Guide](docs/SETUP.md) - ---- - -## License - -See the [LICENSE](../LICENSE) file in the project root. - -## Contributing - -See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines. + +--- + +## License + +See the [LICENSE](../LICENSE) file in the project root. + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines. diff --git a/offline-backend-20260512/backend/docs/houduankai.md b/offline-backend-20260512/backend/docs/houduankai.md new file mode 100644 index 00000000..64a5e8d9 --- /dev/null +++ b/offline-backend-20260512/backend/docs/houduankai.md @@ -0,0 +1,825 @@ +# DeerFlow 后端开发文档 + +> 本文档面向开发人员,梳理后端项目结构、核心模块、API 接口、配置系统和常用开发命令,方便快速上手和维护。 + +--- + +## 目录 + +1. [项目概述](#1-项目概述) +2. [技术栈](#2-技术栈) +3. [目录结构](#3-目录结构) +4. [架构分层](#4-架构分层) +5. [快速启动](#5-快速启动) +6. [配置系统](#6-配置系统) +7. [核心模块详解](#7-核心模块详解) +8. [API 接口汇总](#8-api-接口汇总) +9. [Agent 系统](#9-agent-系统) +10. [中间件链](#10-中间件链) +11. [沙箱执行系统](#11-沙箱执行系统) +12. [内存系统](#12-内存系统) +13. [工具系统](#13-工具系统) +14. [IM 频道集成](#14-im-频道集成) +15. [数据持久化](#15-数据持久化) +16. [测试](#16-测试) +17. [常见问题与排错](#17-常见问题与排错) + +--- + +## 1. 项目概述 + +DeerFlow 是一个基于 **LangGraph** 的 AI 超级 Agent 系统。后端提供: + +- **Lead Agent**:具备沙箱执行、持久内存、子 Agent 委托和可扩展工具集成的主 Agent +- **Gateway API**:FastAPI REST API(端口 8001),同时内嵌 LangGraph 兼容的 Agent 运行时 +- **IM 频道集成**:飞书、Slack、Telegram、钉钉、Discord、微信、企业微信 +- **每线程隔离**:沙箱和工作目录按 `用户 / 线程` 两级隔离 + +整体入口(通过 Nginx,端口 2026): +- `/api/langgraph/*` → 内嵌 LangGraph 运行时(转发到 Gateway `/api/*`) +- `/api/*` → Gateway REST API(8001) +- `/` → 前端(3000) + +--- + +## 2. 技术栈 + +| 层次 | 技术 | +|------|------| +| Web 框架 | FastAPI + Uvicorn | +| Agent 框架 | LangGraph(状态图 + 检查点) | +| LLM 调用 | LangChain(多模型适配) | +| 数据库 | SQLite(默认)/ PostgreSQL | +| ORM | SQLAlchemy + Alembic(迁移) | +| 包管理 | uv(workspace 模式) | +| 测试 | pytest | +| 代码规范 | ruff(检查 + 格式化) | +| Python 版本 | 3.12+ | + +--- + +## 3. 目录结构 + +``` +backend/ +├── Makefile # 开发命令入口 +├── pyproject.toml # 项目配置和依赖 +├── config.yaml # 主配置文件(模型、沙箱、工具等) +├── langgraph.json # LangGraph Studio 图配置 +├── ruff.toml # 代码风格配置(行长 240) +│ +├── app/ # 应用层(FastAPI + IM 集成) +│ ├── gateway/ # FastAPI 网关 API +│ │ ├── app.py # 应用主入口,创建 FastAPI 实例,挂载所有路由 +│ │ ├── auth/ # JWT 身份认证模块 +│ │ ├── routers/ # 16 个 API 路由模块 +│ │ ├── auth_middleware.py # 认证中间件 +│ │ ├── csrf_middleware.py # CSRF 保护中间件 +│ │ └── deps.py # FastAPI 依赖注入 +│ └── channels/ # IM 平台集成 +│ ├── manager.py # 频道管理器(核心调度) +│ ├── message_bus.py # 消息总线(pub/sub) +│ ├── store.py # 线程持久化(chat → thread_id 映射) +│ ├── feishu.py # 飞书集成 +│ ├── slack.py # Slack 集成 +│ ├── telegram.py # Telegram 集成 +│ ├── dingtalk.py # 钉钉集成 +│ ├── discord.py # Discord 集成 +│ ├── wechat.py # 微信集成 +│ └── wecom.py # 企业微信集成 +│ +├── packages/harness/ # deerflow-harness 核心包(可独立发布) +│ └── deerflow/ # 导入前缀:deerflow.* +│ ├── agents/ # Agent 系统 +│ │ ├── lead_agent/ # 主 Agent(工厂 + 提示词) +│ │ ├── middlewares/ # 18 个中间件 +│ │ ├── memory/ # 内存提取、队列、存储 +│ │ └── thread_state.py # ThreadState 数据模型 +│ ├── config/ # 配置系统(25 个模块) +│ ├── sandbox/ # 沙箱执行(本地 + Docker) +│ ├── tools/ # 工具工厂 + 内置工具 +│ ├── subagents/ # 子 Agent 委托系统 +│ ├── mcp/ # Model Context Protocol 集成 +│ ├── models/ # LLM 模型工厂 +│ ├── skills/ # 技能发现和加载 +│ ├── runtime/ # 运行时管理(检查点、流、调度) +│ ├── persistence/ # 数据库 ORM 和迁移 +│ ├── community/ # 社区工具(Tavily、Jina、Firecrawl 等) +│ ├── guardrails/ # 护栏系统(工具调用授权) +│ ├── reflection/ # 动态模块加载 +│ ├── uploads/ # 文件上传和文档转换 +│ ├── utils/ # 网络、可读性等工具函数 +│ └── client.py # 嵌入式 Python 客户端(无需 HTTP) +│ +├── tests/ # 测试套件 +├── scripts/ # 辅助脚本 +├── docs/ # 文档目录 +└── skills/ # 技能目录 + ├── public/ # 公开技能(提交到 git) + └── custom/ # 自定义技能(git 忽略) +``` + +--- + +## 4. 架构分层 + +### Harness / App 分层原则 + +后端严格分为两层,依赖方向单向: + +``` +app.* →(允许导入)→ deerflow.* +deerflow.* ×(禁止导入)× app.* +``` + +- **Harness**(`packages/harness/deerflow/`):可发布的 Agent 框架包,包含 Agent 编排、工具、沙箱、模型、MCP、技能、配置——一切构建和运行 Agent 所需的内容。 +- **App**(`app/`):不对外发布的应用代码,包含 FastAPI Gateway API 和 IM 频道集成。 + +此边界通过 `tests/test_harness_boundary.py` 在 CI 中强制检查。 + +### 运行时数据流 + +``` +前端 / IM 平台 + ↓ +Nginx(2026) + ↓ +Gateway API(8001)— FastAPI + ↓ +RunManager + run_agent() + StreamBridge + ↓ +Lead Agent(LangGraph 状态图) + ↓ +[中间件链] → [LLM] → [工具调用] → [沙箱执行] +``` + +--- + +## 5. 快速启动 + +### 环境要求 + +- Python 3.12+ +- [uv](https://github.com/astral-sh/uv)(包管理器) +- 可选:Docker(用于容器化沙箱) + +### 安装依赖 + +```bash +# 在 backend/ 目录下 +make install +# 等价于:uv sync +``` + +### 启动开发服务器 + +```bash +# 方式一:仅启动后端 Gateway(带热重载,端口 8001) +make dev + +# 方式二:启动完整应用(含前端和 Nginx) +# 在项目根目录执行 +make dev +``` + +启动后访问: +- API 文档:`http://localhost:8001/docs` +- 健康检查:`http://localhost:8001/health` + +### 停止服务 + +```bash +# 项目根目录 +make stop +``` + +### 开发常用命令 + +```bash +make lint # ruff 代码检查 +make format # ruff 自动格式化 +make test # 运行全部测试 +make gateway # 启动 Gateway(不带热重载) + +# 运行特定测试文件 +PYTHONPATH=. uv run pytest tests/test_memory_updater.py -v +``` + +--- + +## 6. 配置系统 + +### 主配置文件 `config.yaml` + +**查找顺序**(优先级从高到低): +1. 显式 `config_path` 参数 +2. 环境变量 `DEER_FLOW_CONFIG_PATH` +3. `backend/config.yaml` +4. 项目根目录 `config.yaml`(**推荐放置位置**) + +**关键配置段**: + +```yaml +config_version: 1 # 配置版本,升级时自动提示 + +log_level: info + +database: + backend: sqlite # 数据库后端(sqlite / postgres) + sqlite_dir: .deer-flow/data + +sandbox: + use: deerflow.sandbox.local:LocalSandboxProvider # 沙箱提供者类路径 + allow_host_bash: false + +models: # LLM 模型配置列表 + - name: my-model # 模型标识名 + display_name: 我的模型 + use: langchain_openai:ChatOpenAI # 提供者类(通过反射加载) + model: gpt-4o + api_key: $OPENAI_API_KEY # 以 $ 开头代表读取环境变量 + base_url: https://api.openai.com/v1 + supports_vision: true + supports_thinking: false + +tools: # 工具配置 + - use: deerflow.community.tavily:tavily_search + group: search + +memory: + enabled: true + injection_enabled: true + debounce_seconds: 30 + max_facts: 100 + fact_confidence_threshold: 0.7 + max_injection_tokens: 2000 + +subagents: + enabled: true # 是否启用子 Agent 委托 + +title: + enabled: true # 是否自动生成对话标题 +``` + +### 扩展配置 `extensions_config.json` + +MCP 服务器和技能的启用状态存放在此文件: + +```json +{ + "mcpServers": { + "my-mcp-server": { + "enabled": true, + "type": "stdio", + "command": "npx", + "args": ["-y", "@my/mcp-server"] + } + }, + "skills": { + "my-skill": { "enabled": true } + } +} +``` + +**查找顺序**同 `config.yaml`,环境变量为 `DEER_FLOW_EXTENSIONS_CONFIG_PATH`。 + +### 环境变量 + +| 变量名 | 说明 | +|--------|------| +| `DEER_FLOW_CONFIG_PATH` | 指定 config.yaml 路径 | +| `DEER_FLOW_EXTENSIONS_CONFIG_PATH` | 指定 extensions_config.json 路径 | +| `GATEWAY_CORS_ORIGINS` | 允许的跨域来源(逗号分隔) | +| `GATEWAY_CORS_ALLOW_ALL` | 允许所有跨域(1/true) | +| `GATEWAY_ENABLE_DOCS` | 是否启用 `/docs` 页面(默认开启) | +| `AUTH_JWT_SECRET` | JWT 签名密钥 | +| `DEER_FLOW_AUTH_DISABLED` | 禁用认证(开发用) | + +--- + +## 7. 核心模块详解 + +### 7.1 应用主入口 `app/gateway/app.py` + +FastAPI 应用工厂,负责: +- 创建 FastAPI 实例,配置 CORS、认证、CSRF 中间件 +- 挂载全部 16 个路由模块 +- 通过 `lifespan` 管理启动/关闭钩子:初始化数据库、运行 Alembic 迁移、启动 IM 频道服务 +- 首次启动时创建管理员用户 + +### 7.2 模型工厂 `deerflow/models/factory.py` + +```python +from deerflow.models import create_chat_model + +model = create_chat_model(name="my-model", thinking_enabled=False) +``` + +- 通过反射(`resolve_variable`)按 `config.yaml` 中的 `use` 字段实例化 LangChain 模型 +- 支持 `thinking_enabled` 标志和按模型的 `when_thinking_enabled` 覆盖 +- 支持 vLLM Qwen 推理模型的 `enable_thinking` 参数 +- 配置值以 `$` 开头时自动解析为环境变量 +- 缺少提供者模块时给出可操作的安装提示 + +### 7.3 嵌入式客户端 `deerflow/client.py` + +无需启动 HTTP 服务即可直接调用所有 DeerFlow 能力: + +```python +from deerflow.client import DeerFlowClient + +client = DeerFlowClient() + +# 同步对话 +text = client.chat("你好", thread_id="thread-1") + +# 流式对话 +for event in client.stream("分析这份文档", thread_id="thread-1"): + print(event) + +# 其他 API +client.list_models() +client.upload_files(thread_id, [Path("doc.pdf")]) +client.get_memory() +``` + +返回格式与 Gateway REST API 一致,方便在两种模式下复用代码。 + +--- + +## 8. API 接口汇总 + +所有接口基础路径:`http://localhost:8001`(或通过 Nginx `http://localhost:2026`) + +### 8.1 核心接口 + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/health` | 健康检查 | +| GET | `/docs` | Swagger API 文档 | + +### 8.2 线程运行(主要对话接口) + +| 方法 | 路径 | 说明 | +|------|------|------| +| POST | `/api/threads/{id}/runs/stream` | 创建运行并 SSE 流式返回 | +| POST | `/api/threads/{id}/runs/wait` | 创建运行并阻塞等待结果 | +| POST | `/api/threads/{id}/runs` | 创建后台运行 | +| GET | `/api/threads/{id}/runs` | 列出线程的所有运行 | +| GET | `/api/threads/{id}/runs/{rid}` | 获取运行详情 | +| POST | `/api/threads/{id}/runs/{rid}/cancel` | 取消运行 | +| GET | `/api/threads/{id}/runs/{rid}/join` | 加入 SSE 流 | +| GET | `/api/threads/{id}/runs/{rid}/messages` | 分页获取消息 | +| GET | `/api/threads/{id}/messages` | 线程所有消息(含反馈) | + +### 8.3 无状态运行 + +| 方法 | 路径 | 说明 | +|------|------|------| +| POST | `/api/runs/stream` | 无状态运行 + SSE 流 | +| POST | `/api/runs/wait` | 无状态运行 + 阻塞 | + +### 8.4 文件上传 + +| 方法 | 路径 | 说明 | +|------|------|------| +| POST | `/api/threads/{id}/uploads` | 上传文件(自动转换 PDF/PPT/Excel/Word) | +| GET | `/api/threads/{id}/uploads/list` | 列出已上传文件 | +| DELETE | `/api/threads/{id}/uploads/{filename}` | 删除文件 | + +### 8.5 其他接口 + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/api/models` | 列出所有可用 LLM 模型 | +| GET | `/api/models/{name}` | 模型详情 | +| GET | `/api/mcp/config` | 获取 MCP 配置 | +| PUT | `/api/mcp/config` | 更新 MCP 配置 | +| GET | `/api/skills` | 列出技能 | +| PUT | `/api/skills/{name}` | 启用/禁用技能 | +| POST | `/api/skills/install` | 安装 .skill 包 | +| GET | `/api/memory` | 获取个人内存数据 | +| POST | `/api/memory/reload` | 强制重新加载内存 | +| GET/POST/PUT/DELETE | `/api/agents` | 自定义 Agent CRUD | +| GET | `/api/threads/{id}/artifacts/{path}` | 下载工件文件 | +| POST | `/api/threads/{id}/suggestions` | 生成后续问题建议 | +| DELETE | `/api/threads/{id}` | 清理线程本地数据 | +| PUT | `/api/threads/{id}/runs/{rid}/feedback` | 提交运行反馈 | +| POST/GET | `/api/scheduled-tasks` | 定时任务管理 | +| POST | `/api/v1/auth/login` | 用户登录 | +| POST | `/api/v1/auth/register` | 用户注册 | + +--- + +## 9. Agent 系统 + +### Lead Agent + +**入口**:`deerflow.agents.lead_agent.agent:make_lead_agent`,注册在 `langgraph.json`。 + +```python +from deerflow.agents import make_lead_agent +from langgraph.types import RunnableConfig + +config = RunnableConfig(configurable={ + "thinking_enabled": False, # 是否启用扩展思考 + "model_name": "my-model", # 指定 LLM 模型 + "is_plan_mode": False, # 是否启用计划模式(TodoList) + "subagent_enabled": True, # 是否启用子 Agent 委托 +}) +agent = make_lead_agent(config) +``` + +### ThreadState 数据模型 + +`deerflow/agents/thread_state.py`: + +| 字段 | 类型 | 说明 | +|------|------|------| +| `messages` | list | LangGraph 消息列表 | +| `sandbox` | SandboxInfo | 当前线程沙箱 | +| `thread_data` | dict | 线程本地数据(路径等) | +| `title` | str | 自动生成的对话标题 | +| `artifacts` | list | 生成的工件文件 | +| `todos` | list | 计划模式下的待办事项 | +| `uploaded_files` | list | 已上传的文件 | +| `viewed_images` | list | 已查看的图像(base64) | + +### 子 Agent 系统 + +- **内置子 Agent**:`general-purpose`(所有工具,除 `task`)、`bash`(命令专家) +- **最大并发**:3 个(`SubagentLimitMiddleware` 强制限制) +- **超时时间**:15 分钟 +- **触发方式**:调用 `task` 工具委托任务 +- **执行流程**:`task()` → `SubagentExecutor` → 后台线程 → 每 5 秒轮询 → SSE 事件 + +```python +# 委托子 Agent 执行任务(在 Agent 提示词中由 LLM 自动调用) +task( + description="...", + prompt="分析这份数据并生成报告", + subagent_type="general-purpose", + max_turns=10 +) +``` + +--- + +## 10. 中间件链 + +Lead Agent 中间件按固定顺序组装,每个中间件负责一个横切关注点: + +| 顺序 | 中间件 | 作用 | +|------|--------|------| +| 1 | `ThreadDataMiddleware` | 创建每线程隔离目录(workspace/uploads/outputs) | +| 2 | `UploadsMiddleware` | 注入新上传的文件到对话 | +| 3 | `SandboxMiddleware` | 获取沙箱实例,存储 sandbox_id | +| 4 | `DanglingToolCallMiddleware` | 为中断的工具调用注入占位 ToolMessage | +| 5 | `LLMErrorHandlingMiddleware` | 将 LLM 调用失败归一化为可恢复错误 | +| 6 | `GuardrailMiddleware` | 工具调用前授权检查(可选) | +| 7 | `SandboxAuditMiddleware` | 沙箱操作安全审计日志 | +| 8 | `ToolErrorHandlingMiddleware` | 将工具异常转为错误 ToolMessage,避免中止 | +| 9 | `SummarizationMiddleware` | 接近 Token 上限时压缩上下文(可选) | +| 10 | `TodoListMiddleware` | 计划模式下的任务追踪(可选) | +| 11 | `TokenUsageMiddleware` | 记录 Token 用量指标(可选) | +| 12 | `TitleMiddleware` | 首次完整交互后自动生成标题 | +| 13 | `MemoryMiddleware` | 将对话加入内存更新队列 | +| 14 | `ViewImageMiddleware` | 调用 LLM 前注入 base64 图像(视觉模型) | +| 15 | `DeferredToolFilterMiddleware` | 隐藏延迟工具 Schema(可选) | +| 16 | `SubagentLimitMiddleware` | 限制最大并发子 Agent 数(可选) | +| 17 | `LoopDetectionMiddleware` | 检测工具调用循环并强制终止 | +| 18 | `ClarificationMiddleware` | 拦截澄清请求,中断到 END(必须最后) | + +--- + +## 11. 沙箱执行系统 + +### 概述 + +每个线程拥有独立的沙箱环境,Agent 通过沙箱执行命令和读写文件。 + +### 虚拟路径映射 + +Agent 看到的虚拟路径 → 物理路径: + +| 虚拟路径 | 物理路径 | +|----------|----------| +| `/mnt/user-data/workspace/` | `backend/.deer-flow/users/{user_id}/threads/{thread_id}/user-data/workspace/` | +| `/mnt/user-data/uploads/` | `backend/.deer-flow/users/{user_id}/threads/{thread_id}/user-data/uploads/` | +| `/mnt/user-data/outputs/` | `backend/.deer-flow/users/{user_id}/threads/{thread_id}/user-data/outputs/` | +| `/mnt/skills/` | `deer-flow/skills/` | + +### 沙箱工具 + +| 工具 | 说明 | +|------|------| +| `bash` | 执行 Shell 命令(含路径转换) | +| `ls` | 目录列表(树形格式,最多 2 层) | +| `read_file` | 读取文件内容(支持行范围) | +| `write_file` | 写入/追加文件(自动创建目录) | +| `str_replace` | 字符串替换(单次或全部) | + +### 沙箱提供者 + +- **LocalSandboxProvider**:单例本地文件系统执行(默认) +- **AioSandboxProvider**:基于 Docker 的隔离执行(需要 Docker) + +在 `config.yaml` 中切换: +```yaml +sandbox: + use: deerflow.sandbox.local:LocalSandboxProvider + # 或 Docker 沙箱: + # use: deerflow.community.aio_sandbox:AioSandboxProvider +``` + +--- + +## 12. 内存系统 + +### 功能 + +- 自动从对话中提取用户偏好、知识和上下文,存储为结构化事实 +- 下次对话时自动注入内存上下文到系统提示词 +- 每用户独立存储,按 Agent 进一步隔离 + +### 存储路径 + +``` +backend/.deer-flow/users/{user_id}/memory.json # 默认 Agent 内存 +backend/.deer-flow/users/{user_id}/agents/{agent_name}/memory.json # 自定义 Agent 内存 +``` + +### 数据结构 + +```json +{ + "workContext": "用户是一名 Python 开发者...", + "personalContext": "...", + "topOfMind": "...", + "recentMonths": "...", + "facts": [ + { + "id": "fact-001", + "content": "用户偏好使用 TypeScript", + "category": "preference", + "confidence": 0.9, + "createdAt": "2025-01-01T00:00:00Z" + } + ] +} +``` + +### 工作流程 + +1. `MemoryMiddleware` 过滤消息(用户输入 + 最终 AI 回复),捕获 `user_id`,加入更新队列 +2. 队列去抖(默认 30 秒),按线程去重 +3. 后台线程调用 LLM 提取上下文更新和事实 +4. 原子写入(临时文件 + rename),跳过重复事实 +5. 下次交互注入前 15 条事实 + 上下文到 `` 标签 + +### 配置 + +```yaml +memory: + enabled: true + injection_enabled: true + debounce_seconds: 30 # 去抖等待时间 + model_name: null # null 表示使用默认模型 + max_facts: 100 # 最大存储事实数 + fact_confidence_threshold: 0.7 # 事实置信度阈值 + max_injection_tokens: 2000 # 注入 Token 上限 +``` + +--- + +## 13. 工具系统 + +### 工具组装 + +`get_available_tools(groups, include_mcp, model_name, subagent_enabled)` 按顺序组装: + +1. **config.yaml 定义的工具**:通过 `resolve_variable()` 反射加载 +2. **MCP 工具**:来自已启用的 MCP 服务器(懒加载,带 mtime 失效) +3. **内置工具**: + - `present_files` — 将输出文件呈现给用户(仅 `/mnt/user-data/outputs`) + - `ask_clarification` — 请求澄清(由 `ClarificationMiddleware` 拦截,中断对话) + - `view_image` — 读取图像为 base64(仅视觉模型) +4. **子 Agent 工具**(启用时):`task` — 委托子 Agent + +### 社区工具 + +| 工具 | 模块 | 说明 | +|------|------|------| +| Tavily 搜索 | `deerflow.community.tavily` | Web 搜索(默认 5 条结果) | +| Tavily 获取 | `deerflow.community.tavily` | Web 页面获取(4KB 限制) | +| Jina AI | `deerflow.community.jina_ai` | 通过 Jina reader API 获取页面 | +| Firecrawl | `deerflow.community.firecrawl` | 网页爬取 | +| 图像搜索 | `deerflow.community.image_search` | DuckDuckGo 图像搜索 | + +### MCP 集成 + +- 使用 `langchain-mcp-adapters` 的 `MultiServerMCPClient` +- 懒加载:首次使用时初始化 +- 配置变更自动失效(mtime 检测) +- 支持 stdio、SSE、HTTP 传输 +- 支持 OAuth 令牌刷新(HTTP/SSE) + +--- + +## 14. IM 频道集成 + +### 消息流程 + +``` +外部平台消息 + ↓ +频道实现(feishu.py / slack.py 等) + ↓ +MessageBus.publish_inbound() + ↓ +ChannelManager._dispatch_loop() + ↓ +查找/创建线程(通过 Gateway API) + ↓ +执行 Agent 运行(stream 或 wait) + ↓ +OutboundMessage → 平台回复 +``` + +### 各平台特性 + +| 平台 | 特性 | +|------|------| +| 飞书 | 增量更新,修改同一消息卡片(`update_multi=true`) | +| Slack | 阻塞等待最终结果 | +| Telegram | 阻塞等待最终结果 | +| 钉钉 | 支持 AI 卡片流式更新(需配置 `card_template_id`) | + +### 配置(`config.yaml` → `channels`) + +```yaml +channels: + langgraph_url: http://localhost:8001/api # LangGraph 兼容 API 基础 URL + gateway_url: http://localhost:8001 # Gateway API URL + feishu: + app_id: $FEISHU_APP_ID + app_secret: $FEISHU_APP_SECRET + slack: + bot_token: $SLACK_BOT_TOKEN + app_token: $SLACK_APP_TOKEN + telegram: + bot_token: $TELEGRAM_BOT_TOKEN + dingtalk: + client_id: $DINGTALK_CLIENT_ID + client_secret: $DINGTALK_CLIENT_SECRET + card_template_id: "" # 可选,启用 AI 卡片流式 +``` + +> **Docker 部署注意**:IM 频道运行在 `gateway` 容器内,应将 `localhost` 改为服务名,例如 `langgraph_url: http://gateway:8001/api`。 + +--- + +## 15. 数据持久化 + +### 数据库 + +默认使用 SQLite,存放在 `backend/.deer-flow/data/`。可切换为 PostgreSQL: + +```yaml +database: + backend: postgres + postgres_url: postgresql://user:pass@localhost:5432/deerflow +``` + +### 主要数据表 + +| 表名 | 说明 | +|------|------| +| `agents` | 自定义 Agent 元数据(id、name、user_id、published) | +| `users` | 用户账号 | +| `thread_meta` | 线程元数据(标题、创建时间等) | +| `feedback` | 运行反馈和评分 | +| `scheduled_tasks` | 定时任务配置 | + +### 数据库迁移 + +使用 Alembic 管理 Schema 变更,应用启动时自动执行: + +```bash +# 查看迁移历史 +PYTHONPATH=. uv run alembic history + +# 手动升级到最新 +PYTHONPATH=. uv run alembic upgrade head +``` + +### 运行时文件目录 + +``` +backend/.deer-flow/ +├── data/ # SQLite 数据库 +└── users/{user_id}/ + ├── memory.json # 个人内存(默认 Agent) + ├── agents/{agent_name}/memory.json # 自定义 Agent 内存 + └── threads/{thread_id}/user-data/ + ├── workspace/ # Agent 工作目录 + ├── uploads/ # 上传的文件 + └── outputs/ # 生成的输出文件 +``` + +--- + +## 16. 测试 + +### 测试套件 + +```bash +# 运行所有测试 +make test + +# 运行特定测试文件 +PYTHONPATH=. uv run pytest tests/test_memory_updater.py -v + +# 运行特定测试函数 +PYTHONPATH=. uv run pytest tests/test_client.py::TestGatewayConformance -v +``` + +### 关键测试文件 + +| 文件 | 说明 | +|------|------| +| `tests/test_harness_boundary.py` | 确保 harness 层不导入 app 层(CI 必须通过) | +| `tests/test_memory_updater.py` | 内存提取和去重逻辑 | +| `tests/test_client.py` | 嵌入式客户端 77 个单元测试,含 Gateway 一致性验证 | +| `tests/test_client_live.py` | 需要 config.yaml 的集成测试 | +| `tests/test_docker_sandbox_mode_detection.py` | Docker 沙箱模式检测回归测试 | +| `tests/test_provisioner_kubeconfig.py` | kubeconfig 处理回归测试 | +| `tests/conftest.py` | pytest fixtures 和通用配置 | + +### TDD 原则 + +**每个新功能或 Bug 修复必须附带单元测试**,无例外。 + +- 测试文件命名:`tests/test_.py` +- 轻量配置/工具模块优先写不依赖外部服务的纯单元测试 +- 如果模块导致循环导入问题,在 `tests/conftest.py` 中添加 `sys.modules` mock + +--- + +## 17. 常见问题与排错 + +### 启动失败:找不到 config.yaml + +确认 `config.yaml` 在项目根目录或 `backend/` 目录中存在。可用环境变量指定路径: + +```bash +export DEER_FLOW_CONFIG_PATH=/path/to/config.yaml +make dev +``` + +### 模型调用失败 + +1. 确认 `config.yaml` 中对应模型的 `api_key` 环境变量已设置 +2. 确认 `base_url` 可访问 +3. 检查模型提供者包是否安装:缺少时启动日志会给出 `uv add langchain-xxx` 提示 + +### 内存功能不工作 + +检查 `config.yaml` 中: +```yaml +memory: + enabled: true + injection_enabled: true +``` + +### 文件上传后 Agent 无法使用 + +确认文件上传到正确线程,且 `UploadsMiddleware` 处于启用状态(默认启用)。上传文件存放在 `/mnt/user-data/uploads/`,Agent 可直接访问。 + +### Windows 下端口占用 + +```bash +# 查找占用 8001 端口的进程 +netstat -ano | findstr :8001 + +# 终止进程(替换 PID) +taskkill /PID /F +``` + +详见 `docs/BACKEND_RUN_STOP_ZH.md`(Windows 启停专项指南)。 + +### 如何新增 API 路由 + +1. 在 `app/gateway/routers/` 新建路由文件 +2. 在 `app/gateway/app.py` 中导入并挂载路由 +3. 编写对应测试文件 `tests/test_.py` + +### 如何新增工具 + +1. 在 `packages/harness/deerflow/community/` 或 `tools/builtins/` 中实现工具函数 +2. 在 `config.yaml` 中的 `tools` 列表添加配置,指定 `use` 类路径和 `group` +3. 工具会在下次 Agent 初始化时自动加载 + +--- + +*文档最后更新:2026-05-13*