Unix 系统通过 errno.h 定义了一套历经数十年检验的错误码体系。从 EACCES(权限不足)到 ETIMEDOUT(连接超时),这些简洁的符号为系统调用提供了统一的故障语义。当 LLM Agent 成为现代应用的核心组件时,我们面临一个相似的问题:如何在异构的模型提供商(OpenAI、Anthropic、Azure、AWS Bedrock)之间建立可互操作的错误语义?
本文提出一种 errno 风格的错误码映射方案,将 Unix 传统的错误分类哲学延伸至 AI 推理领域,并给出可直接落地的结构化重试策略参数。
errno 设计哲学与 LLM 错误的映射
Unix errno 的核心设计原则有三:错误码是整数、按故障域分类、调用者决定重试策略。这三个原则同样适用于 LLM Agent 的错误处理。
首先建立映射表,将 Unix errno 的语义域映射到 LLM 特有的故障场景:
| Unix errno | 语义 | LLM Agent 映射 | 典型场景 |
|---|---|---|---|
ETIMEDOUT |
操作超时 | LLM_TIMEOUT |
推理请求超过 30s 未返回 |
ECONNRESET |
连接重置 | LLM_STREAM_INTERRUPT |
流式响应中途断开 |
EACCES |
权限不足 | LLM_AUTH_FAILURE |
API Key 无效或权限受限 |
ENOMEM |
内存不足 | LLM_CONTEXT_EXCEEDED |
输入超出上下文窗口 |
EAGAIN |
资源暂不可用 | LLM_RATE_LIMIT |
触发提供商限流 |
EINVAL |
参数无效 | LLM_VALIDATION_ERROR |
请求格式或参数错误 |
EIO |
I/O 错误 | LLM_PROVIDER_ERROR |
服务商内部错误(5xx) |
EPERM |
操作不允许 | LLM_CONTENT_FILTER |
内容审核拦截 |
这种映射不是简单的重命名,而是语义层面的对齐。ETIMEDOUT 与 LLM_TIMEOUT 都暗示 "资源可能仍在处理,可安全重试";EPERM 与 LLM_CONTENT_FILTER 则共同表达 "此操作被策略禁止,重试无益"。
三层错误分类体系
借鉴 errno 的简洁性,同时满足 LLM Agent 的复杂需求,建议采用三层分类结构:
Category(故障域):定义错误发生的系统边界
API_ERROR— 提供商 API 层面的故障RATE_LIMIT— 速率限制触发CONTENT_FILTER— 内容审核拦截CONTEXT_LENGTH— 上下文长度超限VALIDATION— 输入 / 输出验证失败TIMEOUT— 请求超时TOOL_ERROR— 工具调用失败NETWORK— 网络连接问题
Severity(严重级别):决定告警与降级策略
LOW— 记录日志,继续执行MEDIUM— 指数退避重试HIGH— 触发告警并尝试降级CRITICAL— 停止执行,人工介入
Retryable(可重试性):布尔标志,由 Category 和 Severity 推导得出
关键设计决策:RATE_LIMIT 和 TIMEOUT 默认 retryable=True,但需携带 retry_after 或计算退避延迟;CONTENT_FILTER 和 VALIDATION 默认 retryable=False,因为重试相同输入不会改变结果。
结构化重试策略参数
基于上述分类,可配置化的重试策略如下:
指数退避公式:
delay = base_delay × (2 ^ attempt) + jitter
推荐参数:
API_ERROR/TIMEOUT:base_delay=1s,max_delay=60s,max_retries=6RATE_LIMIT:base_delay=retry_after(若提供商返回),否则2s,max_delay=300s,max_retries=8NETWORK:同上,但增加熔断器逻辑 —— 连续 3 次失败后进入OPEN状态,暂停请求 30s
抖动策略:在延迟基础上添加 0-10% 的随机扰动,避免多个 Agent 实例同时重试造成的 "惊群效应"。
上下文长度超限的特殊处理:CONTEXT_LENGTH 标记为 retryable=True,但重试前必须执行输入截断或摘要化。建议实现一个 truncate_and_retry 装饰器,自动将超长输入压缩至目标 token 数的 80%。
跨提供商互操作层
实际生产中的痛点在于:同一模型通过不同后端(OpenRouter → AWS Bedrock vs Azure vs Anthropic 直连)返回的错误语义并不一致。有开发者曾构建包含 30+ 细粒度 case 的错误分类器,最终发现维护成本过高,简化为 "除用户取消外全部重试" 的乐观策略。
更务实的做法是建立 "提供商无关的错误归一化层":
-
错误码标准化:无论底层是 OpenAI 的
rate_limit_exceeded、Anthropic 的overloaded_error还是 Azure 的 429 状态码,统一映射为LLM_RATE_LIMIT -
响应头透传:将
retry-after头标准化为retry_after字段,缺失时按指数退退计算 -
流式错误处理:流式响应中断(
ECONNRESET类错误)需记录已接收的 token 数,支持从断点续传或完整重试 -
熔断器状态共享:在 Agent 集群层面共享熔断器状态,避免单点故障时所有实例同时涌入重试
监控与可观测性清单
生产环境的错误处理需要可量化的指标:
- 错误率分桶:按 Category 和 Severity 分维度统计,建议
RATE_LIMIT错误率 < 1%,TIMEOUT< 0.5% - 重试成功率:监控重试是否真正解决问题,若某 Category 的重试成功率 < 30%,考虑调整策略或增加降级路径
- 延迟分布:记录从首次请求到最终成功 / 失败的 P50/P99 延迟
- 熔断器触发频率:频繁触发说明上游稳定性不足,需评估是否切换提供商或模型
结论
Unix errno 的设计经受住了时间考验,其核心思想 —— 简洁的错误码、清晰的故障域、调用者控制重试策略 —— 同样适用于 LLM Agent 的错误处理。通过建立 errno 风格到 AI 推理故障的语义映射,并配套结构化的重试参数,我们可以在异构模型提供商之间实现可互操作、可观测、可优雅降级的错误处理体系。
关键不在于构建完美的 30+ case 分类器,而在于建立足够简洁的抽象层,让 Agent 在故障面前具备自愈能力,同时为运维人员提供清晰的诊断信号。
参考来源
- Linux errno 标准定义与错误码表
- Michael Peña, "Error Handling in LLM Applications: A Comprehensive Guide", 2024
- Reddit r/LLMDevs 讨论:从细粒度错误分类器到简化重试策略的工程实践
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。