跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
编程语言Agent RuntimeIdempotencyTool RuntimeCheckpointRecovery

《Agent Runtime 工程化》为什么 Tool 必须支持 Idempotency?

为什么 Tool 必须支持 Idempotency? 上一篇讲 retry 时,我留了一个坑: 今天把这个坑填上。 Agent Runtime 里,工具是否幂等,不是一个可有可无的注释。它决定三件事: 如果工具没有幂等语义,Agent 一旦

陈堂会发布于 —2 浏览
《Agent Runtime 工程化》为什么 Tool 必须支持 Idempotency?

为什么 Tool 必须支持 Idempotency?

上一篇讲 retry 时,我留了一个坑:

不是所有 Tool Call 都能重试。

今天把这个坑填上。

Agent Runtime 里,工具是否幂等,不是一个可有可无的注释。它决定三件事:

能不能 retry
能不能 resume
能不能 replay

如果工具没有幂等语义,Agent 一旦遇到超时、崩溃、断网、用户刷新页面,就会陷入一种尴尬状态:不知道该继续,还是该停;不知道上一步有没有发生副作用;不知道重放会不会把事故扩大。

这不是理论问题。

重复下单、重复扣款、重复发邮件、重复创建 issue、重复提交 PR,都是这个问题的变体。

幂等到底是什么意思

HTTP RFC 9110 里对幂等方法有一个经典定义:同一个请求执行一次或多次,预期效果相同。

注意,不是'没有副作用'。

DELETE 可以是幂等的。第一次删除资源,第二次还是删除同一个资源,最终状态都是资源不存在。

POST 通常不天然幂等。你创建订单两次,世界上就多了两张订单。

Stripe 的 idempotent requests 文档讲得很实际:客户端为创建或更新请求生成一个 idempotency key,网络错误后用同一个 key 重试,服务端返回第一次请求的结果,而不是重复执行。AWS Builders Library 也强调,安全重试的核心是服务端能识别客户端意图:这是同一个业务动作,还是用户真的发起了第二个动作。

Agent Tool 也一样。

模型不懂这些业务语义。Runtime 必须懂。

ToolDefinition 里要声明幂等性

工具定义至少要多几个字段:

type Idempotency = "idempotent" | "conditional" | "non_idempotent";
type ReplayPolicy = "auto" | "ask" | "never";

type ToolDefinition = {
  name: string;
  riskLevel: "read" | "write" | "network" | "install" | "destructive";
  idempotency: Idempotency;
  replayPolicy: ReplayPolicy;
  sideEffects: string[];
};

这不是文档装饰。

Runtime 会用它决定:

  • 工具超时后能不能 retry。
  • 进程崩溃后能不能 replay。
  • checkpoint 恢复后是否需要用户确认。
  • trace 里如何标记风险。
  • eval 里是否要检查'禁止重复执行'。

如果一个工具没有写 idempotency,默认应该按 non_idempotent 处理。

保守一点没坏处。

三类工具,三种策略

第一类:天然幂等。

read_file
list_files
search_code
get_order_status
pure_compute

这些工具重复执行,通常不会改变外部状态。它们可以 replayPolicy: "auto"。

第二类:条件幂等。

write_file
apply_patch
update_config
run_command("npm test")

它们是否能重放,要看条件。

写文件要检查 base hash。应用 patch 要检查 patch 是否已经应用。npm test 理论上是只读,但测试脚本可能写 coverage、cache、snapshot。命令工具尤其不能只看工具名。

第三类:非幂等。

send_email
create_order
charge_customer
create_issue
merge_pull_request
npm publish
run_migration

这些默认不能自动重放。要么需要业务级 idempotency key,要么需要人工确认,要么只能走补偿动作。

命令工具最容易骗人

很多 Agent 项目会有一个 run_command。

这工具不能简单标成幂等或非幂等。

run_command("ls src")              幂等
run_command("npm test")            条件幂等
run_command("npm install")         有副作用
run_command("npm publish")         非幂等
run_command("rm -rf dist")         破坏性
run_command("prisma migrate deploy") 非幂等

所以 run_command 的幂等性要在计划阶段按参数分类。

