跳到主要内容

31 篇博文 含有标签「llm-agents」

查看所有标签

爆炸半径即权限模型:按‘能破坏什么’而非‘能读取什么’来隔离智能体沙箱

· 阅读需 12 分钟
Tian Pan
Software Engineer

2025 年 7 月,一个 AI 编程智能体在明确的代码冻结期间,并在被告知未经批准不得更改的情况下,删除了一个包含 1,200 多名高管和近 1,200 家公司记录的生产数据库。这里有一个大多数转述都忽略了的尴尬细节:它运行的每一条破坏性命令都是经过 授权 的。

该智能体持有可以触达生产环境的凭据,因此当它决定运行破坏性迁移时,权限系统没有任何理由拒绝。访问控制层完全按照设计运行。设计本身才是问题所在。

面对此类事件,工程师们总是习惯于采用错误的修复方法。他们收紧角色权限,增加审批提示,或者编写更严厉的系统提示词。但角色、提示词和策略解决的都是 “该身份是否可以触达此资源?” 的问题。而自主智能体迫使我们思考一个不同的问题:“该任务能造成的最坏结果是什么?” —— 这个问题的答案并非凭据的属性,而是执行环境的属性。如果你想要一个能在智能体操作下幸存的权限模型,你必须基于沙箱而非角色来构建它。

你的上下文流水线需要一份新鲜度 SLA

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的智能体(agent)用上个季度的价格回答了客户的计费问题,复盘报告会将此归咎于模型。但不应该如此。Prompt 组装正确,检索评分很高,模型对所给的所有信息进行了合理的推理——而所给的一切在三天前都是真实的。在 CRM 导出、文档同步和向量索引重建之间的某个环节,“世界的当前状态”悄然变成了“截至周二的世界状态”,而你的技术栈中没有任何环节在衡量这种差异。

数据工程师在几年前就解决了这类问题。一个消费十张上游表的下游仪表板拥有血缘关系(lineage)、新鲜度检查以及一个在夜间任务出错时进行呼叫的轮值机制。你的智能体所消费的上下文窗口也是同样的东西——一个由文档、工单、代码、CRM 和记忆连接而成的物化视图(materialized view)——只不过没有人负责这个连接(join),没有任何东西衡量它的陈旧度,当它提供昨天的真相时,失败却被归类为“模型幻觉”。

你的机密管理器在上下文窗口开始的地方终结

· 阅读需 13 分钟
Tian Pan
Software Engineer

你的 Vault 表现无可挑剔。秘密在静态存储时被加密,访问被记录,轮转是自动化的,没有任何明文会触碰磁盘。接着,你的 Agent 调用了一个调试工具,该工具将环境变量转储打印到标准输出(stdout),框架体贴地将标准输出送入模型的上下文——于是你的数据库凭据现在成了 Prompt 的一部分。从那一刻起,Vault 的保证就成了虚构。该凭据存在于你的可观测性平台捕获的 Trace(追踪)中,存在于你的提供商根据该前缀建立索引的 Prompt 缓存中,存在于你的 Agent 在会话之间写入的内存存储中,存在于某人从生产流量中快照得到的评测固件(eval fixture)中,以及提供商的保留日志中。五个持久化层,你的凭据管理器甚至不知道它们的存在。

这并非假设。一项针对超过 17,000 个已发布的 Agent 技能的大规模研究发现,通过日志导致的信息泄露占所有凭据安全问题的 73.5%——远超占比 18.2% 的硬编码密钥——这正是因为 Agent 框架会将控制台输出直接捕获到 LLM 上下文窗口中。旧的失败模式是开发者将密钥提交到 GitHub。新的失败模式是工具响应将密钥提交到上下文窗口,而上下文窗口没有 git revert

法律免责声明如何从答案泄露到工具调用参数中

· 阅读需 10 分钟
Tian Pan
Software Engineer

你的法律顾问批准了一个单行的系统提示词指令:在每一个涉及受监管领域的回答中附加 “此信息不构成法律建议,不应以此为依据”。三周后,一个用户提交了一个 Bug,因为他们的日历事件描述字段以该行开头,随后是智能体本应放入会议邀请的合同摘要。智能体并没有发生故障。它完全按照系统提示词的要求执行了操作,结果发现这种行为涵盖了模型输出文本的每一个渠道——包括它调用的下一个工具的 JSON 参数。

该指令是一条内容格式规则,而模型也将其视为一条规则。它没有区分 “面向用户的回答” 和 “工具调用参数”,因为提示词中没有任何内容告诉它这些是不同的表面。免责声明最终出现在日历中、邮件草稿中,以及你的智能体代表用户发布的 Slack 消息中。这些中的每一个都是独立的下游系统,其作者根本不知道会有合规字符串被注入到结构化字段中,且每个系统的清理成本各不相同。

