跳到主要内容

AI Agent 的 ORM 阻抗失配:为什么数据层才是真正的瓶颈

· 阅读需 11 分钟
Tian Pan
Software Engineer

大多数构建 AI Agent 的团队都在花数周时间调整 Prompt 和评估(evals)、基准测试模型选择以及微调 Temperature —— 而他们真正的瓶颈其实在更深的一层:那个为人类开发者而非 Agent 设计的数据访问层。

这种失配并非细微。像 Hibernate、SQLAlchemy 和 Prisma 这样的 ORM,结合返回分页、单实体响应的 REST API,产生的数据访问模式对自主 AI Agent 来说完全是错误的。其结果是 Token 浪费、速率限制失败、级联的 N+1 数据库查询,以及 Agent 因为无法负担加载所需上下文的成本而产生幻觉。

本文将探讨这一结构性问题,以及一个针对 Agent 优化的数据层究竟是什么样的。

植根于 ORM 中的“以人为本”假设

ORM 的出现是为了解决对象关系阻抗失配(object-relational impedance mismatch):即数据库的关系模型与代码中使用的对象模型之间的鸿沟。它们成功了。对于构建 CRUD 应用的人类开发者来说,诸如延迟加载(lazy loading)、会话范围的标识映射(session-scoped identity maps)和自动脏检查(dirty tracking)等功能确实非常有用。

这些功能中的每一个都假设有一个坐在键盘前的人类。开发者点击“加载客户”,ORM 获取客户记录。稍后,当开发者导航到“订单”选项卡时,ORM 根据需求延迟加载该客户的订单。会话范围(Session scope)巧妙地映射到单个用户的 HTTP 请求。

AI Agent 的工作方式并非如此。一个正在思考一组客户订单的 Agent 不会在 UI 中“导航” —— 它会在生成输出之前一次性加载所需的一切。当你给一个 Agent 一个 ORM 连接并让它分析客户流失时,Agent 会:

  1. 获取客户列表(1 次查询)
  2. 循环遍历每个客户以获取其订单(N 次查询)
  3. 对于每个订单,获取订单项(N × M 次查询)
  4. 对于每个产品,获取库存状态(N × M × K 次查询)

这是经典的 N+1 问题,而带有延迟加载功能的 ORM 确保了它的发生。对于只有单个用户的交互式 Web 应用,N+1 是一种性能隐患。而对于在一个包含数百条记录的推理循环中运行的 Agent 来说,N+1 是一场灾难:本应两次数据库查询就能完成的任务,却变成了 10,000 次。

ORM 完全不知道 Agent 会访问每一条相关记录 —— 它是为那些只会访问部分记录的人类设计的。

Agent 的查询模式究竟是什么样的

Agent 的查询模式在四个基本方面与人类模式不同。

批量读取优于探索。 人类应用一次加载一页结果,因为人类一次只能阅读一页。Agent 则需要预先获取完整数据集。一个分析客户留存的 Agent 不会点击分页 —— 它需要在推理之前将所有带有相关属性的客户加载到上下文中。围绕 50 项分页响应设计的 REST 端点迫使 Agent 进行数百次顺序请求来汇编上下文。

查询前的模式内省(Schema introspection)。 在 Agent 查询数据之前,它需要理解 Schema。这看似显而易见,但大多数 ORM 和 REST API 并不提供机器可读的 Schema 端点。Agent 要么接收手动编写的 Schema 描述(很快就会过时),要么在会话开始时运行昂贵的内省查询。在中等规模的数据库上,Schema 内省需要 3–5 秒,并产生足够的输出来在任何实际工作开始前就消耗掉上下文窗口的大部分。

暂定性与推测性写入。 当 Agent 采取行动时,它们通常需要在提交之前“尝试”一些操作 —— 探索如果更新一条记录会发生什么,而不实际更新它。REST 的 POST/PUT 语义不支持这一点。一旦调用 POST,写入就会发生。如果没有 REST 根本无法提供的事务语义,Agent 就无法实现推测执行、预览副作用或回滚推理分支。

高频重复读取。 Agent 通过在推理步骤中重新查询相同数据来维持上下文。与页面状态持久化的缓存浏览器会话不同,Agent 的每个推理步骤都可能重新获取相同的记录以确认当前状态。没有语义缓存的 REST API 会对每个请求一视同仁;Agent 在返回相同结果的查询上浪费 Token 和速率限制配额。

这些失配如何在生产环境中导致失败

失败模式并非仅存在于理论中。一项 2025 年的研究分析了来自五个开源多 Agent 系统的 150 条对话轨迹,识别出了 14 种不同的失败类别。上下文窗口溢出、Agent 在没有进展的情况下重复相同工具调用的推理循环,以及工具调用超时占据了列表的主要部分 —— 这些都是非为 Agent 负载特性设计的数据访问层的症状。

速率限制(Rate limiting)问题尤为尖锐。传统的 API 速率限制假设是人类交互:适度、可预测的请求量,且动作之间有时间间隔。一个为复杂任务构建上下文的 Agent 可能会在 10 秒内触发 50 次工具调用。为人类设计的速率限制(例如每小时 100 次请求,每秒 10 次请求)对于在几分钟内爆发 500 次请求以组装上下文窗口的 Agent 来说毫无意义。当触发速率限制时,Agent 要么直接失败,要么进入重试循环,从而在重试相同的失败调用时耗尽大量的 API 预算。

