mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-07-25 15:37:53 +00:00
Sync the harness docs with the #3875 subagent middleware changes: - subagents.mdx (en/zh): fix built-in max_turns defaults (general-purpose 160->150, bash 80->60) to match code and config.example.yaml; document the new config (global + per-agent override) and add a 'Runaway guards' section covering the LoopDetection / TokenBudget / Summarization middlewares now mirrored on the subagent chain. - middlewares.mdx (en/zh): note that loop-detection, token-budget, and summarization guards are shared with the subagent chain (#3875), instead of implying they are Lead-Agent-only. Code, contracts/subagent_status_contract.json (v2), and config.example.yaml already reflect #3875; only the docs were lagging.
This commit is contained in:
parent
25d9ac0a43
commit
964162747f
@ -17,6 +17,14 @@ Every time the Lead Agent calls the LLM, it runs through a **middleware chain**
|
||||
|
||||
This design keeps the agent core simple and stable while allowing rich, composable behaviors to be layered in.
|
||||
|
||||
<Callout type="info">
|
||||
Each subagent runs its own agent loop and gets its own middleware chain. The
|
||||
loop-detection, token-budget, and summarization guards below are mirrored on
|
||||
the subagent chain (#3875); the other middlewares are Lead-Agent-specific
|
||||
(e.g. memory, title generation, clarification). See
|
||||
[Subagents → Runaway guards](/docs/harness/subagents#runaway-guards).
|
||||
</Callout>
|
||||
|
||||
## How the chain works
|
||||
|
||||
The middleware chain is built once per agent invocation, based on the current configuration and request parameters. The middlewares run in a defined order:
|
||||
@ -58,6 +66,8 @@ Detects when the agent is making the same tool call repeatedly without making pr
|
||||
|
||||
Warning interventions are queued per thread and run, then drained on the next model call as a single hidden `HumanMessage(name="loop_warning")` appended after existing tool results. This keeps provider tool-call pairing valid. Run start/end hooks clear stale or undelivered warnings, and hard stops still strip tool calls before forcing a final text response.
|
||||
|
||||
This middleware is also attached to the subagent chain, where only the tool-loop heuristic can fire (subagents disallow `task`), so a degenerate subagent loop is broken the same way.
|
||||
|
||||
**Configuration**: built-in, no user configuration.
|
||||
|
||||
---
|
||||
@ -145,6 +155,8 @@ The backend stamps bounded structured task-result and skill-read metadata before
|
||||
|
||||
When the conversation grows long, summarizes older messages to reduce context size. The generated summary is stored in thread state and projected into later model calls as hidden durable context data, preserving meaning without keeping the original messages in the active transcript.
|
||||
|
||||
The same middleware and the same `summarization.enabled` switch also compact subagent transcripts, so a single config covers both the Lead Agent and subagent chains (#3875).
|
||||
|
||||
**Configuration**: `summarization:` section in `config.yaml`. See detailed configuration below.
|
||||
|
||||
---
|
||||
|
||||
@ -30,15 +30,15 @@ DeerFlow ships with two built-in subagents:
|
||||
|
||||
A general-purpose reasoning and execution agent. Suitable for delegating complex subtasks that require multi-step reasoning, web search, file operations, and artifact production.
|
||||
|
||||
- **Default timeout**: 900 seconds (15 minutes)
|
||||
- **Default max turns**: 160
|
||||
- **Default timeout**: 1800 seconds (30 minutes)
|
||||
- **Default max turns**: 150
|
||||
|
||||
### bash
|
||||
|
||||
A subagent specialized for command-line task execution inside the sandbox. Suitable for scripting, data processing, file transformation, and environment setup tasks.
|
||||
|
||||
- **Default timeout**: 900 seconds (15 minutes)
|
||||
- **Default max turns**: 80
|
||||
- **Default timeout**: 1800 seconds (30 minutes)
|
||||
- **Default max turns**: 60
|
||||
- **Availability**: only exposed when the sandbox's `bash` tool is available (either `allow_host_bash: true` or a container sandbox is configured)
|
||||
|
||||
## Delegation flow
|
||||
@ -57,32 +57,45 @@ The runtime then:
|
||||
|
||||
1. Looks up the subagent configuration from the registry, applying any `config.yaml` overrides.
|
||||
2. Creates a new agent invocation with the subagent's own prompt and tools.
|
||||
3. Runs the subagent to completion (or until timeout / max turns).
|
||||
3. Runs the subagent to completion — bounded by `max_turns`, the `timeout`, and a **middleware guard chain** that mirrors the Lead Agent's (loop detection, token budget, summarization). See [Runaway guards](#runaway-guards) below.
|
||||
4. Returns the subagent's final output to the Lead Agent as the tool result.
|
||||
|
||||
## Configuration
|
||||
|
||||
Subagent timeouts and max turns are controlled through the `subagents:` section in `config.yaml`:
|
||||
Subagent timeouts, max turns, and the per-run token budget are controlled through the `subagents:` section in `config.yaml`:
|
||||
|
||||
```yaml
|
||||
subagents:
|
||||
# Default timeout in seconds for all subagents (default: 900 = 15 minutes)
|
||||
timeout_seconds: 900
|
||||
# Default timeout in seconds for all subagents (default: 1800 = 30 minutes)
|
||||
timeout_seconds: 1800
|
||||
|
||||
# Optional: override max turns for all subagents
|
||||
# Optional: override max turns for all subagents.
|
||||
# Built-in defaults: general-purpose=150, bash=60. Leave unset to keep them.
|
||||
# max_turns: 120
|
||||
|
||||
# Per-run token ceiling — a backstop against a subagent burning tokens on
|
||||
# trivial work. At the hard-stop the in-flight turn is capped (tool calls
|
||||
# stripped, finish_reason forced to "stop") so the run completes naturally;
|
||||
# the result is stamped completed + subagent_stop_reason=token_capped so the
|
||||
# lead/UI can tell a budget-capped run from a clean one (#3875).
|
||||
token_budget:
|
||||
enabled: true
|
||||
max_tokens: 2000000 # generous default — lower it to tighten cost controls
|
||||
warn_threshold: 0.7 # log a warning once this fraction of the budget is spent
|
||||
|
||||
# Optional: per-agent overrides
|
||||
agents:
|
||||
general-purpose:
|
||||
timeout_seconds: 1800 # 30 minutes for complex tasks
|
||||
max_turns: 160
|
||||
# token_budget: # per-agent override of the global token_budget above
|
||||
# max_tokens: 3000000
|
||||
bash:
|
||||
timeout_seconds: 300 # 5 minutes for quick commands
|
||||
max_turns: 80
|
||||
```
|
||||
|
||||
Per-agent overrides take priority over the global `timeout_seconds` and `max_turns` settings.
|
||||
Per-agent overrides take priority over the global `timeout_seconds`, `max_turns`, and `token_budget` settings.
|
||||
|
||||
## Delegation limits
|
||||
|
||||
@ -94,6 +107,16 @@ The `SubagentLimitMiddleware` controls how many subagents the Lead Agent can inv
|
||||
|
||||
If the agent tries to call more subagents than the limits allow, the middleware trims the excess calls. When the total cap is exhausted, it stops new `task` calls for that run and lets the agent synthesize from already collected results.
|
||||
|
||||
## Runaway guards
|
||||
|
||||
A subagent runs its own agent loop, so it needs the same runaway backstops the Lead Agent has. The subagent middleware chain mirrors three Lead Agent guards (#3875):
|
||||
|
||||
- **`LoopDetectionMiddleware`** — breaks a subagent that repeats the same tool call without making progress. Subagents disallow `task`, so only the tool-loop heuristic can fire here. A hard-stop stamps the result `completed` + `subagent_stop_reason=loop_capped`, symmetric to the token budget below. Controlled by the existing `loop_detection` config.
|
||||
- **`TokenBudgetMiddleware`** — enforces the per-run `subagents.token_budget` ceiling. When the budget is hit the in-flight turn is capped (a final answer is forced) and the result is stamped `completed` + `subagent_stop_reason=token_capped` so the Lead Agent can tell a capped completion from a clean one. Reaching `max_turns` is likewise surfaced as `turn_capped`.
|
||||
- **`SummarizationMiddleware`** — compacts a long subagent transcript the same way it compacts the Lead Agent's, gated on the same `summarization.enabled` switch so a single config covers both chains.
|
||||
|
||||
These guards engage in addition to the `max_turns` and `timeout` limits. The default `max_tokens` for the token budget is coupled to `summarization.enabled` — 1M when compaction is on, 2M when off — but an explicit `subagents.token_budget.max_tokens` (global or per-agent) always wins, so flipping the summarization switch never silently changes a value you pinned.
|
||||
|
||||
## ACP agents (external agents)
|
||||
|
||||
In addition to the built-in subagents, DeerFlow supports delegating to external agents through the **Agent Client Protocol (ACP)**. ACP allows DeerFlow to invoke agents running as separate processes (including third-party CLI tools wrapped with an ACP adapter).
|
||||
|
||||
@ -17,6 +17,10 @@ import { Callout } from "nextra/components";
|
||||
|
||||
这种设计使 Agent 核心保持简单稳定,同时允许丰富的可组合行为分层叠加。
|
||||
|
||||
<Callout type="info">
|
||||
每个子 Agent 运行各自的 Agent 循环,并拥有自己的中间件链。下方的循环检测、token 预算和摘要压缩防护已镜像到子 Agent 链(#3875);其余中间件为 Lead Agent 专属(如记忆、标题生成、澄清)。参见[子 Agent → 失控行为防护](/docs/harness/subagents#失控行为防护)。
|
||||
</Callout>
|
||||
|
||||
## 链的工作方式
|
||||
|
||||
中间件链在每次 Agent 调用时根据当前配置和请求参数构建一次。中间件按定义的顺序运行:
|
||||
@ -58,6 +62,8 @@ import { Callout } from "nextra/components";
|
||||
|
||||
Warning 介入会按 thread 和 run 排队,并在下一次模型调用时合并为一条隐藏的 `HumanMessage(name="loop_warning")`,追加到已有工具结果之后。这样不会破坏 provider 对 tool-call/tool-message 配对的校验。Run 开始和结束时会清理过期或未送达的 warning;达到 hard stop 时仍会清空 tool calls 并强制生成最终文本回复。
|
||||
|
||||
此中间件同样挂载到子 Agent 链——子 Agent 不允许调用 `task`,因此这里只会触发工具循环启发式判定,退化的子 Agent 循环会被同样地打破。
|
||||
|
||||
**配置**:内置,无需用户配置。
|
||||
|
||||
---
|
||||
@ -137,6 +143,8 @@ token_usage:
|
||||
|
||||
当对话变长时,对旧消息进行摘要以减少上下文大小。生成的摘要存入线程状态,并在后续模型调用中作为隐藏的 durable context 数据投影进去,在不把旧消息继续留在活跃 transcript 中的情况下保留含义。
|
||||
|
||||
同一个中间件与同一个 `summarization.enabled` 开关也会压缩子 Agent transcript,使一份配置同时覆盖 Lead Agent 与子 Agent 两条链(#3875)。
|
||||
|
||||
**配置**:`config.yaml` 中的 `summarization:` 部分。详见下方详细配置。
|
||||
|
||||
---
|
||||
|
||||
@ -29,15 +29,15 @@ DeerFlow 内置两个子 Agent:
|
||||
|
||||
通用推理和执行 Agent,适合委派需要多步骤推理、网络搜索、文件操作和产出物生成的复杂子任务。
|
||||
|
||||
- **默认超时**:900 秒(15 分钟)
|
||||
- **默认最大轮次**:160
|
||||
- **默认超时**:1800 秒(30 分钟)
|
||||
- **默认最大轮次**:150
|
||||
|
||||
### bash
|
||||
|
||||
专门用于在沙箱内执行命令行任务的子 Agent,适合脚本编写、数据处理、文件转换和环境设置任务。
|
||||
|
||||
- **默认超时**:900 秒(15 分钟)
|
||||
- **默认最大轮次**:80
|
||||
- **默认超时**:1800 秒(30 分钟)
|
||||
- **默认最大轮次**:60
|
||||
- **可用性**:仅当沙箱的 `bash` 工具可用时才暴露(`allow_host_bash: true` 或配置了容器沙箱)
|
||||
|
||||
## 委派流程
|
||||
@ -56,32 +56,44 @@ task(
|
||||
|
||||
1. 从注册表查找子 Agent 配置,应用任何 `config.yaml` 覆盖。
|
||||
2. 用子 Agent 自己的提示词和工具创建新的 Agent 调用。
|
||||
3. 将子 Agent 运行到完成(或直到超时/最大轮次)。
|
||||
3. 将子 Agent 运行到完成——受 `max_turns`、`timeout` 以及镜像 Lead Agent 的**中间件防护链**(循环检测、token 预算、摘要压缩)共同约束。详见下方[失控行为防护](#失控行为防护)。
|
||||
4. 将子 Agent 的最终输出作为工具结果返回给 Lead Agent。
|
||||
|
||||
## 配置
|
||||
|
||||
子 Agent 超时和最大轮次通过 `config.yaml` 中的 `subagents:` 部分控制:
|
||||
子 Agent 的超时、最大轮次以及单次运行的 token 预算通过 `config.yaml` 中的 `subagents:` 部分控制:
|
||||
|
||||
```yaml
|
||||
subagents:
|
||||
# 所有子 Agent 的默认超时(秒,默认:900 = 15 分钟)
|
||||
timeout_seconds: 900
|
||||
# 所有子 Agent 的默认超时(秒,默认:1800 = 30 分钟)
|
||||
timeout_seconds: 1800
|
||||
|
||||
# 可选:覆盖所有子 Agent 的最大轮次
|
||||
# 可选:覆盖所有子 Agent 的最大轮次。
|
||||
# 内置默认值:general-purpose=150、bash=60。留空则保持默认。
|
||||
# max_turns: 120
|
||||
|
||||
# 单次运行的 token 上限——防止子 Agent 在琐碎任务上烧 token 的兜底。
|
||||
# 触达硬停止时,进行中的那轮会被截断(清空 tool calls,强制 finish_reason
|
||||
# 为 "stop"),使运行自然完成;结果会被标记为 completed +
|
||||
# subagent_stop_reason=token_capped,以便 Lead/UI 区分预算截断与正常完成(#3875)。
|
||||
token_budget:
|
||||
enabled: true
|
||||
max_tokens: 2000000 # 宽松的默认值——调低以收紧成本控制
|
||||
warn_threshold: 0.7 # 预算消耗达到此比例时记录一条 warning
|
||||
|
||||
# 可选:按 Agent 覆盖
|
||||
agents:
|
||||
general-purpose:
|
||||
timeout_seconds: 1800 # 复杂任务 30 分钟
|
||||
max_turns: 160
|
||||
# token_budget: # 对上面全局 token_budget 的按 Agent 覆盖
|
||||
# max_tokens: 3000000
|
||||
bash:
|
||||
timeout_seconds: 300 # 快速命令 5 分钟
|
||||
max_turns: 80
|
||||
```
|
||||
|
||||
按 Agent 覆盖优先于全局 `timeout_seconds` 和 `max_turns` 设置。
|
||||
按 Agent 覆盖优先于全局 `timeout_seconds`、`max_turns` 和 `token_budget` 设置。
|
||||
|
||||
## 委派限制
|
||||
|
||||
@ -93,6 +105,16 @@ subagents:
|
||||
|
||||
如果 Agent 尝试调用超过限制的子 Agent,中间件会裁剪多余的调用。当总量上限耗尽时,它会停止本次 run 的新 `task` 调用,让 Agent 基于已收集结果进行综合。
|
||||
|
||||
## 失控行为防护
|
||||
|
||||
子 Agent 运行各自的 Agent 循环,因此需要和 Lead Agent 一样的失控兜底。子 Agent 中间件链镜像了 Lead Agent 的三个防护(#3875):
|
||||
|
||||
- **`LoopDetectionMiddleware`** —— 打破子 Agent 不取得进展却反复调用同一工具的循环。子 Agent 不允许调用 `task`,因此这里只会触发工具循环启发式判定。硬停止会将结果标记为 `completed` + `subagent_stop_reason=loop_capped`,与下方 token 预算对称。由现有的 `loop_detection` 配置控制。
|
||||
- **`TokenBudgetMiddleware`** —— 强制执行 `subagents.token_budget` 单次运行上限。预算触达时,进行中的那轮会被截断(强制产出最终答案),结果标记为 `completed` + `subagent_stop_reason=token_capped`,使 Lead Agent 能区分预算截断与正常完成。触达 `max_turns` 同样以 `turn_capped` 上报。
|
||||
- **`SummarizationMiddleware`** —— 以和 Lead Agent 相同的方式压缩较长的子 Agent transcript,由同一个 `summarization.enabled` 开关控制,使一份配置同时覆盖两条链。
|
||||
|
||||
这些防护与 `max_turns`、`timeout` 限制叠加生效。token 预算的默认 `max_tokens` 与 `summarization.enabled` 联动——压缩开启时为 1M,关闭时为 2M;但显式设置的 `subagents.token_budget.max_tokens`(全局或按 Agent)始终优先,因此切换摘要开关绝不会悄悄改动你已固定的值。
|
||||
|
||||
## ACP Agent(外部 Agent)
|
||||
|
||||
除内置子 Agent 外,DeerFlow 还通过 **Agent Client Protocol (ACP)** 支持委派给外部 Agent。ACP 允许 DeerFlow 调用作为独立进程运行的 Agent(包括用 ACP 适配器包装的第三方 CLI 工具)。
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user