LLM 函数调用的局限与代码编排最佳实践
分析 function calling 模式的扩展性问题,提出代码编排作为更优的 LLM 集成方案,对架构设计决策有高价值指导。
分析 function calling 模式的扩展性问题,提出代码编排作为更优的 LLM 集成方案,对架构设计决策有高价值指导。
将工具调用的完整输出提供给 LLM 成本高且速度慢。输出 schema 将使我们能获得结构化数据,这样我们就可以让 LLM 通过生成的代码来编排处理。代码中的工具调用简化了流程,效果也更好。
使用 MCP 工具调用的一个常见做法是将工具的输出作为消息放回 LLM,然后要求 LLM 决定下一步行动。这样做的希望是模型能够理解数据的含义,并确定应该采取的正确行动。
当数据量较小时,这种方式可以运作良好,但当我们用真实世界的数据尝试 MCP 服务器时,问题很快就显现了。
我们公司使用 Linear 和 Intercom。上周,我们连接了它们最新发布的官方 MCP 服务器,以了解它们如何返回工具调用。
结果表明,两个服务器都在其文本内容中返回了大型 JSON 数据块。这些看起来类似于它们的 API,但不同之处在于文本内容没有预定义的 schema。这意味着唯一合理的解析方式就是让 LLM 来解读数据。
这些 JSON 数据块非常庞大!当我们要求 Linear 的 MCP 列出项目中的问题时,工具调用默认仅返回 50 个问题,大约 70k 个字符,对应约 25k 个 token。
JSON 中包含许多 id 字段,占用了大量 token,但语义上没有什么意义。
在使用 Claude 和 MCP 时,整个 JSON 数据块会被逐字返回给模型。
这种方法很快就会遇到问题。例如,如果我们想让 AI 按截止日期对所有问题进行排序并显示它们,它需要将所有问题原样重新输出为输出 token!这会很慢、成本很高,而且可能会丢失数据。
我们问题中的数据通常还包含许多令人分心的信息:重现问题的步骤、错误信息、用户可能使用的 prompt,或者与用户后续跟进的说明。模型可能无法准确输出其中某些数据,更糟的是,可能偏离原始说明。
根本问题在于我们在同一个聊天线程中混淆了编排和数据处理。
"多 agent"的方法试图通过创建另一个聊天线程("agent")来解决这个问题,专注于数据处理部分。经过仔细调整后表现会更好,但当数据已经有良好的结构时,这种方法仍然显得很不自然。
如果 MCP 服务器已经以 JSON 格式返回数据,那么解析数据并在结构化数据上操作似乎更加自然。回到我们的排序例子,与其要求 LLM 直接重新生成输出,我们可以改为对数据运行排序操作,并返回新的数组。这样没有幻觉,而且可以处理任意大小的输入。
这听起来很熟悉,因为我们已经有了带 AI 的代码解释器。当我们开始将代码执行作为处理来自 MCP 工具的数据的根本方式时(Code Act、Smol Agents),这为 AI 模型的工作打开了可扩展的大门。
变量作为内存。 与其拥有外部内存系统,LLM 可以使用变量(系统内存)来存储任何数据。存储内存就是给变量赋值,查看变量就是打印它,模型可以选择在调用另一个函数时将变量作为参数传递。更好的是,如果使用的语言是强类型的,模型还可以利用 schema。
工具链式调用。 代码可以编排多个函数调用:并行执行它们,或取一个或多个调用的输出并将其用作另一个调用的输入。函数调用之间的依赖关系通过代码表示的计算图隐式表示。重要的是,LLM 不需要重新输出数据,而且我们有完整性保证。
可扩展的处理。 使用代码很自然地可以处理大量数据。模型可以选择使用循环,或依赖 NumPy 或 pandas 等库进行大规模数据转换。
代码还可以在幕后调用其他 LLM:你可以让 LLM 写代码来调用 LLM 以处理非结构化数据(LLM-inception)。
MCP 规范已经定义了输入 schema,它们刚刚引入了输出 schema。
一旦输出 schema 变得普遍,我们预计它们将解锁大数据集上的用例:构建自定义仪表盘、创建已完成工单的周报告,或让自主 agent 监视并推进陷入停滞的工单。
现在挑战转移到了 MCP 客户端。大多数执行环境今天运行在严格控制的沙箱中;安全性至关重要,因为我们处理的是用户/AI 生成的代码。
允许执行环境也访问 MCP、工具和用户数据需要仔细设计,包括 API 密钥的存储位置以及工具的暴露方式。
在我们的设计中,我们创建了用特定 API 访问权限密钥化的沙箱环境,模型获得了关于如何调用这些 API 的文档,这样它们就能够在不看到任何 secret 的情况下发送/检索信息。
大多数执行环境是有状态的(例如,它们可能依赖于为每个用户会话运行 Jupyter kernel)。如果用户期望能够稍后返回 AI 任务会话,这很难管理且代价昂贵。对于长期运行(多天)的任务会话,一个无状态但持久化的执行环境至关重要。
这些约束正在创造我们认为是新的运行时类别——"AI 运行时",使用 LLM 来编排和执行任务。我们仍处于为这种代码执行方法完善所有细节的早期阶段,我们很想听到任何处理类似问题的人的反馈。如果你对我们的方法感兴趣,可以前往 Lutra 体验一下。
通过 email 或 RSS 订阅我的博客。