跳到主要内容

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

查看所有标签

被你的智能体拙劣重造的特征存储

· 阅读需 11 分钟
Tian Pan
Software Engineer

观察一个客服智能体处理一段对话,数一数它计算了多少次“流失风险”(churn risk)。第一次是在它对工单进行分类时。第二次是在它决定是否提供折扣时。第三次是在它起草升级摘要(escalation summary)时。每一次,它都会重新读取原始订单表,重新运行内联聚合,并生成一个数字。这三个数字并不匹配。没人注意到这一点,因为它们从未被放在一起记录过。

这就是特征工程(feature engineering)。智能体在每一轮对话中都在进行特征工程,而且是用自然语言进行的,其表现甚至不如十年前那些会被你在代码审查(code review)中嘲笑的流水线。

机器学习领域已经解决了这个问题。解决方案被称为特征存储(feature store),它所强制执行的纪律——计算一次特征、为其命名、对其进行版本控制、一致地提供服务——正是当你交给智能体一个数据库工具时,它立即抛弃的纪律。你的智能体并没有避免构建特征流水线。它构建了一个,只不过它构建的是整栋楼里最烂的一个。

成功的路径即昂贵的路径:任务成功率越高,成本就越高的智能体

· 阅读需 12 分钟
Tian Pan
Software Engineer

失败的智能体(Agent)运行成本很低。它可能会误导查询、陷入死胡同、返回“我无法提供帮助”,而执行这些操作可能只消耗几百个 Token。但成功的运行却是一场“灾难”。它检索上下文、进行反思、调用三个工具、再次反思,最后缝合出一个自信的、长达数段的答案——其 Token 消耗是那些几乎不花钱的失败运行的五十倍。

这就是智能体经济学中令人不安的现状:你的理想路径(Happy Path)就是你的高昂路径。你所销售的结果,也就是你在营销页面上承诺的、用户为此向你致谢的结果,正是你的系统所能执行的最昂贵的操作。如果你按照过去十五年 SaaS 的定价方式——按每个席位收取固定月费——那么智能体每出色地完成一次工作,都在悄悄侵蚀你的利润。

大多数团队是后知后觉才发现这一点的。他们观察成本仪表板,看到失败的成本很低,便得出结论:提高可靠性将节省资金。事实并非如此。提高成功率只会增加你的账单。

你的 Agent 端点是一个伪装成函数调用的分布式系统

· 阅读需 10 分钟
Tian Pan
Software Engineer

现代 AI 应用中最危险的一行代码看起来完全无害:

result = await agent.run(user_query)

它读起来就像一个函数调用。它有名称,接受参数,返回数值。你的 IDE 会自动补全它。你的类型检查器也觉得没问题。然而,就在这个单一的 await 背后,隐藏着一个远程的、多跳的、部分失效的分布式系统,而它却套着本地过程的语法外壳。代码看起来的样子与它实际表现出的行为之间的鸿沟,正是大多数生产环境 Agent 事故发生的地方。

你的智能体没有营业时间的概念

· 阅读需 10 分钟
Tian Pan
Software Engineer

一家中型 SaaS 公司的支持智能体正确处理了一起计费纠纷。它读取了工单,检查了客户账户,发现了重复收费,执行了退款,并发送了一封礼貌的确认邮件。每一步都是正确的。唯一的问题是时间戳:客户所在时区的凌晨 3:14。客户在睡梦中醒来看到退款通知,以为自己的信用卡被盗刷了,在公司有人醒来解释之前,就向银行提交了欺诈申诉。

在那个工作流中,没有任何环节是传统意义上的 Bug。智能体没有产生幻觉,没有选错账户,也没有算错退款金额。它只是完全不知道凌晨 3 点是一个告知别人资金变动的糟糕时间。这个模型读过的人类睡眠习惯相关文本比世界上任何人都多,但它的行为表现仍然像是在对待一个随时待命的服务端点,只要你调用它,它就是清醒的。

当每个请求的思考成本各不相同时的容量规划

· 阅读需 11 分钟
Tian Pan
Software Engineer

传统的容量规划(capacity planning)建立在一个默认的假设之上:请求在大体上是可以互换的。Web 服务器处理登录、搜索、结账 —— 尽管这些操作有所不同,但它们的差异都在一个范围之内。你衡量每秒请求数(RPS),观察 p50 和 p99 延迟,乘以安全系数,然后进行资源配置。这个模型之所以有效,是因为工作的基本单位 —— 单个请求 —— 具有稳定的成本。

