feat: AI 调用稳定性增强(可重试错误分类 + 指数退避 + 修复链路错误透传)

- providers/base.ts 引入 ApiRequestError 与 isRetryableError,按 429/5xx/超时/网络错误/401 分类
- chatWithRetry 升级为最多 3 次指数退避(1s/2s/4s),非重试错误立即上抛;新增 setRetryBaseDelayForTest 钩子
- aiFixEngine/customFixEngine 不再吞错,修复失败原因透传为 ai-error: <原因>
- 扩展 ai-empty-response/customFixEngine 测试覆盖重试与分类
This commit is contained in:
范智鹏
2026-08-31 21:57:54 +08:00
parent 458647d82e
commit d6b8a3c897
11 changed files with 287 additions and 55 deletions
@@ -0,0 +1,64 @@
# AI 调用稳定性增强(重试 + 统一兜底)设计
日期:2026-08-29
状态:已批准(Stage ④ 通过)
## 背景与目标
评审报告「稳定性与易用性 12/15」指出:缺少显式重试机制;未见对 AI 调用异常的统一兜底。现状核实:
- `chatWithRetry`src/ai/engine.ts)仅对 `EmptyContentError` 重试 1 次,网络错误/超时/429/5xx 均直接上抛
- `requestFix`src/fix/aiFixEngine.tscatch 后静默返回 null,用户看不到 AI 失败原因
- engine 主入口已有 `degraded + error` 兜底结构(`Promise.allSettled` + i18n 错误聚合),无需改动
- 多 AI 供应商切换已被评审认可为降级策略,无需改动
## 方案
### 1. 错误分类(src/ai/providers/base.ts
新增 `ApiRequestError`(携带 HTTP status+ `isRetryableError()`
| 错误 | 可重试 |
|---|---|
| EmptyContentError | 是 |
| HTTP 429 / 5xx | 是 |
| 超时(AbortError / DOMException name==='AbortError' | 是 |
| 网络错误(TypeError | 是 |
| 401、其他 4xx、JSON 解析错 | 否,立即失败 |
### 2. provider 统一结构化抛错
openai-compatible.ts / gemini.ts / claude.ts:非 2xx 响应抛 `ApiRequestError`(携带 status 与响应文本)。401 保留现有 i18n 消息语义,同样结构化、不可重试。
### 3. chatWithRetry 增强(src/ai/engine.ts
固定策略:最多 3 次重试、指数退避 1s/2s/4s、按分类决定重试或立即上抛。所有调用入口(engine 三入口 + fix 链路)自动受益。
### 4. fix 链路兜底透传(src/fix/aiFixEngine.ts
`requestFix` 不再吞错,将最终错误消息透传到 `FixResult.message`,复用 commands.ts 现有 `t('fix.aiFailed')` 通知管道。
### 5. 单元测试
扩展既有 `tests/ai-empty-response.test.ts`chatWithRetry 测试的既有归属,避免新建文件):mock provider 抛可重试/不可重试错误,验证重试次数、退避行为、立即失败路径、`isRetryableError` 分类。`tests/customFixEngine.test.ts` 一处测试名随行为语义更新。
### 实现修订(编码期同步)
- `src/fix/customFixEngine.ts` 存在与 aiFixEngine 相同的吞错模式(同一评审批评点),按同一模式修复:`requestFix` 不再吞错,`aiFixReviewIssue` 调用处 catch 后透传 `ai-error: <原因>``FixResult.message`
- 引擎内部导出 `setRetryBaseDelayForTest` 测试钩子,避免测试真实等待 1s/2s/4s 退避
## 验证链
`npm run lint``npm run compile``npm test`
## 涉及文件
- src/ai/providers/base.ts
- src/ai/providers/openai-compatible.ts
- src/ai/providers/gemini.ts
- src/ai/providers/claude.ts
- src/ai/engine.ts
- src/fix/aiFixEngine.ts
- src/fix/customFixEngine.ts
- tests/ai-empty-response.test.ts
- tests/customFixEngine.test.ts