diff --git a/docs/superpowers/specs/2026-08-09-llm-errorcode-alignment-design.md b/docs/superpowers/specs/2026-08-09-llm-errorcode-alignment-design.md new file mode 100644 index 0000000..95c1b83 --- /dev/null +++ b/docs/superpowers/specs/2026-08-09-llm-errorcode-alignment-design.md @@ -0,0 +1,121 @@ +# LLM 错误码对齐设计(retry/skip/abort UX) + +## 1. 背景与动机 + +`api-design.md` §7 错误码表定义了 LLM 相关错误码及其「用户选项」(`retry / skip / abort` / `配置 Key`),但 **engine 层没有暴露任何可供调用方/UI 决策的机器可读信息**: + +- `ChatResult` / `StructuredResult` 失败时仅有人类可读的 `error: str` +- 调用方无法区分「值得重试的网络/超时错误」与「重试无用的配置/结构错误」 +- api 层的 `LLM_NETWORK_ERROR` 概念缺失(`LLMNetworkError` 的可重试语义无处映射) + +本 backlog 目标:在 **engine 层**暴露结构化的 `error_code`,并让 api-design §7 表与异常树五类型一一对齐,为后续 UI 的 retry/skip/abort 对话框提供唯一事实源。 + +## 2. 非目标(明确不包含) + +- 不实现 UI、不新增 API 端点、不动 state machine +- 不改变错误字符串格式与既有字段 +- 不实现「按错误码自动重试」的自动化策略(本 backlog 只提供信息,决策权留在调用方) + +## 3. 设计 + +### 3.1 异常树携带 error_code(事实源) + +`src/genesis/inference/exceptions.py`:`LLMError` 基类新增类属性,五子类各覆写: + +```python +class LLMError(Exception): + """LLM 调用异常基类。error_code 与 api-design §7 错误码表一一对应。""" + error_code: str | None = None + + +class LLMNetworkError(LLMError): + error_code = "LLM_NETWORK_ERROR" + +class LLMTimeoutError(LLMError): + error_code = "LLM_TIMEOUT" + +class LLMNotConfiguredError(LLMError): + error_code = "LLM_NOT_CONFIGURED" + +class LLMResponseError(LLMError): + error_code = "LLM_PARSE_ERROR" +``` + +- `error_code` 是 docs api-design §7 表中「错误码」列的机器可读值 +- 新增未来异常类型时**必须**覆写 `error_code`(否则走 `None`,绝不伪造) + +### 3.2 结果对象携带 error_code + +`ChatResult` / `StructuredResult` 增加字段(默认 `None`,向后兼容): + +```python +@dataclass +class ChatResult: + ... + error_code: str | None = None # 失败时的 api §7 错误码;成功为 None + +@dataclass +class StructuredResult: + ... + error_code: str | None = None # 失败/parse_error 时的错误码;成功为 None +``` + +### 3.3 engine 透传(同源语义) + +- `chat()` 失败路径:`error_code` 与 `error` 字符串**同源**——取最后一次失败(fallback 链)的 `LLMError.error_code` +- `chat_structured()` 解析耗尽:`status="parse_error"` + `error_code="LLM_PARSE_ERROR"`(来自导致解析失败的最后一次调用;若无 LLM 异常则取最近一次 JSONDecodeError 对应的 `LLM_PARSE_ERROR`) +- `chat_structured()` 网络/超时失败:`status="failed"` + `error_code=<对应异常>` +- 退化:异常非 `LLMError`(不捕获、直接冒出)→ engine 不产生 error_code(`None`),由上层兜底 + +### 3.4 error_code 语义表(与 api §7 对齐) + +| 异常 | error_code | api §7 用户选项 | retryable 语义 | +|------|-----------|----------------|---------------| +| `LLMTimeoutError` | `LLM_TIMEOUT` | retry / skip / abort | ✅ 可重试 | +| `LLMNetworkError` | `LLM_NETWORK_ERROR` | retry / skip / abort | ✅ 可重试 | +| `LLMResponseError` | `LLM_PARSE_ERROR` | retry | ⚠️ 结构化可重试;chat 不建议 | +| `LLMNotConfiguredError` | `LLM_NOT_CONFIGURED` | 配置 Key | ❌ 配置问题 | +| 其他异常 | `None` | — | 上层兜底(`INTERNAL_ERROR` 语义由 API 层负责) | + +> 注:api-design §7 表仍保留 `INTERNAL_ERROR` 行作为 API 层兜底(非 LLMError 异常由上层翻译),engine 层不产生该值。 + +### 3.5 文档修订 + +`docs/api-design.md` §7 错误码表新增行(对齐异常树): + +``` +| `LLM_NETWORK_ERROR` | 502 | 网络失败/5xx 重试耗尽 | retry / skip / abort | exceptions.LLMNetworkError | +``` + +并对 `LLM_PARSE_ERROR` / `LLM_TIMEOUT` 行的「来源」列保持与异常类型对应(已存在,无需改)。 + +## 4. 测试策略 + +- `tests/test_inference_errors.py`:各异常 `error_code` 类属性恒定值断言 +- `tests/test_inference_types.py`:`ChatResult`/`StructuredResult` 新字段默认 `None` +- `tests/test_inference_engine.py`: + - `chat()` 失败时 `error_code == "LLM_TIMEOUT"` / `"LLM_NETWORK_ERROR"` / `"LLM_NOT_CONFIGURED"`(对应脚本) + - 主模型超时 + 备用网络失败 → `error_code == "LLM_NETWORK_ERROR"`(同源:取最后一次) + - `chat_structured()` parse_error → `error_code == "LLM_PARSE_ERROR"`;网络失败 → `error_code == "LLM_NETWORK_ERROR"` +- 全量回归 `python -m pytest -v`(覆盖红线 fail_under=99,当前基线 119 passed / 100%) + +## 5. 验收标准 + +1. 五异常 `error_code` 与 api-design §7 一一对应 +2. `ChatResult`/`StructuredResult` 失败路径返回结构化 `error_code`,成功路径为 `None` +3. `error_code` 与 `error` 同源(取最后一次失败异常),有测试证明 +4. api-design §7 补 `LLM_NETWORK_ERROR` 行 +5. 全量回归 ≥ 119 passed / 覆盖率 100% +6. 零新增依赖、零 UI 变更、向后兼容(旧构造不含 error_code 字段仍工作) + +## 6. 涉及文件 + +- 修改:`src/genesis/inference/exceptions.py`、`src/genesis/inference/types.py`、`src/genesis/inference/engine.py` +- 修改:`docs/api-design.md`(§7 表格) +- 测试:`tests/test_inference_errors.py`、`tests/test_inference_types.py`、`tests/test_inference_engine.py` + +## 7. 版本与状态 + +| 版本 | 日期 | 说明 | +|------|------|------| +| v1.0 | 2026-08-09 | 定稿版(含 2 项设计健全性修订:保留 INTERNAL_ERROR 兜底行、error_code 与 error 同源明写) | \ No newline at end of file