跳到主要内容

你的数据库模式是 AI Agent 的心智模型

· 阅读需 10 分钟
Tian Pan
Software Engineer

大多数构建智能体(agent)的团队将数据库模式(schema)视为后端关注的问题。这种模式是由工程师为工程师设计的,遵循了数十年关系型数据库的最佳实践:积极规范化、避免冗余、拆分引用表、强制执行外键。这种方法对于联机事务处理(OLTP)系统是正确的,但对于 AI 智能体来说通常是错误的。

当智能体读取你的模式以确定如何回答问题时,它并不是在解析数据结构,而是在构建你业务的心智模型。如果你的模式是为已经理解该领域的应用程序代码构建的,那么智能体将根据为别人绘制的地图进行工作。结果就是幻觉式的联接、错误的聚合,以及原本只需两步却需要八步的工具调用链。

这主要不是提示词(prompting)的问题。你可以在系统提示词中添加模式描述,提供少样本示例(few-shot examples),并通过思维链(chain-of-thought)生成查询——但仍然会得到错误的结果。根本问题在于你的实体模型编码了 LLM 无法读取的假设。对智能体友好的模式仅凭结构就能使正确答案一目了然。

规范化陷阱

规范化是传统数据库设计的优点。第三范式消除了冗余,防止了更新异常,并保持了数据的一致性。它也是智能体 SQL 错误最常见的单一来源。

关于 LLM 如何处理不同模式形式的研究显示了一个清晰的模式:对于简单的检索查询,反规范化模式的表现显著优于规范化模式,而规范化模式在复杂的聚合方面具有优势。问题在于智能体经常面临检索查询——“获取客户当前的计划”或“这类产品有哪些”——而规范化模式将这些查询中的每一个都转化为多表联接,模型必须从头开始进行推理。

规范化模式导致的四种最常见失败模式是:

联接类型混淆。 模型在正确答案需要左外联接(LEFT OUTER JOIN)时默认使用内联接(INNER JOIN)。当记录在联接表中可能没有对应行时(例如没有发货的订单、没有订阅的用户),这很重要,因为内联接会静默丢弃这些行。智能体会返回自信但错误的结果。

基础表选择错误。 当规范化模式拥有如 usersuser_profilesuser_preferences 之类的表时,智能体经常为查询选择错误的锚点表。它们会从听起来与问题最相关的实体开始,而不是从正确代表查询语义的实体开始。

空值处理失败。 反规范化模式以不同方式暴露可空性问题。在规范化模式中,缺失的关系表现为缺失的外键行。在反规范化的模式中,它是一个空的列值。当空值的含义在模式中不明确时,智能体很难正确应用 IS NULL / IS NOT NULL 过滤器。

命名不透明。FLEX_FIELD_1status_cdacct_type 这样的列名对于没有应用层上下文来解码它们的模型来说是不透明的。隐晦的名称迫使智能体猜测或产生关于列代表什么的幻觉。如果它猜错了,它就会使用错误的过滤器,你就会得到结构有效但在语义上错误的结果。

为什么你的实体模型会成为智能体的心智模型

更深层的问题是概念性的。编写应用程序代码的人类工程师通过数月的背景信息来理解领域:产品会议、代码审查、团队内部知识。他们知道 status = 2 意味着“活跃”,因为他们在两年前的 Slack 讨论中读过那条注释。

智能体获取你的模式、可能还有一些描述,以及一个用户问题。它必须仅凭这些来重建你的领域模型。如果你的模式具有表现力——表名读起来像句子,关系显而易见,引用数据具有人类可读性——智能体就可以快速构建准确的模型。如果你的模式是为应用效率而构建的规范化关系结构,那么智能体在每次查询时都在对外部代码库进行逆向工程。

这直接影响工具调用的次数。查询设计不当模式的智能体会发出更多调用:首先探索模式结构,然后检查状态列包含哪些值,接着验证使用哪个联接,最后纠正初始错误结果。这些往返过程增加了延迟和成本。直接表达领域语义的模式可以显著减少这种探索开销。

减少工具调用的引用数据模式

引用数据——状态、类型、类别、省份/州的查找表——是产生不必要工具调用的常见放大器。一个带有独立 order_statuses 表(包含如 1: pending2: shipped3: delivered 等行)的模式要求智能体联接该表或进行单独的查找调用,仅仅为了按状态过滤订单。

替代方案是直接呈现引用数据。这并不意味着从你的存储模型中消除查找表,但它确实意味着你向智能体公开的内容应该是可读的值,而不是 ID。

三个实用的模式:

内联枚举类列。 对于低基数的引用数据,直接存储字符串值(status = 'shipped'),而不是指向状态表的外键。这是一种反规范化,但它是正确的一种。智能体可以编写 WHERE status = 'shipped' 而无需知道 ID 映射。

