6.0 KiB
业务链选链去掉每次 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 已 importget_agent_store,参照multi_agent.py的_get_agents_batch_direct写法)。 - 新增响应专用
ChainSeatResponseSchema(ChainSeatSchema),加available: bool | None、live_name: str | None;ChainResponse.seats改用之。创建/更新请求 schema 不动 → 富化字段永不被持久化(避免 create/updatemodel_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/filterRecommendCandidatesimport(编辑器页与「智能推荐」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()。
兼容性
- 新后端 + 旧前端:响应多两个字段,旧代码忽略,无影响。
- 旧后端 + 新前端:
availableundefined → 全按存活处理,行为等于现在"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。