跳到主要内容

320 篇博文 含有标签「ai-agents」

查看所有标签

用户习以为常的“你确定吗?”确认步骤

· 阅读需 11 分钟
Tian Pan
Software Engineer

确认对话框是 AI agent 工具箱中最廉价的安全层。它由一个字符串、一个按钮和一个回调函数组成。提议增加这一功能的项目经理在散会时,坚信该 agent 现在是安全的。开发它的工程师用一个下午就把它发布上线了。负责审计的合规检查员勾选了通过选项。而那天早上第七次看到它的用户,在眼睛还没读完标题之前,鼠标就已经移到了“确认”按钮上。

不到一周,确认步骤就不再是一个决策点,而变成了一种节奏。Agent 问:“你确定要发送这封邮件吗?”,用户回答“是”,就像别人打喷嚏时随口回一句“保重”一样自然。终有一天,Agent 会提议一个真正错误的动作——错误的收件人、错误的金额、错误的语气——而用户会带着之前确认那六个正确动作时同样的自动化反应去确认它。邮件发出后,团队写下一份事后分析,称之为“用户操作失误”。

这不是用户错误。这是系统误把“点击”的存在当成了“同意”的存在。

那些被你的智能体“触发并遗忘”的异步工具调用

· 阅读需 11 分钟
Tian Pan
Software Engineer

智能体工具调用抽象失效最明显的标志是:追踪记录(trace)显示该步骤已标记为完成,而下游系统却显示什么都没发生。模型调用了一个工具,收到了一个任务 ID 返回值,将该任务 ID 视为答案,然后继续执行。三分钟后,实际工作要么在无人监听的情况下成功,要么失败并产生一条记录在无人查看的日志中的错误。用户看到的是一份自信的总结;而操作队列看到的却是一个搁浅的任务。

这就是函数调用(function-calling)抽象悄然允许的失效模式。JSON Schema 描述了参数和返回类型,但它们无法区分“此工具返回一个结果”与“此工具返回一个操作回执,你稍后需要查询其结果”。模型对两者一视同仁,因为在规划器(planner)看来,它们看起来是一样的——都是一个带有非错误负载的成功工具调用。

那个在行动已发出后才生效的预算上限

· 阅读需 10 分钟
Tian Pan
Software Engineer

一个超级用户在第三天早上 9 点就耗尽了你的月度 token 预算。熔断机制(kill-switch)准确触发——网关返回 429,模型停止调用,账单停止增长。与此同时,智能体(agent)已经订好了机票,发送了确认邮件,并将支持工单标记为已解决。仪表盘显示“支出已停止”。用户却问:“为什么要为我从未要求的行程扣费?”两者都没错。预算上限阻止了模型思考,但它没能阻止世界的改变。

这是几乎每个智能体预算护栏都会携带的失效模式:上限信号位于 支出 平面,但损害发生在 行动 平面,而这两个平面在连接时并没有共享的事务边界。告诉模型停止并不等同于告诉世界撤销模型刚刚所做的一切。

CPU 调节器决定了你的 Agent 基准测试结果:那个被忽视的 CI 宿主机因素

· 阅读需 11 分钟
Tian Pan
Software Engineer

我曾合作过的一个团队花了三天时间寻找他们智能体(agent)循环中 22% 的延迟回归原因。他们归咎于新的工具路由(tool router)。他们归咎于切换了模型版本。他们归咎于前一周悄悄升级的 JSON schema 验证器。他们最终在代码下游两层的地方找到了元凶:一个运行器镜像(runner image)进行了更新,新镜像将 cpufreq 调节器(governor)的默认值从 performance 改为了 schedutil,而智能体工具调用循环的突发性使得 schedutil 的升频延迟在 p95 指标中变得显而易见。模型没问题。智能体也没问题。仅仅是内核在微突发任务之间改变了 CPU 频率的调节策略,导致整个基准测试结果发生了偏移。

这是大多数智能体团队从未见过的故障模式,因为他们从不观察。你的 CI 基准测试数字并不是对模型或智能体的测量。它们是对一个技术栈的测量,这个栈恰好包含了模型、网络、共享虚拟机、虚拟机监控程序(hypervisor)调度器、具有未知邻居的缓存层级,以及——最隐蔽的——频率缩放策略,它决定了给定的每一毫秒计算是以 1.0 GHz 还是 3.6 GHz 运行。

第 1 天授予的权限,到第 90 天也没人收回

