1. 为什么 SubAgent 并发执行总在“配置层”翻车
DeerFlow 2.0 的 SubAgent 并发执行引擎,本质是把 Lead Agent 拆出来的子任务丢进线程池,让多个独立 Agent 同时跑。听起来很美好,但真正落地时,绝大多数人卡住的地方不是算法,而是配置:config.toml里并发数写多少、settings.json里模型通道怎么指、CC Switch 或 Cline 这类客户端怎么把请求统一打到同一个 Key 上。我见过太多人把max_concurrent设成 8,结果 Lead Agent 一轮吐出 6 个task调用,线程池直接排队,超时全红。
这篇不重复讲状态机源码,而是把并发执行链路拆成一份可复制的配置骨架。你会拿到三样东西:一份能直接跑的config.toml、一份settings.json模型通道配置、以及用 CC Switch / Cline 接入统一 API 通道的写法。目标很明确——让 SubAgent 并发真正触发,而不是停在“配置看起来对但就是不并行”的状态。
适合谁:已经在跑 DeerFlow 2.0、想让多个 SubAgent 并行处理多文件/多命令/多检索任务的人;以及用 Cline、CC Switch 做日常编码、想把模型请求收敛到一个通道的人。核心检索词就三个:DeerFlow、SubAgent、并发执行引擎。
2. 前置:把模型通道收敛到 TaoToken
SubAgent 并发执行时,每个子 Agent 都会独立发起模型请求。如果 Lead Agent 和 SubAgent 走不同通道、不同 Key,排查问题时你根本分不清是并发逻辑错了还是鉴权错了。所以第一步是把所有请求收敛到一个统一通道。
TaoToken 在这里的角色是统一 Key / API 通道:官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口 https://taotoken.net/api 。你只需要在控制台生成一个 Key,后面config.toml和settings.json都指向它。
具体动作:打开控制台 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,创建一个 API Key;然后在 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 复制出来。这个 Key 会同时被 Lead Agent、general-purpose SubAgent、bash SubAgent 复用。
注意:并发场景下不要给每个 SubAgent 配不同 Key。统一 Key 的好处是 Token 用量、限流、报错都集中在一处,排查并发超时时能一眼看出是通道限流还是任务本身卡住。
如果你还没确认模型名,可以先去模型对话页 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 试一条请求,确认通道通、模型名对,再写进配置。这一步能省掉后面一半的“Unknown model”报错。
3. 可复制配置骨架:config.toml + settings.json
3.1 config.toml 并发与 SubAgent 段
下面这份骨架覆盖了并发数、超时、内置 SubAgent 开关和自定义 SubAgent 注册。直接改 Key 和模型名即可用。
# config.toml [llm] provider = "openai-compatible" base_url = "https://taotoken.net/api" api_key = "sk-你的TaoTokenKey" default_model = "deepseek-v3" [subagents] # 并发上限,引擎内部会 clamp 到 [2,4] max_concurrent = 3 # 单个 SubAgent 最长执行时间(秒) default_timeout_seconds = 900 # 是否启用内置 general-purpose enable_general_purpose = true # 是否启用内置 bash(Docker 沙箱下会自动隐藏) enable_bash = true [subagents.custom_agents.code-reviewer] description = "代码审查专家,专注 Bug 与安全漏洞" system_prompt = """ 你是一个代码审查专家。请仔细审查代码,关注: 1. 潜在 Bug 2. 安全漏洞 3. 性能问题 4. 代码风格 """ tools = ["read_file", "grep", "glob"] disallowed_tools = ["task", "ask_clarification", "present_files"] model = "deepseek-v3" max_turns = 30 timeout_seconds = 600 [subagents.custom_agents.doc-writer] description = "技术文档撰写专家" system_prompt = "你是一个技术文档撰写专家,输出结构化 Markdown。" tools = ["read_file", "write_file", "glob"] model = "inherit" max_turns = 50几个关键点。max_concurrent = 3是经过实践验证的甜点值:1 等于串行,2 适合简单任务,4 是上限,5 以上 LLM 调度容易乱。default_timeout_seconds = 900对应源码里的默认超时。model = "inherit"表示继承 Lead Agent 的模型,model = "deepseek-v3"则让 SubAgent 用更便宜的模型——Lead 用强模型、SubAgent 用快模型,成本能压下来一大截。
3.2 settings.json 客户端通道配置
如果你用 Cline 或 CC Switch 作为外层客户端,settings.json负责把请求指到同一个通道。
{ "llm": { "provider": "openai", "baseUrl": "https://taotoken.net/api", "apiKey": "sk-你的TaoTokenKey", "model": "deepseek-v3", "temperature": 0.2 }, "deerflow": { "configPath": "./config.toml", "subagentConcurrency": 3, "streamSubagentEvents": true } }streamSubagentEvents打开后,SubAgent 的subagent_started事件会流式推给客户端,你能实时看到哪个子任务先跑完。subagentConcurrency和config.toml里的max_concurrent保持一致,避免两处配置打架。
3.3 CC Switch / Cline 接入写法
CC Switch 里新增一个 provider,Base URL 填https://taotoken.net/api,Key 填同一个。Cline 的配置项在cline_settings里,把apiProvider设为openai,openAiBaseUrl指向同一地址。两边都指向同一个通道后,Lead Agent 和 SubAgent 的请求在服务端看来是同一来源,限流和用量统计才准确。
提示:接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有各客户端的字段对照表,字段名对不上时先查这里。
4. 验证并发是否真的触发
配置写完不代表并发生效。你需要一个能明确触发多个task调用的场景。最直接的办法是给 Lead Agent 一个“多文件同时处理”的指令,比如“分别审查 a.py、b.py、c.py 三个文件”。
4.1 触发并观察事件流
启动后观察日志里是否出现连续的subagent_started,且时间戳接近。如果三个事件几乎同时出现,说明线程池并行提交成功;如果第二个事件比第一个晚了几秒,说明被串行化了。
# 验证脚本:检查并发提交 import time from deerflow.subagents.executor import get_background_task_result start = time.time() # 触发 Lead Agent 后轮询任务状态 while True: result = get_background_task_result(task_id) if result and result.status in {"completed", "failed", "timed_out"}: print(f"task done in {time.time() - start:.2f}s, status={result.status}") break time.sleep(0.5)4.2 成功结果长什么样
并发成功时,你会看到多个SubagentResult的started_at几乎相同,completed_at因任务复杂度不同而错开。Token 用量统计里,多个子任务的token_usage会分别记录。如果所有子任务的started_at严格递增且间隔明显,那就是没并行。
4.3 用模型对话快速验证通道
在正式跑并发前,先用模型对话页 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 发一条请求,确认 Key 有效、模型名正确。通道不通的话,并发日志里全是鉴权错误,你会误以为是并发逻辑问题。
5. 本篇常见错排查
5.1 报错 Unknown subagent
task工具里传的subagent名字不在注册表里。检查config.toml的custom_agents段拼写,以及内置的general-purpose、bash是否被沙箱配置隐藏。Docker 模式下bash会被自动过滤,这是设计行为,不是 Bug。
5.2 并发数设了但不生效
最常见原因是max_concurrent被 clamp 到[2,4]。你写 8,实际生效 4;写 1,实际生效 2。另一个原因是 Lead Agent 一轮只生成了 1 个task调用——并发的前提是模型一次吐出多个task,如果它一次只给一个,线程池再大也没用。可以在 Prompt 里明确要求“对独立任务并行调用 task”。
5.3 子任务全部 timed_out
先看default_timeout_seconds是不是太小。900 秒是默认值,复杂任务可以调到 1200。其次看通道是否限流——统一 Key 下并发请求过多可能触发限流,表现为请求挂起直到超时。这时把max_concurrent降到 2 试试。
5.4 循环检测误触发
LoopDetectionMiddleware的 Hash 层在 3 次相同调用时警告、5 次强制停止。如果你让 SubAgent 反复读同一文件的不同行,read_file的 bucket 策略(200 行一桶)可能把不同行归为同一 hash。排查时看日志里的warn_threshold触发记录,必要时调整任务描述让参数有区分度。
5.5 CC Switch / Cline 里模型名对不上
客户端里的模型名必须和config.toml的default_model一致,否则 SubAgent 继承时会拿到空模型名。统一用同一个字符串,别一处写deepseek-v3另一处写deepseek-v3.1。
6. 长期编码与 Agent 场景的通道选择
如果你只是偶尔验证并发,按上面的配置走就够了。但如果你要把 DeerFlow 的 SubAgent 并发用在长期编码、批量重构、Agent 流水线里,请求量和并发压力会持续存在,这时候建议把通道能力单独规划。
Coding Plan 页面 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 里有针对长期编码场景的通道说明。核心思路是:Lead Agent 用强模型保证调度质量,SubAgent 用快模型保证吞吐,两者共用同一个 Key 但可以在config.toml里用model字段区分。这样并发跑起来后,成本可控、排查路径清晰。
回到并发本身,记住三个数字:并发上限 3、单任务超时 900 秒、循环硬停 5 次。这三个值覆盖了绝大多数翻车场景。配置骨架复制过去,改 Key 和模型名,先跑一个三文件审查任务验证事件流,再逐步加复杂度。并发执行引擎的价值不在于“能并行”,而在于“并行得可控”——配置层把边界划清楚,剩下的交给线程池。