diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..46673bd8 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,343 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +DeerFlow is an open-source **AI super agent harness** built on LangGraph/LangChain. It's a full-stack application that orchestrates sub-agents, memory, sandboxes, and extensible skills to perform complex multi-step tasks. + +**Architecture**: +- **LangGraph Server** (port 2024): Agent runtime and workflow execution +- **Gateway API** (port 8001): FastAPI REST API for models, MCP, skills, memory, artifacts, uploads +- **Frontend** (port 3000): Next.js 16 web interface +- **Nginx** (port 2026): Unified reverse proxy entry point +- **Provisioner** (port 8002, optional): Started only when sandbox is configured for provisioner/Kubernetes mode + +**Key Technologies**: +- **Backend**: Python 3.12+, LangGraph/LangChain, FastAPI, uv package manager +- **Frontend**: Next.js 16, React 19, TypeScript 5.8, Tailwind CSS 4, pnpm +- **AI/ML**: Model-agnostic (any OpenAI-compatible API), multi-model support with thinking/vision capabilities + +## Commands + +### Root Directory (Full Application) +```bash +make check # Check system requirements (Node.js 22+, pnpm, uv, nginx) +make config # Generate local configuration files from templates +make install # Install all dependencies (frontend + backend) +make setup-sandbox # Pre-pull sandbox container image (recommended for Docker sandbox) +make dev # Start all services (LangGraph + Gateway + Frontend + Nginx) on localhost:2026 +make stop # Stop all running services +make clean # Clean up processes and temporary files + +# Docker Development (Recommended) +make docker-init # Build custom k3s image with pre-cached sandbox image +make docker-start # Start Docker services (mode-aware from config.yaml) +make docker-stop # Stop Docker development services +make docker-logs # View Docker development logs +``` + +### Backend Directory (`/backend/`) +```bash +make install # Install backend dependencies with uv +make dev # Run LangGraph server only (port 2024) +make gateway # Run Gateway API only (port 8001) +make lint # Lint with ruff +make format # Format code with ruff +uv run pytest # Run backend tests +``` + +### Frontend Directory (`/frontend/`) +```bash +pnpm dev # Dev server with Turbopack (http://localhost:3000) +pnpm build # Production build +pnpm check # Lint + type check (run before committing) +pnpm lint # ESLint only +pnpm lint:fix # ESLint with auto-fix +pnpm typecheck # TypeScript type check (tsc --noEmit) +pnpm start # Start production server +``` + +## Architecture + +### Microservices 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 │ │ │ + │ │ └──────────┘ │ │ + │ └────────────────┘ │ + └────────────────────┘ +``` + +### Core Components + +**Lead Agent** (`src/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 + +**Middleware Chain** (11 middlewares in strict order): +1. **ThreadDataMiddleware** - Creates per-thread isolated directories +2. **UploadsMiddleware** - Tracks and injects newly uploaded files +3. **SandboxMiddleware** - Acquires sandbox, stores `sandbox_id` in state +4. **DanglingToolCallMiddleware** - Injects placeholder ToolMessages for interrupted tool calls +5. **SummarizationMiddleware** - Context reduction when approaching token limits +6. **TodoListMiddleware** - Task tracking with `write_todos` tool (plan mode) +7. **TitleMiddleware** - Auto-generates thread title +8. **MemoryMiddleware** - Queues conversations for async memory update +9. **ViewImageMiddleware** - Injects base64 image data before LLM call (vision support) +10. **SubagentLimitMiddleware** - Truncates excess `task` tool calls to enforce concurrency limits +11. **ClarificationMiddleware** - Intercepts `ask_clarification` tool calls, interrupts via `Command(goto=END)` + +**Sandbox System** (`src/sandbox/`): +- **Interface**: Abstract `Sandbox` with `execute_command`, `read_file`, `write_file`, `list_dir` +- **Provider Pattern**: `SandboxProvider` with `acquire`, `get`, `release` lifecycle +- **Implementations**: `LocalSandboxProvider` (local filesystem), `AioSandboxProvider` (Docker-based isolation) +- **Virtual Path System**: Agent sees `/mnt/user-data/{workspace,uploads,outputs}`, `/mnt/skills` mapped to physical paths + +**Subagent System** (`src/subagents/`): +- **Built-in Agents**: `general-purpose` (all tools except `task`) and `bash` (command specialist) +- **Concurrency**: `MAX_CONCURRENT_SUBAGENTS = 3` enforced by `SubagentLimitMiddleware` +- **Execution**: Dual thread pool - `_scheduler_pool` (3 workers) + `_execution_pool` (3 workers) +- **Flow**: `task()` tool → `SubagentExecutor` → background thread → poll 5s → SSE events → result + +**Memory System** (`src/agents/memory/`): +- **Components**: `updater.py` (LLM-based memory updates), `queue.py` (debounced update queue), `prompt.py` (templates) +- **Data Structure**: Stored in `backend/.deer-flow/memory.json` with user context, history, and facts +- **Workflow**: MemoryMiddleware filters messages → queue debounces (30s) → background LLM extracts updates → atomic file I/O + +**Tool System** (`src/tools/`): +- **Config-defined tools**: Resolved from `config.yaml` via `resolve_variable()` +- **MCP tools**: From enabled MCP servers (lazy initialized, cached with mtime invalidation) +- **Built-in tools**: `present_files`, `ask_clarification`, `view_image` (vision support) +- **Subagent tool**: `task` (delegate to subagent) +- **Community tools**: `tavily/` (web search/fetch), `jina_ai/` (web fetch), `firecrawl/` (scraping), `image_search/` (DuckDuckGo) + +**Skills System** (`src/skills/`): +- **Location**: `deer-flow/skills/{public,custom}/` +- **Format**: Directory with `SKILL.md` (YAML frontmatter: name, description, license, allowed-tools) +- **Loading**: `load_skills()` scans directories, parses SKILL.md, reads enabled state from extensions_config.json +- **Installation**: `POST /api/skills/install` extracts .skill ZIP archive to custom/ directory + +### Configuration System + +**Main Configuration** (`config.yaml` in project root): +- Setup: Copy `config.example.yaml` to `config.yaml` +- Configuration priority: explicit `config_path` → `DEER_FLOW_CONFIG_PATH` env → `config.yaml` in current dir → `config.yaml` in parent dir +- Config values starting with `$` are resolved as environment variables (e.g., `$OPENAI_API_KEY`) + +**Extensions Configuration** (`extensions_config.json` in project root): +- Contains MCP servers and skills configuration +- Configuration priority: explicit `config_path` → `DEER_FLOW_EXTENSIONS_CONFIG_PATH` env → `extensions_config.json` in current dir → `extensions_config.json` in parent dir + +**Key Config Sections**: +- `models[]` - LLM configs with `use` class path, `supports_thinking`, `supports_vision` +- `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 +- `summarization` - Context summarization +- `subagents.enabled` - Master switch for subagent delegation +- `memory` - Memory system configuration + +### Gateway API (`src/gateway/`) + +FastAPI application on port 8001 with health check at `GET /health`. + +**Routers**: +- **Models** (`/api/models`): `GET /` (list models), `GET /{name}` (model details) +- **MCP** (`/api/mcp`): `GET /config` (get config), `PUT /config` (update config) +- **Skills** (`/api/skills`): `GET /` (list), `GET /{name}` (details), `PUT /{name}` (update enabled), `POST /install` (install from .skill archive) +- **Memory** (`/api/memory`): `GET /` (memory data), `POST /reload` (force reload), `GET /config` (config), `GET /status` (config + data) +- **Uploads** (`/api/threads/{id}/uploads`): `POST /` (upload files with auto-conversion), `GET /list` (list), `DELETE /{filename}` (delete) +- **Artifacts** (`/api/threads/{id}/artifacts`): `GET /{path}` (serve artifacts), `?download=true` for file download + +### Frontend Architecture (`/frontend/`) + +**Source Layout** (`src/`): +- **`app/`** - Next.js App Router: `/` (landing), `/workspace/chats/[thread_id]` (chat) +- **`components/`** - React components: `ui/` (Shadcn UI), `ai-elements/` (Vercel AI SDK), `workspace/` (chat), `landing/` (landing) +- **`core/`** - Business logic: `threads/` (state management), `api/` (LangGraph client), `artifacts/` (loading/caching), `i18n/`, `settings/`, `memory/`, `skills/`, `messages/`, `mcp/`, `models/` +- **`hooks/`** - Shared React hooks +- **`lib/`** - Utilities (`cn()` from clsx + tailwind-merge) +- **`server/`** - Server-side code (better-auth, not yet active) +- **`styles/`** - Global CSS with Tailwind v4 `@import` syntax + +**Data Flow**: +1. User input → thread hooks (`core/threads/hooks.ts`) → LangGraph SDK streaming +2. Stream events update thread state (messages, artifacts, todos) +3. TanStack Query manages server state; localStorage stores user settings +4. Components subscribe to thread state and render updates + +## Development Workflow + +### 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`. + +**Nginx routing**: +- `/api/langgraph/*` → LangGraph Server (2024) +- `/api/*` (other) → Gateway API (8001) +- `/` (non-API) → Frontend (3000) + +### Running Services Separately +From the **backend** directory: +```bash +# Terminal 1: LangGraph server +make dev + +# Terminal 2: Gateway API +make gateway +``` + +Direct access (without nginx): +- LangGraph: `http://localhost:2024` +- Gateway: `http://localhost:8001` + +### Docker Development (Recommended) +```bash +make docker-init # Build images and install dependencies (first time) +make docker-start # Start all services with hot-reload +``` +Access at `http://localhost:2026`. Services automatically restart on code changes. + +## Key Features + +### File Upload +- Endpoint: `POST /api/threads/{thread_id}/uploads` +- Supports: PDF, PPT, Excel, Word documents (auto-converted via `markitdown`) +- Files stored in thread-isolated directories +- Agent receives uploaded file list via `UploadsMiddleware` + +### Plan Mode +- 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 + +### 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 + +### 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 + +### Backend (Python) +- Uses `ruff` for linting and formatting +- Line length: 240 characters +- Python 3.12+ with type hints +- Double quotes, space indentation + +### Frontend (TypeScript) +- **Imports**: Enforced ordering (builtin → external → internal → parent → sibling), alphabetized +- **Unused variables**: Prefix with `_` +- **Class names**: Use `cn()` from `@/lib/utils` for conditional Tailwind classes +- **Path alias**: `@/*` maps to `src/*` +- **Components**: `ui/` and `ai-elements/` are generated from registries - don't manually edit these + +## Testing + +### Backend Tests +```bash +cd backend +uv run pytest +``` +**Regression tests** (run in CI for every PR): +- `tests/test_docker_sandbox_mode_detection.py` - mode detection from `config.yaml` +- `tests/test_provisioner_kubeconfig.py` - kubeconfig file/directory handling + +### Frontend Tests +No test framework is currently configured. + +## 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 + +## 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 +│ ├── Makefile # Backend-only commands (dev, gateway, lint) +│ ├── langgraph.json # LangGraph server configuration +│ ├── src/ +│ │ ├── agents/ # LangGraph agent system +│ │ │ ├── lead_agent/ # Main agent (factory + system prompt) +│ │ │ ├── middlewares/ # 11 middleware components +│ │ │ ├── memory/ # Memory extraction, queue, prompts +│ │ │ └── thread_state.py # ThreadState schema +│ │ ├── gateway/ # FastAPI Gateway API +│ │ │ ├── app.py # FastAPI application +│ │ │ └── routers/ # 6 route modules +│ │ ├── 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) +│ ├── tests/ # Test suite +│ └── docs/ # Documentation +├── frontend/ # Next.js frontend application +│ ├── src/ +│ │ ├── app/ # Next.js App Router +│ │ ├── components/ # React components +│ │ ├── core/ # Business logic +│ │ ├── hooks/ # Shared React hooks +│ │ ├── lib/ # Utilities +│ │ ├── server/ # Server-side code +│ │ └── styles/ # Global CSS with Tailwind v4 +│ └── package.json # Next.js 16, React 19, TypeScript 5.8, pnpm +└── skills/ # Agent skills directory + ├── public/ # Public skills (committed) + └── custom/ # Custom skills (gitignored) +``` \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile index d37044b9..40bf8fc8 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -5,10 +5,22 @@ FROM python:3.12-slim RUN apt-get update && apt-get install -y \ curl \ build-essential \ + docker.io \ && rm -rf /var/lib/apt/lists/* # Install uv -RUN curl -LsSf https://astral.sh/uv/install.sh | sh +# Use IP address for proxy (cai.local may not resolve in Docker container) +ENV http_proxy=http://192.168.1.250:7897 https_proxy=http://192.168.1.250:7897 +# Exclude localhost and container network from proxy +ENV no_proxy=localhost,127.0.0.1,0.0.0.0,frontend,gateway,langgraph,nginx,.local +# RUN curl -LsSf https://astral.sh/uv/install.sh | sh +# Try to install uv via official installer, fallback to pip if fails +RUN (curl -LsSf https://astral.sh/uv/install.sh | sh || pip install uv) && \ + echo "uv installed at:" && \ + which uv || echo "uv not found in PATH" && \ + ls -la /root/.local/bin/ || echo "/root/.local/bin/ not found" && \ + echo "uv version:" && \ + uv --version || echo "uv command not working" ENV PATH="/root/.local/bin:$PATH" # Set working directory @@ -21,6 +33,9 @@ COPY backend ./backend RUN --mount=type=cache,target=/root/.cache/uv \ sh -c "cd backend && uv sync" +# Keep proxy for uv sync (Python package downloads may need proxy in China) +ENV http_proxy= https_proxy= + # Expose ports (gateway: 8001, langgraph: 2024) EXPOSE 8001 2024 diff --git a/backend/src/community/aio_sandbox/local_backend.py b/backend/src/community/aio_sandbox/local_backend.py index 047dcca2..bd3bdb2b 100644 --- a/backend/src/community/aio_sandbox/local_backend.py +++ b/backend/src/community/aio_sandbox/local_backend.py @@ -111,9 +111,12 @@ class LocalContainerBackend(SandboxBackend): release_port(port) raise + # Use host.docker.internal when running inside Docker container + # This allows containers to access the sandbox on the host + sandbox_host = "host.docker.internal" return SandboxInfo( sandbox_id=sandbox_id, - sandbox_url=f"http://localhost:{port}", + sandbox_url=f"http://{sandbox_host}:{port}", container_name=container_name, container_id=container_id, ) @@ -159,7 +162,9 @@ class LocalContainerBackend(SandboxBackend): if port is None: return None - sandbox_url = f"http://localhost:{port}" + # Use host.docker.internal when running inside Docker container + sandbox_host = "host.docker.internal" + sandbox_url = f"http://{sandbox_host}:{port}" if not wait_for_sandbox_ready(sandbox_url, timeout=5): return None diff --git a/docker/docker-compose-dev.yaml b/docker/docker-compose-dev.yaml index ee4cfb05..121d50b9 100644 --- a/docker/docker-compose-dev.yaml +++ b/docker/docker-compose-dev.yaml @@ -77,6 +77,7 @@ services: # Frontend - Next.js Development Server frontend: +# image: deer-flow-dev-frontend:latest build: context: ../ dockerfile: frontend/Dockerfile @@ -104,6 +105,7 @@ services: # Backend - Gateway API gateway: +# image: deer-flow-dev-gateway:latest build: context: ../ dockerfile: backend/Dockerfile @@ -120,9 +122,13 @@ services: - ../backend/.deer-flow:/app/backend/.deer-flow # Mount uv cache for faster dependency installation - ~/.cache/uv:/root/.cache/uv + # Mount Docker socket for aio sandbox + - /var/run/docker.sock:/var/run/docker.sock:ro working_dir: /app environment: - CI=true + # Docker environment for aio sandbox + - DOCKER_HOST=unix:///var/run/docker.sock env_file: - ../.env extra_hosts: @@ -134,6 +140,7 @@ services: # Backend - LangGraph Server langgraph: +# image: deer-flow-dev-langgraph:latest build: context: ../ dockerfile: backend/Dockerfile @@ -150,11 +157,18 @@ services: - ../backend/.deer-flow:/app/backend/.deer-flow # Mount uv cache for faster dependency installation - ~/.cache/uv:/root/.cache/uv + # Mount Docker socket for aio sandbox + - /var/run/docker.sock:/var/run/docker.sock:ro working_dir: /app environment: - CI=true + # Docker environment for aio sandbox + - DOCKER_HOST=unix:///var/run/docker.sock env_file: - ../.env + extra_hosts: + # For Linux: map host.docker.internal to host gateway + - "host.docker.internal:host-gateway" networks: - deer-flow-dev restart: unless-stopped diff --git a/docker/nginx/nginx.local.conf b/docker/nginx/nginx.local.conf index 0cdcf9a2..50e5ac69 100644 --- a/docker/nginx/nginx.local.conf +++ b/docker/nginx/nginx.local.conf @@ -14,17 +14,17 @@ http { access_log /dev/stdout; error_log /dev/stderr; - # Upstream servers (using localhost for local development) + # Upstream servers (using Docker service names for Docker Compose) upstream gateway { - server localhost:8001; + server gateway:8001; } upstream langgraph { - server localhost:2024; + server langgraph:2024; } upstream frontend { - server localhost:3000; + server frontend:3000; } server { diff --git a/memo.md b/memo.md new file mode 100644 index 00000000..08fa0a3d --- /dev/null +++ b/memo.md @@ -0,0 +1,27 @@ + +1. 网络连接问题 +local_backend.py中使用localhost访问sandbox容器 +但在Docker容器内部,localhost指向容器自身,而不是主机 +需要改为host.docker.internal +1. 修改网络配置 +文件: backend/src/community/aio_sandbox/local_backend.py + +第116行: 添加sandbox_host = "host.docker.internal" +第119行: 将sandbox_url=f"http://localhost:{port}"改为sandbox_url=f"http://{sandbox_host}:{port}" +第166-167行: 同样修改了discover方法中的URL构建 + + +2. Docker socket挂载问题 +gateway和langgraph容器需要访问Docker守护进程来启动sandbox容器 +但容器没有挂载Docker socket +2. 添加Docker socket挂载 +文件: docker/docker-compose-dev.yaml + +为gateway和langgraph服务添加: +volumes: + # Mount Docker socket for aio sandbox + - /var/run/docker.sock:/var/run/docker.sock:ro + +environment: + # Docker environment for aio sandbox + - DOCKER_HOST=unix:///var/run/docker.sock