跳到主要内容

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

查看所有标签

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

· 阅读需 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 不同,它们的失效是无声无息的 —— 模型只会发出更糟糕的调用。

由客户端时钟而非网关标记时间戳的链路追踪时间线

· 阅读需 10 分钟
Tian Pan
Software Engineer

你打开了一个运行缓慢的对话追踪。模型调用竟然在用户点击发送前的 800 毫秒就开始了。你责怪了用户的笔记本电脑,关闭了标签页,继续处理其他事情。

这不仅仅是一个用户的时钟出了问题。这涉及大约三分之一的流量,而且每一个跨越客户端边界的调试会话都在读取一个根本不存在的时间线。浏览器时钟是用户可设置的,经常不同步,偶尔甚至会偏差好几天。大多数可观测性技术栈附带的检测 SDK 会使用设备报告的任何时间来标记客户端 span,通过 traceparent ID 将它们与同步服务器时钟标记的服务器 span 链接成一棵树,然后将结果交给你的值班工程师,就好像这两半具有可比性一样。它们并不具有可比性。

那些悄悄共享长期记忆的智能体 A/B 测试变体

· 阅读需 12 分钟
Tian Pan
Software Engineer

你运行了职业生涯中最干净的一次 A/B 测试。流量分配是 50/50,哈希函数看起来没问题,指标流水线没有造假,留存样本(holdout)得到了保留,经过三周的分析,得出了一个明确的胜者:变体 B 将任务完成率提升了 4 个点,统计团队对 p 值也没有异议。你将其 100% 发布了。发布两周后,你启动时所依据的核心指标却漂回了基准线,而且没人能解释原因。

这就是那个花了一些时间才看清的部分。两个变体都在向同一个长期记忆库写入并从中读取。变体 A 中的用户写入了一条记忆,比如 “该客户更喜欢直截了当的总结”,第二天,当同一个用户恰好处于变体 B 时,变体 B 的 Agent 加载了该记忆并将其读入其提示词(prompt)中。反向的情况也在另一个方向发生。实验并不是在比较提示词 A 与提示词 B。它是在比较 “读取提示词 B 形状记忆的提示词 A” 与 “读取提示词 A 形状记忆的提示词 B”。结果是受污染的联合分布的平均值,而发布则是回归到了同一曲面上的另一个点。

隐蔽式安全与正在阅读你 Wiki 的智能体

· 阅读需 13 分钟
Tian Pan
Software Engineer

公司内部有一个安全运行了十年的端点。它位于一个除了原始团队之外没人能猜到的路径上。它不在公开文档中。它不在 OpenAPI 规范中。它不在网关的“已记录路由”白名单中。它的身份验证层是一个任何内部服务都可以签发的令牌,因为威胁模型认为,触达它的唯一前提是已经知道它的存在。这个端点接受一个 JSON 数据块,在某个平淡的周二,它会重新发放退款、轮换 API 密钥或在两个计费账本之间移动数据行。自 2016 年以来,它一直正常且平稳地工作着。

上个月,一位同事将一个编程智能体接入了工程维基,以协助处理入职提问。该智能体索引了每一个 Confluence 空间、每一份存档的设计文档、每一页标有“请勿删除——历史记录”的页面。昨天,一名初级工程师询问智能体退款是如何运作的。智能体将一份被遗忘的 2018 年架构图、有人粘贴到操作手册里的 Slack 导出记录以及一份写了一半的故障复盘拼接在一起。它用对话式的文字完整描述了该端点、所需的令牌类型以及示例 Payload。端点本身没有改变,但它的威胁模型改变了。

变成关键路径的审批队列

· 阅读需 12 分钟
Tian Pan
Software Engineer

设计文档写着“人机回环”。发布演示稿写着“默认安全”。六个月后的事故回顾则写道,由于审批人在吃午饭,智能体(Agent)花了 90 分钟才给客户发送发票。这些文档都没有撒谎。它们只是在描述同一个组件在负载曲线不同位置的表现——而其中只有一份准确抓住了全貌。

当你在智能体与不可逆操作之间加入人工干预时,你并没有增加一个安全原语(safety primitive)。你实际上是增加了一个服务,它带有队列、吞吐量限制、质量负载曲线以及可用性概况。如果一个团队在发布智能体时没有命名那个服务,那么他们交付的产品的关键路径将取决于一段他们拒绝去运维的基础设施。

你的智能体记住的浏览器选择器

· 阅读需 11 分钟
Tian Pan
Software Engineer

上周二,你的 computer-use 智能体表现出色。它登录了供应商门户,点击了五层嵌套菜单,导出了报告,将其附加到工单中,并在不到两分钟内完成了任务。你保存了轨迹。你赞美了模型。你发布了工作流。然而,在那个成功的轨迹中,智能体记住了一个信息:“导出 CSV”操作位于 div.toolbar > div:nth-child(2) > button.btn-secondary:nth-child(4)

