前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
返回 AI 情报前线
All News · 全部资讯8611
  • 自托管Llama部署成本全解析:GPU之外容易被忽视的隐性开销
  • Builder.io开源Agent-Native框架:让人与AI共享同一操作层
  • 让AI编程代理在中断后从断点恢复:session化执行轨迹方案
  • CI如何验证AI代理真的跑了测试而不是伪造结果
  • AI产品定价数学:固定费率如何悄悄亏损
  • ZCode事件:AI编程工具静默上传全部Git历史
  • Anthropic自评:R&D自动化率26%,但完全自主为0
  • 阿里Qwen-Image-2.1:70亿参数开源图像生成拳打闭源模型
  • OWASP CRS 规则引擎:给 LLM 和 MCP 加上 WAF
  • 智谱 MaaS 上线数据不留存机制,可申请开通
  • 如何界定可完成的AI工程范围
  • Cursor中直接查询技术文档的MCP方案
  • AI Agent的过度自信陷阱
  • Cursor与.NET周报:七个实战规则
  • Qwen-Image-2.1开源:7B参数兼顾生图与编辑
  • 阿里开源 Qwen-Image-2.1:7B 参数兼顾透明图生成与多图编辑
  • 免费模型Trace不能支持的五个评估主张
  • AI重构前的副作用冻结术:先录 Ledger 再动格式
  • Agent能改Oracle则Green Build不足为信:独立检查三原则
  • AI编程导致代码质量下降?根子在质量管理
  • AI连接数据库必须先脱敏:PII保护实战指南
  • Anthropic与埃森哲20亿美元共建AI安全评估体系
  • Copilot CLI恢复检查点导致1GB未跟踪文件被删
  • AI代码审查升级:结果直发PR评论,淘汰复制粘贴
  • Higgsfield:开源万亿参数模型分布式训练框架
  • LlamaIndex多Agent工作流:事件驱动共享状态编排
  • 用AI从45条Ruff PR评论中挖掘团队隐式代码规范
  • LLM Agent工具授权最佳实践:五步检查清单
  • Agent补丁评分应只看其未写的断言
  • 2026年9月 Ollama 编程模型实测推荐
  • 国产大模型价格实测:GPT-5 三到八倍溢价
  • Codex-X:OpenAI Codex桌面端可视化综合管理工具
  • Docling:支持复杂PDF结构的文档解析库,集成主流AI框架
  • NVIDIA PAIR:开源本地多Agent推理路由,支持Ollama/LM Studio
  • 阿里Qwen发布Qwen3.8-LiveTranslate:60语言实时翻译,延迟仅2.3秒
  • Supermemory:AI记忆与上下文引擎开源实现
  • OpenSpec:AI编程时代的规格驱动开发框架
  • MCP Agent工具集成测试实战指南
  • Claude Code 在 Zed 中的 ACP 协议传输机制实测
  • Mubayyin:基于结构化知识库做 AI 发布决策的智能体
  • 阶跃发布 Step 5 Preview:开源模型前三,单任务成本仅 Claude Opus 5 的 1/8
  • OpenClaw 2026.9.5:原子更新、插件热重载、对话分享与GPT Live扩展
  • Jev:返回类型化校准决策的System One模型
  • 已加载 43 / 8611
8.0
热点
AI SCORE
技术实践2026-09-20 22:16

AI Agent的过度自信陷阱

dev.to · AI#AI Agent#可靠性#系统设计
Editor brief · 编辑速览

AI Agent执行API调用后可能过度承诺成功,导致用户获得错误结果,需关注状态问题而非仅优化prompt。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

我以为是 prompt 的问题。

其实是状态管理的问题。

我的客服 Agent 能读取 Zendesk 工单、找到 Shopify 订单、发起 Stripe 退款、更新工单、回复客户。在 staging 环境下表现很好。在生产环境中,它开始做一些比「答错」更糟糕的事。

它自信满满地告诉客户退款已完成,而实际上工作流只是触发了一个 API 调用然后听天由命。

这个区别很重要。

因为一旦 LLM 能够调用 Shopify、Stripe、Zendesk 这类真实系统,失败模式就变了。模型不需要凭空捏造一个工具名来害你。它只需要在一次有效的工具调用之后,过度陈述发生的事实。

核心 bug:有效的工具调用,无效的结论

很多团队在遇到「Agent 任务失败」时会立刻做以下其中一件事:

强制执行更严格的 JSON schema

有时候这确实有用。

但如果你的 Agent 已经能用正确的参数调用正确的 API,那 prompt 工程就不再是主要杠杆。

OpenAI 的 Structured Outputs 在这方面确实是真正的改进。他们的公开评测显示 gpt-4o-2024-08-06 在复杂 JSON schema 上达到了 100% 的 schema 遵从率,而 gpt-4-0613 低于 40%。

但 schema 遵从不等于操作层面的真实。

你可能输出了完美的 JSON,但仍然在做一个这样的客服工作流:

在资金实际流动之前就报告成功

重试时不保留确定性

与异步系统失去同步

1) Shopify:Refund 对象不是资金已流动的证明

