AI 查询 ERP 数据库时,为什么不该默认让模型写 SQL
很多 AI + 数据库 demo 都从同一条捷径开始:
- 连接一个大模型。
- 把数据库 schema 给它。
- 让模型生成 SQL。
- 执行 SQL。
- 把结果总结成一段话或一张图。
如果只是演示一个玩具数据库,这条路径没问题。它直观、快,也容易让人看到“自然语言问数”的效果。
但如果目标是 ERP、CRM、财务、库存、人事这类业务系统,这不应该是默认架构。
原因不是模型一定写不好 SQL。真正的问题是:ERP 数据库里的安全边界、业务语义、公司隔离、字段可见性和审计要求,通常并不完整地存在于物理表结构里。让模型直接看 schema 并生成 SQL,很容易把“能查到数据”误认为“应该允许这样查”。
生产系统里更应该问的问题不是:
模型能不能生成 SQL?而是:
系统能不能在保留 ERP 原有治理规则的前提下回答业务问题?这两个问题的工程含义完全不同。
裸 SQL 会跳过太多业务边界
ERP 数据不是普通的表和字段。
一个订单表里不仅有金额、客户和状态,还隐含着销售组织、公司范围、审批状态、会计口径、实施商自定义字段、用户权限和审计要求。一个字段叫 state,在不同模型里可能表示销售订单状态、发票状态、库存调拨状态、审批状态,也可能是本地二开后的业务状态。
数据库 schema 能告诉模型字段名和类型,但它很难告诉模型:
- 当前用户能看哪些业务对象;
- 哪些记录规则应该生效;
- 当前用户属于哪些公司;
- 哪些字段对当前角色不可见;
- 某个金额字段应该按什么业务口径解释;
- 某个查询是否应该被拒绝,而不是让模型猜一个答案。
这就是 raw SQL 作为默认接口的问题。
第一,权限通常不在 schema 里。表名、列名、外键关系不会自动表达“这个用户只能看自己公司”“这个销售只能看自己的客户”“这个字段对普通员工不可见”。
第二,业务语义不能靠字段名猜。模型可能猜对一部分字段,但猜对不是治理。生产系统需要可解释、可复查、可维护的口径。
第三,审计链路会变弱。如果最终只留下一个模型生成的 SQL 字符串,系统还要额外解释:为什么这条查询被允许?它映射到哪个业务模型?应用了哪些权限规则?返回结果是否在用户权限范围内?
第四,能力边界会不断膨胀。一旦系统接受任意 SQL,用户自然会继续要求 join、子查询、窗口函数、CTE、临时字段、“再加一列”。能力看起来变强了,但安全边界会越来越模糊。
所以,问题不是 SQL 本身有罪。问题是把“模型生成 raw SQL”当成 ERP AI 的默认接口。
raw schema 不是业务授权模型
很多 AI 数据查询方案会先做 schema introspection:扫描表、字段、关系,然后把这些信息交给模型。
这对开发辅助、探索环境、临时分析有价值。但它不等于业务授权模型。
业务授权模型至少要回答几类问题:
- 用户是谁:自然人、服务账号,还是某个租户下的应用身份;
- 用户能看什么:业务对象、记录范围、公司范围、字段范围;
- 用户能怎么查:明细、聚合、导出、跨模型分析是否允许;
- 指标是什么意思:销售额、应收、库存、采购额分别用哪些字段和状态计算;
- 结果如何复查:查询过程、权限边界、数据来源是否可审计。
这些内容很少能从裸表结构里可靠推导出来。
对 ERP 来说,更合适的接口不是“这里有数据库,你写 SQL 吧”,而是“这里有一组经过治理的业务查询模型,你可以在这些边界内提问”。
更稳的默认路线:语义模型和查询模型
语义层的价值,不是给 AI 更多自由,而是把自由限制在可维护的业务边界内。
一个面向 AI 查询的语义模型或查询模型,应该明确描述:
- 业务字段的含义;
- 可用维度和指标;
- 支持哪些过滤、排序和聚合;
- 哪些关系是允许使用的;
- 指标计算口径;
- 权限和字段边界;
- 查询能力范围;
- 什么时候需要澄清;
- 什么时候必须拒绝。
这样模型面对的就不是数据库内部结构,而是受控的业务接口。
例如用户问:
统计最近 30 天销售额最高的 5 个客户。更合理的执行路径应该是:
- 识别这是销售分析问题。
- 选择受治理的销售查询模型。
- 确认时间范围、指标口径和聚合粒度。
- 绑定当前有效用户。
- 在查询执行前应用用户权限、记录规则和公司边界。
- 只返回允许范围内的结构化结果。
- 保留查询模型、过滤条件、行数、执行证据和审计信息。
这里可以有 SQL 参与执行,但 SQL 不应该是模型面对用户时的主要接口,更不应该是绕开 ERP 权限体系的捷径。
MCP 解决 transport,不自动解决治理
MCP 给 AI client 调用工具提供了统一方式,这是有价值的。
但 MCP 本身并不会保证工具安全。
一个 MCP tool 可以暴露 raw SQL。也可以直接返回敏感字段。可以忽略业务系统权限。也可以把内部错误、堆栈、表结构泄露给调用方。
所以企业数据访问里,关键不只是“有没有 MCP”,而是 MCP tool 的边界怎么设计。
一个生产可接受的业务数据工具,至少应该在服务器端处理:
- authentication 和 tenant context;
- effective user 或 service principal 绑定;
- 宿主系统权限投影;
- 可见 query model 选择;
- 查询参数校验;
- 结果裁剪和脱敏;
- audit evidence;
- scope 不清楚时 fail closed。
换句话说,MCP 是 transport。真正的安全边界在 governed tool design。
Odoo 是一个很具体的例子
Odoo 很适合说明这个问题。
Odoo 用户不是 PostgreSQL 用户。Odoo 的权限控制在应用层,包括:
ir.model.access:模型级访问权限;ir.rule:记录规则;- multi-company:多公司边界;
- field visibility:字段可见性;
- 各模块自己的业务状态和工作流约束。
如果 AI 工具绕过 Odoo,直接连 PostgreSQL 查询,就可能绕过管理员真正配置和维护的权限体系。
更合理的做法是让 Odoo 继续作为权限和业务上下文的权威来源:
- MCP client 通过 Odoo 侧签发或管理的访问方式接入;
- 当前有效 Odoo 用户决定可见查询模型;
ir.model.access和ir.rule在查询执行前参与;- 多公司边界在查询条件里保留;
- 字段边界在结果返回给 AI 前处理;
- 管理员可以复查查询过程和结果范围。
这里的重点不是 Odoo 独有。Odoo 只是把 ERP 权限问题展示得足够清楚。其他业务系统同样需要类似的宿主权限投影和受控查询边界。
生产级 AI 查询要有澄清和拒绝
很多 demo 希望模型“什么都能答”。生产系统恰好相反:它必须知道什么时候不能答。
当用户没有给时间范围、指标口径、业务粒度时,系统应该澄清。例如:
你说的销售额是确认订单金额,还是已开票金额?当用户请求物理表访问、权限绕过、敏感字段、写回业务数据、预测或因果判断时,系统应该拒绝或转人工流程。例如:
这个请求需要访问物理表或超出当前用户权限范围,无法执行。澄清和拒绝不是体验缺陷,而是生产治理能力的一部分。
一个严肃的 AI 查询系统,不应该把所有问题都硬转成 SQL。它应该清楚地区分:
- 可以直接执行的标准查询;
- 需要补充业务口径的问题;
- 需要管理员授权或另走流程的问题;
- 应该拒绝的问题。
能力应该分层,而不是承诺任意 SQL
“支持任意 SQL”听起来强,但在企业业务数据里不是最值得追求的承诺。
更实际的做法是按问题类型分层:
- 标准 DSL:明细、过滤、聚合、TopN、趋势。
- 受控表达式查询:字段比较、日期差、范围校验、审计规则。
- 分阶段查询:占比、累计贡献、SLA、漏斗、窗口类模式。
- 小结果集二次分析:在已授权、已限制的数据集上做进一步整理。
- CLARIFY:缺少时间范围、指标定义、粒度或业务规则时先澄清。
- REJECT:请求预测、因果、物理表访问、权限绕过或写回业务数据时拒绝。
这比“模型可以写任何 SQL”更诚实,也更容易维护。
一个更安全的默认原则
如果要把 AI 接入 ERP 数据,建议把默认原则改成下面几条:
- 不把 unrestricted database schema 暴露给模型作为主要接口。
- 不让 model-generated SQL 成为生产查询的默认路径。
- 暴露受治理的 semantic query tools。
- 在执行前绑定 effective user,并投影宿主系统权限。
- 返回结构化结果,同时保留 audit 和 provenance。
- 边界不清楚时澄清;越界时拒绝。
- 不承诺任意 SQL、业务写回、权限绕过、预测或因果判断。
这条路线没有 raw SQL demo 那么炫,但更接近企业系统真正需要的东西:可解释、可审计、可维护,并且不破坏原有权限体系。
Foggy Odoo Bridge / Foggy Data MCP 目前走的也是这个方向:通过 Odoo Bridge、语义查询模型和 MCP 工具边界,让 AI client 查询 Odoo 业务数据时,仍然由 Odoo 权限控制可见范围。Community 更适合轻量评估和自托管实验,Pro 则面向 Odoo 侧完整采用、内置 AI Chat、模型治理、审计和支持。
这不是“让 AI 直接查数据库”的故事。更准确地说,是把 AI 数据访问放回业务系统应有的治理边界里。