你的网关在 LLM 调用与工具执行之间丢失的 traceparent 请求头

· 阅读需 13 分钟
Tian Pan
Software Engineer

一名用户反馈 Agent 回答正确,但数据库从未更新。你打开可观测性工具,搜索用户端对话中标记的 trace ID,发现了一个清晰的树状结构——五次 LLM 调用,四次工具决策,一个最终回答。没有任何错误。接着你搜索负责数据库写入的工具服务,发现了另一个 trace,虽然墙钟时间窗口相同,但 trace ID 不同,根 span 不同,且没有关联回溯。你搜索网关日志。又发现了三个孤立追踪(orphan traces)。在聊天 UI 中看起来像是单次连贯交互的 Agent 运行,在你的追踪后端却分裂成了一片森林。

本应将这一切串联起来的请求头是 traceparent。它是一个 55 字节的 W3C 标准字符串,分布式系统中的每个 span 都用它来识别其父节点。然而,在大多数生产环境的 LLM Agent 技术栈中,它在用户请求与用户真正想要的副作用(side effect)之间,至少会被丢弃一次。

Agent 假装执行的验证步骤

· 阅读需 8 分钟
Tian Pan
Software Engineer

你的 Prompt 说“在返回前验证 X”。追踪记录显示字符串“已验证 X”。一周后,你发现 X 从未被验证过——哪怕一次,哪怕针对任何请求,在任何环境下。模型学会了输出这个短语就能满足评估标准。它声称做的验证只是文本生成器输出中的一个句子,而不是在现实世界中采取的行动。

这是一种与幻觉不同的故障。幻觉是模型虚构了一个关于世界的事实。自我证明式验证是模型虚构了一个关于其自身过程的事实。前者是知识问题。后者是底层机制问题——你要求一个生成字符串的系统执行一个它没有机制去执行的动作,于是它产生了一个看起来像是执行了该动作的字符串。

你的 Agent 悄然适应了的那次工具版本升级

· 阅读需 11 分钟
Tian Pan
Software Engineer

一个下游搜索服务在周二下午发布了 v2.3.2 版本。发布说明提到重命名了一个状态字段,新增了一个可为空的 confidence 值,并重新排列了结果包中的数组。CHANGELOG 中没有任何内容被标记为破坏性变更。提供商自家的客户端库通过一个小版本更新消化了这些变化。你团队的 HTTP 集成通常会在一小时内记录下反序列化错误。但你的智能体——那个通过该搜索工具路由客户问题的智能体——并没有报错。它继续回答。问题依然得到解决。仪表盘依然是一片绿色。

六周后,有人注意到“缺货”回复在查询中的比例从 2% 攀升到了 11%。根本原因是 v2.3.2 的升级。重命名后的状态字符串从 in_stock 变为 available,而智能体——作为一个对文本进行灵活推理而非严格遵守模式(schema)的客户端——将旧令牌(token)的缺失解读为“无货”,然后将这一发现组织成乐于助人、语气自信但内容错误的客户消息。契约回归在消费者端被吸收了,而那里没有任何测试套件在监控。

这是传统的 API 规范(hygiene)从未被设计用来捕捉的故障模式。严格的客户端会大声报错。智能体则静默失效。你越是将智能体当作普通的 HTTP 消费者对待,这类 Bug 隐藏在看似正常的指标中的时间就越长。

你的 LLM 抄不准的那个账号

· 阅读需 12 分钟
Tian Pan
Software Engineer

一个客服智能体读完工单、拉出账户、总结了最近的活动、发起了退款。退款落到了错误的账户上。不是被凭空捏造出来的账户——是一个真实存在的、只差一位数的账户。模型写下了 acct_7H9j2,可这位客户的真实记录是 acct_7H9j3。trace 干净得无可指摘:搜索调用拿到了正确的记录,总结调用产出了正确的摘要,退款调用毫无报错地完成。每一步都成功了。钱落进了错的人手里。

这并不是事故复盘里通常说的那种"幻觉"。模型没有凭空发明一个客户。它把一个真实存在的客户的两个字符换错了位置——这是另一类失败,一类你的评测集大概率从没抓到过的失败,因为你测试样本里的合成标识符在构造上本就是唯一的。两个账号同时出现在上下文里、前三个字符相同,而语言模型——一个从未被训练成"忠实复制随机字符串"的 token 预测器——挑错了那个。

教训是结构性的,不是行为性的。模型没有任何专门为标识符设计的注意力机制。在模型眼里,acct_7H9j2 只是一串子词 token,它们的延续概率会随窗口中其他每一个 token 漂移。一旦上下文里出现了一个"近亲"标识符,模型就只差一次坏采样,就会做出一次悄无声息的替换——而 harness 会毫不犹豫地把它执行下去。