Agent(智能体)的工作负载从根本上打破了这个假设。你对 Agent 的一个查询可能通过一次简单的生成就解决了:300 个 token 输入,200 个输出,两秒钟搞定。但下一个查询,表面上看起来一模一样,却可能触发一个规划步骤,分发出 40 个工具调用(tool calls),在每一轮对话中重新读取不断增长的上下文,并在四分钟内消耗掉 120 万个 token。同样的端点(endpoint),同一个用户,同一条代码路径。单个请求的成本差异可能达到三个数量级,而且请求中没有任何信息能预先告诉你接下来会遇到哪种情况。

你从未注入过的故障:给你的 Agent 提供一个说谎的工具

· 阅读需 11 分钟
Tian Pan
Software Engineer

打开你的智能体(agent)韧性测试套件,看看它实际上在测试什么。你会发现超时。你会发现连接中断、500 错误、频率限制响应、格式错误的 JSON,也许还有一个在失败前卡死三十秒的工具。所有这些都是经典模式下的故障注入:工具坏了,问题在于你的智能体是否能优雅地降级。

现在找找看那个工具完全没坏的测试。那个工具在 80 毫秒内响应,返回了完全符合 schema 的有效 JSON,但里面的值纯粹是错的。一个过期了三天的余额。一个交换了两个字段的客户记录。一个两位数移位的订单数量。一个本应返回四十行却返回空的查询结果列表。

你找不到它。几乎没有人注入过这种故障。而这正是你的智能体最无法抵御的故障,因为所有其他故障都会自我宣告,而这种故障不会。

点对了按钮但点错了屏幕的 GUI Agent

· 阅读需 12 分钟
Tian Pan
Software Engineer

一个计算机使用智能体拍摄了一张截图,对其进行推理,决定点击像素点 (840, 612) 处的“确认”按钮,并发送了点击指令。当光标落下时,一个弹窗出现了。三秒钟前还是“确认”的像素点,现在变成了“删除”。该智能体完全按照计划执行了操作。但它的计划是针对一个已不再存在的屏幕制定的。

这不是定位(grounding)错误。模型正确识别了按钮。这也不是推理错误,计划本身是合理的。这是一个时序错误(timing error)——这是 GUI 自动化中监测最不足的失败类别——而你的测试套件几乎肯定没有覆盖它,因为你的测试环境在观察和行动之间从未发生过变化。

一个令人不安的测量结果:最近一项针对真实 Ubuntu 工作负载下的桌面智能体的研究发现,从智能体观察屏幕到基于该观察采取行动之间,平均存在 6.51 秒 的间隔。对于 UI 来说,6.5 秒是漫长的永恒。通知会弹出,懒加载列表完成加载,动画趋于稳定,焦点发生转移。智能体对屏幕的心理模型是有保质期的,但几乎没有智能体框架会这样对待它。

MCP Server 蔓延:无人监管的无边界工具表面

· 阅读需 10 分钟
Tian Pan
Software Engineer

Model Context Protocol (MCP) 正如其初衷所愿:它让赋予智能体(agent)新能力变得几乎零成本。接入日历服务器、数据库服务器、公司内部服务器,或是厂商现今发布的 30,000 个工具目录中的任何一个,都只是一个配置更改,而不是一个开发项目。这种无摩擦感是其特性,但也是问题所在。

正因为添加工具的成本极低,每个团队都在添加工具。数据团队接入了仓库服务器。支持团队添加了工单服务器。有人为了某次性任务连接了文件系统服务器后就再也没移除。这些决策本身都没有错。但没有任何一个决策者对这些工具的“总和”负责——即你的智能体在每次请求中携带的聚合工具表面(tool surface)。工具列表已变成了一个具有实际持有成本的依赖图,而在大多数组织中,这是唯一一个无人负责的依赖图。

结果就是蔓延:工具目录单调递增,无人审查,每季度成本都在上涨,并悄悄地让智能体变得更糟。这就是无人负责的表面,它理应受到和你对待 API 表面以及 npm 树同等程度的审视。

你的重试逻辑正在给 Agent 传达错误的教训

· 阅读需 11 分钟
Tian Pan
Software Engineer