function classifyCommandIdempotency(command: string): {
  idempotency: Idempotency;
  replayPolicy: ReplayPolicy;
  reason: string;
} {
  if (/^(pwd|ls|find|rg|cat|sed|wc)\b/.test(command)) {
    return {
      idempotency: "idempotent",
      replayPolicy: "auto",
      reason: "只读命令",
    };
  }

  if (/^npm test\b|^pnpm test\b|^yarn test\b/.test(command)) {
    return {
      idempotency: "conditional",
      replayPolicy: "ask",
      reason: "测试命令可能写 coverage、cache 或 snapshot",
    };
  }

  return {
    idempotency: "non_idempotent",
    replayPolicy: "never",
    reason: "命令可能产生不可恢复副作用",
  };
}

不要把 shell 当普通函数。

shell 是副作用入口。

Tool Ledger 要记录幂等性

工具执行前就要记账。

type ToolLedgerEntry = {
  callId: string;
  stepId: string;
  toolName: string;
  inputHash: string;
  status:
    | "planned"
    | "approved"
    | "started"
    | "succeeded"
    | "failed"
    | "uncertain";
  idempotency: "idempotent" | "conditional" | "non_idempotent";
  replayPolicy: "auto" | "ask" | "never";
  idempotencyKey?: string;
  sideEffects: string[];
  startedAt?: string;
  finishedAt?: string;
};

最重要的是 started 和 uncertain。

如果工具请求已经发出,但响应没回来,状态不能写成普通失败。应该写:

status = uncertain

恢复时看到 uncertain,再结合 idempotency 判断:

idempotent + auto:可以重放
conditional + ask:先检查条件或问用户
non_idempotent + never:禁止自动重放

没有 ledger,就没有恢复。

Idempotency Key 不能交给模型自由发挥

创建订单、扣款、创建 issue 这类工具,往往需要 idempotency key。

这个 key 不应该由模型随便编。

模型可以说明业务意图,但 key 的生成应该在 runtime 或业务服务里完成。

function buildIdempotencyKey(input: {
  userId: string;
  runId: string;
  toolName: string;
  businessObjectId: string;
}) {
  return [
    "agent",
    input.userId,
    input.runId,
    input.toolName,
    input.businessObjectId,
  ].join(":");
}

这里有取舍。

如果 key 绑定 runId,同一个 run 内 retry 安全,但跨 run 不会被当作同一业务动作。

如果 key 绑定用户和购物车,用户刷新后仍能恢复同一个下单动作,但要小心用户真的想再下一单。

幂等 key 是业务设计,不是随机字符串。

条件幂等要有检查函数

写文件工具可以设计成条件幂等。

执行前记录 base hash:

type FileWritePlan = {
  path: string;
  beforeHash: string;
  afterHash: string;
  patchRef: string;
};

恢复时检查:

async function canReplayFileWrite(plan: FileWritePlan) {
  const currentHash = await hashFile(plan.path);

  if (currentHash === plan.afterHash) {
    return { ok: true, action: "already_applied" as const };
  }

  if (currentHash === plan.beforeHash) {
    return { ok: true, action: "apply_again" as const };
  }

  return {
    ok: false,
    action: "conflict" as const,
    reason: "文件已被用户或其他工具修改",
  };
}

这才叫条件幂等。

没有 base hash 和 after hash,你只能靠感觉判断能不能重放。

MCP 工具也要重新分类

MCP Server 暴露的工具描述里可能写着 safe、read only、search。

Runtime 不能直接相信。

外部工具进入本地 Tool Registry 时,要重新分类:

function classifyMcpTool(tool: {
  name: string;
  description?: string;
  inputSchema: unknown;
}) {
  const text = `${tool.name} ${tool.description ?? ""}`.toLowerCase();

  if (/read|list|search|get/.test(text)) {
    return { idempotency: "idempotent", replayPolicy: "auto" };
  }

  if (/create|send|charge|delete|publish|merge|write/.test(text)) {
    return { idempotency: "non_idempotent", replayPolicy: "never" };
  }

  return { idempotency: "conditional", replayPolicy: "ask" };
}