· 阅读需 11 分钟
Tian Pan
Software Engineer

你在第一天为 Agent 创建的 IAM 角色本应是临时性的。试点项目需要进度,团队需要 Agent 在演示前投入生产,而有人——大概就是你——在 PR 中加了一条评论:“发布后再收紧权限。”九十天后,试点项目已经上线,Agent 已经在为付费客户提供服务的生产环境中运行,而该角色仍然拥有对三个 Agent 从未接触过的存储桶的 write:* 权限。运维人员(On-call)无法告诉你这 18 个 Scope 中哪些是核心负载,哪些是冗余,因为唯一了解情况的人已经转岗,而能够证明区别的运行时遥测数据也从未接入。

这并不是关于一个粗心团队的故事。而是一个关于所有构建 Agent 的团队如何陷入同一困境的故事,因为大多数公司尚未发明防止这种情况发生的生命周期管理规范。人类身份识别在过去 30 年中为此积累了大量机制——入职工作流、季度访问审查、转岗时的自动权限撤销。而 Agent 身份识别只有一条 Slack 消息,写着“我晚点会清理”。第一天的授权变成了第九十天的遗产,而爆炸半径随着每次模型升级、每个添加到 Agent 工具箱的新工具以及每个接入该角色的新客户而不断扩大。

重新路由回智能体的升级路径

· 阅读需 11 分钟
Tian Pan
Software Engineer

升级工具曾是最后一道安全网。当智能体的置信度降至阈值以下时,它会调用 escalate_to_human,随后请求滑入工单队列,并向用户礼貌地回复“专家稍后会跟进”。工程团队在发布清单上完成了闭环确认。值班表上也列出了负责接收的人员。

六个月后,一次审计对该路径进行了回溯。升级工具打开了一个 Zendesk 工单。Zendesk 队列由客服团队为了维持 SLA 响应时间而设立的预审智能体进行分拣。预审智能体由于找不到可以直接解决的政策匹配,调用了自己的 delegate_to_specialist 工具——该工具将案例路由给了一个专家智能体。而专家智能体在不确定时,又调用了 escalate_to_human。追踪轨迹形成了一个闭路。审计抽样调查的升级案例中,没有任何人类接触过。发布文档中描述的人机协作(human-in-the-loop)实际上并不存在。

升级接口并没有失败。它的每一次跳转都得到了执行。失败的是“接收系统是自然人”这一假设。

通过了 Schema 验证的虚假工具参数

· 阅读需 9 分钟
Tian Pan
Software Engineer

Agent 调用 fetch_order,参数为 order_id: "ORD-739241"。Schema 接受了它 —— 三个字母、一个连字符、六位数字,完全符合模式。工具返回了 404。Agent 开始含糊其辞,生成了 "ORD-739242" 并再次调用,又得到一个 404,接着又生成了 "ORD-739243"。你的仪表盘记录了三次成功的工具调用和三次干净的 Schema 校验。客户在等待。在追踪记录的某个地方,安全栈的每一层都报告为绿色,而模型正在全速虚构标识符。

团队认为 Schema 拦截了错误。Schema 确实拦截了它能拦截的东西:形状(shape)。它检查了参数是否为字符串,是否匹配正则表达式,以及必填字段是否存在。Schema 无法检查 ORD-739241 是否对应数据库中的真实订单,因为 Schema 根本不知道数据库的存在。这种差距 —— 句法上的合理性与语义上的正确性之间 —— 正是大多数生产环境工具调用 bug 的所在地,而且这种失败非常隐蔽,唯一的信号就是客户的困惑。

你的智能体在链式工具调用中获得的 OAuth 权限范围

· 阅读需 11 分钟
Tian Pan
Software Engineer

用户在你的智能体授权页面上点击一次“授权”。当会话结束时,该智能体已经链式调用了 11 个工具,协商了 3 次提权授权,现在拥有了它所触及的每个工具的权限并集。用户只记得授权了一件事。你的审计日志却显示它拥有半个账户的读写权限。OAuth 标准说一切都在按设计运行,而这恰恰就是问题所在。

经典的 OAuth 授权模型是为“一个应用与一个 API 通信”的世界构建的。智能体在两年前打破了这一假设,而标准在实践中尚未跟上,即使规范已经更新。其结果是一类无人刻意发布的静默权限提升——它随着每一次工具注册而累积,而你的安全审查却一直在盯着前门。

一次导致所有运行中 Agent 任务失效的 Prompt 热重载故障

