docs: LLM 错误码对齐设计(error_code 字段 + api §7 对齐)
This commit is contained in:
@@ -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 同源明写) |
|
||||
Reference in New Issue
Block a user