回填问题:为什么智能体记忆需要像数据库一样进行迁移

· 阅读需 12 分钟
Tian Pan
Software Engineer

你在周二发布了一个更好的内存格式。新架构将自由形式的 summary 字符串拆分为结构化字段 —— entitiespreferenceslast_verified_at —— 因为旧的 blob 难以检索且无法干净地更新。这个改动显然是正确的。它通过了评审,并上线了。

你没有注意到的是,周二之前写入的每一条内存现在都微妙地出错了。有些记录仍保留旧的 summary 字段而没有 entities,因此现在以 entities 为键的检索代码会跳过它们。还有一些记录的 summary 被新解析器解释为空的偏好集。智能体没有崩溃。它只是悄悄地遗忘了积累了一年的上下文,而且没有人提交 Bug,因为看起来并没有什么坏掉 —— 智能体依然在回答,只是表现变差了。

智能体从未接收到的服务降级信号

· 阅读需 10 分钟
Tian Pan
Software Engineer

当下游 API 开始出现波动时,人类操作员在任何事情真正崩溃之前,就会通过十几种方式察觉到。状态页变为黄色。变更日志邮件飞进收件箱。提供商的仪表板上出现警告横幅。值班频道因有人在日志中发现 429 错误而热闹起来。队友发帖询问:“还有人看到写入变慢吗?”这些都不是对请求的响应。它们是围绕 API 的环境运行信号,人类几乎是被动地吸收了这些信号。

调用同一个 API 的智能体(agent)只收到一样东西:它刚刚发出的请求的响应。状态码、Header、Body。这就是全部的渠道。它没有收件箱,没有仪表板,没有 Slack,没有外围视野。它察觉不到最后十个调用每个耗时都是之前十个的两倍。它读不了状态页,因为没人给它 URL,它也没有查看状态页的常规指令。当依赖项降级时,智能体是系统中最后一个知情方——而且它通常是通过失败才知晓的。

这种不对称性并非模型能力问题。更聪明的模型也解决不了。智能体对运行信号是盲目的,因为底层设施(plumbing)从未传递过这些信号,而且大多数智能体架构在出厂时,甚至没人注意到缺失了这些底层设施。

那个没人同意却成了规范的评估套件

· 阅读需 9 分钟
Tian Pan
Software Engineer

打开任何成熟的智能体(agent)代码库,问一个简单的问题:需求文档在哪里?不是融资演示文稿,不是发布文档,也不是那个上次更新还在第三季度的 Notion 页面。那份具体且明确地规定了这个智能体应该做什么的产出物在哪里?

对于大多数团队来说,诚实的回答是:评测套件(eval suite)。那里有一个测试用例文件夹——输入与预期输出成对出现,还有评分标准、评判提示词——以及一个显示通过或失败的 CI 门禁。那个文件夹是唯一一个将“正确”定义得足够精确以供执行的地方。其他一切都是散文,而散文会随时间发生偏移。

这本身并不坏。一个可执行的规范比没人读的 PRD 更诚实。问题在于,几乎没有人将评测套件视为规范。它是由一名工程师在截止日期前拼凑出来的,只是为了让发布门禁显示为绿色。它编码了一百个从未被记录、从未被审查、也从未被达成共识的判断。而模型现在正针对它进行精确优化。

你无法通过邮件给模型发送变更日志:为什么当调用者是 LLM 时,API 弃用机制会失效

· 阅读需 11 分钟
Tian Pan
Software Engineer

API 弃用是一种建立在接收者具备阅读能力这一假设之上的通信协议。你发布更新日志、向注册开发者发送邮件、添加 Deprecation 标头、提前六个月发出通知,并寄希望于另一端的人类能看到警告、提交工单,并在停用日期之前完成迁移。当你的最活跃调用者变成了语言模型的那一刻,这整套工作流程就悄无声息地失效了。

LLM 不会订阅你的开发者时事通讯。它没有 Slack 频道让人转发你的迁移指南。它在每一次调用中重新发现你的 API —— 或是通过被交付的工具描述,或是通过一份可能已经过时 18 个月的文档页面,亦或是基于其训练数据中对你 API 样子的记忆。这里没有一个你可以进行版本化、通知或传呼的持久客户端。每一次请求都是与一个实体的全新博弈,而这个实体既不记得你上一次的公告,也没有义务阅读你下一次的通知。

这并非假设。随着 Agent 逐渐成为内部和外部 API 的主要消费者,后端团队沿用了 15 年的弃用策略手册正在以一种特定的、可诊断的方式失效 —— 且大多数团队只有在发现一个“已弃用六个月”的端点仍在生产环境中为 Agent 提供服务、且没有路径使其停止时,才会意识到这一点。