18 KiB
多智能体圆桌研讨 · 后端开发文档
本文档面向需要维护或扩展「多智能体圆桌研讨」功能后端的开发者。前端 API 速查见 multi-agent-api.md、multi-agent-step1-api.md、multi-agent-step3-api.md;推荐弹窗见 recommend-agents-progress.md;前端实现见 multi-agent-frontend-dev.md。本文关注实现细节、协议帧、设计取舍。
源码内另有中文模块级注释(app/gateway/routers/intent.py、recommend.py、multi_agent.py 及 clarification_utils.py),与本文互为补充。
1. 功能定位与三步数据流
对应前端页面:/page/roundtable/planning(RoundtablePlanningPage.tsx)的三个步骤。
Step 1 任务意图理解
POST /api/intent/init → 建 thread,绑定内置 roundtable-intent
POST /api/intent/stream (多轮) → 澄清对话,终态 [INTENT_READY] + summary
↓ intentReady { objective, constraints, assumptions }
推荐弹窗(Step 1 → Step 2 之间,可选)
POST /api/recommend/stream → 从候选池选 2–6 个席位,[RECOMMEND_READY] + picks
↓ selectedAgents[]
Step 2 多智能体圆桌研讨
POST /api/multi-agent/init (SSE) → 协调智能体 + 各席位 thread(席位列表由前端传入)
POST /api/multi-agent/run/stream → leader 派活 / special 执行 / 澄清
↓ lastLeaderContent、各席位交付、hasConsensus
Step 3 最终方案展示
GET /api/threads/{tid}/artifacts → 列出 outputs/ 产出文件
GET /api/threads/{tid}/artifacts/{path} → 预览或下载
(总控结论、席位文本主要来自 Step 2 前端内存,一般不再调 run/stream)
| 步骤 | 前端 currentStep |
专属 Router | 内置 Agent |
|---|---|---|---|
| 1 | 1 | intent.py |
roundtable-intent(固定 id,复用) |
| 1→2 | 推荐弹窗 | recommend.py |
roundtable-recommender(每请求新建 thread) |
| 2 | 2 | multi_agent.py |
协调智能体(每次新建)+ 用户选定的 roundtable-* 席位 |
| 3 | 3 | artifacts.py(通用) |
无新 agent |
所有接口需 Authorization: Bearer <JWT>。Loopback 调用统一 trust_env=False,避免本机 HTTP_PROXY 把 127.0.0.1 指到不可达代理(症状:单次请求卡住 40s+)。
2. 涉及的后端文件
offline-backend-20260512/backend/
├── app/gateway/
│ ├── app.py # include_router: multi_agent, intent, recommend
│ └── routers/
│ ├── multi_agent.py # Step 2:init(SSE) + run/stream
│ ├── intent.py # Step 1:init + stream
│ ├── recommend.py # Step 1→2:推荐 stream
│ └── artifacts.py # Step 3:列出/下载 thread 产出
└── .deer-flow/agents/
├── roundtable-intent/ # Step 1 任务理解(SOUL 含完整工作流,可不挂 Skill)
├── roundtable-recommender/ # 推荐智能体
├── roundtable-intelligence/ # 默认可选席位(6 个,前端可替换)
├── roundtable-environment/
├── roundtable-solution-design/
├── roundtable-risk-review/
├── roundtable-execution-plan/
└── roundtable-summary/
内置 agent 注册:各 roundtable-* 目录通过 _sync_legacy_agents()(app/gateway/routers/agents.py)在下次 /api/agents 请求时 upsert 进 agents 表,user_id IS NULL 表示内置。
共享工具逻辑:packages/harness/deerflow/tools/builtins/clarification_utils.py 中的 resolve_allow_multiple() 被 intent.py、multi_agent.py 与 ClarificationMiddleware 共用,决定选项卡片默认单选还是多选。
3. 接口总览
| 方法 | 路径 | 步骤 | 用途 |
|---|---|---|---|
| POST | /api/intent/init |
1 | 创建意图澄清 thread |
| POST | /api/intent/stream |
1 | SSE,一轮用户对话 |
| POST | /api/recommend/stream |
1→2 | SSE,一次性推荐席位 |
| POST | /api/multi-agent/init |
2 | SSE,并行拉起席位 + 协调智能体 |
| POST | /api/multi-agent/run/stream |
2 | SSE,leader 或 special 一轮 |
| GET | /api/threads/{thread_id}/artifacts |
3 | 列出该 thread 的 outputs 文件 |
| GET | /api/threads/{thread_id}/artifacts/{path} |
3 | 预览/下载单个产出 |
字段级请求/响应见各 step 的 API 文档;下文只写后端实现语义。
4. Step 1:任务理解(intent.py)
4.1 定位
轻量包装 Lead Agent 运行时,固定跑内置 roundtable-intent。不做 broadcast、不派活;每轮用户消息对应一次 /stream。
4.2 POST /api/intent/init
_create_thread()→ LangGraph thread- 返回固定
agent_id: "roundtable-intent"(不每次新建 agent)
与 Step 2 /init 的区别:无协调智能体、无 agent_names 列表。
4.3 POST /api/intent/stream
上游:_run_payload(agent_name=roundtable-intent, excluded_tools=[...]),硬排除 agent_orchestration、write_file、web_search 等,仅保留 ask_clarification 等澄清能力,防止模型写方案文档。
流式:透传 LangGraph SSE → 解析 _parse_last_messages(captured)。
终态帧判定顺序(重要):
-
先扫描全部 AI 消息中的
[INTENT_READY]\n\``json {...}```(正则_INTENT_READY_PATTERN`)- 命中 →
status: "done",summary为解析后的 JSON,content为含标记的整段文本 - 优先于
ask_clarification,避免「已汇总仍被当成待澄清」
- 命中 →
-
否则查最后一则带
tool_calls的 AIMessage 是否含ask_clarification- 命中 →
status: "clarification",附带结构化字段供前端选项卡片:
- 命中 →
{
"status": "clarification",
"content": "<引导语/前言>",
"question": "...",
"clarification_type": "missing_info",
"clarification_context": "...",
"options": ["..."],
"allow_custom": true,
"allow_multiple": false,
"summary": null
}
allow_multiple 由 resolve_allow_multiple(cargs) 决定:默认单选(点击即发送);仅模型显式传 allow_multiple: true 时为多选。
- 否则 →
status: "asking"(继续对话,无结构化澄清)
summary 形状(done 时):
{
"objective": "一句话目标",
"constraints": ["硬约束 1", "..."],
"assumptions": ["可选假设"]
}
4.4 内置 Agent:roundtable-intent
- 配置:
.deer-flow/agents/roundtable-intent/config.yaml+SOUL.md - SOUL 内写清:工作流、
ask_clarification用法、[INTENT_READY]格式、禁止派活/写文件 - 可不配置
skills;若配置 Skill,SOUL 要求不得偏离澄清与结束协议
5. 推荐智能体(recommend.py,Step 1 → 2)
5.1 定位
无状态单次调用:每次请求内部 _create_thread(),不暴露 /init。前端传入 Step 1 的 intent + 过滤后的 candidates(排除 roundtable-intent、roundtable-recommender、历史 roundtable-coordinator-*)。
5.2 POST /api/recommend/stream
入参校验:
intent.objective必填candidates非空,且至少一个有效agent_id
Prompt 拼装(_build_prompt):把 objective/constraints/assumptions 与候选池格式化为一条 user message。
上游:roundtable-recommender,excluded_tools 含 ask_clarification、agent_orchestration 等(纯判断,不调工具)。
终态:
- 解析
[RECOMMEND_READY]\n\``json {"picks":[{"agent_id","reason"},...]}```` _validate_picks():只保留候选池内的 id、去重、数量 clamp 到 2–6- 有效 picks ≥ 2 →
status: "done",picks有值 - 否则 →
status: "asking"(前端回退默认席位列表)
{
"status": "done",
"content": "<自然语言推荐理由>",
"picks": [{"agent_id": "roundtable-risk-review", "reason": "..."}]
}
前端流式展示:自然语言理由与 JSON 由 [RECOMMEND_READY] 分界;TitleMiddleware 产生的标题增量会在客户端过滤,避免污染推荐理由区。
6. Step 2:多智能体研讨(multi_agent.py)
6.1 /init 内部流程(SSE,非一次性 JSON)
当前实现为 流式 init,便于前端按席位点亮 UI:
asyncio.as_completed(各席位 _prepare_sub_agent)
每完成一个 → data: {"event":"seat_ready","agent_name","thread_id","display_name","description"}
协调智能体:
POST /api/threads → coord_thread
POST /api/agents(skills=[agent_orchestration],SOUL 动态拼入可调度列表)
→ data: {"event":"coordinator_ready","main_agent_name","thread_id"}
→ data: {"event":"init_done","main_agent_name","thread_ids":{...}}
失败 → data: {"event":"error","detail":"..."}
席位列表:由前端 agent_names 传入(推荐弹窗确认后的 2–6 个 id),不再写死 6 个。
协调智能体 SOUL(动态拼接)包含:只负责协调汇总、通过 agent_orchestration 派活、agent_name 必须用英文 id(不要用中文显示名)。
6.2 /run/stream — agent_type = "leader"
1. (可选)pre_broadcast:总控收到用户消息 → 广播到非自己的 thread
2. POST /api/threads/{coordinator}/runs/stream
- excluded_tools: ["web_search"]
3. 透传 LangGraph 事件
4. 解析 messages → tool_calls
分支 A — ask_clarification:
status: "clarification"
+ question / options / allow_multiple / clarification_type / clarification_context
(空 dispatched 不等于共识,前端展示澄清卡片并暂停自动派活循环)
分支 B — 有 agent_orchestration 派活:
并发 _broadcast 派活消息到相关 thread
status: [["sub_agent_id", "task_text"], ...]
分支 C — 无 tool_calls:
status: [] // 共识达成,content 为总控最终文本
6.3 /run/stream — agent_type = "special"
1. POST /api/threads/{sub}/runs/stream
- excluded_tools: web_search, ask_clarification, present_files, view_image, agent_orchestration
2. 透传 + 解析最后 AI 文本
3. post_broadcast:子智能体完成交付 → 广播到非自己的 thread
4. status: "<完成提示字符串>" 或业务自定义;content 为交付正文;agent_name 为子 agent id
6.4 _broadcast 机制
async def _broadcast(client, base, headers, message, agent_threads, skip):
targets = [tid for name, tid in agent_threads.items() if name not in skip]
# 对每个 target: GET state → append human message → POST state
目的:各 thread 对话历史里保留「谁派活、谁交付」的群聊上下文。skip 通常含发起方与接收方,避免回声。
6.5 终态帧协议(leader / special / clarification)
前端 streamMultiAgent 用 isFinalStatusFrame() 识别:同时存在 status 与 content。
status 类型 |
含义 |
|---|---|
[["agent","task"],...] |
派活列表;[] = 共识 |
"clarification" |
总控追问,需用户回复后再 leader |
"子智能体 X 完成..." |
special 完成(字符串) |
"error" |
异常 |
与 Step 1 的 clarification 帧字段对齐(question、options、allow_multiple 等),便于复用选项卡片组件。
7. Step 3:产出文件(artifacts.py,通用 Thread API)
Step 3 没有 单独的 multi_agent 路由;使用既有 artifacts 能力。
7.1 GET /api/threads/{thread_id}/artifacts
- 鉴权:
threads:read,owner 校验 - 扫描:
users/{user_id}/threads/{thread_id}/user-data/outputs/(collect_outputs_listing) - 目录不存在 →
files: [](非 404)
{
"thread_id": "...",
"files": [
{
"path": "mnt/user-data/outputs/report.md",
"name": "report.md",
"size_bytes": 12480,
"mime_type": "text/markdown",
"modified_at": "2026-05-23T14:32:30.123Z"
}
]
}
前端 listSessionArtifacts(threadId) 封装此接口;Step 3 可对协调 thread 与各席位 thread 分别拉取,合并为下载清单。
7.2 GET /api/threads/{thread_id}/artifacts/{path}
- 虚拟路径前缀
mnt/user-data/outputs/... - 路径穿越防护;
download=true强制附件下载 - HTML/XHTML/SVG 等 active content 始终按下载处理
总控结论、各席位最终段落:主要来自 Step 2 已缓存的 lastLeaderContent、step2RoundtableDialogues;无需为 Step 3 新增聚合接口(可选优化见 multi-agent-step3-api.md 中的 sessions/summary 草案)。
8. 内置席位 SOUL 设计要点
每个 roundtable-*(除 intent / recommender)遵循统一结构:角色定位、核心职责、输出风格(emoji + 简短结论)、边界(只产出本席位、等总控派活)。
| 席位 | agent_id | emoji | 核心职责 |
|---|---|---|---|
| 情报收集 | roundtable-intelligence |
📢 | 同类案例 + 历史风险 |
| 环境评估 | roundtable-environment |
🌍 | 硬件容量 + 承载力 |
| 方案设计 | roundtable-solution-design |
📐 | 主备方案 + 关键路径 |
| 风险审查 | roundtable-risk-review |
⚠️ | 合规红线 + 收敛建议 |
| 执行规划 | roundtable-execution-plan |
📅 | 里程碑 + 资源 |
| 总结陈述 | roundtable-summary |
🎤 | 归纳 + 冲突标记 |
子智能体不安装 agent_orchestration;仅协调智能体安装,保证单调度源。
9. 关键设计决策
9.1 HTTP loopback
三个 router 均通过 httpx 调本机 Gateway(127.0.0.1),复用鉴权与 LangGraph 路由,代价是额外 HTTP 开销;init 已用 asyncio.as_completed 并行化席位准备。
9.2 协调智能体每次新建
main_agent_name 形如 roundtable-coordinator-{timestamp36},会话隔离;长期会累积 agent 目录,需后续 GC 策略。
9.3 澄清协议统一
Step 1 与 Step 2 leader 均通过 ask_clarification + 终态 clarification 帧驱动选项卡片;[INTENT_READY] / 派活列表 / [] 各自表示阶段结束语义,互不混用。
9.4 选项单选默认
resolve_allow_multiple():未传则 false(单选、点击发送);仅显式 allow_multiple: true 时多选。降低模型把 missing_info 全标成多选导致 UX 混乱的概率。
9.5 SSE 末尾状态帧
LangGraph 原生流不含「派活结果 / 意图 summary / 推荐 picks」;各 router 在流末追加 JSON data: 帧,前端与 LangGraph 帧区分解析。
10. 性能特征
| 阶段 | 已做优化 | 典型耗时 |
|---|---|---|
Step 2 /init |
席位 as_completed + 去掉 intro 广播 |
约 0.5–1.2s(视席位数) |
LLM /run/stream |
— | 数百 ms~数十秒(主因) |
| Step 1/推荐 | 单 thread,无 broadcast | 与模型相关 |
仍存在的开销:GET /api/agents/{id} 触发 legacy sync 扫目录、GET/POST /state 广播等。未来可考虑 loopback 改内部函数调用(见原 B/D 优化项)。
11. 依赖与扩展点
11.1 agent_orchestration 技能
协调智能体派活依赖此技能;正则 _TOOL_CALL_PATTERN 从 tool 输出提取 agent_name 与 task。技能缺失时 leader 易变成「无 tool_calls → 空 status → 前端误判共识」。
11.2 模型
model 写入 run 的 context.model_name;前端可按席位覆盖 special 调用的模型。
11.3 新增席位
.deer-flow/agents/roundtable-xxx/增加config.yaml+SOUL.md- 前端候选池 / 推荐逻辑纳入该 id
- Step 2
agent_names传入即可 — multi_agent 路由无需改
11.4 新增 Step 1 工具
若需新工具,须同步修改 intent.py 的 excluded_tools 白名单逻辑(当前为黑名单,仅 ask_clarification 实际可用)。
12. 故障排查
| 现象 | 可能原因 | 排查 |
|---|---|---|
| Step 1 一直有澄清、无法进入 Step 2 | 未产出 [INTENT_READY] 或终态被 clarification 覆盖 |
看 SOUL、最后一帧 status;确认 intent 先扫 READY |
| 选项全是多选 | 旧前端默认 allow_multiple !== false |
确认 allow_multiple 字段与 resolve_allow_multiple |
Step 2 init SSE error |
协调 agent 创建失败 / agent_orchestration 缺失 | Network + 后端 multi_agent 日志 |
leader 立即 status: [] |
未调用 orchestration 技能 | 换模型或加强 SOUL |
| 推荐 picks 为空 | 未输出 [RECOMMEND_READY] 或 picks 不在候选池 |
看 recommend 终态帧 |
| Step 3 无文件 | 子 agent 未写 outputs | GET .../artifacts 对子 thread_id 逐个查 |
日志关键字
grep "intent stream" .runtime/logs/*.log
grep "multi_agent\|multi-agent" .runtime/logs/*.log
grep "recommend stream" .runtime/logs/*.log
grep "dispatch failed" .runtime/logs/*.log
13. 测试建议
backend/tests/test_intent.py # init、INTENT_READY、clarification 优先级
backend/tests/test_recommend.py # picks 校验、候选池过滤
backend/tests/test_multi_agent.py # init SSE 事件、leader/special 终态、clarification
可用 httpx.MockTransport 模拟 loopback,不依赖真实 Gateway。
14. 路径速查
| 资源 | 路径 |
|---|---|
| Step 1 路由 | offline-backend-20260512/backend/app/gateway/routers/intent.py |
| 推荐路由 | offline-backend-20260512/backend/app/gateway/routers/recommend.py |
| Step 2 路由 | offline-backend-20260512/backend/app/gateway/routers/multi_agent.py |
| Step 3 列出/下载 | offline-backend-20260512/backend/app/gateway/routers/artifacts.py |
| 路由注册 | offline-backend-20260512/backend/app/gateway/app.py |
| 澄清单选/多选 | packages/harness/deerflow/tools/builtins/clarification_utils.py |
| 任务理解 Agent | .deer-flow/agents/roundtable-intent/ |
| 推荐 Agent | .deer-flow/agents/roundtable-recommender/ |
| 圆桌席位 | .deer-flow/agents/roundtable-*/ |
| 前端 Step 1 API | frontend-web/src/roundtable-planning/api/intent.ts |
| 前端推荐 API | frontend-web/src/roundtable-planning/api/recommend.ts |
| 前端 Step 2 API | frontend-web/src/roundtable-planning/api/multi-agent.ts |
| 前端页面 | frontend-web/src/roundtable-planning/pages/RoundtablePlanningPage.tsx |
| API 速查 | frontend-web/docs/multi-agent-api.md |
| Step 1 API 说明 | frontend-web/docs/multi-agent-step1-api.md |
| Step 3 API 说明 | frontend-web/docs/multi-agent-step3-api.md |