跳到主要内容

780 篇博文 含有标签「ai-engineering」

查看所有标签

并非“全员回复”:智能体出站扇出风险

· 阅读需 10 分钟
Tian Pan
Software Engineer

用户要求智能体(agent)“告知 Karen 我们完成了”。智能体调用了 send_email,收件人字段设置为 karen-team@,这是它的联系人查询工具返回的最合理的地址。这条包含三段内部专用项目状态的信息——其中包括一行关于客户续约风险的坦率描述——最终发送到了四十个收件箱。其中一个收件箱恰好属于该客户。事后分析(postmortem)持续了两周。

没有提示词注入。没有模型越狱。工具完全按照规范运行。团队为 send_email 编写的契约是“向收件人发送消息”。而现实世界强制执行的契约是“广播给一个发送者未审计其构成的群体”。这种差距——即工具的命名与其核心实际能力之间的鸿沟——正是大多数出站智能体事故的源头。

电子邮件是显而易见的例子,但同样的风险潜伏在智能体接触的每一个消息工具中。人类为这些渠道建立的三十年肌肉记忆,并未转移到那些正在通过联系人列表进行模式匹配的规划器(planner)中。

你的 AI 功能忘记计入的 SIEM 账单

· 阅读需 12 分钟
Tian Pan
Software Engineer

这里的数学逻辑很简单,但没人去算。在 AI 时代之前,单个用户操作(例如“总结这张工单”或“发送这封邮件”)只会产生一行应用程序日志。而在 AI 时代之后,同样的操作会产生一条请求日志、一个 LLM 调用追踪、代理调用的每个工具的工具调用 span、它读取的每个数据块的检索 span、一条响应日志,如果你采样进行离线评分,还会产生一条评估日志。一次用户点击的扇出(fan-out)现在会在你的可观测性流水线中产生 30 到 50 条记录,这还是在重试、子代理以及会让一切翻倍的规划器-执行器(planner-executor)拆分出现之前。

你在第一季度发布了一个 AI 功能。到了第二季度,你的安全总监拿着一份比上一个周期高出 4 倍的 Splunk 续订合同走进预算审查会议。AI 团队的人都不在现场。接下来的对话——关于谁来承担这笔费用、为什么威胁检测规则失效了,以及是否真的必须对每次对话进行法律保留(legal hold)——是你在设计阶段就应该进行但没有进行的对话,因为这笔成本没有出现在 LLM 的发票上。它出现在下游,出现在一个 AI 团队从未登录过的工具中。

当你的模型具有随机性时,快照测试在撒谎

· 阅读需 12 分钟
Tian Pan
Software Engineer

当你团队中的初级工程师第一次输入 --update-snapshots 并推送到 main 分支时,你的测试套件就不再是测试套件了,它变成了一份记录稿。虽然 Diff 依然显示为红绿颜色,CI 徽章依然会变为通过,但信号已经悄然反转:测试套件不再告诉你代码是否正确,而是告诉你是否有人费心看过输出。对于确定性的代码,这种风险尚在可接受范围内,因为大多数 Diff 确实是符合预期的。但当网络调用的另一端是一个随机模型时,同样的流程会让每一个 PR 变成一场硬币投掷,让每一位评审者变成一个橡皮图章。

快照测试曾是确定性世界里的一个美妙构想。你记录下上周二 render(<Button />) 的生成结果,断言本周二它会生成相同的字符串。从定义上讲,任何 Diff 都是值得人工核查的行为变更。这种模式在 Jest、Vitest、Pytest、整个 React 生态系统以及一代又一代的 UI 快照扩展中得以幸存,是因为底层契约依然成立:相同的输入加上相同的代码等于相同的输出。但这个契约对 LLM 调用并不奏效。相同的输入、相同的代码加上相同的提示词(Prompt),却会产生不同的字符串——而且这种差异并非 Bug,而是产品按设计正常运行的结果。

为什么 Token 预测在上线后会发生偏移 —— 以及如何在财务发现前捕捉到异常峰值

· 阅读需 12 分钟
Tian Pan
Software Engineer

发布前的成本模型通常是一张精美的电子表格。它假设通过代表性的提示词(Prompt)运行模拟流量,并在测试过的缓存命中率和干净的工具调用路径下运行。但发布后的现实是,一旦功能真正开始运作,这些假设都将不复存在。模拟流量未涵盖的意图恰恰是用户最常使用的。工程团队没收到会议通知的营销活动所带来的流量,最终落在了路由树中成本最高的分支上。在第三周,使用量超过中位数 40 倍的重度用户群体才会开始出现。

