Files
2026Technology-Competition/docs/superpowers/specs/2026-08-09-llm-errorcode-alignment-design.md

5.5 KiB
Raw Permalink Blame History

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.pyLLMError 基类新增类属性,五子类各覆写:

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_codeerror 字符串同源——取最后一次失败(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_codeNone),由上层兜底

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.pyChatResult/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_codeerror 同源(取最后一次失败异常),有测试证明
  4. api-design §7 补 LLM_NETWORK_ERROR
  5. 全量回归 ≥ 119 passed / 覆盖率 100%
  6. 零新增依赖、零 UI 变更、向后兼容(旧构造不含 error_code 字段仍工作)

6. 涉及文件

  • 修改:src/genesis/inference/exceptions.pysrc/genesis/inference/types.pysrc/genesis/inference/engine.py
  • 修改:docs/api-design.md(§7 表格)
  • 测试:tests/test_inference_errors.pytests/test_inference_types.pytests/test_inference_engine.py

7. 版本与状态

版本 日期 说明
v1.0 2026-08-09 定稿版(含 2 项设计健全性修订:保留 INTERNAL_ERROR 兜底行、error_code 与 error 同源明写)