- providers/base.ts 引入 ApiRequestError 与 isRetryableError,按 429/5xx/超时/网络错误/401 分类 - chatWithRetry 升级为最多 3 次指数退避(1s/2s/4s),非重试错误立即上抛;新增 setRetryBaseDelayForTest 钩子 - aiFixEngine/customFixEngine 不再吞错,修复失败原因透传为 ai-error: <原因> - 扩展 ai-empty-response/customFixEngine 测试覆盖重试与分类
2.8 KiB
2.8 KiB
AI 调用稳定性增强(重试 + 统一兜底)设计
日期:2026-08-29 状态:已批准(Stage ④ 通过)
背景与目标
评审报告「稳定性与易用性 12/15」指出:缺少显式重试机制;未见对 AI 调用异常的统一兜底。现状核实:
chatWithRetry(src/ai/engine.ts)仅对EmptyContentError重试 1 次,网络错误/超时/429/5xx 均直接上抛requestFix(src/fix/aiFixEngine.ts)catch 后静默返回 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