这是我的第一个线索。

Agent 调用了 Shopify 的 refundCreate,拿到了 Refund 对象,然后告诉客户:

您的退款已处理。

看起来合理,对吧?

Shopify 的文档明确指出:Refund 对象的存在并不能保证金融交易已完成。实际结果存在于相关的 OrderTransaction 对象中,可能是 pending、processing、success 或 failure。

所以这个模式是有问题的:

告诉客户完成了

这不是确认。这是带着 JSON 的乐观主义。

mutation 本身不是问题

mutation RefundOrder @idempotent(key: "refund-order-123") {
  refundCreate(input: {
    orderId: "gid://shopify/Order/123",
    note: "Customer requested partial refund"
  }) {
    refund { id }
    userErrors { field message }
  }
}

Bug 发生在 mutation 之后。

你需要在面向客户的消息之前再检查一步交易状态。

async function refundInShopify(orderId: string) {
  const refund = await shopify.refundCreate({ orderId });

  if (refund.userErrors?.length) {
    return { status: "failed", reason: refund.userErrors };
  }

  const txns = await shopify.getOrderTransactions(orderId);
  const refundTxn = txns.find(t => t.kind === "refund");

  if (!refundTxn) {
    return { status: "unknown", reason: "No refund transaction found" };
  }

  switch (refundTxn.status) {
    case "success":
      return { status: "completed", refundId: refund.refund.id };
    case "pending":
    case "processing":
      return { status: "pending", refundId: refund.refund.id };
    case "failure":
      return { status: "failed", refundId: refund.refund.id };
    default:
      return { status: "unknown", refundId: refund.refund.id };
  }
}

就这么一个额外的验证步骤,就把客户消息从猜测变成了可辩护的陈述。

2) Stripe:超时把「重试」变成了可靠性 bug

Stripe 是这里变得危险的地方。

你的 Agent 发送了 POST /v1/refunds

响应从未被持久化

模型决定「再试一次」

现在你有了不确定性。

Stripe 创建退款了吗?失败了?还是成功了只是丢失了响应?

这正是 Stripe 幂等键存在的原因。

而太多 Agent 工作流仍然把幂等性当作可选项。

幂等性让你可以重试而不会破坏证据链。

curl https://api.stripe.com/v1/refunds \
  -u "sk_test_...:" \
  -H "Idempotency-Key: 8b5b9f2e-6f8d-4f3d-a6d8-2f0f4d7f9c21" \
  -d charge=ch_123

Stripe 会为给定的幂等键存储第一次结果,并在重试时返回相同的状态码和响应体,包括 500 错误。如果你用相同的键但不同的参数,Stripe 会拒绝。

这意味着你的工作流需要在调用之前持久化这个键,而不是之后。

async function badRefundRetry(chargeId: string) {
  try {
    return await stripe.refunds.create({ charge: chargeId });
  } catch {
    // terrible: new request identity, no certainty
    return await stripe.refunds.create({ charge: chargeId });
  }
}
import { randomUUID } from "crypto";

async function createRefundWithRecovery(chargeId: string, existingKey?: string) {
  const idempotencyKey = existingKey ?? randomUUID();

  await db.refundAttempts.upsert({
    chargeId,
    idempotencyKey,
    status: "started"
  });

  try {
    const refund = await stripe.refunds.create(
      { charge: chargeId },
      { idempotencyKey }
    );

    await db.refundAttempts.update({
      chargeId,
      idempotencyKey,
      status: "completed",
      refundId: refund.id,
      rawResponse: JSON.stringify(refund)
    });

    return refund;
  } catch (err) {
    await db.refundAttempts.update({
      chargeId,
      idempotencyKey,
      status: "unknown",
      error: String(err)
    });

    throw err;
  }
}

那个 unknown 状态很重要。

很多团队试图避免它,因为它看起来很混乱。但「unknown」是诚实的。告诉模型去猜不是。

3) Zendesk:异步任务和限速会让玩具级工作流很快露馅

Zendesk 是精心打磨的 demo 通常崩溃的地方。

限速不是建议

Zendesk Support 和 Help Center API 限制因计划不同而异。响应可能包含如下响应头:

X-Rate-Limit: 700
X-Rate-Limit-Remaining: 699

如果你触达限速,会收到 429 Too Many Requests 和 Retry-After。

一个因为想帮忙就持续猛击 API 的模型不是在帮忙。

它只是一个昂贵的循环。

有些动作是任务,不是即时完成

批量工单更新就是典型例子。

工作流发送更新、收到确认,然后就假设工单已更改。

但 Zendesk 任务状态可能处于:

如果你的 Agent 退了 40 个订单并批量更新了 40 个工单,你不能因为第一次调用返回 200 或 202 就假设工单端完成了。

你需要轮询任务 URL 并核对失败。

最小轮询示例

