fischer-agentkit/docs/plans/2026-06-30-002-refactor-sys...

424 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: "refactor: 系统性技术债清理"
date: 2026-06-30
type: refactor
depth: deep
origin: 综合评审报告(双 agent 评审 2026-06-30
deepened: 2026-06-30
---
# refactor: 系统性技术债清理
## Summary
针对综合评审识别的 5 项系统性技术债制定分阶段重构 planReActEngine 流式/非流式 ~800 行重复、TeamOrchestrator 2080 行上帝类、`except Exception` 345+ 处滥用(聚焦 core//experts/ 关键路径)、`Any` 类型残留bitable/ 33 处等)、前端 chat.ts 2025 行巨型文件。通过 characterization-first 重构策略,在测试保障下消除架构契约脱节、恢复类型契约、拆分上帝类。
## Problem Frame
综合评审3.78/5发现项目在安全性4.5和文档4.5表现优秀但代码质量3.0和生产就绪度3.5存在系统性技术债。P0/P1 项Dockerfile、jieba、OTel、验收降级标注、skill_routing Any已修复但以下 5 项属于大规模重构,需独立 plan 排期:
1. **ReActEngine 契约脱节**`execute()` ~130 行与 `execute_stream()` ~800 行约 80% 逻辑重复,`_execute_loop` 已存在但 `execute_stream` 未复用,文档注释自认"Same logic as execute()"。stream 版有 `_drain_phase_violations` 而 execute 版无——行为漂移。
2. **TeamOrchestrator 上帝类**:单文件 2080 行、37 个方法、8 项职责(任务分解/阶段执行/辩论/验收/分歧检测/回滚/综合/干预),`_execute_execution_phase` 单方法 ~290 行。
3. **`except Exception` 关键路径降级**:全项目 345+ 处/100 文件,其中 core/ + experts/ 关键路径react.py 23、rewoo.py 21、base.py 12、orchestrator.py 20 等)存在验收 LLM 失败静默降级为"自动通过",无声绕过质量门。已加 `[DEGRADED]` 标注,但需结构性整改。
4. **`Any` 类型残留**bitable/33 处/8 文件service.py 6、db.py 6、repository.py 5、formula/functions.py 7、formula/parser.py 4、recalc_worker.py 2、ingestion/database.py 2、ingestion/excel.py 1、pipeline_state.py9 处、tools/computer_use_session.py8 处)等,违反 AGENTS.md "禁止 any 类型"。
5. **前端 chat.ts 巨型文件**2025 行、20+ 内部函数,`handleWsMessage` 单函数处理 10+ 事件类型vitest 仅 3 个测试。
## Requirements
- **R1**ReActEngine `execute``execute_stream` 共用同一循环骨架,消除 80% 重复代码行为等价golden trajectory 验证)
- **R2**TeamOrchestrator 按职责拆分为 ≤7 个模块,主类 ≤600 行,单方法 ≤100 行
- **R3**:关键路径(`core/`、`experts/`)的 `except Exception` 禁止静默降级为"自动通过",必须返回 `passed=False``degraded=True` 结构化标记
- **R4**bitable/、pipeline_state.py、tools/computer_use_session.py 的 `Any` 替换为具体类型或 `object`
- **R5**:前端 chat.ts 拆分为 chatSocket/chatStream/chatStore 三个模块,每个 ≤500 行vitest 覆盖 `handleWsMessage` discriminated union
- **R6**所有重构在现有测试5989 单测)基础上不引入回归,关键路径补充 characterization/golden 测试
## Scope Boundaries
### In Scope
- ReActEngine `_execute_loop` 事件回调驱动重构
- TeamOrchestrator 按职责拆分为协作模块
- `except Exception``core/`、`experts/` 目录的关键路径整改
- `Any` 在 bitable/、pipeline_state.py、tools/computer_use_session.py 的治理
- 前端 chat.ts 拆分 + 关键路径 vitest 补充
### Out of Scope
- 功能变更或新功能开发
- `except Exception``server/routes/`portal.py 19 处、chat.py 16 处的全量整改——deferred to follow-up
- `Any` 在其他模块llm/、memory/ 等的残留——deferred to follow-up
- ReActEngine 流式路径的 `_drain_phase_violations` 行为对齐到 execute——属 R1 行为等价验证范围,但修复本身 deferred
- 前端 a11y 全量补齐(已修 AssistantText其余 deferred
- OTel exporter 实际启用(已加配置注释,启用 deferred
### Deferred to Follow-Up Work
- `server/routes/``except Exception` 整治portal.py 19、chat.py 16——独立 PR
- `llm/`、`memory/`、`client/` 的 `Any` 残留治理——独立 PR
- bitable/ 内部 `Any` 残留repository.py 5、recalc_worker.py 2、ingestion/database.py 2、ingestion/excel.py 1共 10 处)——独立 PR
- 前端 a11y 全量扫描与补齐——独立前端专项
- OTel exporter 启用 + Grafana dashboard 模板——独立运维任务
## Key Technical Decisions
### KTD1: ReActEngine 重构采用 async generator 统一骨架
**决策**:将 `_execute_loop` 改为 async generator始终 `yield ReActEvent``execute` 收集所有事件并从最终事件提取 `ReActResult``execute_stream` 直接 `async for` 透传事件。
**理由**`_execute_loop` 已是独立方法529-1174`execute_stream` 未复用。async generator 是 Python 原生模式,无需 callback/queue 桥接,最简洁。`ReActEvent` 已存在line 130`event_type: str` 字符串字段,无 EventType 枚举),在 `event_type` 字段新增 `'final_result'` 字符串值、在 `data` dict 中携带 `ReActResult` 即可——无需新建枚举类型。
**替代方案**:事件回调(`event_sink: Callable | None`)——需 queue 桥接 async generator 与 coroutine复杂度高违反 ponytail。
### KTD2: TeamOrchestrator 拆分为 Mixin 而非独立类
**决策**:采用 mixin 模式拆分 `TeamOrchestrator`——`PhaseExecutorMixin`、`DebateRunnerMixin`、`ReviewGateMixin`、`DivergenceDetectorMixin`、`RollbackHandlerMixin`、`SynthesizerMixin`、`InterventionHandlerMixin`,主类组合这些 mixin。
**理由**37 个方法大量访问 `self._experts`、`self._workspace`、`self._broadcast_event` 等共享状态拆分为独立类需注入大量依赖或改用组合模式改动面大、回归风险高。Mixin 保持 `self` 访问,改动最小,符合 ponytail"最小代码"原则。
**替代方案**:组合模式(独立类 + 依赖注入——更解耦但改动面大deferred to follow-up。
### KTD3: except Exception 整改采用"分级降级"策略
**决策**:关键路径(验收/质量门)的 `except Exception` 改为捕获具体异常(`LLMGatewayError`、`asyncio.TimeoutError` 等),降级路径返回 `passed=True, degraded=True` 结构化标记(而非字符串前缀),让调用方可编程判断。
**理由**:已加 `[DEGRADED]` 字符串前缀,但字符串匹配脆弱。结构化 `degraded` 字段让 `_execute_execution_phase` 可在广播事件中体现降级状态,运维可监控。
### KTD4: Any 治理采用 `object` + `TYPE_CHECKING` Protocol 模式
**决策**:对无法直接导入具体类型(循环依赖)的 `Any`,替换为 `object` + 在 `TYPE_CHECKING` 块中定义 Protocol 描述期望接口对可直接导入的类型bitable/ 内部模型),替换为具体 Pydantic 模型。
**理由**`object` 是最严格的"任意类型",禁止属性访问,强制使用 `getattr` 或 cast。Protocol 在类型检查时提供接口契约,运行时零开销。
### KTD5: 前端 chat.ts 按职责层拆分
**决策**:拆分为 `chatSocket.ts`WebSocket 连接/心跳/重连)、`chatStream.ts`(流式步骤聚合/事件分发)、`chatStore.ts`(会话/消息状态/computed。`handleWsMessage` 的事件分发逻辑提取到 `chatStream.ts``dispatchWsEvent` 函数。
**理由**:现有 20+ 函数可清晰按职责分组,拆分后每个文件 ≤500 行,可独立测试。
### KTD6: Characterization-first 执行姿态
**决策**U1ReActEngine和 U2TeamOrchestrator在重构前先补充 characterization/golden 测试,锁定现有行为,再执行重构。
**理由**:核心引擎重构高风险,现有测试虽多但 mock 密度高(评审报告),流式路径缺乏 golden trajectory 快照。先锁行为再重构是安全底线。
## High-Level Technical Design
### ReActEngine 事件回调驱动重构U1
```mermaid
flowchart TD
A[execute 入口] --> B[_execute_loop async generator]
C[execute_stream 入口] --> B
B --> D{每个步骤}
D --> E[yield ReActEvent]
E --> F[Think: LLM 调用]
F --> G[Act: 工具执行]
G --> H[Observe: 结果回灌]
H --> I{停止条件?}
I -->|否| D
I -->|是| J[yield 'final_result' event]
A --> K[收集所有 events\n提取 ReActResult]
C --> L[async for 透传 events]
```
### TeamOrchestrator Mixin 拆分U2
```mermaid
graph TB
subgraph TeamOrchestrator[主类 ≤600 行]
EX[execute / _run_pipeline / resume]
DC[_decompose_task / _parse_phases]
UT[共享状态: _experts / _workspace / _broadcast_event]
end
subgraph Mixins
PE[PhaseExecutorMixin\n阶段执行 + 隔离 agent]
DR[DebateRunnerMixin\n辩论 5 阶段]
RG[ReviewGateMixin\n验收 + risk_flags]
DD[DivergenceDetectorMixin\n分歧检测 + 插入辩论]
RH[RollbackHandlerMixin\n依赖失败 + 回滚]
SY[SynthesizerMixin\n综合 + 单 agent 回退]
IH[InterventionHandlerMixin\n用户干预]
end
TeamOrchestrator -.组合.-> Mixins
```
---
## Implementation Units
### U1. ReActEngine 事件回调驱动重构
**Goal**: 将 `_execute_loop` 改为 async generator`execute` 与 `execute_stream` 共用同一骨架,消除 80% 重复代码。
**Requirements**: R1, R6
**Dependencies**: 无(首个单元)
**Files**:
- `src/agentkit/core/react.py` — 重构 `_execute_loop`、`execute`、`execute_stream``ReActEvent` 扩展 `'final_result'` 事件值
- `tests/unit/test_react_engine.py` — 补充 golden trajectory 测试
- `tests/unit/test_react_token_streaming.py` — 验证流式行为等价
**Approach**:
1. 扩展 `ReActEvent`line 130`event_type: str` 字符串字段)增加 `'final_result'` 字符串值,在 `data` dict 中携带 `ReActResult`(不新建 EventType 枚举)
2.`_execute_loop`529-1174改为 async generator在每个关键节点think/act/observe/phase_violation/compress`yield ReActEvent`,结束时 `yield ReActEvent(event_type='final_result', data={'result': final_result})`
3. `execute`396-527改为 `[e async for e in self._execute_loop(...)]`,从最后一个 event 提取 `ReActResult` 返回
4. `execute_stream`1176-1989改为 `async for event in self._execute_loop(...): yield event`,删除 ~800 行重复逻辑
5. 合并 `_drain_phase_violations` 差异:确认 stream 版有而 execute 版无的行为,在 `_execute_loop` 中统一处理
**Execution note**: Characterization-first。重构前先在 `test_react_engine.py` 补充 golden trajectory 测试(固定输入 → 期望事件序列快照),锁定现有行为。重构后验证快照不变。
**Patterns to follow**: 项目已有的 async generator 安全规则(`return; yield` 守卫,见 `.trae/rules/project_rules.md`
**Test scenarios**:
- **Happy path**: 单步工具调用 → 期望事件序列 [thinking, tool_call, tool_result, final_result]execute 返回 ReActResult.status="success"
- **Happy path 流式等价**: 同一输入分别调用 execute 和 execute_stream验证 execute 返回的 ReActResult 与 execute_stream 最后的 `'final_result'` event 内容一致
- **多步循环**: 3 步工具调用后 LLM 不返回 tool_calls → 停止,事件序列长度正确
- **Edge case: 空工具列表**: 无工具时 LLM 直接返回文本 → 单个 final_result 事件
- **Edge case: max_steps 达到**: 循环达到 max_steps → final_result.status="timeout"
- **Error path: 工具执行失败**: 工具抛异常 → tool_result event 包含错误,循环继续
- **Error path: LLM 调用失败**: LLM gateway 抛异常 → final_result.status="empty_fallback" 或错误状态
- **Phase violation**: phase 不允许的工具调用 → phase_violation event循环继续
- **CancellationToken**: 中途取消 → final_result.status="cancelled"
- **压缩触发**: 上下文超阈值 → compress event循环继续
- **Golden trajectory**: 固定 mock LLM 响应序列 → 完整事件序列快照比对(重构前后一致)
**Verification**: `execute``execute_stream` 对同一输入产生等价结果;现有 5 个 react 测试文件全部通过(`tests/unit/test_react_engine.py`、`tests/unit/test_react_token_streaming.py`、`tests/unit/test_react_phase_enforcement.py`、`tests/unit/test_react_skill_mcp_integration.py`、`tests/unit/test_react_compression.py`);新增 golden trajectory 测试通过;`_execute_loop` 是唯一的循环实现。
---
### U2. TeamOrchestrator Mixin 拆分
**Goal**: 将 2080 行上帝类按职责拆分为 7 个 mixin主类 ≤600 行,单方法 ≤100 行。
**Requirements**: R2, R6
**Dependencies**: U1ReActEngine 重构完成后,减少 TeamOrchestrator 测试耦合)
**Files**:
- `src/agentkit/experts/orchestrator.py` — 主类瘦身,组合 mixin
- `src/agentkit/experts/_phase_executor.py` — 新建PhaseExecutorMixin
- `src/agentkit/experts/_debate_runner.py` — 新建DebateRunnerMixin
- `src/agentkit/experts/_review_gate.py` — 新建ReviewGateMixin
- `src/agentkit/experts/_divergence_detector.py` — 新建DivergenceDetectorMixin
- `src/agentkit/experts/_rollback_handler.py` — 新建RollbackHandlerMixin
- `src/agentkit/experts/_synthesizer.py` — 新建SynthesizerMixin
- `src/agentkit/experts/_intervention_handler.py` — 新建InterventionHandlerMixin
- `tests/unit/experts/test_team_orchestrator.py` — 验证拆分后行为等价
**Approach**:
1. 按职责将 37 个方法分组到 7 个 mixin见 HTD 图):
- `PhaseExecutorMixin``_execute_phase`, `_execute_execution_phase`, `_get_isolated_agent`, `_cleanup_isolated_agent`, `_build_dependency_context`, `_read_dependency_output`, `_offload_result`, `_notify_collaborators`
- `DebateRunnerMixin``_execute_debate_phase`, `_generate_debate_*`4 个), `_format_debate_history`
- `ReviewGateMixin``_review_phase_output`, `_parse_risk_flags`
- `DivergenceDetectorMixin``_detect_divergence`, `_insert_debate_phase`, `_check_divergence_and_insert_debates`, `_maybe_add_plan_review_debate`
- `RollbackHandlerMixin``_mark_dependents_failed`, `_run_phase_rollback`
- `SynthesizerMixin``_synthesize_results`, `_fallback_to_single_agent`
- `InterventionHandlerMixin``_consume_team_interventions`, `_has_stop_command`, `_process_interventions`
2. 主类保留:`execute`, `_run_pipeline`, `resume`, `_decompose_task`, `_parse_phases`, `_get_model`, `_get_llm_gateway`, `_broadcast_event` + 共享状态字段
3. 每个 mixin 文件顶部注明 `# TYPE_CHECKING: 由 TeamOrchestrator 组合,访问 self 共享状态`
4. `_execute_execution_phase`~290 行)拆分为 `_prepare_phase_context`、`_run_agent_steps`、`_finalize_phase` 三个子方法
**Execution note**: Characterization-first。拆分前先运行现有 `test_team_orchestrator.py` 确认绿色,拆分后验证不变。如现有测试覆盖不足,补充关键路径测试(阶段执行/辩论/回滚/综合)。
**Patterns to follow**: Python mixin 模式,`TYPE_CHECKING` 块声明共享状态 Protocol
**Test scenarios**:
- **Happy path 拆分等价**: 现有 `test_team_orchestrator.py` 全部通过(拆分前后行为不变)
- **阶段执行**: 单阶段计划 → COMPLETED 状态,广播事件序列正确
- **多阶段并行**: 3 阶段计划2 个同层并行) → 阶段并行执行,依赖正确
- **辩论阶段**: debate 类型阶段 → 辩论 5 步执行opening/argument/summary/verdict
- **验收降级**: LLM gateway 不可用 → `passed=True, degraded=True`U3 联动)
- **回滚**: 阶段失败 → 依赖阶段标记 FAILED回滚执行
- **分歧检测**: 多轮交互超阈值 → 插入辩论阶段
- **用户干预**: stop 命令 → 计划暂停
- **综合**: 所有阶段完成 → Lead 综合,广播 team_synthesis
- **单 agent 回退**: 所有阶段失败 → 回退到单 agent 模式
**Verification**: 主类 ≤600 行;每个 mixin 文件 ≤400 行;现有 `test_team_orchestrator.py` 全部通过;`ruff check` 通过。
---
### U3. except Exception 关键路径治理
**Goal**: `core/`、`experts/` 目录的 `except Exception` 整改为捕获具体异常 + 结构化降级标记。
**Requirements**: R3, R6
**Dependencies**: U2TeamOrchestrator 拆分后,验收逻辑在 ReviewGateMixin 中)
**Files**:
- `src/agentkit/experts/_review_gate.py` — 验收降级改结构化 `degraded` 字段(联动 U2
- `src/agentkit/core/react.py``_execute_loop` 内的 `except Exception` 分类
- `src/agentkit/core/base.py``execute()``except Exception` 分类
- `src/agentkit/orchestrator/pipeline_engine.py` — 关键路径 `except Exception` 分类
- `tests/unit/experts/test_team_orchestrator.py` — 验收降级测试
- `tests/unit/test_react_engine.py` — 错误路径测试
**Approach**:
1. 验收路径(`_review_phase_output``except Exception` 改为 `except (LLMGatewayError, asyncio.TimeoutError, ConnectionError)`,降级返回 `(True, ReviewResult(degraded=True, reason="..."))` 而非字符串前缀
2. 定义 `ReviewResult` dataclass`passed: bool, degraded: bool = False, feedback: str = ""`,替换裸 tuple 返回
3. **广播层联动AE3**`_review_phase_output` 在广播 `review_result` 事件时payload 必须包含 `degraded: bool` 字段(从 `ReviewResult.degraded` 取值),让前端/运维可编程判断降级状态——而非依赖 `[DEGRADED]` 字符串前缀匹配
4. `core/react.py` `_execute_loop` 内:`except Exception` 按 LLM 错误/工具错误/超时分类,保留"日志 + 继续"但记录结构化错误码
5. `core/base.py` `execute()``except Exception` 改为 `except (AgentError, asyncio.TimeoutError, CancelledError)`,其余 re-raise
6. 非 LLM 不可用类的降级(如工具执行失败)保持现有"日志 + 继续"行为,但用 `logger.warning` 替代 `logger.error` 避免告警疲劳
7. **调用方迁移**:搜索 `_review_phase_output` 的所有调用点(`_execute_execution_phase` 等),将解构 `passed, feedback = ...` 改为 `review = ...; passed, feedback, degraded = review.passed, review.feedback, review.degraded`,确保 `degraded` 字段向后兼容(默认 `False`
**Patterns to follow**: 项目已有的 `ToolValidationError` 类型化错误码模式(`react.py:2269-2277`
**Test scenarios**:
- **验收 LLM 不可用**: gateway 为 None → `ReviewResult(passed=True, degraded=True)`
- **验收 LLM 超时**: gateway 抛 TimeoutError → `ReviewResult(passed=True, degraded=True)`
- **验收 LLM 返回无效**: gateway 返回非 JSON → 解析失败,`ReviewResult(passed=False, feedback="...")`
- **验收正常通过**: gateway 返回 "passed" → `ReviewResult(passed=True, degraded=False)`
- **工具执行失败**: 工具抛 ValueError → `_execute_loop` 记录错误码,循环继续
- **LLM 调用失败**: gateway 抛 ConnectionError → final_result 携带结构化错误码
- **CancellationToken**: 中途取消 → CancelledError 正确传播,不被 except Exception 吞掉
- **调用方迁移回归**: `_review_phase_output` 所有调用点(`_execute_execution_phase` 等)正确解构 `ReviewResult``degraded` 字段向后兼容(旧调用点未迁移时不报错,默认 `False`
- **review_result WS 事件 payloadAE3**: 验收降级时广播的 `review_result` 事件 payload 含 `degraded: true` 字段;正常通过时 `degraded: false`
**Verification**: 基线 core/ + experts/ 共 84 处 `except Exception`react.py 23 + rewoo.py 21 + base.py 12 + orchestrator.py 20 + board_orchestrator.py 6 + 其余 2整改后减少 ≥50%;验收降级返回结构化 `ReviewResult``review_result` WS 事件含 `degraded` 字段;现有测试通过。
---
### U4. Any 类型残留治理
**Goal**: bitable/、pipeline_state.py、tools/computer_use_session.py 的 `Any` 替换为具体类型或 `object` + Protocol。
**Requirements**: R4, R6
**Dependencies**: 无(可与 U1-U3 并行)
**Files**:
- `src/agentkit/bitable/service.py` — 6 处 `Any`
- `src/agentkit/bitable/db.py` — 6 处 `Any`
- `src/agentkit/bitable/formula/functions.py` — 7 处 `Any`
- `src/agentkit/bitable/formula/parser.py` — 4 处 `Any`
- `src/agentkit/orchestrator/pipeline_state.py` — 9 处 `Any``self._redis: Any` 等)
- `src/agentkit/tools/computer_use_session.py` — 8 处 `Any`
- 对应测试文件
**Deferred独立 PR本 U 不处理)**:
- `src/agentkit/bitable/repository.py` — 5 处
- `src/agentkit/bitable/recalc_worker.py` — 2 处
- `src/agentkit/bitable/ingestion/database.py` — 2 处
- `src/agentkit/bitable/ingestion/excel.py` — 1 处
- 注:`bitable/formula/engine.py` 经核实 `: Any` 数量为 0无需处理`bitable/formula.py` 文件不存在(实际为 `formula/` 目录下的 `functions.py` + `parser.py` + `engine.py`
**Approach**:
1. **bitable/ in-scope**23 处service.py 6 + db.py 6 + formula/functions.py 7 + formula/parser.py 4deferred 10 处见上):定义 `BitableRecord = dict[str, str | int | float | None]` TypeAlias 替换 `dict[str, Any]`;公式求值结果用 `FormulaResult = str | int | float | None`
2. **pipeline_state.py**9 处):`self._redis: Any` → `object | None`(运行时用 `isinstance` 检查);`Callable[..., Coroutine[Any, Any, Any]]` 保留Coroutine 类型参数合理);`session_factory: Any` → `object | None`
3. **tools/computer_use_session.py**8 处):定义 `SessionState = dict[str, str | int | bool | None]` TypeAlias截图数据用 `bytes` 而非 `Any`
4. 每个模块顶部用 `TYPE_CHECKING` 块定义 Protocol`_RedisLike`),描述期望接口
5. 对无法静态推断的动态字段,用 `dict[str, object]` + 显式访问器方法
**Patterns to follow**: U0 已修的 `skill_routing.py` 模式(`Any` → `object` + `getattr`
**Test scenarios**:
- **类型检查**: `ruff check` 通过,无 `: Any` 残留(除 `Coroutine[Any, Any, Any]`
- **bitable service 行为等价**: 现有 bitable 测试全部通过
- **pipeline_state Redis 降级**: Redis 不可用 → 降级到 InMemory行为不变
- **computer_use_session**: 现有测试通过,截图数据类型正确
**Verification**: 目标文件 `Any` 数量降至 ≤5保留 `Coroutine[Any, Any, Any]``ruff check` 通过;现有测试通过。
---
### U5. 前端 chat.ts 拆分 + vitest 补充
**Goal**: 将 2025 行 chat.ts 拆分为 chatSocket/chatStream/chatStore 三个模块,补充关键路径 vitest 测试。
**Requirements**: R5, R6
**Dependencies**: 无(前端独立,可与后端并行)
**Files**:
- `src/agentkit/server/frontend/src/stores/chat.ts` — 瘦身为 chatStore.ts会话/消息状态/computed
- `src/agentkit/server/frontend/src/stores/chatSocket.ts` — 新建WebSocket 连接/心跳/重连
- `src/agentkit/server/frontend/src/stores/chatStream.ts` — 新建,流式步骤聚合/事件分发
- `src/agentkit/server/frontend/src/stores/__tests__/chatStream.test.ts` — 新建dispatchWsEvent 测试
- `src/agentkit/server/frontend/src/stores/__tests__/chatSocket.test.ts` — 新建,重连/心跳测试
- `src/agentkit/server/frontend/src/stores/index.ts` — 如有,更新 re-export
**Approach**:
1. **chatSocket.ts**~200 行):提取 `connectWebSocket`, `disconnectWebSocket`, `_heartbeatTimer`, `_reconnectTimer`, `resolveIncomingConvId`, `_intentionalDisconnect`;导出 `useChatSocket()` composable
2. **chatStream.ts**~300 行):提取 `getConvSteps`, `appendStep`, `updateLastStep`, `clearConvSteps`, `handleWsMessage` 的事件分发逻辑(重命名为 `dispatchWsEvent`);导出 `useChatStream()` composable
3. **chatStore.ts**≤500 行):保留 `loadConversations`, `selectConversation`, `createConversation`, `deleteConversation`, `sendMessage`, `sendWsMessage`, computed组合 `useChatSocket``useChatStream`
4. `handleWsMessage` 的 discriminated union 分发改为 `chatStream.ts` 中的 `dispatchWsEvent(event, streamState)` 纯函数,便于单元测试
5. vitest 测试覆盖:`dispatchWsEvent` 的 10+ 事件类型、`resolveIncomingConvId` 启发式、心跳/重连时序
**Patterns to follow**: Vue 3 Composition API composable 模式;现有 `useChatStore = defineStore` 结构
**Test scenarios**:
- **dispatchWsEvent token**: token 事件 → streamingStepsByConv 更新
- **dispatchWsEvent thinking**: thinking 事件 → appendStep(type=thinking)
- **dispatchWsEvent step**: step 事件 → appendStep(type=tool_call)
- **dispatchWsEvent final_answer**: final_answer 事件 → 标记完成,清除 pending
- **dispatchWsEvent team_formed**: team_formed 事件 → planExecState 更新
- **dispatchWsEvent expert_step**: expert_step 事件 → appendStep(type=expert)
- **dispatchWsEvent error**: error 事件 → 错误状态设置
- **resolveIncomingConvId**: 多会话 pending → 返回最近使用的 convId
- **心跳**: 30s 间隔 → 发送 ping
- **重连**: 断连后 3s → 重连,`_intentionalDisconnect` 防级联
**Verification**: 三个文件每个 ≤500 行vitest 测试 ≥10 个;`npm run typecheck` 通过;`npm run build:frontend` 成功。
---
## Risks & Dependencies
### Risk Analysis
| 风险 | 概率 | 影响 | 缓解 |
|---|---|---|---|
| U1 ReActEngine 重构引入流式路径回归 | 高 | 高 | Characterization-first重构前补 golden trajectory 测试,锁定事件序列 |
| U2 TeamOrchestrator mixin 拆分后共享状态访问混乱 | 中 | 中 | TYPE_CHECKING Protocol 声明共享状态接口mixin 文件顶部注明依赖 |
| U3 验收降级结构化改动破坏调用方 | 中 | 中 | `ReviewResult` dataclass 保持 `passed` 字段向后兼容;逐步迁移调用方 |
| U4 bitable formula 动态类型治理过度 | 中 | 低 | 保留 `Coroutine[Any, Any, Any]`;动态字段用 `dict[str, object]` 而非强类型 |
| U5 前端拆分后 composable 间状态同步问题 | 中 | 中 | 保持 `useChatStore` 作为单一状态源socket/stream 作为内部 composable |
| 跨 U 回归U1+U2 同时改 core/experts | 中 | 高 | U1 完成并验证后再启动 U2U4/U5 可并行 |
### Dependencies
- **U1 → U2**U2 的 ReviewGateMixin 依赖 U1 的 ReActEngine 稳定(减少测试耦合)
- **U2 → U3**U3 的验收降级整改在 U2 拆分后的 `ReviewGateMixin` 中进行
- **U4 独立**:可与 U1-U3 并行
- **U5 独立**:前端独立,可与后端并行
- **测试基础**5989 单测 + 5 个 react 测试文件 + test_team_orchestrator.py 必须在重构前绿色
## Acceptance Examples
- **AE1**: `execute()``execute_stream()` 对同一 mock 输入产生等价结果ReActResult 字段一致),事件序列长度一致
- **AE2**: `TeamOrchestrator` 主类 ≤600 行7 个 mixin 文件各自独立,`test_team_orchestrator.py` 全部通过
- **AE3**: 验收 LLM 不可用时,`ReviewResult(passed=True, degraded=True)` 返回,`review_result` WS 事件包含 `degraded: true` 字段
- **AE4**: in-scope 文件bitable/ service.py + db.py + formula/functions.py + formula/parser.py、pipeline_state.py、tools/computer_use_session.py共 40 处 `Any`)中 `Any` 数量降至 ≤5保留 `Coroutine[Any, Any, Any]`
- **AE5**: 前端 chat.ts 拆分为 3 个文件,每个 ≤500 行vitest ≥10 个测试通过
## Documentation Plan
- 更新 `AGENTS.md`TeamOrchestrator 模块映射表补充 mixin 文件列表
- 更新 `CONCEPTS.md`:如需,补充 `ReviewResult`、`ReActEvent` 的 `'final_result'` 事件值术语
- 不新增独立文档(重构不改变外部 API
## Operational / Rollout Notes
- 每个 U 作为独立 PR按依赖顺序合并U1 → U2 → U3U4/U5 可并行)
- 每个 PR 必须通过 `pytest tests/unit/ -x -q` + `ruff check src/` + 前端 `npm run typecheck`(如涉及)
- U1 PR 需额外验证:流式路径 golden trajectory 快照比对
- 回滚策略:任意 PR 引入回归revert 该 PR重构不涉及数据迁移回滚零成本
## Future Considerations
- **U2 升级**mixin 拆分稳定后,可进一步迁移到组合模式(独立类 + 依赖注入),完全消除共享状态耦合
- **`except Exception` 全量整治**U3 完成后,可排期 `server/routes/` 的 35 处整治
- **`Any` 全量治理**U4 完成后,可排期 `llm/`、`memory/`、`client/` 残留治理
- **前端 vitest 覆盖率**U5 完成后,逐步提升到 60% 行覆盖
## Sources & Research
- 综合评审报告(双 agent 评审2026-06-30架构与工程 3.63/5、产品与运维 4.0/5
- 代码取证:`core/react.py` 方法结构Grep 32 方法)、`experts/orchestrator.py`37 方法)、`chat.ts`20+ 函数)
- 项目规则:`.trae/rules/project_rules.md`async generator 安全)、`AGENTS.md`(禁止 any、禁止 except Exception 滥用)