Appearance
功能规格说明:AI 错误处理
创建日期:2026-03-03
用户场景与测试 (必填)
用户故事:截断时自动继续(优先级:P1)
作为用户,我希望代理在响应被输出 token 限制截断时自动继续其响应,这样我不必手动提示它完成工作。继续提示应该从 UI 中隐藏以保持对话整洁。
为什么是这个优先级:这是请求的核心功能。它通过使代理更加自主和减少手动干预来改善用户体验。
独立测试:可以通过模拟 finish_reason: "length" 的 AI 响应并验证代理添加隐藏继续消息并递归来测试。
验收场景:
- 假设AI 响应因长度限制被截断,当代理处理响应时,则它应该添加隐藏的用户消息(带
isMeta: true):"Output token limit hit. Resume directly — no apology, no recap of what you were doing. Pick up mid-thought if that is where the cut happened. Break remaining work into smaller pieces."并自动发起新的 AI 调用
用户故事:带工具调用的截断(优先级:P1)
作为用户,我希望代理在响应被输出 token 限制截断时执行工具然后自动继续其响应,即使调用了工具也是如此。
验收场景:
- 假设AI 响应因长度限制被截断且调用了工具,当代理处理响应时,则它应该仍然添加隐藏的恢复消息(带
isMeta: true)并在工具执行后自动发起新的 AI 调用
用户故事:连续截断恢复上限(优先级:P1,对齐 Claude Code)
作为用户,我希望代理在响应连续被输出 token 限制截断且没有任何工具产出时停止自动续写,这样无产出的规划循环不会无限持续直到平台超时强制终止。
为什么是这个优先级:无上限的自动续写会让模型在高复杂度任务中反复重新规划(纯 Reasoning 段、无工具调用),直到 Harbor 等平台的 Agent timeout 强制杀死任务。对齐 Claude Code 的 MAX_OUTPUT_TOKENS_RECOVERY_LIMIT 语义:连续恢复上限 3 次,工具调用重置计数,达到上限后终止回合并透传错误。
独立测试:可以连续模拟 finish_reason: "length" 的 AI 响应(中间无工具调用),验证前 3 次添加隐藏恢复消息并递归、第 4 次不再添加恢复消息并终止回合;以及模拟"截断 → 工具调用 → 再截断"验证计数重置。
验收场景:
- 假设AI 响应连续因输出 token 限制被截断且中间没有任何工具调用,当连续截断恢复次数达到 3 次(即第 4 次截断发生)时,则代理不应再添加隐藏恢复消息,而应停止自动续写、终止回合并显示一条错误消息,说明响应重复超过输出 token 上限
- 假设AI 响应因长度限制被截断,恢复后调用了工具,当后续轮次再次因长度限制被截断时,则连续截断计数应从 1 重新开始(工具调用重置计数),仍然添加隐藏恢复消息并自动续写
- 假设AI 响应因长度限制被截断且同时包含工具调用,当代理处理响应时,则它仍应执行工具并自动继续(现有行为不变,该轮不消耗连续截断计数)
用户故事:截断恢复时保留推理内容(优先级:P1,对齐 Claude Code)
作为用户,我希望代理在响应被截断后自动续写时能看到自己上一轮的推理内容,这样续写是接着之前的思考继续,而不是从零重新推导。
为什么是这个优先级:当前纯 Reasoning 段(无文本、无工具调用)的 assistant 消息在转换为 API 消息时被过滤,模型每轮续写都看不到自己上一轮的推理,只能重新推导出同量级的 reasoning 再次被截断——这是无限续写循环的放大器之一。Claude Code 要求 thinking 块在整个 assistant trajectory 内保留("Thinking blocks must be preserved for the duration of an assistant trajectory"),其续写是接着上一轮思考继续的。
独立测试:可以构造"user 任务 → assistant 纯 reasoning(截断)→ user 隐藏恢复消息"的消息序列,验证转换后的 API 消息包含 assistant 消息:推理内容保留在 reasoning_content 字段,content 为说明思考未完成、由系统自动保留的提示文案。
验收场景:
- 假设AI 响应因输出 token 限制被截断,且该轮只有 reasoning 内容(无文本、无工具调用),当代理发起下一轮续写调用时,则转换后的 API 消息应包含该 assistant 消息,推理内容保留在
reasoning_content字段(推理模型原生续接通道),content为一段说明文案——OpenAI 兼容上游拒绝无content/tool_calls的 assistant 消息(仅带reasoning_content会触发 400 "content or tool_calls must be set"),说明文案同时告知模型思考未完成、由系统自动保留,可续接或忽略——模型仍能看到自己上一轮的全部推理 - 假设AI 响应因长度限制被截断但该轮包含文本内容,当代理发起下一轮续写调用时,则
reasoning_content照常随 assistant 消息一起发送(现有行为不变) - 假设某 assistant 轮次既无文本、无工具调用也无 reasoning 内容,当转换 API 消息时,则该消息继续被过滤,不产生空的 assistant 消息
用户故事:速率限制弹性(优先级:P2)
作为用户,我希望代理能够应对临时 API 速率限制(429 错误),这样我的工作不会被瞬态网络或 API 问题中断。重试策略应尊重服务器指示的等待时间,避免在限流窗口内无效重试。
验收场景:
- 假设AI API 返回 429 错误且响应包含
Retry-After头,当代理发出请求时,则它应等待Retry-After头指定的秒数后再重试,而非使用指数退避延迟 - 假设AI API 返回 429 错误且响应不包含
Retry-After头,当代理发出请求时,则它应该使用指数退避(500ms 基准、2 的幂次、25% 随机抖动、上限 32 秒)自动重试请求(最多 10 次重试) - 假设AI API 返回 5xx 服务器错误,当代理发出请求时,则它应该使用与 429 相同的重试策略自动重试(最多 10 次重试)
用户故事:调试 API 错误(优先级:P3)
作为开发者,我希望系统在发生 400 Bad Request 错误时保存调试信息,这样我可以轻松诊断格式错误请求或无效参数的问题。
验收场景:
- 假设AI API 返回 400 错误,当代理发出请求时,则它应该将原始消息、模型配置和错误详情保存到临时目录以供调试
用户故事:处理格式错误的工具参数(优先级:P2)
作为用户,我希望代理处理 AI 为工具参数提供格式错误 JSON 的情况,特别是当响应被截断时,这样我能得到关于出了什么问题的清晰说明。
验收场景:
- 假设AI 为工具参数提供了格式错误的 JSON 且响应被截断(
finish_reason: "length"),当代理尝试解析参数时,则它应该显示包含以下内容的错误消息:"(output truncated, please reduce your output)"
用户故事:恢复截断的工具参数(优先级:P2)
作为用户,我希望代理尝试恢复截断的工具参数(如缺少闭合大括号的 Write/Edit),这样工具仍然可以执行而不是直接失败。
为什么是这个优先级:当 finish_reason 为 "length" 时,截断的工具参数很常见。恢复避免了不必要的失败和重试,提高了 Write/Edit 操作的可靠性。
独立测试:可以通过提供截断 JSON(缺少闭合大括号)的工具调用并验证 recoverTruncatedJson() 闭合它们、工具执行且警告被追加来测试。
验收场景:
- 假设AI 提供的工具参数包含截断的 JSON(缺少闭合
}大括号或"字符串),当代理尝试解析参数时,则它应该尝试通过recoverTruncatedJson()恢复,闭合未闭合的字符串和大括号,如果恢复的 JSON 解析成功,则执行工具并在结果中追加截断警告 - 假设AI 提供的工具参数包含截断的 JSON 且有未闭合的
[方括号,当代理尝试解析参数时,则不应尝试恢复(无法猜测数组内容),工具应该失败并显示标准错误消息 - 假设恢复的 JSON 在恢复后仍然无法解析,当代理尝试解析恢复的参数时,则它应该回退到错误消息(如果
finish_reason === "length"则带截断提示)
边界情况
- 无限递归:如果 AI 不断被截断会发生什么?
- 对齐 Claude Code:连续截断恢复上限为 3 次(中间无工具调用则计数不重置),达到上限后终止回合并显示错误消息,不再无限续写;工具调用(正常进度)会重置计数
- 截断恢复上下文:纯 reasoning 截断轮(无文本、无工具调用)的推理内容是否应该保留到下一轮?
- 对齐 Claude Code:保留。推理内容放在
reasoning_content原生字段(推理模型天然从中续接),content携带说明文案(思考未完成、系统自动保留,可续接或忽略)以满足 OpenAI 兼容上游"assistant 消息必须有content或tool_calls"的校验(仅带reasoning_content会被剥离并触发 400 "content or tool_calls must be set");有文本的轮次reasoning_content照常随行、content用真实文本;既无文本、无工具调用也无 reasoning 的空消息仍被过滤
- 对齐 Claude Code:保留。推理内容放在
- 中断:如果用户在截断响应期间中止会话会发生什么?
- 假设:代理应该尊重中止信号并停止递归
- 后台工具:如果工具在截断响应期间被放到后台会发生什么?
- 假设:代理应该停止递归,就像正常工具调用一样