Files
deerflow-code/frontend-web/docs/multi-agent-step3-api.md
T
2026-05-23 23:05:01 +08:00

12 KiB
Raw Blame History

多智能体圆桌 · Step 3 · 接口与对接说明

Step 3(/page/roundtable/planning 的"最终方案展示"页)对应 frontend-web/src/roundtable-planning/components/HighFidelityReport.tsx。当前页面是产品早期的"展厅效果",绝大多数内容是硬编码 mock。本文档梳理真正需要的数据、用哪些接口、前后端怎么对接。


1. 改造方向(在做什么,不做什么)

✅ 保留并对接真实数据

区块 现状 改造后
标题栏 + 生成时间 硬编码"2026-05-21 11:01:12" 用本地 Date.now() 或 init/最后一次 leader 完成时刻
"已达成共识"徽章 静态显示 step2 自然收敛(leaderRes.dispatched.length === 0)后才显示
总控最终结论 完全是 mock 文本 取最后一次 leaderRes.content(已经在 step2 拿到了)
各席位最终交付(折叠卡片) 当前 step3 没渲染 取每个 sub 线程的最后一条 AIMessage 文本
生成的报告文件清单 5 张假"档案" 真实列出 outputs/ 目录里的所有产出(markdown / pdf / csv 等)
文件下载 假 toast 用现有 GET /api/threads/{tid}/artifacts/{path}
"重新起草新规划" 调用 setCurrentStep(1) 重置 保留,含义就是回到 step1 重新开始

❌ 不做(直接删 / 不接口)

区块 原因
4 张 metric 卡片(设计全要素通过率 99.4% / Tier III+ / 终审共识度 42 轮研商 / 90.3分) 全是按 budgetLimit 编出来的"展厅指标",没有真实数据源
三阶段推进路线图(0-24h / 24-60h / 60-72h 三张卡) 假设业务必然是 72 小时交付,写死场景,不通用
"采纳理由与最终裁定判词"段 整段都是模板话术,没接口能算出"90.3 分总评"
"12 轮极限对抗研商" 数据 当前没记录"轮次"语义,临时就用 leader 实际轮数代替即可,不值得为它出接口
"签发起草成果并投产运行" 按钮 无对应业务动作(不是要部署、不是要审批),删掉
"执行摘要" 段写死的 3 个 bullet 模板话术,跟实际任务无关

最终 step3 页面只保留四块:头部状态 + 总控结论 + 席位交付列表 + 文件下载清单,再加底部"重新起草"。


2. 数据来源全景

┌───────────────────────────────────────────────────────────┐
│ step2 全程结束时,前端手里已经有的本地状态                  │
├───────────────────────────────────────────────────────────┤
│ coordinatorName: "roundtable-coordinator-xxx"             │
│ threadIds:       { [agent_name]: thread_id, ... }         │  ← /init 返回
│ step2RoundtableDialogues: [...所有气泡, 含每个 sub 最终  │
│                           交付的 content...]              │
│ leaderRes.content (最后一次 leader 的 final text)         │
└───────────────────────────────────────────────────────────┘

后端权威数据(需要时从后端拉):
- 每个 sub 线程的完整 state    → GET /api/threads/{tid}/state
- 单个 outputs 文件内容/下载    → GET /api/threads/{tid}/artifacts/{path}
- outputs 目录文件清单 ❗暂无    → 新增接口(见 §3)

关键判断:席位最终交付文本 + 总控结论文本 已经在 step2 内存里,无需重新调接口。文件清单是目前唯一缺失的数据源。


3. 接口需求

3.1 新增(必需):列出某 thread 的产出文件

GET /api/multi-agent/sessions/artifacts?thread_id=<sub_thread_id>

或者更通用、放进 artifacts 路由下:

GET /api/threads/{thread_id}/artifacts

为什么需要:现有 artifacts endpoint 只能"按已知路径下载文件",没有"列出目录"功能。Step 3 需要知道 sub-agent 写到 /mnt/user-data/outputs/ 下哪些文件,才能在前端列出可下载清单。