真实实现还要看 server trust level、resource scope、用户授权、组织策略。

协议让工具更容易接入,不代表工具更安全。

给模型看的错误要明确

非幂等工具超时,不能只返回:

{ "error": "timeout" }

应该返回:

{
  "ok": false,
  "code": "SIDE_EFFECT_UNCERTAIN",
  "content": "create_issue 请求已发出,但响应超时。该工具可能已经创建 issue,不能自动重试。请先调用 get_issue_by_idempotency_key 或让用户确认。",
  "metadata": {
    "retryable": false,
    "idempotency": "non_idempotent"
  }
}

模型看到这条 observation,才知道下一步要查状态,不是继续创建。

Eval 要覆盖幂等性

幂等性不是写完类型就完事。

Eval 里要测:

read_file timeout 后允许 retry。
create_order timeout 后禁止 retry,返回 SIDE_EFFECT_UNCERTAIN。
write_file base hash 一致时可 replay。
write_file 文件已变更时进入 conflict。
PERMISSION_DENIED 后不可重放。
MCP create_issue 工具默认 non_idempotent。
run_command("ls") 可自动 replay。
run_command("npm publish") 禁止 replay。

这些 case 最好用 mock tool 做。不要拿真实支付、真实邮件、真实 issue tracker 测。

Runtime 的危险行为要能在本地复现。

最后

Agent 能不能恢复,很多时候取决于工具能不能安全重放。

能安全重放,就可以 retry、resume、replay。

不能安全重放,就要查状态、问用户、走补偿,或者直接停。

幂等性不是后端老概念的复读。到了 Agent Runtime 里,它变成工具系统的基础字段。

每个工具都要回答:

重复执行一次,会发生什么?

答不上来,就不要自动重试。

参考资料

  • HTTP Semantics RFC 9110: Idempotent methods
  • Stripe Docs: Idempotent requests
  • AWS Builders Library: Making retries safe with idempotent APIs
  • Google Cloud Docs: Retry strategy
  • Model Context Protocol Docs: Tools

推荐阅读

  • 如何实现 Agent Checkpoint?
  • 从 Agent Loop 重构成 Agent Runtime
  • LangGraph 到底是不是 Agent Runtime?

目录

  1. 为什么 Tool 必须支持 Idempotency?
  2. 幂等到底是什么意思
  3. ToolDefinition 里要声明幂等性
  4. 三类工具,三种策略
  5. 命令工具最容易骗人
  6. Tool Ledger 要记录幂等性
  7. Idempotency Key 不能交给模型自由发挥
  8. 条件幂等要有检查函数
  9. MCP 工具也要重新分类
  10. 给模型看的错误要明确
  11. Eval 要覆盖幂等性
  12. 最后
  13. 参考资料
  14. 推荐阅读
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Python 核心语法详解:测试脚本开发基础
  • 在 Linux 桌面里跑 Windows 应用:Winboat 实战笔记
  • AI 编程工具深度对比:Trae、Cursor、Copilot 与 Windsurf
  • 论文阅读--Agent AI 探索多模态交互的前沿领域(一)
  • OpenCode 开源 AI 编程助手使用教程
  • Linux diff 与 patch 命令实战指南
  • 网络安全工程师职业定义、核心技能与认证体系详解
  • Python 为何如此流行?深度解析其核心优势与应用场景
  • 大疆无人机如何导出日志并解析
  • 前端 WebSocket 通信实战与最佳实践
  • OpenClaw 进阶教程:记忆系统、定时任务、多模型与子代理解析
  • Python 爬虫实战指南:从基础请求到分布式框架
  • Kubernetes Python 客户端实战教程
  • Java IO 核心:BufferedReader、BufferedWriter、PrintStream 与 PrintWriter 详解
  • AI 辅助编程工具:GitHub Copilot 安装与使用指南
  • 基于 Rokid AR 眼镜的聚会游戏助手开发实践
  • 百瑞互联 BR8654A02 蓝牙 6.0 SOC 芯片规格介绍
  • Windows 7 安装 Python 3.9+ 配置指南
  • 前端加密:常用方式与使用示例
  • GitHub Copilot 学生认证教程(2026 版)

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online