async function waitForZendeskJob(jobStatusUrl: string) {
  for (let attempt = 0; attempt < 20; attempt++) {
    const job = await zendesk.get(jobStatusUrl);

    switch (job.status) {
      case "completed":
        return job;
      case "failed":
        throw new Error(`Zendesk job failed: ${job.id}`);
      case "queued":
      case "working":
        await sleep(3000);
        continue;
      default:
        throw new Error(`Unknown Zendesk job state: ${job.status}`);
    }
  }

  throw new Error("Zendesk job did not reach terminal state in time");
}

这不是什么光鲜的工程。

然而,这就是靠谱的客服 Agent 和只是听起来靠谱的客服 Agent 之间的区别。

Prompt 修复 vs 编排修复

这是我希望早点分清的。

这就是为什么我认为很多「LLM 工具使用可靠性」的讨论格局太小了。

一旦你接触到资金、工单、订单或客户记录,你就不再是在调试一个聊天机器人了。

你在做分布式系统的工作。

LLM 只是链中的一个组件。

如果我必须把这条压缩成一条规则:

永远不要让模型从第一个有副作用的响应中传达成功。

这是速查表。

| 检查项 | Shopify 退款流程 | Stripe 退款流程 | | ---------- | ---------- | | 初始对象能保证资金流动吗? | 不能。单独的 Refund 对象不是退款已结算的证明 | 通常退款响应是主要记录,但重试必须保持幂等性以维持确定性 | | 需要调用后验证吗? | 需要。检查关联的 OrderTransaction 状态,如 pending、processing、success 或 failure | 需要,尤其是在超时或网络故障后;使用相同的幂等请求历史或后续检索来验证 | | 重试应该依赖什么? | 存储的工作流状态和显式验证逻辑 | 相同参数下的相同幂等键 |

最后那行是很多 Agent 工作流悄悄失败的地方。

不是因为 Shopify 或 Stripe 不稳定。

是因为工作流在重试时是无状态的。

你需要 LangGraph 吗,还是 n8n / Make 就够了?

我的看法:不是每个客服自动化都需要完整的 Agent 运行时。

如果流程大多是确定性的:

那么 n8n、Make,甚至带明确分支的 Zapier,可能比自主循环更安全。

人工 escalation 分支

如果工作流更开放、需要跨长时间窗口的可恢复性、局部失败恢复或分支调查,那么 LangGraph 开始更有意义。

Tiny LangGraph 骨架

from langgraph.graph import StateGraph, MessagesState, START, END

def mock_llm(state: MessagesState):
    return {"messages": [{"role": "ai", "content": "hello world"}]}

graph = StateGraph(MessagesState)
graph.add_node(mock_llm)
graph.add_edge(START, "mock_llm")
graph.add_edge("mock_llm", END)
graph = graph.compile()
graph.invoke({"messages": [{"role": "user", "content": "hi!"}]})

这个例子很 trivial。

重要的部分是心态转变:状态转换、检查点、可恢复性、确定性恢复。

不是「也许 GPT-5 或 Claude Opus 4.6 下次会更谨慎」。

最终阻止说谎的模式

真正解决这个问题的方法很无聊。

我把工作流分成了几个阶段:

用验证过的输入准备操作

用幂等键或等效的请求标识执行

立即持久化外部 ID 和原始响应

在 Shopify、Stripe 或 Zendesk 中验证下游状态

只从已验证状态进行沟通

将模糊或非终态的情况 escalation 给人工

这比所有 prompt 调优加起来对可靠性的提升都大。

如果你在生产环境中运行 AI 代理,成本压力会让情况更糟

还有一件事人们谈得不够多:按 token 计价会推动团队做出糟糕的可靠性决策。

当每次重试、轮询、验证步骤和恢复分支都像在计费时,人们开始砍掉那些无聊的部分。

他们跳过验证。缩短重试。避免持久化状态。让模型即兴发挥,因为看起来当下更便宜。

这完全搞反了。

生产级 Agent 工作流需要空间来:

执行对账过程

运行长时间运行的自动化

这正是像 Standard Compute 这样的工具对构建客服 Agent、n8n 流程、Make 场景和自定义自动化的团队很有吸引力的一个大原因。如果你使用的是 OpenAI 兼容的 API 形态,但想要可预测的固定费用计算而不是按 token 焦虑,这会改变你设计可靠性时的激进程度。

你不再问「我们能承受再来一次验证吗?」

你开始问更好的问题:

「什么能让这个工作流停止对客户说谎?」

这才是正确的优化目标。

如果你的退款 Agent 听起来聪明但偶尔说谎,不要假设修复方法是更好的 prompt。

检查你的工作流是否在做这些事:

从初始 API 响应确认成功

重试时没有幂等性

未能持久化请求标识

跳过异步任务轮询

对账前发送面向客户的消息

真正的 bug 通常就在那里。

对我来说痛苦的教训很简单:

副作用不是聊天回合。

它们是穿着聊天机器人外衣的分布式事务。

一旦你这样对待它们,你的 Agent 就会变得不那么讨人喜欢,但更值得信赖。

Original source

本文由 AI 翻译整理自 dev.to · AI,原文版权归原作者所有。

阅读英文原文
上一篇
Cursor中直接查询技术文档的MCP方案
下一篇
Cursor与.NET周报:七个实战规则