这类问题在全行业内已屡见不鲜:调查显示,约 80% 的企业对 AI 成本的预测偏差超过 25%,并报告在成功发布后的几个月内,成本通常会增加 5 到 10 倍。这些数字中关键的细节是“成功”二字。失败的 AI 功能才能维持在预算内。成本偏差是由功能的成功运行驱动的,而不是因为团队做错了什么。这使得它成为一个规划产物(planning artifact)问题,而不是工程问题 —— 而大多数团队依赖的规划产物,即每月账单,其实是最糟糕的检测器。

工具 Schema 设计即是你的爆炸半径:当函数定义成为安全边界

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的 Agent 代码库中最危险的文件是你一直当作 API 文档来编写的那个。工具注册表(Tool Registry)——即告诉模型存在哪些函数以及它们接受哪些参数的 JSON 或 Pydantic schema —— 不再仅仅是一个 docstring。它是你的授权层(authorization layer)。如果你像大多数团队那样设计它,你就是把万能钥匙交给了大模型(LLM),并称之为优秀的工程设计。

考虑一个典型的工具初步尝试:query_database(sql: string)。初衷是合理的 —— 让模型根据用户的问题制定正确的 SQL。现实情况是,模型现在成了一个不受信任的客户端,拥有连接字符串所指向的任何数据库的无限 DDL 和 DML 权限。系统提示词说“仅在 orders 表上运行 SELECT” 只是一个建议,而不是控制手段。当一个受到提示注入(prompt-injected)的工具结果 —— 比如邮件正文、网页或 PDF —— 告诉模型运行 DROP TABLE users 时,你的授权模型就变成了对模型指令遵循能力的纪律要求。那不是授权。那是祈祷。

为什么每周会话记录审查优于你的 AI 仪表板

· 阅读需 14 分钟
Tian Pan
Software Engineer

在你的 AI 团队中,被低估最严重的资产是每周一小时,由三个人坐在房间里阅读你的产品实际对用户说了什么。不是综合评分。不是移动平均值。不是仪表盘。而是实际的对话记录。逐字逐句的输出。模型悄然形成的懒散措辞。你的分类体系中未涵盖的意图。用户尝试了三次,用三种不同的方式表达需求,而你的评估准则(eval rubric)却将这三次对话都评为“满意”。

将这一小时制度化的团队,能够建立起仪表盘永远无法呈现的 AI 功能心理模型。跳过这一步的团队,会根据看起来不错的指标发布六个月的产品,然后在下一次季度业务回顾(QBR)中发现,在无人察觉时,中位数体验已经漂移到了令人遗憾的境地。

你的 Embedding 模型选择决定了 RAG 的上限,而 LLM 无法突破它

· 阅读需 13 分钟
Tian Pan
Software Engineer

我建议的一个团队花了两个月时间在其 RAG 流水中不断更换 LLM。从 Claude 到 GPT,再到 Gemini,最后又换了回来。每一次更换都能让幻觉率降低几个百分点,但从未在关键指标上有所进展:他们的支持代理找到正确知识库文章的概率仍然不到 60%。他们调优的层级错了。检索器返回的是无关的文本块,而无论 LLM 多聪明,都无法根据检索器从未呈现过的文档来回答问题。

嵌入模型是 RAG 系统中决定 LLM 甚至“被允许”看到什么的部分。它描绘了语料库的几何结构——即在向量空间中,哪些文档会落在哪些查询附近。一旦这种几何结构出错,LLM 就只是一个对错误上下文侃侃而谈的自信叙述者。换一个更聪明的 LLM 通常只会让回答更显“文采”,而不会让回答更准确。

评估集腐化:为什么评估分数在上升,而用户满意度在下降

· 阅读需 11 分钟
Tian Pan
Software Engineer

评估分数连续两个季度呈上升趋势。仪表盘是一片绿色,回归测试套件自三月以来从未标记过真实的失败,团队交付 prompt 更改的速度也变快了,因为评估(eval)给出了清晰的通过/失败答案。与此同时,用户反馈的质量正在下滑。NPS 下降了 4 分,支持队列里堆满了没人标记过的失败模式,产品负责人开始质疑:如果客户这么生气,为什么评估结果看起来却这么好?

评估集没有撒谎。它正在回答六个月前它被构建时要回答的问题,针对的是发布周存在的流量分布。产品已经发生了偏移。用户群体已经发生了偏移。发布时团队未预见到的长尾用例现在占了流量的三分之一。评估集仍在衡量第一周的世界,而团队正在用昨天的产品对今天的模型求平均值。

这就是评估集腐化(eval set rot)。它是现代 AI 工程中最隐蔽的失败模式之一,而且随着评估集的变大而变得更糟,因为维护它的人将“更多用例”误认为是“更好的覆盖”。

为什么你的提示词库应该是 Monorepo,而不是 Cookbook