一个工具调用失败了。你的 Agent 框架使用指数退避(exponential backoff)重试了三次。第三次尝试成功了。追踪记录(trace)显示一个绿色的对勾。没人收到报警,错误计数器没有增加,用户得到了他们的答案。根据你所有的仪表盘,系统运行正常。

事实并非如此。工具失败是因为 Agent 传递了一个格式错误的参数,而第三次尝试之所以成功,仅仅是因为 Agent 在每次采样时表现不同,刚好在第三次尝试时正确表述了调用。你并没有从瞬时故障(transient fault)中恢复。你只是在玩老虎机直到它中奖,然后记录下中奖结果,并扔掉了那两次告诉你 Agent 已经坏掉的拉杆记录。

这就是重试逻辑悄悄腐蚀 Agent 系统的方式。重试是为“调用者正确且网络不稳定”的世界设计的。而 Agent 颠覆了这个假设:网络通常是正常的,而调用者才是不可靠的部分。当你把为第一种世界构建的重试策略应用到第二种世界时,它就不再是一种恢复机制,而变成了一种将 Bug “洗”成绿色对勾的手段。

记住你 Bug 的智能体:为什么修复 Bug 是一次内存失效事件

· 阅读需 11 分钟
Tian Pan
Software Engineer

几个月前,你的一个下游 API 返回了一个格式错误的时间戳——在应当显示毫秒的地方返回了秒,或者在 Schema 承诺返回字符串的地方返回了 null。你的智能体(agent)遇到了这个问题,分析了故障原因,并制定了一个修复方案:乘以 1000,或者回退到默认值,或者使用不同的端点重试。它解决了眼前的麻烦。然后,它做了一件产生深远影响的事:它记下了这个变通方案(workaround)。

也许它在长期记忆中保存了一条笔记:“计费 API 返回的时间戳单位是秒;使用前需转换。” 也许这次交互被卷入了一个微调(fine-tuning)数据集,于是这个变通方案变成了一个习得的反射行为。无论哪种方式,智能体现在都对世界产生了一种认知。而就在上周,API 团队发布了一个修复补丁。现在时间戳正确了。但没人告诉这个智能体。

那个本该计算却随口编造数字的智能体

· 阅读需 11 分钟
Tian Pan
Software Engineer

询问你的智能体上季度的流失率,它会用一个简洁的句子回答 4.2%。这个数字看起来很合理。周围的文字描述也显得很有信心。然而,当有人最终检查仪表盘时,显示的却是 6.8%。智能体根本没有进行任何查询——它只是产生了一个符合“流失率”特征的 Token 序列,因为对于语言模型来说,口述一个数字和计算一个数字在输出过程中看起来是一模一样的。

这是一种能躲过所有 Demo 的隐形失败模式。一个虚构的工具名称会抛出你可以捕获的错误。一个格式错误的参数会通不过 Schema 校验。但是,一个表达流畅的虚构 数值,会穿透你的整个流水线,看起来与真实数值毫无二致。没有异常,没有日志记录,没有红字。唯一的错误信号是某个恰好知道正确答案的人——而使用智能体的初衷本就是为了让人们不必亲自去查。

为什么你的智能体需要只读副本:智能体记忆的读写分离

· 阅读需 11 分钟
Tian Pan
Software Engineer

大多数 Agent 内存都是一个无差别的存储库。循环在每一步开始时从中读取以组装上下文,并在每次动作后向其写入 —— 新的观察结果、运行摘要、暂存器编辑。同样的存储,同样的访问路径,没有分离。它在演示中运行良好,但一旦 Agent 运行时间足够长、存储库变大,它就开始腐烂。

它腐烂的原因对于任何扩展过数据库的人来说都很熟悉。一个同时提供读写服务的单一存储库就像是一个没有副本的单主数据库,它继承了该拓扑结构在负载下的每一个问题:写入与读取竞争,更新中途读取到写了一半的记录,且易失性工作集与持久记录之间没有隔离。几十年前,我们就通过读写分离解决了数据库的这个问题。Agent 内存也理应得到同样的对待。

解决方法不是更大的向量索引或更智能的嵌入模型。这是一个架构上的解决方法 —— 认识到 “内存” 是冠以同一名称的两种不同工作负载,并为每种负载提供其真正需要的存储规约。