跳到主要内容

你的智能体把指针当成了值:工具输出里的引用 vs 值

· 阅读需 12 分钟
Tian Pan
Software Engineer

一个搜索工具返回了十个文档 ID。一个素材工具返回了一个 S3 预签名 URL。一个数据库工具返回了一行的 handle。一个文件工具返回了一条路径。这些返回值,从形式上看,全都是指针——一串简短的字符串,命名着智能体当前还没有真正拿到手的那个值。模型接下来怎么走,完全取决于它是否意识到这一点、并在推理之前先做一次解引用,还是说它把指针当成了对象本身。

这个失败模式在 trace 里是看不见的。工具调用成功了。返回结构正常。模型也输出了看上去合理的文本。日志里没有任何一行会说:"智能体在对一个文件名做推理,并把它当成了文档。"指针 vs 值的混淆,发生在可见行为底下的那一层——一个你的工具 schema 从未命名过的层。

这不是模型缺陷。模型没有"间接寻址"这个原生概念。它只有 token,并且基于前面的 token 产出更多的 token。如果它眼前的 token 是 s3://bucket/report-q3.pdf,模型其实面对的是一次选择——调用 fetch 工具去拿内容,或者直接在这串字符串上继续往下编,仿佛"report-q3.pdf"本身就足以概括那份报告。两条都是 prompt 的合理续写。哪一条胜出,取决于 prompt 的框架、最近的上下文、模型的训练、以及这一轮温度采样吐出来的那枚骰子。你的工具目录从来没告诉过它该选哪一条。

你的工具注册表从未命名的那层间接寻址

大多数工具注册表把输入写得很细,把输出写得很潦草。输入 schema 是智能体调用工具必须满足的契约。输出 schema——如果存在的话——往往只是一个扁平的说明:"返回一个文档 ID 列表"、"返回一个 URL"、"返回一行"。这种描述对一个看文档的人来说够用了——按 ID 去取文档、GET 那个 URL、查那行记录。但对一个模型来说,远远不够。

缺失的那个维度是:这个返回值到底是用来"被推理"的,还是用来"被解析"的。文档 ID 不是文档。URL 不是页面。行 handle 不是那一行。这些返回值的每一个都默认了一个后续的解引用步骤——一个工具作者心里清楚、却指望模型自己推断出来的步骤。大多数时候模型确实能推断出来。少数时候,可以预见地,它做不到。

这个问题在混合型工具目录里看得最清楚。一个团队加了一个 search_docs 工具,返回 ID。后来又加了一个 summarize_doc 工具,接收 ID、返回文本。再后来加一个 get_doc_metadata 工具,接收 ID、返回一段小小的 JSON。现在智能体手里有三个都在倒腾文档 ID 的工具,而"我有一个 ID"和"我有内容"之间的关系完全没被显式编码过——它只活在智能体那种 prompt 塑形出来的直觉里,那种"下一步该调哪个工具"的感觉。直觉对的时候,智能体看上去很能干。直觉不对的时候,智能体就会拿文件名当文档来总结。

你在生产环境会看到的失败形态

一旦你知道往哪儿看,指针 vs 值类的失败其实就那么几种,反复出现:

  • 智能体在回答一个关于文档的问题时,对着它的标题或文件名做推理,因为标题是上下文里唯一一串字符串。
  • 智能体比较两条数据库记录的方式,是比对它们的主键,然后宣布它们"是不同的记录"——而从未真正拉取过任何一行的字段。
  • 智能体描述一张图片的办法,是去解析它的 URL——"看起来像是 Q3 仪表盘的图",仅仅因为路径里有 q3-dashboard——而从未对图像字节调用过任何视觉工具。
  • 智能体把一条预签名下载链接当成素材本身来对待,输出的文字仿佛它已经读过这个文件。
  • 智能体回答关于一份长 PDF 的问题,但它其实只看到了前一个工具返回的一段元数据(文件名、页数、MIME 类型)。
  • 智能体在面向用户的输出里,直接拿"doc_4f2a1"这样的标识符当作引用证据——只因为这个 ID 是上下文里最显眼的字符串,模型就把它擢升成了证据。

每一种 trace 在你的可观测面板上都看上去没问题。工具被调用了。工具返回了 200。模型给出了答案。bug 藏在"模型有一串命名内容的字符串"和"模型有内容"之间的那道缝隙里,而 trace 里没有任何一处会把那道缝隙标记为错误。

更深一层的失败形态是:模型在这里的行为是被 prompt 塑形出来的。同一个工具、返回同一个 ID,在两个不同的 prompt 里——一个里会被正确解引用,另一个里会被当作已解析的值组合进去。你没法用一条 prompt 去回归测试这个 bug。bug 存在于 prompt、历史、工具表面三者的联合分布里,并且会随着 prompt 演化而漂移。

把间接寻址显式地写进类型

修复方式是计算机科学里最显而易见的那一个:在模型能看到的类型系统里给"间接寻址"一个名字。如果你的工具返回的是一个指针,就在 schema 里把它标记成指针。智能体的规划器于是能把"解引用"作为一个需要发生的步骤来推理,而不是一个可能发生也可能不发生、取决于这次 prompt 上下文是否凸显它的步骤。

具体来说,下面这些模式有效:

加载中…
References:Let's stay in touch and Follow me for more thoughts and updates