sync update from LAN repo (source dhk-1 @ 3b1ec5b4)

This commit is contained in:
gozeros committed 2026-11-05 10:56:34 +08:00
1 parent da6521b6cb
commit 264e2200d6
995 files changed
+148105 -13399

No files matched your search

@@ -0,0 +1,64 @@
# 业务链选链去掉每次 `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`。