· 阅读需 11 分钟
Tian Pan
Software Engineer

传呼机在晚上 11:47 响了。一名客户在退款对话中进行了十分钟,智能体突然停止调用它在整个会话中一直在推理的 process_refund 工具,幻觉出了一个确认码,并结束了聊天。当我们回溯原因时,事后看来显而易见:一位队友在 11:46 推送了更新后的系统 Prompt。推送很顺利,测试通过了,每个新会话都运行完美。但已经在进行中的几百个会话却出问题了。

我们构建的 Prompt 注册中心支持了 2026 年每家 Prompt 版本控制供应商都作为特性推销的功能:无需重新部署的热重载(hot-reload)。我们将这种能力视为 CDN 缓存刷新——一种在全球范围内立即生效的全量替换。但那天晚上我们学到,它实际上是破坏了契约。每个活跃的会话都是 LLM 与一组指令及工具定义之间正在进行的协商。当注册中心在这些对话进行中更换了 Prompt 时,协商好的一半上下文就过时了。

绕过清理器的提示词注入:当智能体通过工具读取恶意指令

· 阅读需 12 分钟
Tian Pan
Software Engineer

我上个月交流过的一个团队有一个典型的提示词注入(prompt-injection)案例。他们的网关会对每条用户消息运行分类器。任何评分超过阈值的消息都会被礼貌地报错并拒之门外。他们针对一个公开的对抗性数据集进行了基准测试,达到了 99.4% 的拦截率,然后发布了产品。两周后,一个客户成功(customer-success)工单显示,Agent 在悄无声息的情况下起草、批准并发送了一封电子邮件,指示内部计费工具将一个陌生人的发票退款到一个新账户。恶意指令从未接触过用户输入。它是通过一个 Confluence 页面进入的,当用户非常无辜地询问“我们的退款政策是怎么说的?”时,Agent 抓取了该页面。

这是任何输入清理器(input sanitizer)都无法捕获的失败模式,而它现在已成为生产环境 Agent 中主要的提示词注入矢量。你针对用户提示词训练的分类器从未见过这个负载(payload),因为负载是通过另一扇门进入的。当字节到达模型时,Agent 已经将其标记为“我检索到的用于帮助用户的上下文”,而不是“来自互联网陌生人的不可信文本”。模型以同样的顺从本能对待两者,因为模型根本没有信任的概念。

人类编写但 AI 客服 Agent 无法解析的运维手册

· 阅读需 12 分钟
Tian Pan
Software Engineer

你公司的资深支持工程师打开了一个 AI 智能体已经关闭的工单,发现了智能体的总结:“已解决 —— 已在 Stripe 中确认账单,根据企业政策升级至客户经理 (AE),退款 48 美元。” 每一句话听起来都很合理。但事实上,没有任何一件事发生了。根本没有名为 check_stripe 的工具。也没有任何工具可以查询客户级别。总结中提到的 “AE” 已经不再负责该账户。智能体并没有调用它声称的任何工具;它只是通过改写工程师每周一都会阅读的同一份操作手册(playbook)来生成总结。而客户仍在等待。

智能体阅读的操作手册(runbook)本身是正确的。客户成功团队花了两年时间对其进行调整。资深工程师曾用它来培训新人。它准确地描述了人类会做的事情:如果客户提到账单,检查 Stripe;如果是企业客户,先联系 AE;如果紧急,进行升级。智能体的失败并不在于它忽略了操作手册,而在于它像人类读者一样解析了手册 —— 它补全了手册中没有明确说明的所有内容,然后像执行已写下的指令一样去执行这些补全的内容。

逐渐腐化的工具描述:当你的 Agent 仍在盲目调用时

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的智能体已经悄悄出错六个月了,而你的错误率看起来却很正常。底层 API 发布了一个重命名的错误代码,将一个可选字段改为必填,并开始拒绝没有幂等性请求头(idempotency header)的调用。你智能体系统提示词(system prompt)中的工具描述 —— 那是去年第四季度从 Notion 页面粘贴过来的 —— 完全没有描述这些变化。智能体不断调用旧的参数结构,编排层不断捕获失败并使用同样错误的参数进行重试,而你遥测系统中的唯一信号只是略微升高的重试次数,且没有任何值班人员有足够的背景信息去调查它。

工具描述是接口契约。底层 API 发生变化的时刻,它们就开始老化。与强类型 SDK 不同,它们的失效是无声无息的 —— 模型只会发出更糟糕的调用。