5.5 KiB
5.5 KiB
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 基类新增类属性,五子类各覆写:
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,向后兼容):
@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_codechat_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新字段默认Nonetests/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. 验收标准
- 五异常
error_code与 api-design §7 一一对应 ChatResult/StructuredResult失败路径返回结构化error_code,成功路径为Noneerror_code与error同源(取最后一次失败异常),有测试证明- api-design §7 补
LLM_NETWORK_ERROR行 - 全量回归 ≥ 119 passed / 覆盖率 100%
- 零新增依赖、零 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 同源明写) |