基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
AIHT
内容
精选全部 AI 动态热点榜AI 日报收藏
分类
模型行业编程产品技术观点开源
更多
Agent 接入
返回首页开发者导航
AIHT
精选 / 资讯详情
8.0
热点
AI SCORE
技术实践2026-10-31 06:00

Agent工具接口也要防重试与越权

dev.to · AI#Agent#接口设计#幂等性
Editor brief · 编辑速览

文章以Agent遇到502后重复退款为例,说明工具封装缺少幂等机制会直接造成业务事故。作者主张按公共API标准设计工具层,落实版本、超时、权限范围和限流,并按操作风险分类。

文章思维导图
Knowledge map
拖拽缩放
AI deep read

AI 深度解读

一个 Agent 在测试环境里跑了六周,上线第二天却把同一笔退款执行了三次。原因并不玄乎:支付 API 返回 502,模型决定重试,工具封装层照单转发,却没有幂等保护。System Prompt 里那句「执行破坏性操作前先确认」,没能阻止事故。

作者借此提出一个判断:把模型当成 API 调用方,把工具层当成公共 API 来设计。对程序员来说,这篇值得读的地方,是把 Agent 的风险拆成了熟悉的工程问题:重复请求、执行预算、超时取消、权限范围,以及模拟环境与生产环境的偏差。

工具怎么分组,决定了运行时能拦住什么

作者最初的 Agent 有 11 个工具。工具数量并非事故关键,更关键的是工具注册表有没有表达风险。

常见的 stripe.*、github.*、s3.* 按供应商分组,只能告诉你调用谁,无法说明调用后会发生什么。作者建议给每个工具增加 side_effect,分成三类:

  • read 不改变外部状态,可以重试、并行或循环执行。
  • write 创建或修改状态,需要通过幂等键避免重复执行。
  • destructive 包括删除、撤销、向人发送消息、转移资金,需要幂等键和模型之外的审批关口。

这里的「破坏性」比删除文件更宽。邮件发出后,即使数据库可以回滚,对方也已经收到;退款执行后,也不能靠撤销一次函数调用恢复现场。

风险分类只有进入运行时的执行规则,才算真正生效。

作者主张,只有 read 可以放宽超时和重试限制,destructive 则必须经过独立审批,或者仅在 dry-run 模拟环境中运行。分类无法确定的工具,暂时就不该暴露给模型。

幂等键要由程序生成,不能让模型自由发挥

不要靠提示词要求模型「别重试」。输出被截断、JSON 解析失败、遇到 429,甚至升级模型,都可能改变它的重试行为。作者建议按「每次工具调用至少会发生两次」来设计。

幂等解决的是:同一项操作即使收到多次请求,也只产生一次效果。作者用 run_id、工具名和参数生成稳定键,让下游 API 负责去重。

下面这段代码避免参数字段顺序改变去重结果:

def _stable_key(run_id: str, name: str, args: dict) -> str:
    # Sorted JSON so dict ordering from the model can't change the key.
    blob = json.dumps(args, sort_keys=True, separators=(",", ":"))
    return hashlib.sha256(f"{run_id}:{name}:{blob}".encode()).hexdigest()

重点是 sort_keys=True。模型先输出 {"a":1,"b":2},再输出 {"b":2,"a":1},两次生成的键仍然相同。使用 run_id 而非时间戳,则让同一轮重试复用键,新一轮请求获得新键。

调用入口负责扣预算和注入幂等键:

def call(run_id: str, name: str, args: dict, budget: Budget) -> dict:
    budget.charge()
    spec = REGISTRY[name]
    if spec.side_effect != "read" and "idempotency_key" not in args:
        args = {**args, "idempotency_key": _stable_key(run_id, name, args)}
    # Production: run in a worker with spec.timeout_s and kill it on overrun.
    return spec.fn(**args)

要盯住包含 "idempotency_key" not in args 的那一行:它只在参数缺少键时补齐,实际上仍接受调用方传入的键。这与作者后文「绝不能让模型提供幂等键」的要求存在缺口。照搬时,应从模型可填写的参数中移除该字段,并由运行时统一生成。

这套方案还有一个前提:下游确实支持去重。示例中的 refund.create 把键传给 payments.refund;哈希函数自身不能阻止重复退款。