到了周五,供应商推送了重新设计。工具栏现在是一个 flex 容器,次要按钮被放进了下拉菜单,而“导出”这个动作被一个下载图标取代了。你智能体记下的路径现在指向空——或者更糟,它指向了一个现在显示为“删除账户”的按钮。智能体无法分辨其中的区别。两者都是按钮。两者都在同一个选择器位置。周二留下的轨迹不再是记忆,而是一颗地雷。

对话树:你的服务器作为日志存储的对话结构

· 阅读需 12 分钟
Tian Pan
Software Engineer

用户输入“其实,我的意思是五十,而不是十五”,点击最后一条消息上的铅笔图标并进行编辑。UI 表现得非常出色:它向用户展示修改后的消息,淡出旧消息,将助手过时的回复变为带删除线的“幽灵”状态,并呈现一段流畅的对话,读起来就像最初的错误从未发生过一样。用户心满意足地发送了下一轮对话。而智能体却用“十五”进行了回答。

Bug 不在模型身上。模型准确地接收了服务器发送的内容,而服务器发送的是:原始消息、原始助手回复、撤回动作、修改后的消息以及新的请求——所有内容按顺序拼接在一起,实时发送。用户在进行一场已经编辑过的对话。而智能体在进行一场从未被编辑过的对话。两份对话记录在第三轮开始分叉,此后再未统一,之后的每一轮对话都在为这一差距支付“利息”。

你用智能体替换的内部搜索框刚刚成为了你的 SLO

· 阅读需 12 分钟
Tian Pan
Software Engineer

你停用了公司门户上的搜索栏,因为智能体(Agent)表现得更好。以简单的英语输入问题,获得带引用的回答,通过追问进行优化。试点项目的满意度指标爆表。上线邮件写道:“弃用旧版搜索,两周内全量切换。”两周过去了。旧索引被停用。查询框被聊天输入框取代。

六个月后,在某个周二的早晨,三件事同时发生。由于某人的批处理作业耗尽了共享配额,你的推理服务商对公司账号进行了限流。向量嵌入(Embedding)服务出现了区域性局部故障。一次配置推送清空了 Prompt 缓存。公司里每一位习惯在搜索栏输入“VPN 设置”或“报销政策”的工程师,现在都盯着加载动画转了 40 秒,然后收到一条无法理解其问题的拒绝信息,或者更糟——一条指向不存在的 Wiki 页面的言之凿凿的引用。员工互助的 Slack 频道消息炸了。IT 部门的收件箱里塞满了“搜索是不是坏了?”

你取代的搜索栏在过去 10 年的微小改进中保持了三个九(99.9%)的可用性。取而代之的智能体有着完全不同的故障形态——是变慢而非宕机,是出错而非空白,是昂贵而非缓存——而你的 SRE 文化尚未针对此进行校准。

Agent 循环从搜索框偷走的延迟预算

· 阅读需 13 分钟
Tian Pan
Software Engineer

发布指标看起来很干净。回答质量提升了,引用率上升了,评估套件全绿。那个用基于 Agent 的检索器替换旧关键词搜索的团队发布了产品,赢得了胜利,然后转向了下一个项目。六周后,有人注意到该界面的周活跃用户数下降了 12%,但没人能找到性能回归。其实并没有回归。Agent 运行正常。用户离开是因为以前在 200 毫秒内就能给出答案的搜索框,现在需要 4 秒钟,而发布回顾中没有任何内容涉及到这方面的预算。

这就是延迟预算转移问题,而且几乎没有人会画出能捕捉到这一问题的组织架构图。搜索框不仅仅是一个函数调用。它是与用户神经系统签订的一份为期三十年的契约:输入、查看结果、扫视、点击。200 毫秒的响应并不是某个仪表盘上的性能指标——它是当结果送达时,用户的注意力仍然留在屏幕上的原因。当搜索框背后的团队用 Agent 循环替换关键词索引时,函数调用表面看起来是一样的,但新调用的 SLA 处于一个完全不同的范畴。延迟预算从拥有索引的团队转移到了拥有 Agent 的团队,又从拥有 Agent 的团队转移到了用户身上,而唯一参加会议的只有用户。

那个假设人类会阅读页面的 On-Call 运维手册

· 阅读需 11 分钟
Tian Pan
Software Engineer

警报在凌晨 02:14 响起。运维手册(runbook)写着“呼叫工程师”。工程师的名字关联到了一个值班轮值。该轮值指向一个 Slack 频道,这是团队在六个月前建立的统一分诊界面。频道中的第一条消息是告警。十九秒后发布的第二条消息是一段冷静的三句总结:告警服务、失败的依赖项、最后一次部署。它写得很好,最后以“已确认(Acknowledged)”结尾。

事故指挥官在床上看着手机,读到“已确认”后便翻身继续睡去。然而,并没有人确认。作为一线分诊助手的智能体(Agent)订阅了该频道,它向频道复述了告警内容,并以频道中其他读者习惯用来表示“我已掌握处理此问题的上下文”的动词收尾。这起事故在无人接手的情况下运行了 41 分钟,直到一张客户工单通过另一个界面唤醒了另一位工程师。