响应:

{
  "thread_id": "<sub_thread_id>",
  "files": [
    {
      "path": "mnt/user-data/outputs/方案设计报告_生产系统安全改造技术方案.md",
      "name": "方案设计报告_生产系统安全改造技术方案.md",
      "size_bytes": 12480,
      "mime_type": "text/markdown",
      "modified_at": "2026-05-23T14:32:30.123Z"
    },
    ...
  ]
}

实现建议:

  • 列 backend/.deer-flow/users/{user_id}/threads/{thread_id}/user-data/outputs/ 下的文件
  • 鉴权复用 @require_permission("threads", "read", owner_check=True)
  • 路径要做 traversal 保护(虚拟路径仅限 mnt/user-data/outputs/)

3.2 新增(可选,体感优化):一次性聚合 step3 数据

POST /api/multi-agent/sessions/summary
Body: {
  "main_agent_name": "roundtable-coordinator-xxx",
  "thread_ids": { "<agent_name>": "<thread_id>", ... }
}

为什么可选:不加这个接口的话,前端需要为每个席位发一次 GET /state + 一次 GET /artifacts(共 7-13 次 round-trip)。加了这个接口,一次返回所有数据。

响应:

{
  "main_agent_name": "roundtable-coordinator-xxx",
  "coordinator": {
    "thread_id": "...",
    "final_text": "...",          // 最后一条 AIMessage content
    "dispatch_rounds": 3           // leader 派活的轮数(可选展示)
  },
  "seats": [
    {
      "agent_name": "roundtable-intelligence",
      "thread_id": "...",
      "display_name": "情报收集",
      "final_text": "...",         // 最后一条 AIMessage content
      "files": [                   // 该席位写到 outputs/ 的文件
        { "path": "...", "name": "...", "size_bytes": ..., "mime_type": "..." }
      ]
    },
    ...
  ]
}

取舍:

  • 优点:1 次请求 vs 13 次;体验更顺
  • 缺点:等于把 3.1 + N 次 state 读放到一个 endpoint,实现稍复杂
  • 推荐:先做 3.1,3.2 暂缓;前端用现有 endpoint 串起来够用

3.3 复用(无需改动)

Endpoint 用途
GET /api/threads/{tid}/state 取席位 / 总控线程的完整状态,提取最后一条 AIMessage content
GET /api/threads/{tid}/artifacts/{path} 点击文件名预览或下载(已有 ?download=true 支持)

Note: 这两个 endpoint 已经在 app.gateway.routers.threads / app.gateway.routers.artifacts 里实现,前端 fetch-client 也有现成调用方式。


4. 前后端对接流程

4.1 进入 step3 时

// 触发条件:runOrchestration 自然结束(leaderRes.dispatched.length === 0
// 且无 clarification),用户点"达成共识生成全案成果"按钮 / 自动跳转。

// 前端手里已有:threadIds, coordinatorName, 最后的 leaderRes
// 1. 总控结论文本 — 已在内存,直接用
const consensusText = lastLeaderRes.content;

// 2. 各席位交付 — 已在 step2RoundtableDialogues 里,过滤"子智能体交付"的卡片
const seatDeliveries = step2RoundtableDialogues
  .filter(d => d.confidence === "子智能体交付")
  .map(d => ({ sender: d.sender, content: d.content }));

// 3. 文件清单 — 调新接口
const filesPerSeat = await Promise.all(
  Object.entries(threadIds)
    .filter(([name]) => name !== coordinatorName)
    .map(async ([name, tid]) => ({
      agentName: name,
      files: (await fetch(`/api/threads/${tid}/artifacts`).then(r => r.json())).files,
    })),
);

4.2 用户点击文件时

// 在线预览(mime 是 markdown/text/pdf 自动 inline)
window.open(`/api/threads/${threadId}/artifacts/${encodeURIComponent(filePath)}`);

// 强制下载
window.open(`/api/threads/${threadId}/artifacts/${encodeURIComponent(filePath)}?download=true`);

4.3 "重新起草新规划" 按钮

