跳到主内容
创建时间:最后更新:作者:白川

定位失败、重试与取消发生的位置

用阶段与结构化证据区分故障,避免把所有异常归因模型或盲目重试。

定位失败、重试与取消发生的位置

同一条“任务失败”提示,可能来自鉴权拒绝、网络连接、响应解析、工具业务或界面状态。先按发生边界分类,才能选择下一项检查,而不是给所有失败加一次重试。

错误分类描述当前证据,并不宣称已经找到根因。例如工具内请求返回 401,工具层失败是事实,凭据是否配置错误还需要沿工具子调用继续查证。

本系列的独立示例位于仓库 columns/coding-agent/examples/debugging-observability/,固定使用 pi-agent-core、pi-ai、pi-schema、pi-telemetry 1.5.0。先在该目录执行 npm install,下文命令也从该目录运行。示例不需要模型密钥,不读取个人配置。

用结构化证据分类

src/failure.ts 接受当前阶段、状态码、错误码和取消状态,不解析任意报错文本来做权限或重试决定。

ts
export type FailureKind =
  | "cancelled"
  | "auth"
  | "rate-limit"
  | "transport"
  | "tool"
  | "decode"
  | "resource"
  | "unknown"
export interface FailureEvidence {
  aborted?: boolean
  status?: number
  code?: string
  stage?: "request" | "decode" | "tool" | "resource"
}

// 这是教学分类规则,不猜测任意错误文本,不自动决定重试或权限。
export function classify(evidence: FailureEvidence): FailureKind {
  if (evidence.aborted) {
    return "cancelled"
  }
  if (evidence.stage === "tool") {
    return "tool"
  }
  if (evidence.stage === "resource") {
    return "resource"
  }
  if (evidence.status === 401 || evidence.status === 403) {
    return "auth"
  }
  if (evidence.status === 429) {
    return "rate-limit"
  }
  if (evidence.stage === "decode") {
    return "decode"
  }
  if (
    evidence.stage === "request" &&
    ["ENOTFOUND", "ECONNRESET", "ETIMEDOUT"].includes(evidence.code ?? "")
  ) {
    return "transport"
  }
  return "unknown"
}

阶段优先级是教学规则:明确处在工具内部时先归工具,再沿子调用追踪;主动取消与普通失败分开。无法判断就保留 unknown,不把所有 5xx、超时或字符串“network”自动归为某个根因。

错误链可能还包含嵌套 cause,生产采集应以深度和字段白名单读取,保留可用结构码,避免默认导出完整 stack、请求体或服务端回显。助手消息 diagnostics、资源 diagnostic 和工具 isError 也不是同一种结构,应先在各自边界提取。

把重试当作多次尝试观察

情况需要的证据下一步
401/403
请求阶段、服务和凭据来源
检查配置与访问权限,避免无条件重复
429
Provider 状态和退避提示
看允许的等待预算、次数和并发
ENOTFOUND/连接中断
有效错误码、阶段、端点摘要
区分 DNS、代理和链路,保留 cause
输出解析失败
原始响应边界与解析诊断
用受控样本复现协议差异
工具业务错误
调用 ID、参数形状与结果
判断是否修输入或停止
资源加载冲突
winner/loser 与资源来源
检查同名扩展、工具或命令

一项任务重试三次,通常需要一个逻辑任务身份与三个尝试身份。统计任务失败率时,不能把成功前两次失败当成三个失败任务;统计请求成本时,也不能只看最终成功的一次。两种口径服务不同问题。

结束事件为什么容易误判

coding-agent 的一次底层 agent_end 后可能还有自动重试、压缩恢复、steering 或 follow-up。需要判断会话是否收束,应观察 agent_settled,并结合其中的取消信息。

底层 Agent 示例没有这些完整宿主流程,lesson_run_end 只表示等待 prompt 返回。后续把示例接入 coding-agent 时,应适配真实事件,不能在看见 agent_end 时自己伪造一个 settled。

取消并不撤销已经发生的事

AbortSignal 表达取消意图,真正的文件、网络、子进程实现必须处理它。工具在取消前可能已经写文件或请求服务,进程结束也不意味着副作用已回滚。

诊断记录应区分“用户发出取消”“底层确认终止”“外部效果已核对”。不要把取消记为普通模型错误,也不要向用户显示未经确认的“已恢复原状”。

把分类结果变成可执行的排查顺序

先确认任务与尝试身份,再找最后一个有证据的成功边界,检查下一个边界的输入、输出或缺失事件。错误只说明某一层没完成时,继续向下取证,而不是给它加一个更宽泛的标签。

运行 npm test 会覆盖取消、鉴权、限流、网络结构码、解析、资源和未知分支。它验证的是分类规则,不验证真实 DNS、Provider 重试或宿主压缩恢复;这些需要专门的集成场景。

依据与版本

本地官方快照为 8efd314fa,相关包标记 1.3.0;配套代码另按已发布的 1.5.0 类型和运行行为核验。主分支文档持续变化,不能混作同一版本。