· 阅读需 13 分钟
Tian Pan
Software Engineer

我最近合作的一个团队有三个不同的“总结这份合同”提示词。一个存在于 Notion 页面中,法律科技小队将其复制粘贴到他们的服务里。一个存在于客户成功后端的 prompts/ 文件夹中,为了适应他们的语气偏好做了轻微修改。还有一个内联在数据团队 notebook 里的 Python 文件中,被硬编码在两个 f-string 插值之间。当 OpenAI 弃用了它们运行的所有模型时,迁移计划变成了一场 “Slack 考古” —— 必须追踪到每个所有者,重新评估每个变体,其中两个变体在生产环境中默默地出了一周的故障才被察觉。

这就是规模化后的提示词 Cookbook 的样子。对于十个提示词和一个团队来说,Cookbook 是合理的。但当提示词达到一百个、团队达到四个左右时,它们就会变得难以管理。当你运行一个 AI 组织时,你的 prompts/ 文件夹(装满 .md 文件)的表现就像 2008 年那种靠复制粘贴引入的第三方代码:每个消费者都有自己的快照,偏差(drift)是不可见的,而破坏性变更会以不可预测的方式向外扩散。

智能体灾难恢复:当工作记忆随区域一同失效时

· 阅读需 14 分钟
Tian Pan
Software Engineer

你团队每季度演练的灾备 (DR) 操作手册是为了一套你已经不再完全运行的技术栈编写的。手册上写着:提升从库、重新指向 DNS、清空队列。它假设状态存储在数据库、队列和对象存储中 —— 这些是 SRE 团队已经管理、命名并测试了十年的地方。接着在上个季度,你上线了一个智能体 (agent)。现在,工作内存存在于推理提供商的会话缓存中、工作节点本地磁盘上的草稿文件里、尚未回写的在途工具调用结果中,以及仅存在于单次模型调用提示词历史中的部分“计划-执行”轨迹 (trace) 里。这些都不在资产登记簿上,也不在操作手册里。

当区域宕机时,智能体并不会干净利落地失败,而是处于一种“半完成”的状态。用户看到工作流已经开始,但故障转移后的区域无法恢复进度;客户收到了两次账单,或者根本没收到,因为幂等键存在于已经失效的工作节点上;值班工程师读着 Slack 频道里的讨论,开头是“编排器已启动,但是...”,六小时后以处理信用卡拒付队列告终。

这就是没人点破的鸿沟:智能体特性拥有现有灾备计划未曾描述的状态模型。如果团队还没有记录下这些状态表面,那么只需一次区域性停机,他们就能深刻体会到操作手册的缺失所带来的代价。

智能体事件取证:在需要之前即刻捕获

· 阅读需 12 分钟
Tian Pan
Software Engineer

周二,客户给支持团队发了一张截图。他们的账户显示六天前有一笔他们从未要求的退款。你的 CRO 转发了这张截图,并问了一个问题:“这是怎么产生的?”你知道是智能体(agent)干的——审计日志显示 actor: refund-agent-v3。但自那以后,提示词(prompt)已经修改了四次。由于财务部门为了追求 12% 的成本削减而更换了供应商,模型 ID 在上周四进行了轮换。系统提示词是根据三个检索到的文档生成的模板,而检索索引在周一重新进行了索引。对话历史被运行时(runtime)裁剪,以适应更小的上下文窗口。

你可以告诉 CRO 是智能体做的。你无法告诉他们为什么。这种差距——即知道发生了某个操作与能够重建导致该操作的输入之间的差距——是大多数智能体团队在工程团队之外的人提出真正的取证问题时发现的。

你的 Agent 发布说明只是在列出文件,但集成商需要的是行为差异(Behavior Diffs)。

· 阅读需 14 分钟
Tian Pan
Software Engineer

一个平台团队在周三下午发布了他们的每周智能体 (agent) 版本。内部更新日志写得很尽职:三次系统提示词 (system-prompt) 提交,模型别名从 -0815 快照升级到 -1019,四处工具描述修改,新的评估准则 (eval-rubric) 权重,以及更新后的检索器索引。到了周五,支持队列里出现了 18 个工单,平台团队中没人能把这些工单与变更对应起来。工单 2 和 7 说 “机器人突然拒绝总结私有仓库”。工单 11 说 “输出中的每个代码块现在都带有语言标签,我们的下游解析器因此崩溃了”。工单 15 说 “在长输入下工具 X 的调用频率翻了一番,我们触及了速率限制”。

这些工单没有一个提到更新日志中的任何一行。平台团队的发布说明是一份文件移动清单。集成方的工单是一份行为变更清单。这两份文档互不交集,而信任就在这个鸿沟中流失。