mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-05-23 00:16:48 +00:00
Merge refactor/config-deerflow-context into release/2.0-rc
Cherry-pick PR #2271's config refactor onto release/2.0-rc. Used 'git merge -X theirs' to auto-resolve content conflicts in favor of the PR's design (frozen AppConfig + explicit-parameter passing). Limitations: - Release-only changes that overlapped with PR's refactor in 119 files are NOT preserved — those files reflect PR's version. Follow-up commits on this branch will need to re-apply release-only modifications where meaningful. - See PR #2271 for design rationale.
This commit is contained in:
@@ -55,7 +55,7 @@ import {
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { fetch } from "@/core/api/fetcher";
|
||||
import { fetchWithAuth } from "@/core/api/fetcher";
|
||||
import { getBackendBaseURL } from "@/core/config";
|
||||
import { useI18n } from "@/core/i18n/hooks";
|
||||
import { useModels } from "@/core/models/hooks";
|
||||
@@ -403,16 +403,19 @@ export function InputBox({
|
||||
setFollowupsLoading(true);
|
||||
setFollowups([]);
|
||||
|
||||
fetch(`${getBackendBaseURL()}/api/threads/${threadId}/suggestions`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
messages: recent,
|
||||
n: 3,
|
||||
model_name: context.model_name ?? undefined,
|
||||
}),
|
||||
signal: controller.signal,
|
||||
})
|
||||
fetchWithAuth(
|
||||
`${getBackendBaseURL()}/api/threads/${threadId}/suggestions`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
messages: recent,
|
||||
n: 3,
|
||||
model_name: context.model_name ?? undefined,
|
||||
}),
|
||||
signal: controller.signal,
|
||||
},
|
||||
)
|
||||
.then(async (res) => {
|
||||
if (!res.ok) {
|
||||
return { suggestions: [] as string[] };
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useState } from "react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { fetch, getCsrfHeaders } from "@/core/api/fetcher";
|
||||
import { fetchWithAuth, getCsrfHeaders } from "@/core/api/fetcher";
|
||||
import { useAuth } from "@/core/auth/AuthProvider";
|
||||
import { parseAuthError } from "@/core/auth/types";
|
||||
|
||||
@@ -36,7 +36,7 @@ export function AccountSettingsPage() {
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await fetch("/api/v1/auth/change-password", {
|
||||
const res = await fetchWithAuth("/api/v1/auth/change-password", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
|
||||
@@ -11,10 +11,10 @@ DeerFlow App is configured through two files and a set of environment variables.
|
||||
|
||||
## Configuration files
|
||||
|
||||
| File | Purpose |
|
||||
| ------------------------ | --------------------------------------------------------------------------------------- |
|
||||
| `config.yaml` | Backend configuration: models, sandbox, tools, skills, memory, and all Harness settings |
|
||||
| `extensions_config.json` | MCP servers and skill enable/disable state (managed by the App UI and Gateway API) |
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `config.yaml` | Backend configuration: models, sandbox, tools, skills, memory, and all Harness settings |
|
||||
| `extensions_config.json` | MCP servers and skill enable/disable state (managed by the App UI and Gateway API) |
|
||||
|
||||
Frontend environment variables control the Next.js build and runtime behavior.
|
||||
|
||||
@@ -144,7 +144,6 @@ sandbox:
|
||||
```
|
||||
|
||||
Install: `cd backend && uv add 'deerflow-harness[aio-sandbox]'`
|
||||
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab>
|
||||
```yaml
|
||||
@@ -162,7 +161,7 @@ Configure which tools the agent has access to. The defaults use DuckDuckGo (no A
|
||||
```yaml
|
||||
tools:
|
||||
# Web search (choose one)
|
||||
- use: deerflow.community.ddg_search.tools:web_search_tool # default, no key required
|
||||
- use: deerflow.community.ddg_search.tools:web_search_tool # default, no key required
|
||||
# - use: deerflow.community.tavily.tools:web_search_tool
|
||||
# api_key: $TAVILY_API_KEY
|
||||
|
||||
@@ -189,7 +188,7 @@ By default, DeerFlow uses an SQLite checkpointer for thread state persistence:
|
||||
```yaml
|
||||
checkpointer:
|
||||
type: sqlite
|
||||
connection_string: checkpoints.db # stored in backend/.deer-flow/
|
||||
connection_string: checkpoints.db # stored in backend/.deer-flow/
|
||||
```
|
||||
|
||||
For production deployments with multiple processes:
|
||||
@@ -225,12 +224,12 @@ memory:
|
||||
|
||||
Set these before running `pnpm build` or starting the frontend in production:
|
||||
|
||||
| Variable | Required | Description |
|
||||
| --------------------- | -------------------------- | ---------------------------------------------------------------- |
|
||||
| `BETTER_AUTH_SECRET` | **Required** in production | Secret for session signing. Use `openssl rand -base64 32`. |
|
||||
| `BETTER_AUTH_URL` | Recommended | Public-facing base URL (e.g., `https://your-domain.com`) |
|
||||
| `SKIP_ENV_VALIDATION` | Optional | Set to `1` to skip env validation during build (not recommended) |
|
||||
| `NEXT_PUBLIC_API_URL` | Optional | Override the API base URL for the frontend |
|
||||
| Variable | Required | Description |
|
||||
|---|---|---|
|
||||
| `BETTER_AUTH_SECRET` | **Required** in production | Secret for session signing. Use `openssl rand -base64 32`. |
|
||||
| `BETTER_AUTH_URL` | Recommended | Public-facing base URL (e.g., `https://your-domain.com`) |
|
||||
| `SKIP_ENV_VALIDATION` | Optional | Set to `1` to skip env validation during build (not recommended) |
|
||||
| `NEXT_PUBLIC_API_URL` | Optional | Override the API base URL for the frontend |
|
||||
|
||||
In development, set these in a `.env` file at the repo root:
|
||||
|
||||
@@ -269,12 +268,6 @@ make config-upgrade
|
||||
```
|
||||
|
||||
<Cards num={2}>
|
||||
<Cards.Card
|
||||
title="Deployment Guide"
|
||||
href="/docs/application/deployment-guide"
|
||||
/>
|
||||
<Cards.Card
|
||||
title="Harness Configuration"
|
||||
href="/docs/harness/configuration"
|
||||
/>
|
||||
<Cards.Card title="Deployment Guide" href="/docs/application/deployment-guide" />
|
||||
<Cards.Card title="Harness Configuration" href="/docs/harness/configuration" />
|
||||
</Cards>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Deployment Guide
|
||||
title: Deployment Guide
|
||||
description: "This guide covers all supported deployment methods for DeerFlow App: local development, Docker Compose, and production with Kubernetes-managed sandboxes."
|
||||
---
|
||||
|
||||
|
||||
@@ -17,15 +17,15 @@ DeerFlow App is the reference implementation of what a production DeerFlow exper
|
||||
|
||||
## What the App provides
|
||||
|
||||
| Capability | Description |
|
||||
| ----------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| **Web workspace** | Browser-based conversation UI with support for threads, artifacts, file uploads, and skill selection |
|
||||
| **Custom agents** | Create and manage named agents with different models, skills, and tool sets |
|
||||
| **Thread management** | Persistent conversation threads with checkpointing and history |
|
||||
| **Streaming responses** | Real-time token streaming with thinking steps and tool call visibility |
|
||||
| **Artifact viewer** | In-browser preview and download of files and outputs produced by the agent |
|
||||
| **Extensions UI** | Enable/disable MCP servers and skills without editing config files |
|
||||
| **Gateway API** | FastAPI-based REST API that bridges the frontend and the LangGraph runtime |
|
||||
| Capability | Description |
|
||||
|---|---|
|
||||
| **Web workspace** | Browser-based conversation UI with support for threads, artifacts, file uploads, and skill selection |
|
||||
| **Custom agents** | Create and manage named agents with different models, skills, and tool sets |
|
||||
| **Thread management** | Persistent conversation threads with checkpointing and history |
|
||||
| **Streaming responses** | Real-time token streaming with thinking steps and tool call visibility |
|
||||
| **Artifact viewer** | In-browser preview and download of files and outputs produced by the agent |
|
||||
| **Extensions UI** | Enable/disable MCP servers and skills without editing config files |
|
||||
| **Gateway API** | FastAPI-based REST API that bridges the frontend and the LangGraph runtime |
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -58,18 +58,15 @@ The DeerFlow App runs as four services behind a single nginx reverse proxy:
|
||||
|
||||
## Technology stack
|
||||
|
||||
| Layer | Technology |
|
||||
| ----------------- | -------------------------------------------------------------------- |
|
||||
| Frontend | Next.js 16, React 19, TypeScript, pnpm |
|
||||
| Gateway | FastAPI, Python 3.12, uvicorn |
|
||||
| Agent runtime | LangGraph, LangChain, DeerFlow Harness |
|
||||
| Reverse proxy | nginx |
|
||||
| Layer | Technology |
|
||||
|---|---|
|
||||
| Frontend | Next.js 16, React 19, TypeScript, pnpm |
|
||||
| Gateway | FastAPI, Python 3.12, uvicorn |
|
||||
| Agent runtime | LangGraph, LangChain, DeerFlow Harness |
|
||||
| Reverse proxy | nginx |
|
||||
| State persistence | LangGraph Server (default) + optional SQLite/PostgreSQL checkpointer |
|
||||
|
||||
<Cards num={2}>
|
||||
<Cards.Card title="Quick Start" href="/docs/application/quick-start" />
|
||||
<Cards.Card
|
||||
title="Deployment Guide"
|
||||
href="/docs/application/deployment-guide"
|
||||
/>
|
||||
<Cards.Card title="Deployment Guide" href="/docs/application/deployment-guide" />
|
||||
</Cards>
|
||||
|
||||
@@ -13,12 +13,12 @@ This page covers day-to-day operational tasks and solutions to common problems w
|
||||
|
||||
All services write logs to the `logs/` directory when started with `make dev`:
|
||||
|
||||
| File | Service |
|
||||
| -------------------- | ------------------------------------ |
|
||||
| File | Service |
|
||||
|---|---|
|
||||
| `logs/langgraph.log` | LangGraph / DeerFlow Harness runtime |
|
||||
| `logs/gateway.log` | FastAPI Gateway API |
|
||||
| `logs/frontend.log` | Next.js frontend dev server |
|
||||
| `logs/nginx.log` | nginx reverse proxy |
|
||||
| `logs/gateway.log` | FastAPI Gateway API |
|
||||
| `logs/frontend.log` | Next.js frontend dev server |
|
||||
| `logs/nginx.log` | nginx reverse proxy |
|
||||
|
||||
Tail logs in real time:
|
||||
|
||||
@@ -30,7 +30,7 @@ tail -f logs/gateway.log
|
||||
Adjust the runtime log level in `config.yaml`:
|
||||
|
||||
```yaml
|
||||
log_level: debug # debug | info | warning | error
|
||||
log_level: debug # debug | info | warning | error
|
||||
```
|
||||
|
||||
## Health checks
|
||||
@@ -171,9 +171,6 @@ make dev
|
||||
Individual service restart scripts are in `scripts/`. For targeted restarts, you can kill and relaunch individual processes manually using the PIDs in the log files.
|
||||
|
||||
<Cards num={2}>
|
||||
<Cards.Card
|
||||
title="Deployment Guide"
|
||||
href="/docs/application/deployment-guide"
|
||||
/>
|
||||
<Cards.Card title="Deployment Guide" href="/docs/application/deployment-guide" />
|
||||
<Cards.Card title="Configuration" href="/docs/application/configuration" />
|
||||
</Cards>
|
||||
|
||||
@@ -24,13 +24,13 @@ make check
|
||||
|
||||
Required:
|
||||
|
||||
| Tool | Minimum version |
|
||||
| ------- | ------------------ |
|
||||
| Python | 3.12 |
|
||||
| uv | latest |
|
||||
| Node.js | 22 |
|
||||
| pnpm | 10 |
|
||||
| nginx | any recent version |
|
||||
| Tool | Minimum version |
|
||||
|---|---|
|
||||
| Python | 3.12 |
|
||||
| uv | latest |
|
||||
| Node.js | 22 |
|
||||
| pnpm | 10 |
|
||||
| nginx | any recent version |
|
||||
|
||||
On macOS, install with `brew install python uv node pnpm nginx`. On Linux, use your distribution's package manager.
|
||||
|
||||
@@ -87,7 +87,6 @@ make dev
|
||||
```
|
||||
|
||||
This starts:
|
||||
|
||||
- LangGraph server on port `2024`
|
||||
- Gateway API on port `8001`
|
||||
- Frontend on port `3000`
|
||||
@@ -111,12 +110,12 @@ make stop
|
||||
|
||||
Log files:
|
||||
|
||||
| Service | Log file |
|
||||
| --------- | -------------------- |
|
||||
| Service | Log file |
|
||||
|---|---|
|
||||
| LangGraph | `logs/langgraph.log` |
|
||||
| Gateway | `logs/gateway.log` |
|
||||
| Frontend | `logs/frontend.log` |
|
||||
| nginx | `logs/nginx.log` |
|
||||
| Gateway | `logs/gateway.log` |
|
||||
| Frontend | `logs/frontend.log` |
|
||||
| nginx | `logs/nginx.log` |
|
||||
|
||||
<Callout type="tip">
|
||||
If something is not working, check the log files first. Most startup errors
|
||||
@@ -125,9 +124,6 @@ Log files:
|
||||
</Callout>
|
||||
|
||||
<Cards num={2}>
|
||||
<Cards.Card
|
||||
title="Deployment Guide"
|
||||
href="/docs/application/deployment-guide"
|
||||
/>
|
||||
<Cards.Card title="Deployment Guide" href="/docs/application/deployment-guide" />
|
||||
<Cards.Card title="Configuration" href="/docs/application/configuration" />
|
||||
</Cards>
|
||||
|
||||
@@ -74,9 +74,6 @@ If you have created custom agents, use the **Agent** selector in the input bar t
|
||||
Custom agents may have different models, skills, tool sets, and system prompts. See [Agents and Threads](/docs/application/agents-and-threads) for how to create and manage custom agents.
|
||||
|
||||
<Cards num={2}>
|
||||
<Cards.Card
|
||||
title="Agents and Threads"
|
||||
href="/docs/application/agents-and-threads"
|
||||
/>
|
||||
<Cards.Card title="Agents and Threads" href="/docs/application/agents-and-threads" />
|
||||
<Cards.Card title="Configuration" href="/docs/application/configuration" />
|
||||
</Cards>
|
||||
|
||||
@@ -81,7 +81,7 @@ models:
|
||||
use: langchain_openai:ChatOpenAI
|
||||
model: gpt-4o
|
||||
api_key: $OPENAI_API_KEY
|
||||
some_provider_specific_option: value # passed through to ChatOpenAI constructor
|
||||
some_provider_specific_option: value # passed through to ChatOpenAI constructor
|
||||
```
|
||||
|
||||
## Configuration version
|
||||
@@ -104,27 +104,27 @@ This merges new fields from `config.example.yaml` into your existing `config.yam
|
||||
|
||||
The following table maps each top-level `config.yaml` section to its documentation page:
|
||||
|
||||
| Section | Description | Documentation |
|
||||
| ----------------- | ------------------------------------------------ | -------------------------------------------------------- |
|
||||
| `log_level` | Logging level (`debug`/`info`/`warning`/`error`) | — |
|
||||
| `models` | Available LLM models | [Lead Agent](/docs/harness/lead-agent) |
|
||||
| `token_usage` | Token tracking per model call | [Middlewares](/docs/harness/middlewares) |
|
||||
| `tools` | Available agent tools | [Tools](/docs/harness/tools) |
|
||||
| `tool_groups` | Named groups of tools | [Tools](/docs/harness/tools) |
|
||||
| `tool_search` | Deferred/on-demand tool loading | [Tools](/docs/harness/tools) |
|
||||
| `sandbox` | Sandbox provider and options | [Sandbox](/docs/harness/sandbox) |
|
||||
| `skills` | Skills directory and container path | [Skills](/docs/harness/skills) |
|
||||
| `skill_evolution` | Agent-managed skill creation | [Skills](/docs/harness/skills) |
|
||||
| `subagents` | Subagent timeouts and max turns | [Subagents](/docs/harness/subagents) |
|
||||
| `acp_agents` | External ACP agent integrations | [Subagents](/docs/harness/subagents) |
|
||||
| `memory` | Cross-session memory storage | [Memory](/docs/harness/memory) |
|
||||
| `summarization` | Conversation summarization | [Middlewares](/docs/harness/middlewares) |
|
||||
| `title` | Automatic thread title generation | [Middlewares](/docs/harness/middlewares) |
|
||||
| `checkpointer` | Thread state persistence | [Agents & Threads](/docs/application/agents-and-threads) |
|
||||
| `guardrails` | Tool call authorization | — |
|
||||
| `stream_bridge` | Streaming configuration | — |
|
||||
| `uploads` | File upload settings (PDF converter) | — |
|
||||
| `channels` | IM channel integrations (Feishu, Slack, etc.) | — |
|
||||
| Section | Description | Documentation |
|
||||
|---|---|---|
|
||||
| `log_level` | Logging level (`debug`/`info`/`warning`/`error`) | — |
|
||||
| `models` | Available LLM models | [Lead Agent](/docs/harness/lead-agent) |
|
||||
| `token_usage` | Token tracking per model call | [Middlewares](/docs/harness/middlewares) |
|
||||
| `tools` | Available agent tools | [Tools](/docs/harness/tools) |
|
||||
| `tool_groups` | Named groups of tools | [Tools](/docs/harness/tools) |
|
||||
| `tool_search` | Deferred/on-demand tool loading | [Tools](/docs/harness/tools) |
|
||||
| `sandbox` | Sandbox provider and options | [Sandbox](/docs/harness/sandbox) |
|
||||
| `skills` | Skills directory and container path | [Skills](/docs/harness/skills) |
|
||||
| `skill_evolution` | Agent-managed skill creation | [Skills](/docs/harness/skills) |
|
||||
| `subagents` | Subagent timeouts and max turns | [Subagents](/docs/harness/subagents) |
|
||||
| `acp_agents` | External ACP agent integrations | [Subagents](/docs/harness/subagents) |
|
||||
| `memory` | Cross-session memory storage | [Memory](/docs/harness/memory) |
|
||||
| `summarization` | Conversation summarization | [Middlewares](/docs/harness/middlewares) |
|
||||
| `title` | Automatic thread title generation | [Middlewares](/docs/harness/middlewares) |
|
||||
| `checkpointer` | Thread state persistence | [Agents & Threads](/docs/application/agents-and-threads) |
|
||||
| `guardrails` | Tool call authorization | — |
|
||||
| `stream_bridge` | Streaming configuration | — |
|
||||
| `uploads` | File upload settings (PDF converter) | — |
|
||||
| `channels` | IM channel integrations (Feishu, Slack, etc.) | — |
|
||||
|
||||
## Minimal config to get started
|
||||
|
||||
@@ -157,12 +157,6 @@ tools:
|
||||
Start from `config.example.yaml` in the repository root and uncomment the sections you need.
|
||||
|
||||
<Cards num={2}>
|
||||
<Cards.Card
|
||||
title="Deployment Guide"
|
||||
href="/docs/application/deployment-guide"
|
||||
/>
|
||||
<Cards.Card
|
||||
title="Application Configuration"
|
||||
href="/docs/application/configuration"
|
||||
/>
|
||||
<Cards.Card title="Deployment Guide" href="/docs/application/deployment-guide" />
|
||||
<Cards.Card title="Application Configuration" href="/docs/application/configuration" />
|
||||
</Cards>
|
||||
|
||||
@@ -170,9 +170,6 @@ guardrails:
|
||||
For custom guardrail logic, implement a class with `evaluate()` and `aevaluate()` methods and reference it via `use:`.
|
||||
|
||||
<Cards num={2}>
|
||||
<Cards.Card
|
||||
title="Integration Guide"
|
||||
href="/docs/harness/integration-guide"
|
||||
/>
|
||||
<Cards.Card title="Integration Guide" href="/docs/harness/integration-guide" />
|
||||
<Cards.Card title="Configuration" href="/docs/harness/configuration" />
|
||||
</Cards>
|
||||
|
||||
@@ -110,15 +110,15 @@ Environment variable interpolation (`api_key: $OPENAI_API_KEY`) keeps secrets ou
|
||||
|
||||
## Summary
|
||||
|
||||
| Principle | What it means in practice |
|
||||
| --------------------------- | -------------------------------------------------------------- |
|
||||
| Harness, not framework | Ready-to-run runtime with all the infrastructure already wired |
|
||||
| Long-horizon first | Architecture assumes multi-step, multi-tool, multi-turn tasks |
|
||||
| Middleware over inheritance | Behavior is composed from small, isolated plugins |
|
||||
| Skills for specialization | Domain capability injected on demand, keeping the base clean |
|
||||
| Sandbox for execution | Isolated workspace for real file and command work |
|
||||
| Context engineering | Active management of what the agent sees to stay effective |
|
||||
| Config-driven | All key behaviors are controlled through `config.yaml` |
|
||||
| Principle | What it means in practice |
|
||||
|---|---|
|
||||
| Harness, not framework | Ready-to-run runtime with all the infrastructure already wired |
|
||||
| Long-horizon first | Architecture assumes multi-step, multi-tool, multi-turn tasks |
|
||||
| Middleware over inheritance | Behavior is composed from small, isolated plugins |
|
||||
| Skills for specialization | Domain capability injected on demand, keeping the base clean |
|
||||
| Sandbox for execution | Isolated workspace for real file and command work |
|
||||
| Context engineering | Active management of what the agent sees to stay effective |
|
||||
| Config-driven | All key behaviors are controlled through `config.yaml` |
|
||||
|
||||
<Cards num={2}>
|
||||
<Cards.Card title="Lead Agent" href="/docs/harness/lead-agent" />
|
||||
|
||||
@@ -141,9 +141,9 @@ The same Lead Agent runtime powers both the default agent and any custom agents
|
||||
Custom agents are created through the DeerFlow App UI or via the `/api/agents` endpoint. Their configuration is stored in `agents/{name}/config.yaml` relative to the backend directory.
|
||||
|
||||
<Callout type="tip">
|
||||
When a custom agent is selected in a thread, the Lead Agent loads that agent's
|
||||
config at runtime. Switching models or skills for a specific agent does not
|
||||
require restarting the server.
|
||||
When a custom agent is selected in a thread, the Lead Agent loads that
|
||||
agent's config at runtime. Switching models or skills for a specific agent
|
||||
does not require restarting the server.
|
||||
</Callout>
|
||||
|
||||
## Bootstrap mode
|
||||
|
||||
@@ -44,7 +44,6 @@ The default location is the project root (same directory as `config.yaml`). The
|
||||
```
|
||||
|
||||
Each server entry supports:
|
||||
|
||||
- `command`: the executable to run (e.g., `npx`, `uvx`, `python`)
|
||||
- `args`: command arguments as an array
|
||||
- `enabled`: whether the server is active (can be toggled without removing the entry)
|
||||
@@ -93,7 +92,6 @@ With tool search enabled, MCP tools are listed by name in the system prompt but
|
||||
Some MCP servers require OAuth authentication. DeerFlow's `mcp/oauth.py` handles the OAuth flow for servers that declare OAuth requirements in their capability headers.
|
||||
|
||||
When an OAuth-protected MCP server is connected, DeerFlow will:
|
||||
|
||||
1. Detect the OAuth requirement from the server's capability headers
|
||||
2. Build the appropriate authorization headers using `get_initial_oauth_headers()`
|
||||
3. Wrap tool calls with an OAuth interceptor via `build_oauth_tool_interceptor()`
|
||||
|
||||
@@ -89,7 +89,7 @@ title:
|
||||
enabled: true
|
||||
max_words: 6
|
||||
max_chars: 60
|
||||
model_name: null # use default model
|
||||
model_name: null # use default model
|
||||
```
|
||||
|
||||
---
|
||||
@@ -159,7 +159,7 @@ summarization:
|
||||
|
||||
# Trigger conditions — summarization runs when ANY threshold is met
|
||||
trigger:
|
||||
- type: tokens # trigger when context exceeds N tokens
|
||||
- type: tokens # trigger when context exceeds N tokens
|
||||
value: 15564
|
||||
# - type: messages # trigger when there are more than N messages
|
||||
# value: 50
|
||||
@@ -169,7 +169,7 @@ summarization:
|
||||
# How much recent history to keep after summarization
|
||||
keep:
|
||||
type: messages
|
||||
value: 10 # keep the 10 most recent messages
|
||||
value: 10 # keep the 10 most recent messages
|
||||
# Alternative: keep by tokens
|
||||
# type: tokens
|
||||
# value: 3000
|
||||
@@ -182,7 +182,6 @@ summarization:
|
||||
```
|
||||
|
||||
**Trigger types**:
|
||||
|
||||
- `tokens`: triggers when the total token count in the conversation exceeds `value`.
|
||||
- `messages`: triggers when the number of messages exceeds `value`.
|
||||
- `fraction`: triggers when the context reaches `value` fraction of the model's maximum input token limit.
|
||||
@@ -190,7 +189,6 @@ summarization:
|
||||
Multiple triggers can be listed; summarization runs when **any** of them fires.
|
||||
|
||||
**Keep types**:
|
||||
|
||||
- `messages`: keep the last `value` messages after summarization.
|
||||
- `tokens`: keep up to `value` tokens of recent history.
|
||||
- `fraction`: keep up to `value` fraction of the model's max input token limit.
|
||||
|
||||
@@ -85,15 +85,15 @@ agent = create_deerflow_agent(
|
||||
|
||||
Common parameters:
|
||||
|
||||
| Parameter | Description |
|
||||
| ------------------ | ----------------------------------------------- |
|
||||
| `tools` | Additional tools available to the agent |
|
||||
| `system_prompt` | Custom system prompt |
|
||||
| `features` | Enable or replace built-in runtime features |
|
||||
| Parameter | Description |
|
||||
|---|---|
|
||||
| `tools` | Additional tools available to the agent |
|
||||
| `system_prompt` | Custom system prompt |
|
||||
| `features` | Enable or replace built-in runtime features |
|
||||
| `extra_middleware` | Insert custom middleware into the default chain |
|
||||
| `plan_mode` | Enable Todo-style task tracking |
|
||||
| `checkpointer` | Persist agent state across runs |
|
||||
| `name` | Logical agent name |
|
||||
| `plan_mode` | Enable Todo-style task tracking |
|
||||
| `checkpointer` | Persist agent state across runs |
|
||||
| `name` | Logical agent name |
|
||||
|
||||
## When to use DeerFlowClient instead
|
||||
|
||||
@@ -109,10 +109,7 @@ Use `DeerFlowClient` when you want the higher-level embedded app interface, such
|
||||
## Next steps
|
||||
|
||||
<Cards num={3}>
|
||||
<Cards.Card
|
||||
title="Design Principles"
|
||||
href="/docs/harness/design-principles"
|
||||
/>
|
||||
<Cards.Card title="Design Principles" href="/docs/harness/design-principles" />
|
||||
<Cards.Card title="Lead Agent" href="/docs/harness/lead-agent" />
|
||||
<Cards.Card title="Configuration" href="/docs/harness/configuration" />
|
||||
</Cards>
|
||||
|
||||
@@ -9,8 +9,8 @@ import { Callout, Cards, Tabs } from "nextra/components";
|
||||
|
||||
<Callout type="info" emoji="📦">
|
||||
The sandbox is the isolated workspace where the agent does file and
|
||||
command-based work. It is what makes DeerFlow capable of real action, not just
|
||||
conversation.
|
||||
command-based work. It is what makes DeerFlow capable of real action, not
|
||||
just conversation.
|
||||
</Callout>
|
||||
|
||||
The sandbox gives the Lead Agent a controlled environment where it can read files, write outputs, run shell commands, and produce artifacts. Without a sandbox, the agent can only generate text. With a sandbox, it can write and execute code, process data files, generate charts, and build deliverables.
|
||||
@@ -29,7 +29,7 @@ Commands run directly on the host machine's filesystem. There is no container is
|
||||
```yaml
|
||||
sandbox:
|
||||
use: deerflow.sandbox.local:LocalSandboxProvider
|
||||
allow_host_bash: false # default; set to true only for fully trusted workflows
|
||||
allow_host_bash: false # default; set to true only for fully trusted workflows
|
||||
```
|
||||
|
||||
### Container-based AIO Sandbox
|
||||
@@ -83,10 +83,10 @@ The provisioner service is included in `docker/docker-compose-dev.yaml` and mana
|
||||
|
||||
The sandbox uses path mappings to bridge the host filesystem and the container's virtual filesystem. Two key mappings are always configured:
|
||||
|
||||
| Host path | Container path | Access |
|
||||
| ------------------------------------------- | -------------------------------------------- | ---------- |
|
||||
| `skills/` (from `skills.path`) | `/mnt/skills` (from `skills.container_path`) | Read-only |
|
||||
| `.deer-flow/threads/{thread_id}/user-data/` | `/mnt/user-data/` | Read-write |
|
||||
| Host path | Container path | Access |
|
||||
|---|---|---|
|
||||
| `skills/` (from `skills.path`) | `/mnt/skills` (from `skills.container_path`) | Read-only |
|
||||
| `.deer-flow/threads/{thread_id}/user-data/` | `/mnt/user-data/` | Read-write |
|
||||
|
||||
The skills directory is always mounted read-only. Threads write their working data (uploads, outputs, intermediate files) to `/mnt/user-data/`.
|
||||
|
||||
@@ -136,7 +136,7 @@ The `LocalSandbox` runs commands directly on the host. By default, the `bash` to
|
||||
|
||||
```yaml
|
||||
sandbox:
|
||||
allow_host_bash: true # Dangerous: grants the agent shell access to your machine
|
||||
allow_host_bash: true # Dangerous: grants the agent shell access to your machine
|
||||
```
|
||||
|
||||
Even without `bash`, the agent can still read and write files through the dedicated file tools.
|
||||
|
||||
@@ -44,23 +44,23 @@ The `SKILL.md` file is the authoritative definition of the skill. It is parsed b
|
||||
|
||||
DeerFlow ships with the following public skills:
|
||||
|
||||
| Skill | Description |
|
||||
| ------------------------------ | -------------------------------------------------------------------------------- |
|
||||
| `deep-research` | Multi-step research with source gathering, cross-checking, and structured output |
|
||||
| `data-analysis` | Data exploration, statistical analysis, and insight generation |
|
||||
| `chart-visualization` | Chart and graph creation from data |
|
||||
| `ppt-generation` | Presentation slide generation |
|
||||
| `image-generation` | AI image generation workflows |
|
||||
| `code-documentation` | Automated code documentation generation |
|
||||
| `newsletter-generation` | Newsletter content creation |
|
||||
| `podcast-generation` | Podcast script and outline generation |
|
||||
| `academic-paper-review` | Structured academic paper analysis |
|
||||
| `consulting-analysis` | Business consulting frameworks and analysis |
|
||||
| `systematic-literature-review` | Literature review methodology and synthesis |
|
||||
| `github-deep-research` | Repository and code deep-dive research |
|
||||
| `frontend-design` | Frontend design and UI workflow |
|
||||
| `web-design-guidelines` | Web design standards and review |
|
||||
| `video-generation` | Video content planning and generation |
|
||||
| Skill | Description |
|
||||
|---|---|
|
||||
| `deep-research` | Multi-step research with source gathering, cross-checking, and structured output |
|
||||
| `data-analysis` | Data exploration, statistical analysis, and insight generation |
|
||||
| `chart-visualization` | Chart and graph creation from data |
|
||||
| `ppt-generation` | Presentation slide generation |
|
||||
| `image-generation` | AI image generation workflows |
|
||||
| `code-documentation` | Automated code documentation generation |
|
||||
| `newsletter-generation` | Newsletter content creation |
|
||||
| `podcast-generation` | Podcast script and outline generation |
|
||||
| `academic-paper-review` | Structured academic paper analysis |
|
||||
| `consulting-analysis` | Business consulting frameworks and analysis |
|
||||
| `systematic-literature-review` | Literature review methodology and synthesis |
|
||||
| `github-deep-research` | Repository and code deep-dive research |
|
||||
| `frontend-design` | Frontend design and UI workflow |
|
||||
| `web-design-guidelines` | Web design standards and review |
|
||||
| `video-generation` | Video content planning and generation |
|
||||
|
||||
## Skill lifecycle
|
||||
|
||||
@@ -139,8 +139,8 @@ DeerFlow includes an optional **skill evolution** feature that allows the agent
|
||||
|
||||
```yaml
|
||||
skill_evolution:
|
||||
enabled: false # Set to true to allow agent-managed skill creation
|
||||
moderation_model_name: null # Model for security scanning (null = use default)
|
||||
enabled: false # Set to true to allow agent-managed skill creation
|
||||
moderation_model_name: null # Model for security scanning (null = use default)
|
||||
```
|
||||
|
||||
<Callout type="warning">
|
||||
|
||||
@@ -75,10 +75,10 @@ subagents:
|
||||
# Optional: per-agent overrides
|
||||
agents:
|
||||
general-purpose:
|
||||
timeout_seconds: 1800 # 30 minutes for complex tasks
|
||||
timeout_seconds: 1800 # 30 minutes for complex tasks
|
||||
max_turns: 160
|
||||
bash:
|
||||
timeout_seconds: 300 # 5 minutes for quick commands
|
||||
timeout_seconds: 300 # 5 minutes for quick commands
|
||||
max_turns: 80
|
||||
```
|
||||
|
||||
@@ -122,8 +122,8 @@ The Lead Agent invokes ACP agents through the `invoke_acp_agent` built-in tool.
|
||||
<Callout type="tip">
|
||||
ACP agents run as child processes managed by DeerFlow. They communicate over
|
||||
the ACP wire protocol. The standard CLI tools (like the plain `claude` or
|
||||
`codex` commands) are not ACP-compatible by default — use the adapter packages
|
||||
listed above or a compatible ACP wrapper.
|
||||
`codex` commands) are not ACP-compatible by default — use the adapter
|
||||
packages listed above or a compatible ACP wrapper.
|
||||
</Callout>
|
||||
|
||||
## Custom agents as subagents
|
||||
|
||||
@@ -78,15 +78,15 @@ Searches for tools by name or description and loads them into the agent's contex
|
||||
|
||||
The following tools interact with the sandbox filesystem. They require a sandbox to be configured and active.
|
||||
|
||||
| Tool | Description |
|
||||
| ------------- | --------------------------------------------------------------------------------- |
|
||||
| `ls` | List files in a directory |
|
||||
| `read_file` | Read file contents |
|
||||
| `glob` | Find files matching a pattern |
|
||||
| `grep` | Search file contents |
|
||||
| `write_file` | Write content to a file |
|
||||
| `str_replace` | Replace a string in a file |
|
||||
| `bash` | Execute a shell command (requires `allow_host_bash: true` or a container sandbox) |
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `ls` | List files in a directory |
|
||||
| `read_file` | Read file contents |
|
||||
| `glob` | Find files matching a pattern |
|
||||
| `grep` | Search file contents |
|
||||
| `write_file` | Write content to a file |
|
||||
| `str_replace` | Replace a string in a file |
|
||||
| `bash` | Execute a shell command (requires `allow_host_bash: true` or a container sandbox) |
|
||||
|
||||
These are configured in `config.yaml` under `tools:`:
|
||||
|
||||
@@ -98,7 +98,7 @@ tools:
|
||||
- use: deerflow.sandbox.tools:grep_tool
|
||||
- use: deerflow.sandbox.tools:write_file_tool
|
||||
- use: deerflow.sandbox.tools:str_replace_tool
|
||||
- use: deerflow.sandbox.tools:bash_tool # requires host bash or container sandbox
|
||||
- use: deerflow.sandbox.tools:bash_tool # requires host bash or container sandbox
|
||||
```
|
||||
|
||||
## Community tools
|
||||
@@ -124,7 +124,6 @@ tools:
|
||||
High-quality search with structured results. Requires a [Tavily](https://tavily.com) API key.
|
||||
|
||||
Install: `cd backend && uv add 'deerflow-harness[tavily]'`
|
||||
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab>
|
||||
```yaml
|
||||
@@ -135,7 +134,6 @@ tools:
|
||||
Semantic search with neural retrieval. Requires an [Exa](https://exa.ai) API key.
|
||||
|
||||
Install: `cd backend && uv add 'deerflow-harness[exa]'`
|
||||
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab>
|
||||
```yaml
|
||||
@@ -154,7 +152,6 @@ tools:
|
||||
Firecrawl-powered search and crawl. Requires a [Firecrawl](https://firecrawl.dev) API key.
|
||||
|
||||
Install: `cd backend && uv add 'deerflow-harness[firecrawl]'`
|
||||
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -165,10 +162,9 @@ Install: `cd backend && uv add 'deerflow-harness[firecrawl]'`
|
||||
```yaml
|
||||
tools:
|
||||
- use: deerflow.community.jina_ai.tools:web_fetch_tool
|
||||
api_key: $JINA_API_KEY # optional; anonymous usage has rate limits
|
||||
api_key: $JINA_API_KEY # optional; anonymous usage has rate limits
|
||||
```
|
||||
Converts web pages to clean Markdown. Works without an API key at reduced rate
|
||||
limits.
|
||||
Converts web pages to clean Markdown. Works without an API key at reduced rate limits.
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab>
|
||||
```yaml
|
||||
|
||||
@@ -35,7 +35,6 @@ Press Enter to send.
|
||||
### Watch the agent work
|
||||
|
||||
You will see the agent start working:
|
||||
|
||||
- Expand the **thinking steps** to see which tools it is calling
|
||||
- Watch search results stream in
|
||||
- Wait for the final report to be generated
|
||||
@@ -43,7 +42,6 @@ You will see the agent start working:
|
||||
### Interact with the result
|
||||
|
||||
Once the report is generated, you can:
|
||||
|
||||
- Ask for more detail on a specific section
|
||||
- Ask to export the report as a file (the agent will use the `present_files` tool)
|
||||
- Ask to create a chart based on the research findings
|
||||
@@ -53,7 +51,6 @@ Once the report is generated, you can:
|
||||
## What just happened
|
||||
|
||||
The agent used the DeerFlow Harness to:
|
||||
|
||||
1. Receive your message and add it to the thread state
|
||||
2. Run the middleware chain (memory injection, title generation)
|
||||
3. Call the LLM, which decided to search the web
|
||||
|
||||
@@ -33,7 +33,6 @@ tools:
|
||||
Enable skills through the DeerFlow app's extensions panel, or edit `extensions_config.json` directly.
|
||||
|
||||
**Via the app UI:**
|
||||
|
||||
1. Open the DeerFlow app
|
||||
2. Click the Extensions/Skills icon in the sidebar
|
||||
3. Find `deep-research` and toggle it on
|
||||
|
||||
@@ -32,7 +32,6 @@ Memory works automatically through `MemoryMiddleware`:
|
||||
## Example
|
||||
|
||||
**First conversation:**
|
||||
|
||||
```
|
||||
I am a Python backend developer primarily using FastAPI and PostgreSQL.
|
||||
My team follows PEP 8 and prefers type annotations everywhere.
|
||||
@@ -40,7 +39,6 @@ Please remember this for future code suggestions.
|
||||
```
|
||||
|
||||
**Later conversation** (no need to repeat background):
|
||||
|
||||
```
|
||||
Help me write a user authentication module
|
||||
```
|
||||
|
||||
@@ -8,8 +8,7 @@ import { Callout, Cards, Steps } from "nextra/components";
|
||||
# Agent 与线程
|
||||
|
||||
<Callout type="info" emoji="🤖">
|
||||
Agent
|
||||
是配置单元——它们定义了一组能力。线程是对话实例,带有持久化状态和历史记录。
|
||||
Agent 是配置单元——它们定义了一组能力。线程是对话实例,带有持久化状态和历史记录。
|
||||
</Callout>
|
||||
|
||||
## 自定义 Agent
|
||||
@@ -116,8 +115,5 @@ checkpointer:
|
||||
|
||||
<Cards num={2}>
|
||||
<Cards.Card title="工作区使用" href="/docs/application/workspace-usage" />
|
||||
<Cards.Card
|
||||
title="运维与排障"
|
||||
href="/docs/application/operations-and-troubleshooting"
|
||||
/>
|
||||
<Cards.Card title="运维与排障" href="/docs/application/operations-and-troubleshooting" />
|
||||
</Cards>
|
||||
|
||||
@@ -46,18 +46,17 @@ models:
|
||||
启用扩展思考:
|
||||
|
||||
```yaml
|
||||
- name: claude-extended-thinking
|
||||
use: langchain_anthropic:ChatAnthropic
|
||||
model: claude-sonnet-4-5
|
||||
api_key: $ANTHROPIC_API_KEY
|
||||
max_tokens: 16000
|
||||
thinking_enabled: true
|
||||
extra_body:
|
||||
thinking:
|
||||
type: enabled
|
||||
budget_tokens: 10000
|
||||
- name: claude-extended-thinking
|
||||
use: langchain_anthropic:ChatAnthropic
|
||||
model: claude-sonnet-4-5
|
||||
api_key: $ANTHROPIC_API_KEY
|
||||
max_tokens: 16000
|
||||
thinking_enabled: true
|
||||
extra_body:
|
||||
thinking:
|
||||
type: enabled
|
||||
budget_tokens: 10000
|
||||
```
|
||||
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab>
|
||||
```yaml
|
||||
@@ -104,7 +103,6 @@ models:
|
||||
```bash
|
||||
ollama pull llama3.3
|
||||
```
|
||||
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -164,11 +162,11 @@ checkpointer:
|
||||
|
||||
前端通过 `.env.local`(本地开发)或 Docker Compose 环境中的环境变量配置。
|
||||
|
||||
| 变量 | 必需 | 描述 |
|
||||
| --------------------- | ---------- | -------------------------------------------------- |
|
||||
| `BETTER_AUTH_SECRET` | 是(生产) | 会话管理的密钥(最少 32 个字符) |
|
||||
| `BETTER_AUTH_URL` | 推荐 | 你的应用公开 URL(例如 `https://your-domain.com`) |
|
||||
| `SKIP_ENV_VALIDATION` | 否 | 设为 `1` 跳过构建时环境变量验证 |
|
||||
| 变量 | 必需 | 描述 |
|
||||
|---|---|---|
|
||||
| `BETTER_AUTH_SECRET` | 是(生产) | 会话管理的密钥(最少 32 个字符) |
|
||||
| `BETTER_AUTH_URL` | 推荐 | 你的应用公开 URL(例如 `https://your-domain.com`) |
|
||||
| `SKIP_ENV_VALIDATION` | 否 | 设为 `1` 跳过构建时环境变量验证 |
|
||||
|
||||
本地开发:
|
||||
|
||||
@@ -178,8 +176,7 @@ BETTER_AUTH_SECRET=local-dev-secret-at-least-32-chars
|
||||
```
|
||||
|
||||
<Callout type="warning">
|
||||
不要在生产中使用 <code>SKIP_ENV_VALIDATION=1</code>。为{" "}
|
||||
<code>BETTER_AUTH_SECRET</code> 设置一个真实值。
|
||||
不要在生产中使用 <code>SKIP_ENV_VALIDATION=1</code>。为 <code>BETTER_AUTH_SECRET</code> 设置一个真实值。
|
||||
</Callout>
|
||||
|
||||
## extensions_config.json
|
||||
@@ -210,12 +207,12 @@ BETTER_AUTH_SECRET=local-dev-secret-at-least-32-chars
|
||||
|
||||
这些变量在 DeerFlow 进程中设置(通过 `.env`、Docker 环境或 shell):
|
||||
|
||||
| 变量 | 默认值 | 描述 |
|
||||
| ----------------------- | ---------------- | ------------------------------------------------ |
|
||||
| `DEER_FLOW_CONFIG_PATH` | 自动发现 | `config.yaml` 的绝对路径 |
|
||||
| `LOG_LEVEL` | `info` | 日志详细程度(`debug`/`info`/`warning`/`error`) |
|
||||
| `DEER_FLOW_ROOT` | 仓库根目录 | 用于 Docker 中的技能和线程挂载 |
|
||||
| `LANGGRAPH_UPSTREAM` | `langgraph:2024` | nginx 代理的 LangGraph 地址 |
|
||||
| 变量 | 默认值 | 描述 |
|
||||
|---|---|---|
|
||||
| `DEER_FLOW_CONFIG_PATH` | 自动发现 | `config.yaml` 的绝对路径 |
|
||||
| `LOG_LEVEL` | `info` | 日志详细程度(`debug`/`info`/`warning`/`error`) |
|
||||
| `DEER_FLOW_ROOT` | 仓库根目录 | 用于 Docker 中的技能和线程挂载 |
|
||||
| `LANGGRAPH_UPSTREAM` | `langgraph:2024` | nginx 代理的 LangGraph 地址 |
|
||||
|
||||
<Cards num={2}>
|
||||
<Cards.Card title="Harness 配置" href="/docs/harness/configuration" />
|
||||
|
||||
@@ -21,15 +21,14 @@ make dev
|
||||
|
||||
启动的服务:
|
||||
|
||||
| 服务 | 端口 | 描述 |
|
||||
| ----------- | ---- | ----------------------- |
|
||||
| LangGraph | 2024 | DeerFlow Harness 运行时 |
|
||||
| Gateway API | 8001 | FastAPI 后端 |
|
||||
| 前端 | 3000 | Next.js 界面 |
|
||||
| nginx | 2026 | 统一反向代理 |
|
||||
| 服务 | 端口 | 描述 |
|
||||
|---|---|---|
|
||||
| LangGraph | 2024 | DeerFlow Harness 运行时 |
|
||||
| Gateway API | 8001 | FastAPI 后端 |
|
||||
| 前端 | 3000 | Next.js 界面 |
|
||||
| nginx | 2026 | 统一反向代理 |
|
||||
|
||||
访问地址:**http://localhost:2026**
|
||||
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab>
|
||||
```bash
|
||||
@@ -37,7 +36,6 @@ make stop
|
||||
```
|
||||
|
||||
停止所有四个服务。即使某个服务没有运行也可以安全执行。
|
||||
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab>
|
||||
```
|
||||
@@ -48,11 +46,9 @@ logs/nginx.log # nginx 访问/错误日志
|
||||
```
|
||||
|
||||
实时追踪日志:
|
||||
|
||||
```bash
|
||||
tail -f logs/langgraph.log
|
||||
```
|
||||
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -90,8 +86,7 @@ BETTER_AUTH_SECRET=your-secret-here-min-32-chars
|
||||
`docker-compose*.yaml` 文件包含 `env_file: ../.env` 指令,会自动加载该文件。
|
||||
|
||||
<Callout type="warning">
|
||||
在部署前始终将 <code>BETTER_AUTH_SECRET</code>{" "}
|
||||
设置为强随机字符串。没有它,前端构建会使用一个公开已知的默认值。
|
||||
在部署前始终将 <code>BETTER_AUTH_SECRET</code> 设置为强随机字符串。没有它,前端构建会使用一个公开已知的默认值。
|
||||
</Callout>
|
||||
|
||||
### 数据持久化
|
||||
@@ -109,11 +104,11 @@ BETTER_AUTH_SECRET=your-secret-here-min-32-chars
|
||||
|
||||
### 沙箱模式选择
|
||||
|
||||
| 沙箱 | 使用场景 |
|
||||
| -------------------------------------- | -------------------------- |
|
||||
| `LocalSandboxProvider` | 单用户、受信任的本地工作流 |
|
||||
| `AioSandboxProvider`(Docker) | 多用户、中等隔离需求 |
|
||||
| `AioSandboxProvider` + K8s Provisioner | 生产环境、强隔离、多用户 |
|
||||
| 沙箱 | 使用场景 |
|
||||
|---|---|
|
||||
| `LocalSandboxProvider` | 单用户、受信任的本地工作流 |
|
||||
| `AioSandboxProvider`(Docker) | 多用户、中等隔离需求 |
|
||||
| `AioSandboxProvider` + K8s Provisioner | 生产环境、强隔离、多用户 |
|
||||
|
||||
对于有多个并发用户的任何部署,使用基于容器的沙箱,防止用户之间的执行环境相互干扰。
|
||||
|
||||
@@ -158,10 +153,10 @@ SKILLS_PVC_NAME=deer-flow-skills-pvc
|
||||
|
||||
nginx 路由所有流量,控制路由的关键环境变量:
|
||||
|
||||
| 变量 | 默认值 | 描述 |
|
||||
| -------------------- | ---------------- | ----------------------------- |
|
||||
| `LANGGRAPH_UPSTREAM` | `langgraph:2024` | LangGraph 服务地址 |
|
||||
| `LANGGRAPH_REWRITE` | `/` | LangGraph 路由的 URL 重写前缀 |
|
||||
| 变量 | 默认值 | 描述 |
|
||||
|---|---|---|
|
||||
| `LANGGRAPH_UPSTREAM` | `langgraph:2024` | LangGraph 服务地址 |
|
||||
| `LANGGRAPH_REWRITE` | `/` | LangGraph 路由的 URL 重写前缀 |
|
||||
|
||||
这些在 Docker Compose 环境中设置,并在容器启动时由 `envsubst` 处理。
|
||||
|
||||
@@ -179,12 +174,12 @@ openssl rand -base64 32
|
||||
|
||||
### 资源建议
|
||||
|
||||
| 服务 | 最低配置 | 推荐配置 |
|
||||
| ------------------------- | ---------------- | ---------------- |
|
||||
| 服务 | 最低配置 | 推荐配置 |
|
||||
|---|---|---|
|
||||
| LangGraph(Agent 运行时) | 2 vCPU、4 GB RAM | 4 vCPU、8 GB RAM |
|
||||
| Gateway | 0.5 vCPU、512 MB | 1 vCPU、1 GB |
|
||||
| 前端 | 0.5 vCPU、512 MB | 1 vCPU、1 GB |
|
||||
| 沙箱容器(每会话) | 1 vCPU、1 GB | 2 vCPU、2 GB |
|
||||
| Gateway | 0.5 vCPU、512 MB | 1 vCPU、1 GB |
|
||||
| 前端 | 0.5 vCPU、512 MB | 1 vCPU、1 GB |
|
||||
| 沙箱容器(每会话) | 1 vCPU、1 GB | 2 vCPU、2 GB |
|
||||
|
||||
## 部署后验证
|
||||
|
||||
@@ -205,8 +200,5 @@ curl http://localhost:2026/api/models
|
||||
|
||||
<Cards num={2}>
|
||||
<Cards.Card title="配置" href="/docs/application/configuration" />
|
||||
<Cards.Card
|
||||
title="运维与排障"
|
||||
href="/docs/application/operations-and-troubleshooting"
|
||||
/>
|
||||
<Cards.Card title="运维与排障" href="/docs/application/operations-and-troubleshooting" />
|
||||
</Cards>
|
||||
|
||||
@@ -8,24 +8,22 @@ import { Callout, Cards } from "nextra/components";
|
||||
# DeerFlow 应用
|
||||
|
||||
<Callout type="info" emoji="🚀">
|
||||
DeerFlow 应用是构建在 DeerFlow Harness 之上的完整 Super Agent
|
||||
应用。它将运行时能力打包成一个可部署的产品,包含 Web 界面、API Gateway
|
||||
和运维工具。
|
||||
DeerFlow 应用是构建在 DeerFlow Harness 之上的完整 Super Agent 应用。它将运行时能力打包成一个可部署的产品,包含 Web 界面、API Gateway 和运维工具。
|
||||
</Callout>
|
||||
|
||||
DeerFlow 应用是 DeerFlow 生产体验的参考实现。它将 Harness 运行时、基于 Web 的对话工作区、API Gateway 和反向代理组合成一个可部署的完整系统。
|
||||
|
||||
## 应用提供什么
|
||||
|
||||
| 能力 | 描述 |
|
||||
| ---------------- | ----------------------------------------------------- |
|
||||
| **Web 工作区** | 浏览器对话界面,支持线程、产出物、文件上传和技能选择 |
|
||||
| **自定义 Agent** | 创建和管理具有不同模型、技能和工具集的命名 Agent |
|
||||
| **线程管理** | 带检查点和历史记录的持久化对话线程 |
|
||||
| **流式响应** | 实时 token 流式传输,带思考步骤和工具调用可见性 |
|
||||
| **产出物查看器** | Agent 生成文件和输出的浏览器内预览和下载 |
|
||||
| **扩展界面** | 无需编辑配置文件即可启用/禁用 MCP 服务器和技能 |
|
||||
| **Gateway API** | 桥接前端和 LangGraph 运行时的基于 FastAPI 的 REST API |
|
||||
| 能力 | 描述 |
|
||||
|---|---|
|
||||
| **Web 工作区** | 浏览器对话界面,支持线程、产出物、文件上传和技能选择 |
|
||||
| **自定义 Agent** | 创建和管理具有不同模型、技能和工具集的命名 Agent |
|
||||
| **线程管理** | 带检查点和历史记录的持久化对话线程 |
|
||||
| **流式响应** | 实时 token 流式传输,带思考步骤和工具调用可见性 |
|
||||
| **产出物查看器** | Agent 生成文件和输出的浏览器内预览和下载 |
|
||||
| **扩展界面** | 无需编辑配置文件即可启用/禁用 MCP 服务器和技能 |
|
||||
| **Gateway API** | 桥接前端和 LangGraph 运行时的基于 FastAPI 的 REST API |
|
||||
|
||||
## 架构
|
||||
|
||||
@@ -58,13 +56,13 @@ DeerFlow 应用以四个服务的形式运行,通过单个 nginx 反向代理
|
||||
|
||||
## 技术栈
|
||||
|
||||
| 层次 | 技术 |
|
||||
| ------------ | ------------------------------------------------------- |
|
||||
| 前端 | Next.js 16、React 19、TypeScript、pnpm |
|
||||
| Gateway | FastAPI、Python 3.12、uvicorn |
|
||||
| Agent 运行时 | LangGraph、LangChain、DeerFlow Harness |
|
||||
| 反向代理 | nginx |
|
||||
| 状态持久化 | LangGraph Server(默认)+ 可选 SQLite/PostgreSQL 检查点 |
|
||||
| 层次 | 技术 |
|
||||
|---|---|
|
||||
| 前端 | Next.js 16、React 19、TypeScript、pnpm |
|
||||
| Gateway | FastAPI、Python 3.12、uvicorn |
|
||||
| Agent 运行时 | LangGraph、LangChain、DeerFlow Harness |
|
||||
| 反向代理 | nginx |
|
||||
| 状态持久化 | LangGraph Server(默认)+ 可选 SQLite/PostgreSQL 检查点 |
|
||||
|
||||
<Cards num={2}>
|
||||
<Cards.Card title="快速上手" href="/docs/application/quick-start" />
|
||||
|
||||
@@ -13,12 +13,12 @@ import { Callout, Cards } from "nextra/components";
|
||||
|
||||
DeerFlow 应用在 `logs/` 目录中写入每个服务的日志:
|
||||
|
||||
| 文件 | 内容 |
|
||||
| -------------------- | -------------------------------------- |
|
||||
| 文件 | 内容 |
|
||||
|---|---|
|
||||
| `logs/langgraph.log` | Agent 运行时、工具调用、LangGraph 错误 |
|
||||
| `logs/gateway.log` | API 请求/响应、Gateway 错误 |
|
||||
| `logs/frontend.log` | Next.js 服务器日志 |
|
||||
| `logs/nginx.log` | 代理访问和错误日志 |
|
||||
| `logs/gateway.log` | API 请求/响应、Gateway 错误 |
|
||||
| `logs/frontend.log` | Next.js 服务器日志 |
|
||||
| `logs/nginx.log` | 代理访问和错误日志 |
|
||||
|
||||
**实时追踪日志**:
|
||||
|
||||
@@ -31,7 +31,7 @@ tail -f logs/gateway.log # 查看 API 请求
|
||||
|
||||
```yaml
|
||||
# config.yaml
|
||||
log_level: debug # debug | info | warning | error
|
||||
log_level: debug # debug | info | warning | error
|
||||
```
|
||||
|
||||
## 健康检查
|
||||
@@ -66,14 +66,12 @@ make config-upgrade
|
||||
**症状**:Agent 在响应第一条消息时报错,日志中有 API 认证错误。
|
||||
|
||||
**诊断**:
|
||||
|
||||
```bash
|
||||
# 检查 LangGraph 日志中的模型错误
|
||||
grep -i "error\|apikey\|unauthorized" logs/langgraph.log | tail -20
|
||||
```
|
||||
|
||||
**解决**:
|
||||
|
||||
1. 验证 `config.yaml` 中 API key 字段名称正确(例如 `$OPENAI_API_KEY`)。
|
||||
2. 确认对应的环境变量已设置(`echo $OPENAI_API_KEY`)。
|
||||
3. 检查 `base_url`(如有)是否与提供商的实际端点匹配。
|
||||
@@ -85,14 +83,12 @@ grep -i "error\|apikey\|unauthorized" logs/langgraph.log | tail -20
|
||||
**症状**:工具报"文件未找到"或权限错误,即使 Agent 声称已创建文件。
|
||||
|
||||
**诊断**:
|
||||
|
||||
```bash
|
||||
# 检查线程用户数据目录是否存在且可写
|
||||
ls -la backend/.deer-flow/threads/
|
||||
```
|
||||
|
||||
**解决**:
|
||||
|
||||
1. 确保 `backend/.deer-flow/` 对运行 DeerFlow 的进程可写。
|
||||
2. 在 Docker 部署中,验证卷挂载路径正确(`DEER_FLOW_ROOT` 设置为绝对路径)。
|
||||
3. 如果使用基于容器的沙箱,确认 Docker 已运行并且容器镜像已拉取。
|
||||
@@ -104,7 +100,6 @@ ls -la backend/.deer-flow/threads/
|
||||
**症状**:`make install` 或前端构建步骤失败,提示 `BETTER_AUTH_SECRET` 错误。
|
||||
|
||||
**解决**:
|
||||
|
||||
```bash
|
||||
# 选项 1:设置环境变量(推荐)
|
||||
export BETTER_AUTH_SECRET=your-secret-here-at-least-32-chars
|
||||
@@ -121,14 +116,12 @@ SKIP_ENV_VALIDATION=1 pnpm build
|
||||
**症状**:MCP 工具未出现,`logs/langgraph.log` 中有超时错误。
|
||||
|
||||
**诊断**:
|
||||
|
||||
```bash
|
||||
# 检查 MCP 相关错误
|
||||
grep -i "mcp\|timeout" logs/langgraph.log | tail -20
|
||||
```
|
||||
|
||||
**解决**:
|
||||
|
||||
1. 验证 `extensions_config.json` 中 MCP 服务器的 `command` 和 `args` 在服务器外部正常工作(手动运行命令)。
|
||||
2. 确认 MCP 服务器的依赖(如 `npx`、`uvx`)已安装并在 PATH 中。
|
||||
3. 检查 MCP 服务器是否需要网络访问或特定环境变量。
|
||||
@@ -140,7 +133,6 @@ grep -i "mcp\|timeout" logs/langgraph.log | tail -20
|
||||
**症状**:沙箱工具请求挂起,日志中有连接拒绝错误。
|
||||
|
||||
**解决**:
|
||||
|
||||
1. 验证 `config.yaml` 中 `provisioner_url` 正确且 Provisioner Pod 运行正常。
|
||||
2. 检查 `K8S_NAMESPACE` 和 RBAC 配置是否允许 Provisioner 创建 Pod。
|
||||
3. 验证 `SANDBOX_IMAGE` 可以从 K8s 节点拉取。
|
||||
@@ -148,7 +140,6 @@ grep -i "mcp\|timeout" logs/langgraph.log | tail -20
|
||||
## 数据备份
|
||||
|
||||
DeerFlow 将持久化数据存储在:
|
||||
|
||||
- **线程数据**:`backend/.deer-flow/threads/` — 每个线程的上传文件、输出和工作区文件
|
||||
- **检查点**:取决于检查点器配置(SQLite:`backend/.deer-flow/checkpoints.db`,Redis:外部存储)
|
||||
- **记忆**:`backend/.deer-flow/memory.json`(以及 `agents/*/memory.json`)
|
||||
|
||||
@@ -8,8 +8,7 @@ import { Callout, Cards, Steps } from "nextra/components";
|
||||
# 快速上手
|
||||
|
||||
<Callout type="info" emoji="⚡">
|
||||
大约 10 分钟即可在本地运行 DeerFlow 应用。你需要一台安装了 Python
|
||||
3.12+、Node.js 22+ 的机器,以及至少一个 LLM API Key。
|
||||
大约 10 分钟即可在本地运行 DeerFlow 应用。你需要一台安装了 Python 3.12+、Node.js 22+ 的机器,以及至少一个 LLM API Key。
|
||||
</Callout>
|
||||
|
||||
本指南引导你使用 `make dev` 工作流在本地机器上启动 DeerFlow 应用。所有四个服务(LangGraph、Gateway、前端、nginx)一起启动,通过单个 URL 访问。
|
||||
@@ -24,13 +23,13 @@ make check
|
||||
|
||||
必需工具:
|
||||
|
||||
| 工具 | 最低版本 |
|
||||
| ------- | ------------ |
|
||||
| Python | 3.12 |
|
||||
| uv | 最新版 |
|
||||
| Node.js | 22 |
|
||||
| pnpm | 10 |
|
||||
| nginx | 任何近期版本 |
|
||||
| 工具 | 最低版本 |
|
||||
|---|---|
|
||||
| Python | 3.12 |
|
||||
| uv | 最新版 |
|
||||
| Node.js | 22 |
|
||||
| pnpm | 10 |
|
||||
| nginx | 任何近期版本 |
|
||||
|
||||
在 macOS 上,使用 `brew install python uv node pnpm nginx` 安装。在 Linux 上,使用你的发行版包管理器。
|
||||
|
||||
@@ -87,7 +86,6 @@ make dev
|
||||
```
|
||||
|
||||
这会启动:
|
||||
|
||||
- LangGraph 服务,端口 `2024`
|
||||
- Gateway API,端口 `8001`
|
||||
- 前端,端口 `3000`
|
||||
@@ -111,17 +109,15 @@ make stop
|
||||
|
||||
日志文件:
|
||||
|
||||
| 服务 | 日志文件 |
|
||||
| --------- | -------------------- |
|
||||
| 服务 | 日志文件 |
|
||||
|---|---|
|
||||
| LangGraph | `logs/langgraph.log` |
|
||||
| Gateway | `logs/gateway.log` |
|
||||
| 前端 | `logs/frontend.log` |
|
||||
| nginx | `logs/nginx.log` |
|
||||
| Gateway | `logs/gateway.log` |
|
||||
| 前端 | `logs/frontend.log` |
|
||||
| nginx | `logs/nginx.log` |
|
||||
|
||||
<Callout type="tip">
|
||||
如果有问题,先检查日志文件。大多数启动错误(缺失 API
|
||||
Key、配置解析失败)会出现在 <code>logs/langgraph.log</code> 或{" "}
|
||||
<code>logs/gateway.log</code> 中。
|
||||
如果有问题,先检查日志文件。大多数启动错误(缺失 API Key、配置解析失败)会出现在 <code>logs/langgraph.log</code> 或 <code>logs/gateway.log</code> 中。
|
||||
</Callout>
|
||||
|
||||
<Cards num={2}>
|
||||
|
||||
@@ -8,8 +8,7 @@ import { Callout, Cards } from "nextra/components";
|
||||
# 工作区使用
|
||||
|
||||
<Callout type="info" emoji="💬">
|
||||
DeerFlow 工作区是你与 Agent
|
||||
交互的地方。本页面涵盖主要用户界面工作流——创建对话、上传文件、查看产出物和使用技能。
|
||||
DeerFlow 工作区是你与 Agent 交互的地方。本页面涵盖主要用户界面工作流——创建对话、上传文件、查看产出物和使用技能。
|
||||
</Callout>
|
||||
|
||||
DeerFlow 工作区是一个基于浏览器的对话界面,你可以在其中向 Agent 发送消息、上传文件、查看中间步骤,以及下载生成的产出物。
|
||||
@@ -42,15 +41,13 @@ DeerFlow 工作区是一个基于浏览器的对话界面,你可以在其中
|
||||
拖放文件到消息输入区域,或点击附件图标上传。上传的文件挂载在沙箱的 `/mnt/user-data/uploads/` 路径下,Agent 可以直接读取和处理。
|
||||
|
||||
**支持的操作**:
|
||||
|
||||
- 读取和分析文件内容
|
||||
- 处理数据文件(CSV、JSON、Excel)
|
||||
- 提取 PDF 内容
|
||||
- 分析图像(需要支持视觉的模型)
|
||||
|
||||
<Callout type="tip">
|
||||
上传大文件时,告诉 Agent
|
||||
文件的具体内容,以便获得更好的结果(例如"分析这个包含季度销售数据的 CSV")。
|
||||
上传大文件时,告诉 Agent 文件的具体内容,以便获得更好的结果(例如"分析这个包含季度销售数据的 CSV")。
|
||||
</Callout>
|
||||
|
||||
## 使用技能
|
||||
@@ -75,7 +72,6 @@ DeerFlow 工作区是一个基于浏览器的对话界面,你可以在其中
|
||||
当 Agent 生成文件(报告、图表、代码文件、演示文稿)时,它们会以**产出物**的形式出现在对话中。
|
||||
|
||||
点击产出物卡片:
|
||||
|
||||
- 在浏览器中预览文件。
|
||||
- 下载文件到本地机器。
|
||||
- 复制文件内容。
|
||||
@@ -91,12 +87,6 @@ DeerFlow 工作区是一个基于浏览器的对话界面,你可以在其中
|
||||
**删除线程**:从线程侧边栏菜单中选择删除。这会移除线程状态和所有相关的用户数据文件。
|
||||
|
||||
<Cards num={2}>
|
||||
<Cards.Card
|
||||
title="Agent 与线程"
|
||||
href="/docs/application/agents-and-threads"
|
||||
/>
|
||||
<Cards.Card
|
||||
title="运维与排障"
|
||||
href="/docs/application/operations-and-troubleshooting"
|
||||
/>
|
||||
<Cards.Card title="Agent 与线程" href="/docs/application/agents-and-threads" />
|
||||
<Cards.Card title="运维与排障" href="/docs/application/operations-and-troubleshooting" />
|
||||
</Cards>
|
||||
|
||||
@@ -8,8 +8,7 @@ import { Callout, Cards } from "nextra/components";
|
||||
# 配置
|
||||
|
||||
<Callout type="info" emoji="⚙️">
|
||||
所有 DeerFlow Harness 行为都由 <code>config.yaml</code>{" "}
|
||||
驱动。一个文件控制哪些模型可用、沙箱如何运行、加载哪些工具,以及每个子系统的行为。
|
||||
所有 DeerFlow Harness 行为都由 <code>config.yaml</code> 驱动。一个文件控制哪些模型可用、沙箱如何运行、加载哪些工具,以及每个子系统的行为。
|
||||
</Callout>
|
||||
|
||||
DeerFlow 的配置系统围绕一个目标设计:每一个有意义的行为都应该可以在配置文件中表达,而不是硬编码在应用程序中。这使部署可重现、可审计,并且易于按环境定制。
|
||||
@@ -80,7 +79,7 @@ models:
|
||||
use: langchain_openai:ChatOpenAI
|
||||
model: gpt-4o
|
||||
api_key: $OPENAI_API_KEY
|
||||
some_provider_specific_option: value # 传递给 ChatOpenAI 构造函数
|
||||
some_provider_specific_option: value # 传递给 ChatOpenAI 构造函数
|
||||
```
|
||||
|
||||
## 配置版本
|
||||
@@ -103,26 +102,26 @@ make config-upgrade
|
||||
|
||||
下表将 `config.yaml` 中的每个顶层章节映射到其文档页面:
|
||||
|
||||
| 章节 | 描述 | 文档 |
|
||||
| ----------------- | -------------------------------------------- | ---------------------------------------------------- |
|
||||
| `log_level` | 日志级别(`debug`/`info`/`warning`/`error`) | — |
|
||||
| `models` | 可用的 LLM 模型 | [Lead Agent](/docs/harness/lead-agent) |
|
||||
| `token_usage` | 每次模型调用的 token 追踪 | [中间件](/docs/harness/middlewares) |
|
||||
| `tools` | 可用的 Agent 工具 | [工具](/docs/harness/tools) |
|
||||
| `tool_groups` | 工具的命名分组 | [工具](/docs/harness/tools) |
|
||||
| `tool_search` | 延迟/按需工具加载 | [工具](/docs/harness/tools) |
|
||||
| `sandbox` | 沙箱提供者和选项 | [沙箱](/docs/harness/sandbox) |
|
||||
| `skills` | 技能目录和容器路径 | [技能](/docs/harness/skills) |
|
||||
| `skill_evolution` | Agent 管理的技能创建 | [技能](/docs/harness/skills) |
|
||||
| `subagents` | 子 Agent 超时和最大轮次 | [子 Agent](/docs/harness/subagents) |
|
||||
| `acp_agents` | 外部 ACP Agent 集成 | [子 Agent](/docs/harness/subagents) |
|
||||
| `memory` | 跨会话记忆存储 | [记忆系统](/docs/harness/memory) |
|
||||
| `summarization` | 对话摘要压缩 | [中间件](/docs/harness/middlewares) |
|
||||
| `title` | 自动生成线程标题 | [中间件](/docs/harness/middlewares) |
|
||||
| `checkpointer` | 线程状态持久化 | [Agent 与线程](/docs/application/agents-and-threads) |
|
||||
| `guardrails` | 工具调用授权 | — |
|
||||
| `uploads` | 文件上传设置(PDF 转换器) | — |
|
||||
| `channels` | IM 频道集成(飞书、Slack 等) | — |
|
||||
| 章节 | 描述 | 文档 |
|
||||
|---|---|---|
|
||||
| `log_level` | 日志级别(`debug`/`info`/`warning`/`error`) | — |
|
||||
| `models` | 可用的 LLM 模型 | [Lead Agent](/docs/harness/lead-agent) |
|
||||
| `token_usage` | 每次模型调用的 token 追踪 | [中间件](/docs/harness/middlewares) |
|
||||
| `tools` | 可用的 Agent 工具 | [工具](/docs/harness/tools) |
|
||||
| `tool_groups` | 工具的命名分组 | [工具](/docs/harness/tools) |
|
||||
| `tool_search` | 延迟/按需工具加载 | [工具](/docs/harness/tools) |
|
||||
| `sandbox` | 沙箱提供者和选项 | [沙箱](/docs/harness/sandbox) |
|
||||
| `skills` | 技能目录和容器路径 | [技能](/docs/harness/skills) |
|
||||
| `skill_evolution` | Agent 管理的技能创建 | [技能](/docs/harness/skills) |
|
||||
| `subagents` | 子 Agent 超时和最大轮次 | [子 Agent](/docs/harness/subagents) |
|
||||
| `acp_agents` | 外部 ACP Agent 集成 | [子 Agent](/docs/harness/subagents) |
|
||||
| `memory` | 跨会话记忆存储 | [记忆系统](/docs/harness/memory) |
|
||||
| `summarization` | 对话摘要压缩 | [中间件](/docs/harness/middlewares) |
|
||||
| `title` | 自动生成线程标题 | [中间件](/docs/harness/middlewares) |
|
||||
| `checkpointer` | 线程状态持久化 | [Agent 与线程](/docs/application/agents-and-threads) |
|
||||
| `guardrails` | 工具调用授权 | — |
|
||||
| `uploads` | 文件上传设置(PDF 转换器) | — |
|
||||
| `channels` | IM 频道集成(飞书、Slack 等) | — |
|
||||
|
||||
## 最小配置示例
|
||||
|
||||
|
||||
@@ -8,9 +8,7 @@ import { Callout, Cards } from "nextra/components";
|
||||
# 自定义与扩展
|
||||
|
||||
<Callout type="info" emoji="🔧">
|
||||
DeerFlow
|
||||
设计为可适配的。你可以通过编写自定义中间件、添加新工具、构建技能包以及通过
|
||||
config.yaml 的 <code>use:</code> 字段替换任何内置组件来扩展 Agent 行为。
|
||||
DeerFlow 设计为可适配的。你可以通过编写自定义中间件、添加新工具、构建技能包以及通过 config.yaml 的 <code>use:</code> 字段替换任何内置组件来扩展 Agent 行为。
|
||||
</Callout>
|
||||
|
||||
DeerFlow 的可插拔架构意味着系统的大多数部分都可以在不 fork 核心的情况下被替换或扩展。本页面列举了扩展点,并解释如何使用每一个。
|
||||
|
||||
@@ -8,8 +8,7 @@ import { Callout, Cards } from "nextra/components";
|
||||
# 设计理念
|
||||
|
||||
<Callout type="info" emoji="🏗️">
|
||||
DeerFlow 围绕一个核心思想构建:Agent
|
||||
行为应该由小型、可观察、可替换的组件组合而成——而不是硬编码到固定的工作流图中。
|
||||
DeerFlow 围绕一个核心思想构建:Agent 行为应该由小型、可观察、可替换的组件组合而成——而不是硬编码到固定的工作流图中。
|
||||
</Callout>
|
||||
|
||||
了解 DeerFlow Harness 背后的设计理念,有助于你有效地使用它、自信地扩展它,并推断 Agent 在生产环境中的行为方式。
|
||||
@@ -102,15 +101,15 @@ DeerFlow 中所有有意义的行为都通过 `config.yaml` 控制。系统的
|
||||
|
||||
## 总结
|
||||
|
||||
| 设计原则 | 实践含义 |
|
||||
| ---------------------- | ---------------------------------------- |
|
||||
| 设计原则 | 实践含义 |
|
||||
|---|---|
|
||||
| Harness 而非 Framework | 开箱即用的运行时,所有基础设施已预先连接 |
|
||||
| 长时序优先 | 架构假设多步骤、多工具、多轮次任务 |
|
||||
| 中间件优于继承 | 行为由小型、隔离的插件组合而成 |
|
||||
| 技能提供专业化 | 领域能力按需注入,保持基础干净 |
|
||||
| 沙箱用于执行 | 真实文件和命令操作的隔离工作区 |
|
||||
| 上下文工程 | 主动管理 Agent 所见内容以保持有效性 |
|
||||
| 配置驱动 | 所有关键行为通过 `config.yaml` 控制 |
|
||||
| 长时序优先 | 架构假设多步骤、多工具、多轮次任务 |
|
||||
| 中间件优于继承 | 行为由小型、隔离的插件组合而成 |
|
||||
| 技能提供专业化 | 领域能力按需注入,保持基础干净 |
|
||||
| 沙箱用于执行 | 真实文件和命令操作的隔离工作区 |
|
||||
| 上下文工程 | 主动管理 Agent 所见内容以保持有效性 |
|
||||
| 配置驱动 | 所有关键行为通过 `config.yaml` 控制 |
|
||||
|
||||
<Cards num={2}>
|
||||
<Cards.Card title="Lead Agent" href="/docs/harness/lead-agent" />
|
||||
|
||||
@@ -8,8 +8,7 @@ import { Callout, Cards } from "nextra/components";
|
||||
# 安装 DeerFlow Harness
|
||||
|
||||
<Callout type="info" emoji="📦">
|
||||
DeerFlow Harness Python 包将以 <code>deerflow</code>{" "}
|
||||
名称发布。目前尚未正式发布,安装方式<strong>即将推出</strong>。
|
||||
DeerFlow Harness Python 包将以 <code>deerflow</code> 名称发布。目前尚未正式发布,安装方式<strong>即将推出</strong>。
|
||||
</Callout>
|
||||
|
||||
DeerFlow Harness 是构建自己 Super Agent 系统的 Python SDK 和运行时基础。
|
||||
|
||||
@@ -8,8 +8,7 @@ import { Callout, Cards } from "nextra/components";
|
||||
# 集成指南
|
||||
|
||||
<Callout type="info" emoji="🔌">
|
||||
DeerFlow Harness 可以嵌入任何 Python 应用程序。本指南涵盖在你自己的系统中将
|
||||
DeerFlow 作为库使用的集成模式。
|
||||
DeerFlow Harness 可以嵌入任何 Python 应用程序。本指南涵盖在你自己的系统中将 DeerFlow 作为库使用的集成模式。
|
||||
</Callout>
|
||||
|
||||
DeerFlow Harness 不仅仅是一个独立应用程序——它是一个可以导入并在你自己的后端、API 服务器、自动化系统或多 Agent 协调器中使用的 Python 库。
|
||||
|
||||
@@ -8,9 +8,7 @@ import { Callout, Cards, Steps } from "nextra/components";
|
||||
# Lead Agent
|
||||
|
||||
<Callout type="info" emoji="🧠">
|
||||
Lead Agent 是每个 DeerFlow
|
||||
线程中的主要推理和编排单元。它决定要做什么、调用工具、委派子
|
||||
Agent,并返回产出物。
|
||||
Lead Agent 是每个 DeerFlow 线程中的主要推理和编排单元。它决定要做什么、调用工具、委派子 Agent,并返回产出物。
|
||||
</Callout>
|
||||
|
||||
Lead Agent 是 DeerFlow 线程中的核心执行者。每个对话、任务和工作流都通过它进行。理解它的工作方式有助于你有效地配置它,并在需要时扩展它。
|
||||
@@ -124,8 +122,7 @@ models:
|
||||
自定义 Agent 通过 DeerFlow 应用界面或 `/api/agents` 端点创建。其配置存储在后端目录的 `agents/{name}/config.yaml` 中。
|
||||
|
||||
<Callout type="tip">
|
||||
当在线程中选择自定义 Agent 时,Lead Agent 在运行时加载该 Agent 的配置。为特定
|
||||
Agent 切换模型或技能不需要重启服务器。
|
||||
当在线程中选择自定义 Agent 时,Lead Agent 在运行时加载该 Agent 的配置。为特定 Agent 切换模型或技能不需要重启服务器。
|
||||
</Callout>
|
||||
|
||||
<Cards num={2}>
|
||||
|
||||
@@ -8,8 +8,7 @@ import { Callout, Cards } from "nextra/components";
|
||||
# 记忆系统
|
||||
|
||||
<Callout type="info" emoji="💾">
|
||||
记忆让 DeerFlow 在多个会话中保留有用信息。Agent
|
||||
记住用户偏好、项目背景和反复出现的事实,这样它可以在不每次从零开始的情况下给出更好的响应。
|
||||
记忆让 DeerFlow 在多个会话中保留有用信息。Agent 记住用户偏好、项目背景和反复出现的事实,这样它可以在不每次从零开始的情况下给出更好的响应。
|
||||
</Callout>
|
||||
|
||||
记忆是 DeerFlow Harness 的一个运行时功能。它不是简单的对话日志,而是跨多个独立会话持久化、在未来对话中影响 Agent 行为的结构化事实和上下文摘要存储。
|
||||
|
||||
@@ -8,9 +8,7 @@ import { Callout } from "nextra/components";
|
||||
# 中间件
|
||||
|
||||
<Callout type="info" emoji="🔌">
|
||||
中间件包裹 Lead Agent 中的每次 LLM
|
||||
调用。它们是添加跨领域行为(如记忆、摘要压缩、澄清和 token
|
||||
追踪)的主要扩展点。
|
||||
中间件包裹 Lead Agent 中的每次 LLM 调用。它们是添加跨领域行为(如记忆、摘要压缩、澄清和 token 追踪)的主要扩展点。
|
||||
</Callout>
|
||||
|
||||
每次 Lead Agent 调用 LLM 时,都会先后执行一条**中间件链**。中间件可以读取和修改 Agent 的状态、向系统提示注入内容、拦截工具调用,并对模型输出做出反应。
|
||||
@@ -89,7 +87,7 @@ title:
|
||||
enabled: true
|
||||
max_words: 6
|
||||
max_chars: 60
|
||||
model_name: null # 使用默认模型
|
||||
model_name: null # 使用默认模型
|
||||
```
|
||||
|
||||
---
|
||||
@@ -151,7 +149,7 @@ summarization:
|
||||
|
||||
# 触发条件——满足任意一个条件时运行摘要
|
||||
trigger:
|
||||
- type: tokens # 当上下文超过 N 个 token 时触发
|
||||
- type: tokens # 当上下文超过 N 个 token 时触发
|
||||
value: 15564
|
||||
# - type: messages # 当消息数超过 N 时触发
|
||||
# value: 50
|
||||
@@ -161,7 +159,7 @@ summarization:
|
||||
# 摘要后保留多少最近历史
|
||||
keep:
|
||||
type: messages
|
||||
value: 10 # 保留最近 10 条消息
|
||||
value: 10 # 保留最近 10 条消息
|
||||
# 或者按 token 保留:
|
||||
# type: tokens
|
||||
# value: 3000
|
||||
@@ -174,13 +172,11 @@ summarization:
|
||||
```
|
||||
|
||||
**触发类型**:
|
||||
|
||||
- `tokens`:当对话中总 token 数超过 `value` 时触发。
|
||||
- `messages`:当消息数超过 `value` 时触发。
|
||||
- `fraction`:当上下文达到模型最大输入 token 限制的 `value` 比例时触发。
|
||||
|
||||
**保留类型**:
|
||||
|
||||
- `messages`:摘要后保留最后 `value` 条消息。
|
||||
- `tokens`:保留最近 `value` 个 token 的历史。
|
||||
- `fraction`:保留模型最大输入 token 限制的 `value` 比例的最近历史。
|
||||
|
||||
@@ -85,15 +85,15 @@ agent = create_deerflow_agent(
|
||||
|
||||
常用参数:
|
||||
|
||||
| 参数 | 说明 |
|
||||
| ------------------ | -------------------------- |
|
||||
| `tools` | 提供给 Agent 的额外工具 |
|
||||
| `system_prompt` | 自定义系统提示词 |
|
||||
| `features` | 启用或替换内置运行时能力 |
|
||||
| 参数 | 说明 |
|
||||
|---|---|
|
||||
| `tools` | 提供给 Agent 的额外工具 |
|
||||
| `system_prompt` | 自定义系统提示词 |
|
||||
| `features` | 启用或替换内置运行时能力 |
|
||||
| `extra_middleware` | 将自定义中间件插入默认链路 |
|
||||
| `plan_mode` | 启用 Todo 风格的任务跟踪 |
|
||||
| `checkpointer` | 为多轮运行持久化状态 |
|
||||
| `name` | Agent 的逻辑名称 |
|
||||
| `plan_mode` | 启用 Todo 风格的任务跟踪 |
|
||||
| `checkpointer` | 为多轮运行持久化状态 |
|
||||
| `name` | Agent 的逻辑名称 |
|
||||
|
||||
## 什么时候使用 DeerFlowClient
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@ import { Callout, Cards, Tabs } from "nextra/components";
|
||||
# 沙箱
|
||||
|
||||
<Callout type="info" emoji="📦">
|
||||
沙箱是 Agent 进行文件和命令操作的隔离工作区。它让 DeerFlow
|
||||
能够采取真实行动,而不仅仅是对话。
|
||||
沙箱是 Agent 进行文件和命令操作的隔离工作区。它让 DeerFlow 能够采取真实行动,而不仅仅是对话。
|
||||
</Callout>
|
||||
|
||||
沙箱为 Lead Agent 提供一个受控环境,在其中可以读取文件、写入输出、运行 Shell 命令并生成产出物。没有沙箱,Agent 只能生成文本;有了沙箱,它可以编写和执行代码、处理数据文件、生成图表并构建交付物。
|
||||
@@ -28,7 +27,7 @@ DeerFlow 支持三种沙箱模式,选择适合你部署的一种:
|
||||
```yaml
|
||||
sandbox:
|
||||
use: deerflow.sandbox.local:LocalSandboxProvider
|
||||
allow_host_bash: false # 默认;仅对完全受信任的工作流设置为 true
|
||||
allow_host_bash: false # 默认;仅对完全受信任的工作流设置为 true
|
||||
```
|
||||
|
||||
### 基于容器的 AIO 沙箱
|
||||
@@ -73,10 +72,10 @@ sandbox:
|
||||
|
||||
沙箱使用路径映射来桥接主机文件系统和容器的虚拟文件系统。始终配置两个关键映射:
|
||||
|
||||
| 主机路径 | 容器路径 | 访问权限 |
|
||||
| ------------------------------------------- | --------------------------------------------- | -------- |
|
||||
| `skills/`(来自 `skills.path`) | `/mnt/skills`(来自 `skills.container_path`) | 只读 |
|
||||
| `.deer-flow/threads/{thread_id}/user-data/` | `/mnt/user-data/` | 读写 |
|
||||
| 主机路径 | 容器路径 | 访问权限 |
|
||||
|---|---|---|
|
||||
| `skills/`(来自 `skills.path`) | `/mnt/skills`(来自 `skills.container_path`) | 只读 |
|
||||
| `.deer-flow/threads/{thread_id}/user-data/` | `/mnt/user-data/` | 读写 |
|
||||
|
||||
技能目录始终以只读方式挂载。线程将其工作数据(上传文件、输出、中间文件)写入 `/mnt/user-data/`。
|
||||
|
||||
@@ -95,8 +94,7 @@ sandbox:
|
||||
|
||||
<Callout type="warning">
|
||||
自定义挂载的 <code>container_path</code> 不能与保留前缀冲突:
|
||||
<code>/mnt/skills</code>、<code>/mnt/acp-workspace</code> 或{" "}
|
||||
<code>/mnt/user-data</code>。
|
||||
<code>/mnt/skills</code>、<code>/mnt/acp-workspace</code> 或 <code>/mnt/user-data</code>。
|
||||
</Callout>
|
||||
|
||||
## 输出截断
|
||||
@@ -127,7 +125,7 @@ sandbox:
|
||||
|
||||
```yaml
|
||||
sandbox:
|
||||
allow_host_bash: true # 危险:授予 Agent 对你机器的 Shell 访问权限
|
||||
allow_host_bash: true # 危险:授予 Agent 对你机器的 Shell 访问权限
|
||||
```
|
||||
|
||||
即使没有 `bash`,Agent 也可以通过专用文件工具读写文件。
|
||||
|
||||
@@ -8,8 +8,7 @@ import { Callout, Cards, FileTree, Steps } from "nextra/components";
|
||||
# 技能
|
||||
|
||||
<Callout type="info" emoji="🎯">
|
||||
技能是面向任务的能力包,教会 Agent 如何完成特定类型的工作。基础 Agent
|
||||
保持通用;技能在需要时提供专业化。
|
||||
技能是面向任务的能力包,教会 Agent 如何完成特定类型的工作。基础 Agent 保持通用;技能在需要时提供专业化。
|
||||
</Callout>
|
||||
|
||||
技能不仅仅是提示词。它是一个自包含的能力包,可以包含结构化指令、分步工作流、领域最佳实践、支撑资源和工具配置。技能按需加载——在任务需要时注入内容,否则不影响上下文。
|
||||
@@ -40,23 +39,23 @@ import { Callout, Cards, FileTree, Steps } from "nextra/components";
|
||||
|
||||
DeerFlow 内置以下公共技能:
|
||||
|
||||
| 技能 | 描述 |
|
||||
| ------------------------------ | -------------------------------------------- |
|
||||
| `deep-research` | 带来源收集、交叉验证和结构化输出的多步骤研究 |
|
||||
| `data-analysis` | 数据探索、统计分析和洞察生成 |
|
||||
| `chart-visualization` | 从数据创建图表和可视化 |
|
||||
| `ppt-generation` | 演示文稿幻灯片生成 |
|
||||
| `image-generation` | AI 图像生成工作流 |
|
||||
| `code-documentation` | 自动化代码文档生成 |
|
||||
| `newsletter-generation` | 新闻简报内容创作 |
|
||||
| `podcast-generation` | 播客脚本和大纲生成 |
|
||||
| `academic-paper-review` | 结构化学术论文分析 |
|
||||
| `consulting-analysis` | 商业咨询框架和分析 |
|
||||
| `systematic-literature-review` | 文献综述方法论和综合 |
|
||||
| `github-deep-research` | 仓库和代码深度研究 |
|
||||
| `frontend-design` | 前端设计和 UI 工作流 |
|
||||
| `web-design-guidelines` | 网页设计标准和审查 |
|
||||
| `video-generation` | 视频内容规划和生成 |
|
||||
| 技能 | 描述 |
|
||||
|---|---|
|
||||
| `deep-research` | 带来源收集、交叉验证和结构化输出的多步骤研究 |
|
||||
| `data-analysis` | 数据探索、统计分析和洞察生成 |
|
||||
| `chart-visualization` | 从数据创建图表和可视化 |
|
||||
| `ppt-generation` | 演示文稿幻灯片生成 |
|
||||
| `image-generation` | AI 图像生成工作流 |
|
||||
| `code-documentation` | 自动化代码文档生成 |
|
||||
| `newsletter-generation` | 新闻简报内容创作 |
|
||||
| `podcast-generation` | 播客脚本和大纲生成 |
|
||||
| `academic-paper-review` | 结构化学术论文分析 |
|
||||
| `consulting-analysis` | 商业咨询框架和分析 |
|
||||
| `systematic-literature-review` | 文献综述方法论和综合 |
|
||||
| `github-deep-research` | 仓库和代码深度研究 |
|
||||
| `frontend-design` | 前端设计和 UI 工作流 |
|
||||
| `web-design-guidelines` | 网页设计标准和审查 |
|
||||
| `video-generation` | 视频内容规划和生成 |
|
||||
|
||||
## 技能生命周期
|
||||
|
||||
@@ -133,14 +132,12 @@ DeerFlow 包含一个可选的**技能进化**功能,允许 Agent 在 `skills/
|
||||
|
||||
```yaml
|
||||
skill_evolution:
|
||||
enabled: false # 设为 true 允许 Agent 管理技能创建
|
||||
moderation_model_name: null # 安全扫描模型(null = 使用默认模型)
|
||||
enabled: false # 设为 true 允许 Agent 管理技能创建
|
||||
moderation_model_name: null # 安全扫描模型(null = 使用默认模型)
|
||||
```
|
||||
|
||||
<Callout type="warning">
|
||||
只在你信任 Agent
|
||||
输出的环境中启用技能进化。新创建的技能在加载前会经过安全扫描,但该功能给予
|
||||
Agent 对技能目录的写访问权限。
|
||||
只在你信任 Agent 输出的环境中启用技能进化。新创建的技能在加载前会经过安全扫描,但该功能给予 Agent 对技能目录的写访问权限。
|
||||
</Callout>
|
||||
|
||||
## 编写自定义技能
|
||||
|
||||
@@ -8,8 +8,7 @@ import { Callout, Cards } from "nextra/components";
|
||||
# 子 Agent
|
||||
|
||||
<Callout type="info" emoji="👥">
|
||||
子 Agent 是 Lead Agent
|
||||
委派子任务的专注执行者。它们以隔离的上下文运行,在处理并行或专业工作的同时保持主对话清晰。
|
||||
子 Agent 是 Lead Agent 委派子任务的专注执行者。它们以隔离的上下文运行,在处理并行或专业工作的同时保持主对话清晰。
|
||||
</Callout>
|
||||
|
||||
当一个任务对单个推理线程来说太宽泛,或者部分任务可以并行完成时,Lead Agent 将工作委派给**子 Agent**。子 Agent 是一个独立的 Agent 调用,接收特定任务、执行并返回结果。
|
||||
@@ -74,10 +73,10 @@ subagents:
|
||||
# 可选:按 Agent 覆盖
|
||||
agents:
|
||||
general-purpose:
|
||||
timeout_seconds: 1800 # 复杂任务 30 分钟
|
||||
timeout_seconds: 1800 # 复杂任务 30 分钟
|
||||
max_turns: 160
|
||||
bash:
|
||||
timeout_seconds: 300 # 快速命令 5 分钟
|
||||
timeout_seconds: 300 # 快速命令 5 分钟
|
||||
max_turns: 80
|
||||
```
|
||||
|
||||
@@ -116,9 +115,7 @@ acp_agents:
|
||||
Lead Agent 通过 `invoke_acp_agent` 内置工具调用 ACP Agent。
|
||||
|
||||
<Callout type="tip">
|
||||
ACP Agent 作为 DeerFlow 管理的子进程运行,通过 ACP 协议通信。标准 CLI
|
||||
工具(如原始的 `claude` 或 `codex` 命令)默认不兼容
|
||||
ACP——请使用上面列出的适配器包或兼容的 ACP 封装器。
|
||||
ACP Agent 作为 DeerFlow 管理的子进程运行,通过 ACP 协议通信。标准 CLI 工具(如原始的 `claude` 或 `codex` 命令)默认不兼容 ACP——请使用上面列出的适配器包或兼容的 ACP 封装器。
|
||||
</Callout>
|
||||
|
||||
<Cards num={2}>
|
||||
|
||||
@@ -8,8 +8,7 @@ import { Callout, Cards, Tabs } from "nextra/components";
|
||||
# 工具
|
||||
|
||||
<Callout type="info" emoji="🔧">
|
||||
工具是 Lead Agent 可以采取的行动。DeerFlow 提供内置工具、社区集成、MCP
|
||||
工具和技能工具——全部通过 <code>config.yaml</code> 控制。
|
||||
工具是 Lead Agent 可以采取的行动。DeerFlow 提供内置工具、社区集成、MCP 工具和技能工具——全部通过 <code>config.yaml</code> 控制。
|
||||
</Callout>
|
||||
|
||||
Lead Agent 是一个工具调用 Agent。工具是它与世界交互的方式:搜索网络、读写文件、运行命令、委派任务以及向用户呈现输出。
|
||||
@@ -75,15 +74,15 @@ task(agent="general-purpose", task="...", context="...")
|
||||
|
||||
以下工具与沙箱文件系统交互,需要配置并激活沙箱。
|
||||
|
||||
| 工具 | 描述 |
|
||||
| ------------- | ---------------------------------------------------------- |
|
||||
| `ls` | 列出目录中的文件 |
|
||||
| `read_file` | 读取文件内容 |
|
||||
| `glob` | 查找匹配模式的文件 |
|
||||
| `grep` | 搜索文件内容 |
|
||||
| `write_file` | 向文件写入内容 |
|
||||
| `str_replace` | 替换文件中的字符串 |
|
||||
| `bash` | 执行 Shell 命令(需要 `allow_host_bash: true` 或容器沙箱) |
|
||||
| 工具 | 描述 |
|
||||
|---|---|
|
||||
| `ls` | 列出目录中的文件 |
|
||||
| `read_file` | 读取文件内容 |
|
||||
| `glob` | 查找匹配模式的文件 |
|
||||
| `grep` | 搜索文件内容 |
|
||||
| `write_file` | 向文件写入内容 |
|
||||
| `str_replace` | 替换文件中的字符串 |
|
||||
| `bash` | 执行 Shell 命令(需要 `allow_host_bash: true` 或容器沙箱) |
|
||||
|
||||
在 `config.yaml` 的 `tools:` 下配置:
|
||||
|
||||
@@ -121,7 +120,6 @@ tools:
|
||||
高质量搜索,带结构化结果。需要 [Tavily](https://tavily.com) API Key。
|
||||
|
||||
安装:`cd backend && uv add 'deerflow-harness[tavily]'`
|
||||
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab>
|
||||
```yaml
|
||||
@@ -132,7 +130,6 @@ tools:
|
||||
带神经检索的语义搜索。需要 [Exa](https://exa.ai) API Key。
|
||||
|
||||
安装:`cd backend && uv add 'deerflow-harness[exa]'`
|
||||
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab>
|
||||
```yaml
|
||||
@@ -151,7 +148,7 @@ Firecrawl 驱动的搜索和爬取。需要 [Firecrawl](https://firecrawl.dev) A
|
||||
```yaml
|
||||
tools:
|
||||
- use: deerflow.community.jina_ai.tools:web_fetch_tool
|
||||
api_key: $JINA_API_KEY # 可选;匿名使用有速率限制
|
||||
api_key: $JINA_API_KEY # 可选;匿名使用有速率限制
|
||||
```
|
||||
将网页转换为干净的 Markdown。无 API Key 也可使用,但有更严格的速率限制。
|
||||
</Tabs.Tab>
|
||||
|
||||
@@ -63,3 +63,4 @@ Harness 章节是技术文档的核心,面向想要构建基于 DeerFlow 系
|
||||
### 参考
|
||||
|
||||
参考章节提供详细的查阅资料,包括配置、运行时模式、API 和代码映射。
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@ import { Callout, Cards } from "nextra/components";
|
||||
# 核心概念
|
||||
|
||||
<Callout type="important" emoji="🧠">
|
||||
如果你将 DeerFlow 理解为一个长时序 Agent
|
||||
的运行时,而不仅仅是聊天界面或工作流图,它将最易于理解。
|
||||
如果你将 DeerFlow 理解为一个长时序 Agent 的运行时,而不仅仅是聊天界面或工作流图,它将最易于理解。
|
||||
</Callout>
|
||||
|
||||
在深入了解 DeerFlow 之前,先建立一些贯穿整个系统的核心概念。这些概念解释了 DeerFlow 的优化目标以及其架构设计的原因。
|
||||
|
||||
@@ -8,8 +8,7 @@ import { Callout, Cards } from "nextra/components";
|
||||
# Harness 与应用
|
||||
|
||||
<Callout type="info" emoji="⚙️">
|
||||
DeerFlow 应用是构建在 DeerFlow Harness 之上的最佳实践 Super Agent 应用,而
|
||||
DeerFlow Harness 是构建自己 Agent 系统的 Python SDK 和运行时基础。
|
||||
DeerFlow 应用是构建在 DeerFlow Harness 之上的最佳实践 Super Agent 应用,而 DeerFlow Harness 是构建自己 Agent 系统的 Python SDK 和运行时基础。
|
||||
</Callout>
|
||||
|
||||
DeerFlow 有两个紧密相关但服务于不同目的的层次:
|
||||
|
||||
@@ -8,8 +8,7 @@ import { Callout, Cards } from "nextra/components";
|
||||
# 为什么选择 DeerFlow
|
||||
|
||||
<Callout type="info" emoji="🦌">
|
||||
DeerFlow 起源于深度研究,但逐渐演化为一个通用的长时序 Agent
|
||||
运行时——支持技能、记忆、工具和协作调度。
|
||||
DeerFlow 起源于深度研究,但逐渐演化为一个通用的长时序 Agent 运行时——支持技能、记忆、工具和协作调度。
|
||||
</Callout>
|
||||
|
||||
DeerFlow 的诞生是因为现代 Agent 系统需要的不仅仅是一个聊天循环。一个真正有用的 Agent 必须能够进行长时序规划、将任务拆解为子任务、使用工具、操作文件、安全地运行代码,并在复杂任务中保持足够的上下文连贯性。DeerFlow 正是为提供这样的运行时基础而构建的。
|
||||
|
||||
@@ -31,7 +31,6 @@ description: 本教程引导你在 DeerFlow 中完成第一次完整的 Agent
|
||||
### 3. 观察 Agent 的工作过程
|
||||
|
||||
你将看到 Agent 进入工作状态:
|
||||
|
||||
- 展开**思考步骤**查看它调用了哪些工具
|
||||
- 观察网络搜索结果的流入
|
||||
- 等待最终报告生成
|
||||
@@ -39,7 +38,6 @@ description: 本教程引导你在 DeerFlow 中完成第一次完整的 Agent
|
||||
### 4. 与结果互动
|
||||
|
||||
报告生成后,你可以:
|
||||
|
||||
- 要求对某部分进行详细说明
|
||||
- 要求将报告导出为文件(Agent 会使用 `present_files` 工具)
|
||||
- 要求基于研究结果创建图表
|
||||
|
||||
@@ -30,7 +30,6 @@ memory:
|
||||
## 示例
|
||||
|
||||
**第一次对话**:
|
||||
|
||||
```
|
||||
我是一名 Python 后端开发者,主要使用 FastAPI 和 PostgreSQL。
|
||||
我的团队遵循 PEP 8 代码规范,偏好类型注解。
|
||||
@@ -38,7 +37,6 @@ memory:
|
||||
```
|
||||
|
||||
**后续对话**(无需重复背景):
|
||||
|
||||
```
|
||||
帮我写一个用户认证模块
|
||||
```
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { fetch } from "@/core/api/fetcher";
|
||||
import { fetchWithAuth } from "@/core/api/fetcher";
|
||||
import { getBackendBaseURL } from "@/core/config";
|
||||
|
||||
import type { Agent, CreateAgentRequest, UpdateAgentRequest } from "./types";
|
||||
@@ -29,7 +29,7 @@ export async function getAgent(name: string): Promise<Agent> {
|
||||
}
|
||||
|
||||
export async function createAgent(request: CreateAgentRequest): Promise<Agent> {
|
||||
const res = await fetch(`${getBackendBaseURL()}/api/agents`, {
|
||||
const res = await fetchWithAuth(`${getBackendBaseURL()}/api/agents`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(request),
|
||||
@@ -45,7 +45,7 @@ export async function updateAgent(
|
||||
name: string,
|
||||
request: UpdateAgentRequest,
|
||||
): Promise<Agent> {
|
||||
const res = await fetch(`${getBackendBaseURL()}/api/agents/${name}`, {
|
||||
const res = await fetchWithAuth(`${getBackendBaseURL()}/api/agents/${name}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(request),
|
||||
@@ -58,7 +58,7 @@ export async function updateAgent(
|
||||
}
|
||||
|
||||
export async function deleteAgent(name: string): Promise<void> {
|
||||
const res = await fetch(`${getBackendBaseURL()}/api/agents/${name}`, {
|
||||
const res = await fetchWithAuth(`${getBackendBaseURL()}/api/agents/${name}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
if (!res.ok) throw new Error(`Failed to delete agent: ${res.statusText}`);
|
||||
|
||||
@@ -13,8 +13,8 @@ import { sanitizeRunStreamOptions } from "./stream-mode";
|
||||
*
|
||||
* Reading the cookie per-request (rather than baking it into the SDK's
|
||||
* ``defaultHeaders`` at construction) handles login / logout / password
|
||||
* change cookie rotation transparently. Both the ``/api/langgraph/*`` SDK
|
||||
* path and the direct REST endpoints in ``fetcher.ts:fetchWithAuth``
|
||||
* change cookie rotation transparently. Both the ``/langgraph-compat/*``
|
||||
* SDK path and the direct REST endpoints in ``fetcher.ts:fetchWithAuth``
|
||||
* share :func:`readCsrfCookie` and :const:`STATE_CHANGING_METHODS` so
|
||||
* the contract stays in lockstep.
|
||||
*/
|
||||
@@ -35,7 +35,7 @@ function createCompatibleClient(isMock?: boolean): LangGraphClient {
|
||||
const apiUrl = getLangGraphBaseURL(isMock);
|
||||
console.log(`Creating API client with base URL: ${apiUrl}`);
|
||||
const client = new LangGraphClient({
|
||||
apiUrl,
|
||||
apiUrl: getLangGraphBaseURL(isMock),
|
||||
onRequest: injectCsrfHeader,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getBackendBaseURL } from "../config";
|
||||
|
||||
import { fetch } from "./fetcher";
|
||||
import { fetchWithAuth } from "./fetcher";
|
||||
|
||||
export interface FeedbackData {
|
||||
feedback_id: string;
|
||||
@@ -14,7 +14,7 @@ export async function upsertFeedback(
|
||||
rating: number,
|
||||
comment?: string,
|
||||
): Promise<FeedbackData> {
|
||||
const res = await fetch(
|
||||
const res = await fetchWithAuth(
|
||||
`${getBackendBaseURL()}/api/threads/${encodeURIComponent(threadId)}/runs/${encodeURIComponent(runId)}/feedback`,
|
||||
{
|
||||
method: "PUT",
|
||||
@@ -32,7 +32,7 @@ export async function deleteFeedback(
|
||||
threadId: string,
|
||||
runId: string,
|
||||
): Promise<void> {
|
||||
const res = await fetch(
|
||||
const res = await fetchWithAuth(
|
||||
`${getBackendBaseURL()}/api/threads/${encodeURIComponent(threadId)}/runs/${encodeURIComponent(runId)}/feedback`,
|
||||
{ method: "DELETE" },
|
||||
);
|
||||
|
||||
@@ -53,7 +53,7 @@ export function readCsrfCookie(): string | null {
|
||||
* preserved; the helper only ADDS the CSRF header when it isn't already
|
||||
* present, so explicit overrides win.
|
||||
*/
|
||||
export async function fetch(
|
||||
export async function fetchWithAuth(
|
||||
input: RequestInfo | string,
|
||||
init?: RequestInit,
|
||||
): Promise<Response> {
|
||||
@@ -74,7 +74,7 @@ export async function fetch(
|
||||
}
|
||||
}
|
||||
|
||||
const res = await globalThis.fetch(url, {
|
||||
const res = await fetch(url, {
|
||||
...init,
|
||||
headers,
|
||||
credentials: "include",
|
||||
|
||||
@@ -10,18 +10,6 @@ const SSR_AUTH_TIMEOUT_MS = 5_000;
|
||||
* Returns a tagged AuthResult — callers use exhaustive switch, no try/catch.
|
||||
*/
|
||||
export async function getServerSideUser(): Promise<AuthResult> {
|
||||
if (process.env.DEER_FLOW_AUTH_DISABLED === "1") {
|
||||
return {
|
||||
tag: "authenticated",
|
||||
user: {
|
||||
id: "e2e-user",
|
||||
email: "e2e@test.local",
|
||||
system_role: "admin",
|
||||
needs_setup: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const cookieStore = await cookies();
|
||||
const sessionCookie = cookieStore.get("access_token");
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { fetch } from "@/core/api/fetcher";
|
||||
import { fetchWithAuth } from "@/core/api/fetcher";
|
||||
import { getBackendBaseURL } from "@/core/config";
|
||||
|
||||
import type { MCPConfig } from "./types";
|
||||
@@ -9,12 +9,15 @@ export async function loadMCPConfig() {
|
||||
}
|
||||
|
||||
export async function updateMCPConfig(config: MCPConfig) {
|
||||
const response = await fetch(`${getBackendBaseURL()}/api/mcp/config`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
const response = await fetchWithAuth(
|
||||
`${getBackendBaseURL()}/api/mcp/config`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(config),
|
||||
},
|
||||
body: JSON.stringify(config),
|
||||
});
|
||||
);
|
||||
return response.json();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { fetch } from "../api/fetcher";
|
||||
import { fetchWithAuth } from "../api/fetcher";
|
||||
import { getBackendBaseURL } from "../config";
|
||||
|
||||
import type {
|
||||
@@ -86,14 +86,14 @@ export async function loadMemory(): Promise<UserMemory> {
|
||||
}
|
||||
|
||||
export async function clearMemory(): Promise<UserMemory> {
|
||||
const response = await fetch(`${getBackendBaseURL()}/api/memory`, {
|
||||
const response = await fetchWithAuth(`${getBackendBaseURL()}/api/memory`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
return readMemoryResponse(response, "Failed to clear memory");
|
||||
}
|
||||
|
||||
export async function deleteMemoryFact(factId: string): Promise<UserMemory> {
|
||||
const response = await fetch(
|
||||
const response = await fetchWithAuth(
|
||||
`${getBackendBaseURL()}/api/memory/facts/${encodeURIComponent(factId)}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
@@ -108,26 +108,32 @@ export async function exportMemory(): Promise<UserMemory> {
|
||||
}
|
||||
|
||||
export async function importMemory(memory: UserMemory): Promise<UserMemory> {
|
||||
const response = await fetch(`${getBackendBaseURL()}/api/memory/import`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
const response = await fetchWithAuth(
|
||||
`${getBackendBaseURL()}/api/memory/import`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(memory),
|
||||
},
|
||||
body: JSON.stringify(memory),
|
||||
});
|
||||
);
|
||||
return readMemoryResponse(response, "Failed to import memory");
|
||||
}
|
||||
|
||||
export async function createMemoryFact(
|
||||
input: MemoryFactInput,
|
||||
): Promise<UserMemory> {
|
||||
const response = await fetch(`${getBackendBaseURL()}/api/memory/facts`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
const response = await fetchWithAuth(
|
||||
`${getBackendBaseURL()}/api/memory/facts`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(input),
|
||||
},
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
);
|
||||
return readMemoryResponse(response, "Failed to create memory fact");
|
||||
}
|
||||
|
||||
@@ -135,7 +141,7 @@ export async function updateMemoryFact(
|
||||
factId: string,
|
||||
input: MemoryFactPatchInput,
|
||||
): Promise<UserMemory> {
|
||||
const response = await fetch(
|
||||
const response = await fetchWithAuth(
|
||||
`${getBackendBaseURL()}/api/memory/facts/${encodeURIComponent(factId)}`,
|
||||
{
|
||||
method: "PATCH",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { fetch } from "@/core/api/fetcher";
|
||||
import { fetchWithAuth } from "@/core/api/fetcher";
|
||||
import { getBackendBaseURL } from "@/core/config";
|
||||
|
||||
import type { Skill } from "./type";
|
||||
@@ -10,7 +10,7 @@ export async function loadSkills() {
|
||||
}
|
||||
|
||||
export async function enableSkill(skillName: string, enabled: boolean) {
|
||||
const response = await fetch(
|
||||
const response = await fetchWithAuth(
|
||||
`${getBackendBaseURL()}/api/skills/${skillName}`,
|
||||
{
|
||||
method: "PUT",
|
||||
@@ -39,13 +39,16 @@ export interface InstallSkillResponse {
|
||||
export async function installSkill(
|
||||
request: InstallSkillRequest,
|
||||
): Promise<InstallSkillResponse> {
|
||||
const response = await fetch(`${getBackendBaseURL()}/api/skills/install`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
const response = await fetchWithAuth(
|
||||
`${getBackendBaseURL()}/api/skills/install`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(request),
|
||||
},
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
// Handle HTTP error responses (4xx, 5xx)
|
||||
|
||||
@@ -8,7 +8,8 @@ import { toast } from "sonner";
|
||||
import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
|
||||
|
||||
import { getAPIClient } from "../api";
|
||||
import { fetch } from "../api/fetcher";
|
||||
import type { FeedbackData } from "../api/feedback";
|
||||
import { fetchWithAuth } from "../api/fetcher";
|
||||
import { getBackendBaseURL } from "../config";
|
||||
import { useI18n } from "../i18n/hooks";
|
||||
import type { FileInMessage } from "../messages/utils";
|
||||
@@ -275,6 +276,9 @@ export function useThreadStream({
|
||||
onFinish(state) {
|
||||
listeners.current.onFinish?.(state.values);
|
||||
void queryClient.invalidateQueries({ queryKey: ["threads", "search"] });
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: ["thread-message-enrichment"],
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -300,7 +304,7 @@ export function useThreadStream({
|
||||
useEffect(() => {
|
||||
if (
|
||||
optimisticMessages.length > 0 &&
|
||||
thread.messages.length > prevMsgCountRef.current
|
||||
thread.messages.length > prevMsgCountRef.current + 1
|
||||
) {
|
||||
setOptimisticMessages([]);
|
||||
}
|
||||
@@ -696,7 +700,7 @@ export function useDeleteThread() {
|
||||
mutationFn: async ({ threadId }: { threadId: string }) => {
|
||||
await apiClient.threads.delete(threadId);
|
||||
|
||||
const response = await fetch(
|
||||
const response = await fetchWithAuth(
|
||||
`${getBackendBaseURL()}/api/threads/${encodeURIComponent(threadId)}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
@@ -769,3 +773,65 @@ export function useRenameThread() {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** Per-message enrichment data attached by the backend ``/history`` helper. */
|
||||
export interface MessageEnrichment {
|
||||
run_id: string;
|
||||
/** ``undefined`` = not feedback-eligible; ``null`` = eligible but unrated. */
|
||||
feedback?: FeedbackData | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch ``/history`` once and index feedback + run_id by message id.
|
||||
*
|
||||
* Replaces the old ``useThreadFeedback`` hook which keyed by AI-message
|
||||
* ordinal position — an inherently fragile mapping that broke whenever
|
||||
* ``ai_tool_call`` messages were interleaved with ``ai_message`` messages.
|
||||
* Keying by ``message.id`` is stable regardless of run count, tool-call
|
||||
* chains, or summarization.
|
||||
*
|
||||
* The ``/history`` response is refreshed on every stream completion via
|
||||
* ``invalidateQueries(["thread-message-enrichment"])`` in ``onFinish``.
|
||||
*/
|
||||
export function useThreadMessageEnrichment(
|
||||
threadId: string | null | undefined,
|
||||
) {
|
||||
return useQuery({
|
||||
queryKey: ["thread-message-enrichment", threadId],
|
||||
queryFn: async (): Promise<Map<string, MessageEnrichment>> => {
|
||||
const empty = new Map<string, MessageEnrichment>();
|
||||
if (!threadId) return empty;
|
||||
const res = await fetchWithAuth(
|
||||
`${getBackendBaseURL()}/api/threads/${encodeURIComponent(threadId)}/history`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ limit: 1 }),
|
||||
},
|
||||
);
|
||||
if (!res.ok) return empty;
|
||||
const entries = (await res.json()) as Array<{
|
||||
values?: {
|
||||
messages?: Array<{
|
||||
id?: string;
|
||||
run_id?: string;
|
||||
feedback?: FeedbackData | null;
|
||||
}>;
|
||||
};
|
||||
}>;
|
||||
const messages = entries[0]?.values?.messages ?? [];
|
||||
const map = new Map<string, MessageEnrichment>();
|
||||
for (const m of messages) {
|
||||
if (!m.id || !m.run_id) continue;
|
||||
const entry: MessageEnrichment = { run_id: m.run_id };
|
||||
// Preserve presence: "feedback" key absent → ineligible; present with
|
||||
// null → eligible but unrated; present with object → rated.
|
||||
if ("feedback" in m) entry.feedback = m.feedback;
|
||||
map.set(m.id, entry);
|
||||
}
|
||||
return map;
|
||||
},
|
||||
enabled: !!threadId,
|
||||
staleTime: 30_000,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* API functions for file uploads
|
||||
*/
|
||||
|
||||
import { fetch } from "../api/fetcher";
|
||||
import { fetchWithAuth } from "../api/fetcher";
|
||||
import { getBackendBaseURL } from "../config";
|
||||
|
||||
export interface UploadedFileInfo {
|
||||
@@ -51,7 +51,7 @@ export async function uploadFiles(
|
||||
formData.append("files", file);
|
||||
});
|
||||
|
||||
const response = await fetch(
|
||||
const response = await fetchWithAuth(
|
||||
`${getBackendBaseURL()}/api/threads/${threadId}/uploads`,
|
||||
{
|
||||
method: "POST",
|
||||
@@ -92,7 +92,7 @@ export async function deleteUploadedFile(
|
||||
threadId: string,
|
||||
filename: string,
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
const response = await fetch(
|
||||
const response = await fetchWithAuth(
|
||||
`${getBackendBaseURL()}/api/threads/${threadId}/uploads/${filename}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
|
||||
Reference in New Issue
Block a user