跳到主要内容

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

查看所有标签

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

· 阅读需 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 分钟,直到一张客户工单通过另一个界面唤醒了另一位工程师。

没有模型推理项的故障复盘模板

· 阅读需 11 分钟
Tian Pan
Software Engineer

第一次智能体导致我们团队出现真正的停机事故时,复盘报告的作者打开模板,划过时间线,盯着“根因”字段沉思了良久,然后输入:“队列阻塞恢复的操作指南 (runbook) 有误。” 但实际上操作指南没问题。智能体阅读了指南,认定队列的症状符合另一种场景,并针对该场景运行了恢复脚本。那份文档产生的改进措施——“细化操作指南用词”、“在恢复脚本中增加确认提示”——对于实际的故障模式完全无用。实际情况是一个推理系统推导错误,而模板中没有任何字段知道该如何表达这一点。

自那以后,我看到同样的失败在不同团队中反复上演。模板是为确定性系统设计的。代码做错了,你就修复代码;配置设错了,你就修复配置。复盘文档的模式 (schema) 就是团队关于故障理论的模式,当这个理论无法表达“智能体的计划错了”时,文档就会将实际故障强行降维成模板表达的最接近的事物——通常是文档缺失或缺乏护栏——从而导致改进措施试图用确定性的修复方案去解决概率性的故障。然后,同一类事故会再次发生,团队下次依然会以同样的方式记录它。

你为人类设置的速率限制,AI 智能体三秒钟就会让其饱和

· 阅读需 11 分钟
Tian Pan
Software Engineer

速率限制从来就不是一种公平性原语。它只是一个逐渐“演化”而来的销售工程指标——是三年前某个解决方案工程师在客户接入期间随手写进文档、被复制到套餐定义中,且由于从未有人触发过而从未被重新审视的一个数字。这个限制写着“每分钟 100 次请求”,其真实含义是“超出了任何理性的集成方案的需求”,因为当时平台上的每一个集成都是由人类在键盘前驱动的后端服务,而人类每分钟敲不了 100 次字。

然后,一个付费租户将一个智能体(agent)指向了该端点。智能体不会打字。它不会为了阅读响应而停顿。它没有需要在请求之间渲染的 UI。它执行一个规划循环,每一个推理步骤调用一次 API,而模型制定一个推理步骤只需要大约 30 毫秒的实际时间。智能体在 3 秒内就触及了每分钟的限额,在 3 分钟内触及了每小时的限额,而在轮值工程师的咖啡还没变凉之前,它就已经耗尽了每日配额。在限流仪表盘更新之前,技术支持的升级请求就已经送达了。

智能体安排的无人继承的周期性任务

· 阅读需 10 分钟
Tian Pan
Software Engineer

用户输入:“每周二提醒我检查那个集成。”Agent 创建了一个 cron 条目,返回礼貌的确认,随后会话关闭。六个月后,用户调换了团队。该集成在上个季度已被弃用。Cron 仍在运行,调用着在 4 月份轮换过的 API 密钥,发送到在 5 月份归档的 Slack 频道,费用计入无人查看的项目预算。Agent 完全按照要求执行了操作。出问题的是那个“要求”,它随着时间的流逝变得不再适用。

这不是某个特定 Agent 的 bug。它是这类事物的共性。当我们赋予 Agent 调度持久副作用的能力时——如 cron 任务、Webhook、轮询循环、工作流触发器、日历邀请、定期查询——我们就创建了一类生来就没有生命周期的基础设施。“创建”原语显眼且容易,“删除”原语、“审计”原语、“继承”原语——它们并不具有同等的地位,因此未被使用。

除非你主动去寻找,否则代价是隐形的,而此时恰恰没有人会去寻找。