预算、超时和 dry-run,必须真的约束执行

Token 用量容易看到,但便宜的工具调用也可能产生昂贵后果。一次只花 80ms、消耗零 Token 的动作,仍然可能发送客户邮件、创建法律工单,或者启动每小时 4 美元的 VM。

作者提出三种独立预算:每轮工具调用次数、每轮实际耗时、每轮修改状态或破坏性动作次数。他给交互式 Agent 默认设置 20 次调用,批处理设为 100 次;破坏性工具的独立上限通常更小,往往只有 1 次。代码中的 Budget 默认是 20 次、120 秒,并在每次调用前检查。

超限后应抛出 RuntimeError("tool budget exhausted"),让运行时终止后续工具调度,并允许模型收尾。只记日志没有约束力;直接杀掉生成进程,又可能丢掉排障所需的轨迹。重试也必须逐次扣费,不能把多次派发算成一次。

超时同样不能只停在配置上。原文 ToolSpec 默认 timeout_s=5.0,退款工具设为 8.0,但示例没有实现超时执行器。作者提醒,asyncio.wait_for 用于 HTTP 调用可以成立,线程里的 subprocess.run 却可能在停止等待后继续运行,需要终止底层进程组。

dry-run 则应返回与真实调用相同结构的结果,同时不产生实际副作用。它可以便宜到合成响应,也可能贵到维护影子数据库。作者更倾向通过凭证和 sandbox endpoint 切换环境,减少 if dry_run: 提前返回的分支;否则六个月后,真实路径新增的校验可能没有同步进模拟路径。

还有两个容易引发连锁反应的入口:把 40KB JSON 硬截成 2KB,可能制造格式错误并诱发重试,应返回摘要和完整结果的句柄;网页、工单正文等外部文本可能包含提示注入,应隔离标记或清理,并配合每轮短期、最小权限凭证限制影响范围。

我的判断

我认同作者把责任放回工具运行时。模型行为会变化,而去重、预算和权限都应提供可验证的边界。

但这篇提供的是一次事故复盘和工程经验,没有给出引入这些措施前后的故障率,也没有对照实验。20 次、100 次、120 秒和破坏性动作 1 次,都只能作为配置起点。

原文自身也有需要补齐的地方。分类把转移资金列为 destructive,退款示例却标成 write,没有解释依据;代码只实现总调用数和截止时间检查,没有实现独立的破坏性动作预算。

run_id + 参数 也不等于业务操作身份。模型改了参数,或者用户因网络问题开启新一轮请求,都会产生新键。涉及资金时,是否还需要订单或退款业务层面的唯一约束,必须另外设计。

幂等负责防重复,审批和权限负责判断能不能做,三者不能互相替代。

同理,给外部文本加边界标记并不能保证消除提示注入。作者提出的这些措施适合作为多层约束,不能被理解成单项配置就能解决问题。

能拿来做什么

  • 给现有 AI 编程工具逐项补上 side_effect,区分读取文件、修改文件、删除资源和对外发送消息,并明确哪些动作需要独立审批。
  • 在 Node 工具调度入口集中实现预算检查、逐次计数和幂等键注入,让所有重试都经过同一个入口。
  • 检查长输出和超时路径:大结果返回摘要与句柄,启动子进程的工具要验证超时后底层进程是否真的退出。
  • 选一个会修改外部状态的工具接入 sandbox endpoint,用相同参数和返回结构验证流程,再检查短期凭证是否只覆盖必需的接口。
Original source

本文由 AI 通读原文后重新编写,观点部分为站内判断,细节以原文为准,来源 dev.to · AI,原文版权归原作者所有。

阅读原文
上一篇
电商搜索评测如何避免标签与裁判偏差
下一篇
用JSON Schema守住LLM自动操作边界

资讯速览

dev.to · AI · 2026-10-31 06:00

AI 评分 8.0 / 10

相关标签

#Agent#接口设计#幂等性

继续阅读

  • 用五阶段流程管住Bedrock模型开支
  • 电商搜索评测如何避免标签与裁判偏差
  • 用JSON Schema守住LLM自动操作边界
  • 开源实验把 NES 处理器模拟写进模型权重
  • 可靠 Agent 如何处理未知信息