Token 浪费是更隐蔽的成本。无论 Agent 需要多少字段,ORM 都会返回完整的实体对象。一个拥有 30 个字段的 Customer 对象,即使 Agent 只需要 3 个字段,也会返回全部 30 个字段。乘以数千条记录,上下文窗口的 60–70% 就会被 Agent 永远不会使用的字段填满。这不仅是 Token 浪费 —— 随着模型注意力被无关数据分散,它还会降低推理质量。

Schema 发现的开销会在不同会话间累加。如果你的 Agent 在启动时执行 Schema 内省,且内省结果非常冗长(正如 SQL 和大多数 REST Schema 那样),那么在回答任何用户查询之前,你就已经消耗了上下文预算。已有团队报告称,在模型开始推理之前,Schema 文档就耗尽了整个上下文窗口。

Agent 优化的数据层长什么样

好消息是,这是一个具有具体解决方案的工程问题,而不是 Agent 的根本局限。

带有字段选择的批量端点。 与其使用返回固定响应形状的分页端点,适配 Agent 的 API 会公开具有显式字段选择的批量端点。单个端点可以仅返回 Agent 指定的字段以及 10,000 条记录,并附带元数据 —— 总数、截断状态、每条记录的估计 Token 成本 —— 这样 Agent 就可以在获取数据之前对规模进行推理。这能将数百个顺序请求转化为一个。

用 GraphQL 替代 REST。 这是在结构上影响最大的改变。GraphQL 的查询模型直接映射到 Agent 的访问模式:一个查询可以仅凭选择的字段遍历多个关系层级。GraphQL 内置的内省模式(introspection schema)是机器可读且一致的,消除了模式发现问题。Apollo 在 MCP 兼容的模式压缩方面的工作表明,GraphQL 模式可以被压缩以适应 Agent 的上下文窗口,且不丢失结构信息。

业务实体的语义层。 最深层的改进来自于为 Agent 提供语义层 —— 一个公开业务概念而非原始表格的 API。Agent 不再需要跨四个规范化表连接 order_itemsproductscustomersinventory,而是通过语义层在单次调用中查询 “过去 30 天按客户细分的毛利率”,语义层内部处理连接逻辑。这也防止了多 Agent 系统中的语义偏移(semantic drift)问题:即子 Agent 和编排器由于各自构建连接而导致指标定义不同。

重复读取的语义缓存。 对于在推理步骤中重复查询相同或相似数据的 Agent,使用向量相似度(Redis 等)的语义缓存会存储查询嵌入及其结果。当 Agent 再次询问相同问题时,缓存会返回之前的结果而无需访问数据库。团队报告称,在查询重复率高的 Agent 工作负载中,成本降低了 60–73%。

写入的事务语义。 Agent 的写入模式需要显式的事务支持:能够暂存写入、在事务中尝试执行、观察效果,然后提交或回滚。REST 的 POST/PUT 不提供这些功能。你是使用显式事务 ID、带有版本字段的乐观锁,还是 “草稿” 状态模式,取决于你的领域 —— 但关键在于将写入意图与写入执行分离,以便 Agent 可以在不破坏共享状态的情况下进行推测。

为 Agent 设计的速率限制。 基于 Token 的速率限制 —— 计算请求的实际资源消耗而不是请求次数 —— 能更好地匹配 Agent 的负载情况。一个发出返回 50,000 个 Token 的批量查询的 Agent,应该根据这些 Token 受到限制,而不是被视为与返回 100 个 Token 的 API 调用相同。Gartner 预测,到 2026 年,超过 30% 的 API 需求增长将来自 AI 工具;针对人类请求模式构建的架构将产生日益不准确的速率限制。

结构性转变

底层转变是将 Agent 视为具有不同访问模式的另一类 API 消费者,而不是异常活跃的人类。人类通过选择与你的 API 交互:加载哪个页面,点击哪个记录。Agent 通过穷举式探索进行交互:加载所有相关内容,对其进行推理,然后采取行动。

你的数据访问架构的每一层在构建时都考虑到了第一种模型。REST API、ORM、默认分页、延迟加载、会话范围、人类规模的速率限制 —— 所有这些都假设另一端是一个人类。

解决方法并不是替换你的整个技术栈。而是在现有面向人类的接口之外,添加一个面向 Agent 的接口:批量端点、感知模式的查询 API、语义层以及支持尝试性操作的事务语义。许多团队发现,前端已有的 GraphQL,只需少量补充,就能满足 Agent 80% 的需求。

当前的一个讽刺现象是,团队正投入巨大精力进行提示词工程(prompt engineering)以使 Agent 变得更聪明,而数据访问层却让 Agent 显得更笨 —— 迫使它们在不完整的上下文中工作,在无关字段上浪费 Token,并在推理中途触发速率限制时失败。先修复数据层。一旦 Agent 能够真正读取它需要的数据,它看起来会能力强得多。

References:Let's stay in touch and Follow me for more thoughts and updates