预联接视图。 创建实现常用联接的数据库视图。一个预先联接了客户、订单和订单状态描述的 customer_orders_view 消除了整类联接错误。智能体看到的是一个单一的逻辑实体——一个带有订单的客户——而不是它必须在脑海中组装的三个表。

计算指标列。 如果你的模式要求智能体计算诸如 days_since_signup = CURRENT_DATE - created_attotal_spend = SUM(order_amount) 之类的指标,那么每个需要这些指标的查询都会迫使智能体重新发现公式。公开这些预计算的视图或物化列使模型更快、更可靠。智能体不再猜测业务逻辑。

在实践中,实施了这些模式的团队报告称,在读密集型的智能体工作流中,工具调用次数大幅减少。这种收益并非源于让智能体变得更聪明,而是源于消除了它所读取内容的歧义。

对 Agent 友好的数据架构是什么样的

设计为 Agent 服务的架构,需要刻意将你的存储模型与面向 Agent 的模型分开。这两者可以有所不同,而且这种分歧是故意的。

存储模型: 规范化、使用外键、专为数据完整性和高效写入而设计。这是你的 OLTP 事实来源。请保留它。

面向 Agent 的模型: 一个由视图、反规范化投影和文档化关系组成的语义层,专门设计为易于被语言模型阅读。这是你通过工具暴露出来的内容。

具体来说,一个对 Agent 友好的 Schema 具有以下属性:

自描述名称。 每个表名和列名都应该是易读的纯英文。使用 user_account 而不是 usr_acct。使用 order_placed_at 而不是 ord_ts。使用 subscription_tier 而不是 sub_type_cd。如果你不会在与非技术利益相关者的对话中使用某个名称,请在视图层中将其重命名。

显式的关系文档。 当你将 Schema 上下文传递给 Agent 时,请显式包含外键关系。不要指望模型能从列命名约定中推断出这些关系。添加注释或 Schema 注解,说明 “orders.customer_id 引用了 customers.id”,可以消除一整类错误的 Join 操作。

受限的 Schema 暴露。 不要将整个数据库 Schema 传递给 Agent。使用检索或静态过滤,仅展示与当前任务相关的表。相比于上下文中有 50 张表的 Agent,只有 5 张相关表的 Agent 做出决策的质量会更高。在 Text-to-SQL 研究中,通过 Schema 子集化获得的准确率提升已有详尽的记录。

使用类型化枚举而非 ID。 在你的存储模型使用整数外键查询查找表的地方,面向 Agent 的模型应该暴露易于理解的字符串。status: "active" 是可解释的,而 status_id: 3 则不是——除非你也暴露了查找表,但这又增加了 Agent 必须正确执行的 Join 操作。

聚合面。 如果你的 Agent 经常回答诸如 “该客户的平均订单金额是多少” 或 “上个月新增了多少活跃用户” 之类的问题,请创建直接回答这些问题的视图或物化表。相比于从基础原理开始构建聚合查询,Agent 更擅长读取预计算好的答案。

自适应 Schema 策略

一个复杂的问题是:最优的 Schema 结构取决于查询类型。扁平的反规范化 Schema 更适合检索;具有显式 Join 路径的规范化 Schema 更适合聚合。你无法拥有一个对两者都是最优的 Schema。

实际的应对方案是为你的 Agent 维护多个 Schema 投影,并根据意图将查询路由到正确的那一个。一个轻量级的分类器——甚至是基于关键词的——就可以确定传入的查询是查找(“获取客户方案”)、聚合(“按地区汇总订单”)还是遍历(“查找与此营销活动关联的所有订单”)。每种类型都可以由不同的视图或 Schema 变体提供服务。

这增加了维护开销,但如果不这样做,单一的 Schema 在这两类查询中表现都会欠佳,从而在生产规模下导致 Agent 质量下降。

做好这一切比听起来要容易

好消息是,对 Agent 友好的 Schema 设计不需要重写你的数据库。存储模型保持不变。改变的是接口层:即你专门为 Agent 构建的视图、描述和 Schema 上下文。

从你的 Agent 最常接触的表开始。梳理它们尝试过但失败的查询。将失败分类:错误的 Join、缺失的过滤器、不正确的空值处理、晦涩的列名。每种失败类型都有一个 Schema 级别的修复方法,这不需要 Agent 变得更聪明——它只需要 Schema 变得更清晰。

Agent 不是能够阅读你的 ADR、在 Slack 中提问并逐渐学习你的领域模型的软件工程师。它读取你给它的 Schema 并尽其所能。那个 Schema 就是模型通用能力与你特定数据之间的认知接口。请据此进行设计。

那些做得好的团队并不一定使用了更好的模型或更好的 Prompt。他们不再将数据层视为一个由 Agent 自己去摸索的黑盒,而是开始将 Schema 设计视为 Agent 工程中的头等大事。

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