Skip to content

fix(tools): 同源终态失败后不再重发等价请求 - #301

Closed
ruanzhengxin-zhuxing wants to merge 1 commit into
mainfrom
harness/d4c200b6
Closed

fix(tools): 同源终态失败后不再重发等价请求#301
ruanzhengxin-zhuxing wants to merge 1 commit into
mainfrom
harness/d4c200b6

Conversation

@ruanzhengxin-zhuxing

Copy link
Copy Markdown
Collaborator

背景

证据会话 2916da860b314205badbbda2356834d9(AnalysisId sarpt-auto-2026072918)中,ROS 的 ListStackEvents / ListStackResources / GetStack 三次调用都返回 HTTP 400(资源类型 ALIYUN::Milvus::Instance 无法识别)。Agent 把错误文本当成诊断结论,既没有切换查询路径,也反复重发等价请求。

传输层重试策略只覆盖 {429, 502, 503, 504},400 从未被重试,所以重复请求来自 Agent 主循环:ToolExecutor 没有"等价请求已终态失败"的记忆,云侧 4xx 也没有和可重试错误区分,公共错误文案还在提示 "before retrying"。

改动

  1. 新增 src/iac_code/tools/failure_recovery.py

    • terminal_failure_signature(status, code) 生成稳定签名(如 http_400:ResourceTypeNotFound
    • mark_terminal_failure / take_terminal_failure:内部元数据标记 _iac_code_terminal_failure,仿照既有 TOOL_RENDER_METADATA_KEY 约定,在 executor 内被摘除,不会泄漏到事件流或前端
    • TerminalFailureLedger:以 (tool_name, canonical_input_sha256) 为键的有界 LRU 记忆(128 条)
  2. tools/tool_executor.py:执行前查账本,命中则直接返回明确错误(提示先定位根因、再换路径或给出结论),不再真正调用工具;执行后消费并记录终态失败标记。等价性复用既有的 canonical_input_sha256

  3. tools/cloud/aliyun/aliyun_api.py:非 2xx 时计算终态签名。408/429 依赖时序而非请求形状,被显式排除,保持可重复。

  4. tools/cloud/aliyun/public_errors.py:请求被拒绝的 4xx 文案改为"等价请求会再次被拒绝,请修正请求或切换不依赖该输入的 API 路径",其余状态维持原有可重试提示。

  5. agent/system_prompt.py:工具约定新增一条——错误文本是症状不是诊断,禁止重发等价请求,先定位根因,再换路径或给出显式结论。

  6. 6 个 locale 的 messages.po 补齐 3 条新 msgid 的实际译文。

测试

  • 新增 tests/tools/test_failure_recovery.py(7 例):签名格式、元数据标记/摘除、账本按 (tool, input) 隔离、LRU 淘汰
  • tests/tools/test_tool_executor.py:新增 TestTerminalFailureRecovery(5 例)——等价请求被拒且不再调用工具、内部标记不外泄、不同入参仍执行、非终态失败可重复、无 invocation_binding 时不记账
  • tests/tools/cloud/aliyun/test_public_errors.pytest_aliyun_api.pytests/agent/test_system_prompt.py 同步覆盖新文案与元数据
  • 全量:14360 个测试,14346 通过,12 skipped,14 failed;14 个失败全部已核验与本次改动无关(zh 目录 {s:.0} msgfmt 问题在 baseline 即存在;3 个用 git stash 验证为 pre-existing;其余 10 个由全量跑自身删除仓库 symlink / 改写 __release_date__ 导致,恢复后重跑 401 通过)
  • ruff checkruff format --checkty check 全部通过

An Alibaba Cloud 4xx rejects the request shape itself, so an identical
request fails identically. Transport retry already excludes those statuses,
so the repetition observed in the field (three ROS calls returning HTTP 400
for an unrecognized resource type) came from the agent loop re-issuing
equivalent tool calls while the error text was mistaken for a diagnosis.

Remember terminal failures per (tool name, canonical input) in the executor
and refuse an equivalent call locally instead of hitting the cloud again.
aliyun_api marks 4xx results as terminal (408/429 excluded, since those are
timing dependent and stay repeatable), the public 4xx message now asks to
fix the request or switch to a path that does not depend on the rejected
input, and the tools prompt convention forbids resending equivalent
requests and requires either a path switch or an explicit conclusion.
@guima-why guima-why closed this Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants