Files
deerflow-code/.zcode/plans/plan-session_00e04f204fab48e1d48ae6b407c045ab.md
T

6.0 KiB
Raw Blame History

业务链选链去掉每次 listAgents() 查询

背景与思路

选链各环节展示只用到 seat 的 agent_id + name(快照已随链条落库,编辑器保存时刷新),额外全量拉 /api/agents 只为两件事:席位存活判断(existingIds)和最新改名(nameById)。存活是实时状态、无法落库保鲜,因此方案是:

  • 后端在返回链条(list/get 等)时,用一次批量 SQL(agent_store.list_by_ids,与 /api/agents 同一套可见性过滤)给每个 seat 附带 available: bool + live_name: string | null 两个只读计算字段(不落库,seats JSON 存储结构零变化、零迁移);
  • 前端选链全链路改用这两个字段,删除所有选链路径上的 listAgents() 调用;左栏点选一条链变为零网络请求(链条数据本就在内存里);选链弹窗打开只剩 1 个 listChains 小请求。

「只映射目前展示的这些」:仅 name + 存活状态,其他智能体字段一概不存(description 等快照维持现状,运行期配置由后端按 agent_id 自取,本就不依赖前端传参)。

后端改动

1. offline-backend-20260512/backend/app/gateway/routers/roundtable_chains.py

  • 新增常量 _FUNCTIONAL_AGENT_EXCLUSIONS(roundtable-intent / roundtable-recommender / roundtable-coordinator / roundtable-report + 前缀 roundtable-coordinator-,与前端 api/recommend.ts 的 filterRecommendCandidates 排除集逐字对齐,保持"功能型内置智能体不可作席位"语义不变)。
  • 新增纯函数 _compute_seat_enrichment(chains, live_records) -> None:对每条链每个 seat 写入 available(live 存在且不在排除集 → True)与 live_name(无则 None);配套薄异步封装 _enrich_chain_seats(request, chains, user_id):收集去重小写 agent_id → get_agent_store(request).list_by_ids(ids, user_id) 一次查询(router 已 import get_agent_store,参照 multi_agent.py 的 _get_agents_batch_direct 写法)。
  • 新增响应专用 ChainSeatResponseSchema(ChainSeatSchema),加 available: bool | None、live_name: str | None;ChainResponse.seats 改用之。创建/更新请求 schema 不动 → 富化字段永不被持久化(避免 create/update model_dump() 把默认值写进 seats JSON)。
  • 在所有返回 ChainResponse(列表/单条/更新等)的出口调用富化。可见性语义与现状完全一致(_listing_filter:本人 / 内置 / 已发布、非私享、非管理员隐藏)。

2. 测试 tests/test_roundtable_chain_seat_enrichment.py(新建)

镜像 test_roundtable_chain_stages.py 的风格:纯函数用 dict 直接测(存活/缺失/排除集/改名刷新/混合多条链);list_by_ids 交互用同名小写归一化断言(stub store)。

前端改动(frontend-web)

3. src/roundtable-planning/lib/business-chain.ts

  • ChainSeat 增加可选 available?: boolean(undefined = 旧后端未标注 → 视为存活,与现有"查询失败不判删"降级语义一致)和 live_name?: string | null,附注释说明来源与回退顺序。
  • buildChainSeatSelection(chain, candidates?):candidates 改可选;不传时存活 = seat.available !== false、名称 = live_name ?? name ?? agent_id;传入时保持现行为(测试兼容)。

4. src/roundtable-planning/components/BusinessChainPicker.tsx

加载 effect 只留 listChains(scope);删 listAgents()、filterRecommendCandidates 及 existingIds/nameById state——存活判断用 seat.available !== false,tag 名称用 live_name ?? seat.name ?? agent_id;已删置灰/删除线/「存活不足 2 个」等 UI 保持不变(改由 available 驱动)。handleUse 的 nameOf 同步改。

5. src/roundtable-planning/components/BusinessChainListDialog.tsx

BusinessChainListBody 删除挂载时的 listAgents() effect 与 nameById map;ChainSeatsFlow 的 seatTag 标签改 seat.live_name ?? seat.name ?? agentId。

6. src/roundtable-planning/pages/RoundtablePlanningPage.tsx(3 处调用点)

  • L486 固定链 effect(8BF 等):Promise.all([getChain, listAgents]) → 仅 getChain;席位不足提示改为按 seat.available === false 计算(文案不变)。
  • L1356 handleSelectChainFromSidebar(左栏点选,本次体验痛点核心):删 listAgents(),直接 buildChainSeatSelection(chain) —— 点选零请求。
  • L1503 C3 深链自动选链:删 listAgents() 分支。
  • 清理不再使用的 listAgents / filterRecommendCandidates import(编辑器页与「智能推荐」tab 的 listAgents 是选智能体池所必需,保留不动)。

7. 测试 src/roundtable-planning/lib/business-chain.test.ts(新建,node:test 风格同 chain-stages.test.ts)

覆盖无 candidates 分支:available 缺省视为存活、available:false 过滤、不足 2 席返回 null、live_name 优先于快照名、dag 分支不受影响;package.json 加 test:business-chain 脚本。

8. 文档 frontend-web/docs/multi-agent-business-chain-dev.md

补一段:链条响应 seat 附带 available/live_name(后端读时计算、不落库);选链前端不再调 listAgents()。

兼容性

  • 新后端 + 旧前端:响应多两个字段,旧代码忽略,无影响。
  • 旧后端 + 新前端:available undefined → 全按存活处理,行为等于现在"agents 查询失败"的降级路径;若智能体真被删,Step 2 init 的后端 404 硬校验仍兜底(现状即有)。
  • 存量链条 seats JSON 无需迁移;8BF / 岗位协同 / official-task-analysis 等消费 seat 快照的模块零改动。

验证

  • 后端:PYTHONPATH=. uv run pytest tests/test_roundtable_chain_seat_enrichment.py tests/test_roundtable_chain_stages.py tests/test_roundtable_chain_hidden.py -v + make lint(ruff)。
  • 前端:pnpm typecheck + node --experimental-strip-types --test src/roundtable-planning/lib/business-chain.test.ts。