// 跟现在一样,纯前端:清掉 dialogues + 跳 step1
setCurrentStep(1);
setInteractiveMessages(...);  // 已有逻辑
setStep2RoundtableDialogues([]);
// 不需要调任何接口

5. 改造前端的具体文件清单

文件 改动
frontend-web/src/roundtable-planning/components/HighFidelityReport.tsx 大改。删除四张 metric 卡 / 三阶段路线图 / 论证要件 / 裁定判词;新增结论卡 + 席位折叠列表 + 文件清单
frontend-web/src/roundtable-planning/api/multi-agent.ts 新增 listSessionArtifacts(threadId) helper(封装 §3.1 接口)
frontend-web/src/roundtable-planning/pages/RoundtablePlanningPage.tsx 把 threadIds / coordinatorName / lastLeaderContent 传进 HighFidelityReport 的 props(目前没传)

6. 改造后端的具体文件清单

文件 改动
offline-backend-20260512/backend/app/gateway/routers/artifacts.py 新增 GET /api/threads/{tid}/artifacts(不带 path,列目录)
offline-backend-20260512/backend/CLAUDE.md 在 artifacts 路由表里增加新条目说明
offline-backend-20260512/backend/tests/test_artifacts_list.py 列目录新接口的单测(覆盖:空目录返回 [];traversal 路径返回 403;不存在 thread 返回 404)

7. 实施优先级

优先级 项 备注
P0 新增 GET /api/threads/{tid}/artifacts 列文件接口 必需,没它前端无法列文件
P0 step3 大改:删 mock 区块、加结论/席位/文件三块 主要价值改造
P1 前端 helper listSessionArtifacts 封装 fetch + 类型
P2 单测 test_artifacts_list.py 上线后跟进
P3 POST /api/multi-agent/sessions/summary 聚合接口 13 次→1 次,体感优化,非必需

8. 显式不做的事 ❌

  1. 不做"PDF 导出":后端没有现成 PDF 渲染管线,要做就是单独的项目。当前页面那个 导出方案 PDF 按钮直接删掉,或者保留按钮但点击只触发 window.print()(让浏览器自己生成 PDF)。
  2. 不做"安全分享":项目里已有 /api/thread-shares(per-thread 分享)和 /api/public/shares(公开页面),但那是针对单个 thread 的,不是多 thread 会话的。要做"多 thread 整合分享"是新功能,远超 step3 的范围。
  3. 不做"指标雷达图"(什么 90.3 分总评):没有真实数据源,强行加只会变成下一个 mock。
  4. 不做"签发投产":纯展示按钮,不接业务系统,删。
  5. 不做"轮次/对抗次数"显示:除非前端记一下 dispatch_count,否则后端没现成统计。如果真要展示,用现成的"派活了几次"代替"对抗 N 轮"。

9. 验收清单

step3 改造完成后应该能:

  • 看到当前 session 的总控最终结论,markdown 渲染
  • 看到 6 个席位各自的最终交付内容(默认折叠,点击展开)
  • 看到所有 sub-agent 写到 outputs 的文件列表,含文件名、大小、类型 icon
  • 点击文件名能在线预览(markdown / pdf / 图片)
  • 点击下载按钮能下载(带正确文件名)
  • 点"重新起草新规划"能回到 step1 且 step2 内存清空
  • 页面不再包含任何来自 budgetLimit 的"假指标"

10. 路径速查

资源 位置
Step 3 组件 frontend-web/src/roundtable-planning/components/HighFidelityReport.tsx
父页面 (state 持有者) frontend-web/src/roundtable-planning/pages/RoundtablePlanningPage.tsx
前端 API helper frontend-web/src/roundtable-planning/api/multi-agent.ts
后端 artifacts 路由 offline-backend-20260512/backend/app/gateway/routers/artifacts.py
后端 multi-agent 路由 offline-backend-20260512/backend/app/gateway/routers/multi_agent.py
物理 outputs 目录 backend/.deer-flow/users/{user_id}/threads/{thread_id}/user-data/outputs/