424 lines
28 KiB
Markdown
424 lines
28 KiB
Markdown
---
|
||
title: "refactor: 系统性技术债清理"
|
||
date: 2026-06-30
|
||
type: refactor
|
||
depth: deep
|
||
origin: 综合评审报告(双 agent 评审 2026-06-30)
|
||
deepened: 2026-06-30
|
||
---
|
||
|
||
# refactor: 系统性技术债清理
|
||
|
||
## Summary
|
||
|
||
针对综合评审识别的 5 项系统性技术债制定分阶段重构 plan:ReActEngine 流式/非流式 ~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.py(9 处)、tools/computer_use_session.py(8 处)等,违反 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 执行姿态
|
||
|
||
**决策**:U1(ReActEngine)和 U2(TeamOrchestrator)在重构前先补充 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**: U1(ReActEngine 重构完成后,减少 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**: U2(TeamOrchestrator 拆分后,验收逻辑在 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 事件 payload(AE3)**: 验收降级时广播的 `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 4;deferred 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 完成并验证后再启动 U2;U4/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 → U3,U4/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 滥用)
|