案例·Neo·2026.05.01

Vercel: 我们删掉了 Agent 80% 的工具,它反而变好了

Vercel 内部 Text-to-SQL Agent d0 的重构故事:从 17 个专用工具精简到 2 个,成功率从 80% 提升到 100%,速度快了 3.5 倍。

Vercel 的 Text-to-SQL Agent 重构故事

本文译自 We removed 80% of our agent's tools,作者:Andrew Qu(Vercel)


它变好了。

我们花了数月时间,为内部 Text-to-SQL Agent d0 构建了一套精密的架构——专用工具、大量 prompt 工程、精心的上下文管理。它能用……勉强能用。但系统很脆,速度很慢,需要持续维护。

于是我们尝试了一种不同的方式。我们删掉了大部分代码,把 Agent 精简到只剩一个工具:执行任意 bash 命令。我们称之为"文件系统 Agent"。Claude 直接访问文件,用 grepcatls 自己摸清楚。

Agent 变简单了,同时也变好了。成功率从 80% 提升到 100%。步骤更少,token 更少,响应更快。靠的是做得更少。

如果说 v0 是我们用于构建 UI 的 AI,那么 d0 就是我们用于理解数据的 AI。

d0 Slack 交互界面

d0 让任何人都能通过 Slack 提问来做出数据驱动的决策

d0 将自然语言问题转化为针对分析基础设施的 SQL 查询,让团队中的任何人都能在不写代码、不等待数据团队的情况下获得答案。

d0 运转顺畅时,它让数据访问在整个公司民主化。它一旦出错,人们就会失去信任,重新回到 Slack 上找数据分析师。我们需要 d0 既快、又准、又可靠。

我们在替模型思考

回过头看,我们当时解决的那些问题,模型本可以自己处理。我们假设它会在复杂 schema 中迷失、做出错误的 JOIN、或者产生幻觉表名。所以我们修建了护栏。我们预先过滤上下文、限制选项、用验证逻辑包裹每次交互。我们在替模型思考:

  • 构建了多个专用工具(schema 查询、查询验证、错误恢复等)
  • 加入大量 prompt 工程来约束推理
  • 精心管理上下文,避免"淹没"模型
  • 手写检索逻辑,以浮现"相关"schema 信息和维度属性

每个边缘情况意味着又一个补丁,每次模型更新意味着重新校准约束。我们花在维护脚手架上的时间,比花在改进 Agent 上的时间还要多。

import { ToolLoopAgent } from 'ai';
import { GetEntityJoins, LoadCatalog,
  /*...*/ } from '@/lib/tools'
 
const agent = new ToolLoopAgent({
  model: "anthropic/claude-opus-4.5",
  instructions: "",
  tools: {
    GetEntityJoins, LoadCatalog,
    RecallContext, LoadEntityDetails,
    SearchCatalog, ClarifyIntent,
    SearchSchema, GenerateAnalysisPlan,
    FinalizeQueryPlan, FinalizeNoData,
    JoinPathFinder, SyntaxValidator,
    FinalizeBuild, ExecuteSQL,
    FormatResults, VisualizeData,
    ExplainResults
  },
});

我们意识到,我们在逆势而为。我们限制了模型的推理能力。把它本可以自己阅读的信息提前摘要。构建工具来保护它免受它其实能处理的复杂性冲击。

文件系统 Agent

于是我们停下来了。假设是这样:如果我们直接把 Cube DSL 文件给 Claude,让它自己搞定,会怎样?如果 bash 就是你所需要的全部呢?模型越来越聪明,上下文窗口越来越大——也许最好的 Agent 架构,几乎就是没有架构。

新技术栈:

  • 模型: 通过 AI SDK 使用 Claude Opus 4.5
  • 执行: 使用 Vercel Sandbox 进行上下文探索
  • 路由: 使用 Vercel Gateway 处理请求和可观测性
  • 服务器: 使用 Vercel Slack Bolt 的 Next.js API 路由
  • 数据层: 由 YAML、Markdown 和 JSON 文件组成的 Cube 语义层目录

文件系统 Agent 现在像一个人类数据分析师一样浏览语义层。它读取文件,用 grep 寻找规律,建立心智模型,然后用 grepcatfindls 这些标准 Unix 工具编写 SQL。

这之所以有效,是因为语义层本身已经是很好的文档。这些文件包含维度定义、度量计算和 JOIN 关系。我们构建工具是为了摘要那些本已清晰易读的内容。Claude 只需要直接读取的权限。

import { Sandbox } from "@vercel/sandbox";
import { files } from './semantic-catalog'
import { tool, ToolLoopAgent } from "ai";
import { ExecuteSQL } from "@/lib/tools";
 
const sandbox = await Sandbox.create();
await sandbox.writeFiles(files);
 
const executeCommandTool = (sandbox: Sandbox) => {
  return tool({
    /* ... */
    execute: async ({ command }) => {
      const result = await sandbox.exec(command);
      return { /* */ };
    }
  })
}
 
const agent = new ToolLoopAgent({
  model: "anthropic/claude-opus-4.5",
  instructions: "",
  tools: {
    ExecuteCommand: executeCommandTool(sandbox),
    ExecuteSQL,
  },
})

基准测试结果

我们用 5 个有代表性的查询,对旧架构和新文件系统方案进行了基准测试。

指标 复杂方案(旧) 文件系统(新) 变化
平均执行时间 274.8 秒 77.4 秒 快 3.5 倍
成功率 4/5(80%) 5/5(100%) +20%
平均 token 用量 约 102k 约 61k 减少 37%
平均步骤数 约 12 步 约 7 步 减少 42%

文件系统 Agent 赢得了每一项对比。旧架构最差的情况耗时 724 秒、100 步、145,463 个 token,最终还失败了。文件系统 Agent 用 141 秒、19 步、67,483 个 token 完成了同一个查询,而且成功了。

定性上的转变同样重要。Agent 能捕捉到我们从未预料到的边缘情况,并以我们能理解的方式解释其推理过程。

三点启示

不要逆势而为。 文件系统是一个极其强大的抽象。Grep 已经有 50 年历史,依然完美地完成我们需要的工作。我们当时在为 Unix 已经解决的问题构建自定义工具。

我们约束推理,是因为我们不信任模型去推理。 面对 Opus 4.5,这种约束变成了负担。当我们停止替模型做选择,模型反而做出了更好的选择。

这之所以奏效,是因为我们的语义层本身已经是好文档。 YAML 文件结构良好、命名一致、包含清晰的定义。如果你的数据层是一堆历史遗留命名和没有文档的 JOIN,把原始文件访问权给 Claude 并不能拯救你——只会让你更快地得到错误的查询结果。

减法即加法

最好的 Agent 可能就是工具最少的那个。每一个工具,都是你替模型做的一个选择。有时候,模型自己做选择会更好。

诱惑总是存在的:试图覆盖每一种可能性。抵制它。从最简单的架构开始:模型 + 文件系统 + 目标。只有在你证明了复杂性是必要的时候,才去添加它。

但简单的架构本身还不够。模型需要好的上下文来工作。投资于文档、清晰的命名和结构良好的数据。那个基础,比聪明的工具更重要。

模型的进步速度,快过你的工具所能跟上的速度。为你六个月后将拥有的模型而构建,而不是为你今天手里的模型。

Vercel: 我们删掉了 Agent 80% 的工具,它反而变好了