raw schema 不是业务授权模型:AI 数据访问为什么需要语义层
很多 AI 数据查询方案会从数据库 schema 开始。
这条路线很自然:
- 扫描数据库表。
- 读取字段名、类型、注释和外键。
- 把这些信息交给大模型。
- 让模型根据自然语言生成 SQL。
- 执行 SQL 并解释结果。
如果目标是帮助开发者理解一个测试库,或者在受控环境里做临时探索,这种方式有价值。schema introspection 可以降低接入成本,也能让模型更快知道数据库里大概有什么。
但在企业系统里,尤其是 ERP、CRM、财务、库存、人事这类业务系统里,raw schema 不能被当成业务授权模型。
原因很简单:数据库 schema 描述的是物理结构,不是业务可见性;字段存在,不代表当前用户可以看;表能 join,不代表当前场景应该允许这样 join;SQL 能执行成功,也不代表它符合宿主系统的权限、公司边界、字段可见性和审计要求。
AI 数据访问真正需要回答的问题不是:
数据库里有哪些表和字段?而是:
在当前用户、当前租户、当前公司和当前业务上下文下,系统允许暴露哪些业务问题的查询能力?这两个问题看起来相近,工程边界完全不同。
schema 能说明什么,不能说明什么
schema 能说明很多底层事实。
它能告诉你表名、列名、数据类型、索引、主键、外键、部分字段注释,以及数据库层面的约束。这些信息对查询编译、性能优化、数据建模都很重要。
但 schema 通常不能可靠说明下面这些问题:
- 当前用户能访问哪些业务对象;
- 当前用户能看到哪些记录;
- 当前用户能看到哪些字段;
- 当前用户所在公司或组织边界是什么;
- 某个状态字段在业务上如何解释;
- 某个金额字段是否含税、是否已折扣、是否已记账;
- 一个指标应该排除哪些异常状态、草稿状态或取消状态;
- 某个查询是否应该被拒绝;
- 查询结果应该如何被审计和复核。
这些问题不是数据库结构问题,而是业务授权和语义治理问题。
把 raw schema 直接交给 AI,等于把一个偏物理层的视图当成业务查询契约。短期看接入快,长期看会把权限、语义和审计问题后移到 SQL 生成之后,甚至后移到结果已经返回之后。
这不是一个可靠的生产边界。
字段存在,不代表字段可见
企业系统里最常见的误判之一,是把字段存在等同于字段可用。
一个表里可能同时包含销售额、成本、毛利、折扣、供应商价格、员工薪资、内部备注和审批意见。数据库 schema 会把这些列都列出来,但业务系统通常不会把它们对所有用户开放。
例如:
- 销售人员可以看订单金额,但不一定能看成本和毛利;
- 采购人员可以看供应商信息,但不一定能看客户授信;
- 普通员工可以看自己的报销记录,但不一定能看全公司的报销明细;
- 多公司环境下,一个用户可能只允许看自己公司的订单和库存;
- 某些字段在 UI 上对特定角色隐藏,或者只允许特定组访问。
如果 AI 查询入口直接基于 raw schema 生成 SQL,字段级权限就很容易被绕开。模型并不知道某个字段在当前用户上下文下是否可见,除非系统把这个信息作为强约束注入到查询执行前。
提示词里写“不要查询敏感字段”不够。提示词是软约束,授权模型必须是可执行的硬边界。
更稳妥的做法是:AI 不直接面对完整字段集合,而是面对一个经过裁剪的查询模型。查询模型只暴露当前场景允许使用的字段,并且在执行前再次校验请求字段是否在可见集合内。
字段不存在于查询模型里,就不能被引用。字段不可见,就应该 fail closed,而不是让模型猜一个替代字段。
表能关联,不代表业务上允许关联
数据库 schema 里的关系也容易被过度解读。
外键和 join path 描述的是数据结构之间的关联能力,但企业授权并不总是沿着物理关系自然传播。
一个订单可以关联客户,一个客户可以关联应收,一个应收可以关联发票,一个发票又可能关联付款和会计分录。从数据库视角看,这些关系都可能存在;从业务授权视角看,当前用户是否能沿着这条路径查询,取决于角色、公司、部门、对象权限、记录规则和字段可见性。
AI 生成 SQL 时,如果只根据表关系寻找可 join 路径,可能会把“技术上能连起来”的数据组合成“业务上不该暴露”的结果。
这在 ERP 系统里尤其明显。ERP 数据不是孤立表,而是业务流程的沉淀。销售、采购、库存、会计、人事之间有大量引用关系。仅靠 raw schema 很难表达:
- 哪些跨模块查询是允许的;
- 哪些跨公司聚合需要被阻止;
- 哪些明细只能在聚合后返回;
- 哪些字段只能出现在内部审计角色的查询里;
- 哪些模型之间的连接会引入越权推断风险。
所以,AI 查询需要的不只是表关系,而是受治理的查询面。
这个查询面应该显式定义:可查询的业务模型、可用字段、可用维度、可用指标、允许的过滤条件、允许的聚合粒度,以及必要的行级切片规则。
raw schema 缺少业务口径
除了权限问题,raw schema 还缺少业务口径。
字段名看起来很直接,但在真实系统里经常不够用。
amount_total 是含税还是不含税?state 的哪些值算已确认?取消订单是否应该计入销售额?草稿发票是否应该计入应收?库存数量看 qty_available 还是某个仓库维度下的可用量?跨币种金额按什么汇率折算?多公司数据是否可以合并?
这些问题无法通过字段名稳定推导。
模型可以猜,但生产系统不能把猜测当成口径。
业务口径需要显式建模。对 AI 查询来说,比较可靠的方式是把物理字段收敛成语义字段和指标,例如:
confirmedSalesAmount:只统计已确认销售订单,排除取消状态;openReceivableAmount:只统计未结清应收,按公司和币种规则处理;availableStockQty:基于指定仓库和库存状态计算可用库存;grossMargin:只对具备权限的角色开放,并明确计算公式。
这些字段和指标不应该由模型临场从 schema 猜出来,而应该由系统作为查询模型的一部分提供。
这也是语义层存在的价值:把物理结构、业务含义、权限边界和查询能力收敛成稳定契约。
授权模型应该在查询执行前生效
很多系统会尝试在 SQL 生成之后再做安全检查。
例如:
- 模型生成 SQL。
- 系统解析 SQL。
- 检查是否访问了敏感表和字段。
- 尝试补充权限过滤条件。
- 执行查询。
这比完全不检查要好,但它仍然把治理放在了太晚的位置。
SQL 是一个表达能力很强的语言。它可以包含子查询、CTE、函数、表达式、别名、窗口函数、union、case when 和各种方言特性。越到 SQL 层再做权限推断,检查器需要覆盖的语法面越大,遗漏风险越高。
更稳的设计是把授权模型前置:
- 先确定 effective user。
- 根据用户上下文列出可见 query model。
- 只向 AI 暴露当前可用模型和字段。
- AI 提交结构化查询请求。
- 引擎在查询模型上做字段校验、操作校验和权限注入。
- 最后再编译为底层 SQL。
在这条路径里,SQL 是引擎的编译产物,不是 AI 的原始授权入口。
这不是否定 SQL。SQL 仍然是底层数据库执行查询的核心语言。区别在于:业务授权不应该依赖模型自觉写出正确 SQL,而应该由语义层和查询引擎在执行前确定。
语义层不是“给 AI 更多字段”
有些团队会把语义层理解成更漂亮的 schema 文档:给表和字段加中文名、描述和示例,然后继续让模型写 SQL。
这还不够。
面向企业 AI 数据访问的语义层,至少应该承担四类责任。
第一,暴露业务查询模型,而不是完整数据库结构。
查询模型应该以业务对象和分析场景组织,而不是把所有表都交给 AI。销售订单分析、应收账款分析、库存可用量分析、采购交付分析,可以是不同查询模型。每个模型只暴露该场景需要的字段和指标。
第二,定义字段和指标口径。
字段不只是名字和类型。字段需要描述业务含义、可筛选性、可排序性、聚合方式、计算公式、单位、枚举值和适用范围。指标尤其需要明确计算规则。
第三,承载权限和可见性。
不同用户、角色、公司和租户看到的模型与字段可以不同。行级规则也应该在查询执行前注入,而不是让 AI 自己记住。
第四,保留审计证据。
系统应该能记录:用户问了什么、选择了哪个模型、提交了什么结构化查询、引用了哪些字段、注入了哪些权限条件、生成了什么底层 SQL、返回了多少行、是否发生拒绝或澄清。
这些责任合在一起,语义层才不只是 schema 注释,而是 AI 数据访问的治理边界。
一个更合理的工具契约
如果把这个思路落到 MCP 或其他 tool calling 协议上,工具契约应该尽量避免直接暴露 raw SQL 执行能力。
更合理的工具链通常是:
list_models
-> 返回当前用户可见的业务查询模型
describe_model
-> 返回某个模型里可用字段、指标、过滤条件和说明
query_model
-> 接收结构化查询请求,执行字段校验、权限注入和查询编译
clarify_or_reject
-> 当问题超出模型能力、权限范围或语义不清时,要求澄清或拒绝执行这类工具契约的关键不是名字,而是边界:AI 看到的是经过治理的查询面,不是数据库全貌;AI 提交的是受限查询意图,不是任意 SQL;执行层负责把语义请求转换成底层数据库查询,并留下可复核证据。
当用户问“上季度华东区大客户销售额同比变化”时,AI 不应该直接翻表猜 SQL,而应该先找到当前用户可见的销售分析模型,确认“华东区”“大客户”“销售额”“上季度”和“同比”的语义是否可用,再提交结构化查询。
如果当前模型没有客户分层字段,或者当前用户无权看区域维度,系统应该澄清或拒绝,而不是编一个看起来合理的 SQL。
对 Odoo 这类 ERP 尤其重要
Odoo 是一个很好的例子。
Odoo 的业务权限不等于 PostgreSQL schema。一个 Odoo 用户能看什么,通常取决于:
ir.model.access定义的模型级权限;ir.rule定义的记录规则;- 当前用户所属 group;
- multi-company 上下文;
- 字段级 group 可见性;
- 模块和二开的业务约束。
这些规则不会自然出现在 PostgreSQL 的表结构里。直接把 Odoo 数据库 schema 交给 AI,让它生成 SQL,容易绕过 Odoo ORM 和应用层权限。
更合理的做法是把 Odoo 的 effective user、company context、model access、record rules 和字段可见性纳入查询模型生成与执行前校验。AI 查询看到的不是 sale_order、account_move、stock_quant 这些裸表组合,而是面向当前用户裁剪后的业务查询模型。
这也是为什么“raw schema 不是业务授权模型”不是抽象原则,而是 Odoo / ERP 场景里很具体的工程边界。
什么时候 raw schema 仍然有用
这并不意味着 raw schema 完全没有价值。
schema introspection 仍然适合:
- 开发阶段理解数据库结构;
- 生成初始语义模型草稿;
- 辅助数据平台工程师做字段盘点;
- 在隔离环境里做临时分析;
- 检查语义模型与物理表之间的映射是否过期。
但这些用途应该发生在建模、开发、验证和运维环节,而不是直接作为面向最终用户的 AI 查询授权入口。
换句话说,raw schema 可以帮助系统构建语义层,但不应该替代语义层。
结论
企业 AI 数据访问的关键问题,不是让模型知道更多表和字段,而是让系统明确哪些业务查询在当前上下文下是被允许、可解释、可审计的。
raw schema 描述物理结构。业务授权模型描述谁能在什么上下文下查询什么业务对象、什么字段、什么指标,以及系统在什么情况下应该澄清或拒绝。
把这两者混在一起,会让 AI 查询看起来接入很快,但权限、语义和审计边界会变得模糊。
更稳的默认架构是:
- 用 schema introspection 辅助建模;
- 用语义模型表达物理结构与业务含义的映射;
- 用查询模型裁剪 AI 可见的业务查询面;
- 在执行前做字段校验、权限注入和上下文约束;
- 对每次查询保留可复核证据;
- 对越权、歧义或超出能力边界的问题明确澄清或拒绝。
Foggy Data MCP / Foggy Odoo Bridge 采用的也是这个方向:不把 AI 当成任意 SQL 生成器,而是通过受治理的查询模型、结构化查询 DSL、权限上下文和执行证据,把企业数据访问收敛到可维护的工程边界内。