前言
几个月前,我做了一份 AI 转型规划。
规划里有智能问数、自动分析、知识问答,也有一条看起来很合理的实施路线:整理数据、接入模型、建设知识库,再逐步推广到业务部门。
领导听完没有直接否定,只问了几个很朴素的问题:
- 业务人员自己都说不清需求,AI 怎么听懂?
- 数据口径还没有完全统一,AI 查出来的数字能信吗?
- 用户没有权限意识,AI 会不会把不该查的数据也查出来?
- 每个行业都有自己的黑话和规则,通用模型真的懂吗?
- 为了让 AI 可用,要先写多少文档、整理多少关系?
- 很多工作用 SQL 和脚本就能做,为什么非要用 AI?
- AI 能保证准确、复现和审计吗?
- 真出了问题,谁负责?
我当时回答得并不好。
因为我想的是"模型能做什么",领导问的却是"系统凭什么可以进生产"。
后来我读了一些受控 Agent 和企业 Copilot 的实现,又开始做一个结构化数据问答原型,才意识到:这两个问题之间隔着一整套确定性工程。
模型擅长理解模糊表达,却不适合直接掌握数据库和生产权限。真正可落地的做法,不是让模型从一句话直接生成 SQL,而是把它放进一条受控链路:
text
用户自然语言
-> 识别意图,拆解参数
-> 标准化、补全、消歧
-> 校验格式、指标、主体和权限
-> 查询面向业务的安全数据
-> 执行固定的参数化 SQL
-> 补充时点、口径、单位和来源
-> 返回回答并记录审计
这条链路改变了我对 AI 数据工程的理解:
传统数据工程主要是在铺铁轨,让数据沿着确定路线流动;AI 数据工程则是在存在岔路的空间里设计迷宫与护栏,让概率系统只能在允许的范围内行动。
这篇文章不准备罗列框架名词,而是围绕三个问题展开:
| 文章主线 | 要回答的核心问题 | 主要工程手段 |
|---|---|---|
| 理解 | AI 如何把模糊问题变成标准动作? | 意图判断、参数拆解、标准化、补全 |
| 控制 | 系统如何阻止 AI 查错、越权和乱执行? | 工具契约、指标注册表、权限校验、固定 SQL |
| 可信 | 默认 AI 会犯错时,结果如何做到可信、可查和可负责? | 安全消费层、数据时点、审计、回归、人工审批 |
这三条主线也对应后文的三个核心章节。
一、理解不是直接生成 SQL,而是把模糊语言变成标准参数
上图表达的是第一层变化:左侧是松散、缺失上下文的自然语言,中间经过理解和分流,右侧形成可以被程序检查的结构化参数。
问题一:业务人员说不清需求,AI 怎么听懂?
假设业务人员问:
一建现在有多少个在建项目?
人听起来觉得很清楚,数据库却无法直接执行。至少有四处不确定:
- "一建"是哪一个标准组织?
- "在建项目数"统计所有项目,还是某个业务专题内的项目?
- "现在"表示当前数据快照,还是截至今天的历史累计?
- 一个项目有多份合同时,是数项目还是数合同?
所以自然语言问数的第一步不是生成 SQL,而是拆出结构:
text
主体:一建
指标:在建项目数
时间:当前
其他条件:未提供
随后系统做三件事。
1. 转换
把用户表达转换成系统认识的对象:
text
"一建" -> 组织主体
"在建项目数" -> 指标候选 active_project_count
"现在" -> 当前快照 current_snapshot
2. 标准化
用户说的是简称,数据库保存的是标准名称和组织 ID。系统需要通过组织别名表完成映射:
text
一建
-> 某建设集团有限公司
-> 标准组织 ID:sub-001
如果简称对应多个组织,不能让模型挑一个"看起来最像的",而应返回候选项,请用户确认。
3. 补全
用户不会主动说明统计粒度、单位和默认时间。缺失部分必须来自预先定义的指标规则,而不是模型临场猜测:
text
统计对象:业务专题范围内的项目
去重字段:project_id
聚合方式:COUNT(DISTINCT project_id)
单位:个
默认时间:当前快照
最后形成的不是 SQL,而是一组工具参数:
json
{
"metric": "active_project_count",
"subject_type": "subsidiary",
"subject_id": "sub-001",
"time_mode": "current_snapshot"
}
因此,"AI 能不能听懂模糊需求"不是一个纯模型能力问题。
更准确的答案是:
模型负责从模糊表达中提出结构化候选,确定性系统负责标准化、补全、消歧和拒绝。
用户不必理解数据库字段,但系统必须知道哪些信息可以默认、哪些必须追问、哪些绝不能猜。
问题二:通用模型不懂行业黑话,怎么办?
通用模型不可能天然知道每家公司的组织简称、指标口径和业务规则。
解决办法不是期待一个"更懂行业"的模型,而是把业务特殊性拆成不同性质的资产:
text
组织、项目、人员等标准事实 -> 数据表
指标定义和允许条件 -> 指标注册表
工具输入输出格式 -> 工具契约
表粒度、链路和规则说明 -> 可检索文档
权限和责任 -> 后端身份与审批流程
例如:
- "一建"对应哪个标准组织,放在组织标准表和别名表中;
- "在建项目数"统计什么范围、如何去重,放在指标注册表中;
- 某张主题表为何只包含有合同的项目,放在数据字典和口径文档中;
- 修改预警状态是否需要审批,放在权限和流程规则中。
所谓"让 AI 懂业务",不是把所有资料塞进 Prompt,而是把不同知识放到正确的位置,再让模型负责语义连接。
可以把这些资产的职责归纳成一张表:
| 资产 | 解决的问题 | 适合承载的内容 |
|---|---|---|
| 组织标准表/别名表 | 用户简称如何对应标准主体 | 组织 ID、标准名称、简称、历史名称 |
| 指标注册表 | 这个指标到底怎么算 | 统计范围、粒度、聚合、单位、时间模式、版本 |
| 工具契约 | 工具输入输出是否合规 | 必填字段、类型、枚举、成功和失败结构 |
| 数据字典/知识文档 | 为什么这样加工、如何解释 | 表粒度、链路、业务规则、预警判定依据 |
| 权限与审批规则 | 谁能查、谁能改 | 角色、组织范围、人工审批条件 |
问题三:为了让 AI 可用,要先写多少文档?
不需要先写一套企业百科全书,但也不能零规则起步。
应该区分三类材料。
必须先有的最小规则
凡是会决定系统能否执行的内容,必须先明确:
- 首批工具的输入输出格式;
- 首批指标口径;
- 主体标准值和别名;
- 数据权限范围;
- 失败和拒绝规则。
它们通常不是长篇文档,而是 JSON、YAML、数据库表和少量代码。
可以从现有资产转换的内容
企业通常已经有表字典、SQL、需求文档、看板口径和预警规则。真正的问题不是"从零写多少文档",而是如何把现有资产整理成机器可读、可引用的形式。
运行中增量沉淀的内容
无法识别的简称、被拒绝的指标、错误口径和用户追问,都可以进入失败案例集合,再决定沉淀为别名、规则、测试还是知识文档。
所以文档负担的合理答案不是"系统会自己长出全部知识",而是:
先整理足以支撑首批场景的最小确定性资产,再用真实失败推动增量建设。
二、参数只是请求,真正的控制发生在工具里

这张流程图给出精确链路:结构化参数必须依次经过输入契约、指标注册表、主体标准化、权限与兼容性检查,之后才能选择固定 SQL、查询安全消费视图。任意一关失败,都进入拒绝、澄清或系统错误分支。
问题四:参数已经是 JSON 了,为什么还要反复校验?
因为格式整齐不等于内容正确。
Agent 生成一段 JSON,只能说明它提出了一个结构化请求,不能说明这个请求已经被批准。
工具至少要过五关。
第一关:格式是否合法
工具会有一份 JSON Schema,可以把它理解为参数的格式说明书:
json
{
"type": "object",
"required": ["metric", "subject_type", "subject_id", "time_mode"],
"properties": {
"metric": {"type": "string"},
"subject_type": {
"type": "string",
"enum": ["subsidiary", "project"]
},
"subject_id": {"type": "string", "minLength": 1},
"time_mode": {
"type": "string",
"enum": ["current_snapshot"]
}
},
"additionalProperties": false
}
这份 JSON 本身不会检查任何东西。Python 程序会读取它,再调用校验库比较实际参数:
- 必填字段是否存在;
- 字段类型是否正确;
- 值是否在允许范围内;
- 是否夹带
sql、table_name等未声明字段。
工具契约的作用不是"把模糊输入自动变规范",而是:
先声明工具允许接收什么、必须返回什么,再由程序在运行时强制执行。
各道校验可以先用一张表区分:
| 校验阶段 | 检查对象 | 能回答什么 | 失败后果 |
|---|---|---|---|
| 输入契约 | 参数结构和类型 | 这份请求格式合法吗? | 格式拒绝 |
| 指标注册表 | 指标定义 | 这个指标被正式定义了吗? | 指标不支持 |
| 主体标准化 | 组织/项目等主体 | 用户说法能唯一对应标准对象吗? | 要求补充或澄清 |
| 参数兼容性 | 参数组合与指标规则 | 这个时间、主体和筛选适合该指标吗? | 业务拒绝 |
| 权限校验 | 用户身份与授权范围 | 当前用户有权查这个主体吗? | 权限拒绝 |
| 数据库执行 | 固定 SQL 和消费视图 | 系统能否正常取到结果? | 系统错误或无数据 |
第二关:指标是否真实存在
格式正确不代表业务正确。
下面这个参数完全符合"字符串"的格式要求:
json
{"metric": "employee_happiness_score"}
但如果系统没有正式定义这个指标,工具必须拒绝。
这就需要指标注册表:
yaml
name: active_project_count
business_name: 业务专题内在建项目数
source_view: approved_metric_view
subject_type: subsidiary
time_mode: current_snapshot
aggregation: count_distinct
measure_column: project_id
unit: 个
指标名称只是标识。真正决定答案含义的是它的全部属性:统计范围、粒度、聚合方式、单位、时间语义、来源、允许维度、权限和版本。
第三关:主体是否真实且唯一
sub-001 的格式可能完全合法,但数据库里未必存在这个组织;"一建"也可能映射到多个候选主体。
工具必须查询组织标准表或别名表,确认主体能够唯一落到受控数据中的标准 ID。
第四关:参数之间是否兼容
格式合法的参数,组合起来仍可能不合法。
例如接口允许传入历史日期,但某个指标的数据源只保存当前快照,不能还原过去某一天的状态。此时不能随便找一个日期字段硬查,而应明确返回:
当前指标只支持最新数据状态,不支持历史时点查询。
这一步检查主体类型、时间模式、过滤条件和分组维度是否适用于当前指标。
第五关:用户是否有权限
工具参数表示的是:
text
我想查询 sub-001。
权限数据表示的是:
text
当前用户只允许查询 sub-002。
两者不是一回事。
这就是"工具参数不是数据库权限"的含义:
请求里写了哪个组织,只能说明用户想查哪个组织;是否允许查询,必须由请求之外的可信身份和授权信息决定。
用户没有权限意识,模型也不能替用户授予权限。模型是请求的生成者,不是规则的裁判,更不是权限的签发者。
问题五:为什么 AI 不应该直接查询原始数仓表?
基础表往往服务多个业务场景。相同的"项目数",在项目管理、财务、劳务和安全专题中可能有不同范围。
上图把数据职责拆成四层:基础层负责清洗和公共关系,主题层确定业务范围与粒度,安全消费视图控制 AI 能看到的字段,受控工具负责参数、权限、SQL 和审计。
合理的数据链路应该是:
text
基础数据
-> 业务主题数据
-> 面向 AI 的安全消费视图
-> 指标查询工具
每层解决不同问题:
- 基础数据层处理多源合并、有效状态、删除标记和公共组织关系;
- 业务主题层确定哪些记录属于当前业务范围;
- 安全消费视图只暴露已批准字段,隐藏身份证、工资明细等不必要的敏感数据;
- 查询工具校验参数和权限,执行允许的固定查询。
这和给后端看板准备消费数据,本质上没有区别。后端不会每次展示指标时重新理解原始表的复杂加工逻辑,AI 也不应该。
但把数据加工到消费层,不等于可以丢掉口径说明。AI 无需知道每个删除标记、状态码如何过滤,开发者仍然必须知道,否则出了差异无法解释。
安全消费视图的价值不是给表换一个高级名字,而是明确:
text
AI 能看到哪些数据;
AI 看不到哪些数据;
哪些复杂加工已经在上游完成。
问题六:最终 SQL 是怎么执行的?
Agent 不生成任意 SQL。已经注册的指标绑定固定 SQL 模板或安全视图,用户输入只作为参数值传入。
例如固定模板:
sql
SELECT COUNT(DISTINCT project_id) AS metric_value
FROM approved_metric_view
WHERE subsidiary_id = %s
参数单独传入:
python
params = ["sub-001"]
这不是把字符串直接拼进 SQL,而是:
text
固定 SQL 结构 + 独立参数绑定
这样用户输入可以改变查询值,却不能增加表名、修改字段、插入新的 SQL 语句。
问题七:哪些工作应该用 AI,哪些不该用?
如果输入、规则和输出都确定,脚本往往更合适:
- 字段转义和别名生成;
- 固定格式校验;
- 定时同步;
- 主键重复检查;
- 已知规则的 SQL 改写。
AI 的价值主要出现在"输入表达不稳定,但最终动作可以被标准化"的地方:
- 用户用不同说法询问同一个指标;
- 需要从问题中识别组织、项目、时间和意图;
- 需要跨文档解释一条规则;
- 需要把复杂材料整理成人可以判断的候选方案。
一个实用判断标准是:
text
确定输入 + 确定规则 + 确定输出
-> 优先脚本
模糊输入 + 可枚举动作 + 可验证结果
-> 适合受控 AI
模糊输入 + 无法验证结果 + 高风险执行
-> 暂不自动化,或必须人工审批
AI 转型不是把脚本换成模型,而是让模型负责语义入口,让确定性代码负责执行边界。
三、不要假设 AI 会正确,要假设它一定会错
第三张图的中心不是"聪明的 AI",而是一个不稳定的概率核心。周围的防线各自检查不同类型的问题,错误路径被拦截、隔离,只有验证后的路径到达输出;最后仍然保留人工控制。
问题八:AI 能保证每次回答准确吗?
不能。
如果一个方案以"模型足够聪明,所以应该不会错"为前提,它还不具备生产条件。
这里可以借用分布式系统的一种基本思想:
不要把系统建立在某个组件永远正常的假设上,而要从设计之初就假设它会失败。
分布式系统会考虑节点宕机、网络中断、数据损坏和重复执行;AI 数据工程也应该从一开始就假设模型会误解问题、漏掉条件、选错主体,甚至生成一段格式正确但业务错误的参数。
但这不意味着简单地让多个模型各回答一次,再用多数票决定答案。
多个模型可能共享相同上下文、相同偏见和相同口径错误。三个模型一起算错,并不会让错误变正确。
AI 场景真正需要的是异构冗余:让不同机制检查不同类型的错误。
| 防线 | 主要防什么错 | 具体做法 |
|---|---|---|
| 结构防线 | 参数结构错误、夹带任意指令 | 工具参数 + JSON Schema,不直接接收 SQL |
| 语义防线 | 指标或主体理解错误 | 指标注册表、组织标准表、别名映射 |
| 权限防线 | 越权查询 | 可信用户身份 + 服务端组织授权范围 |
| 执行防线 | 查询范围失控、数据层越界 | 固定 SQL + 安全消费视图 + 结果检查 |
| 证据防线 | 无依据解释、口径遗漏 | 数据时点、口径、单位、来源;证据不足则拒答 |
| 版本防线 | 无法复现和定位变化 | 指标版本、SQL 模板、参数、数据时点、trace_id |
| 人工防线 | 高风险动作自动执行 | 修改、解除预警、对外发送等操作人工审批 |
| 故障兜底 | 失败后继续编造 | 明确报错、不编造结果、失败案例进入回归测试 |
这些措施不能证明 AI 永远正确,但能把一个模糊的"模型说错了",拆成多个可以发现、拒绝、定位和追责的失败点。
准确性的工程目标不应是空泛地承诺 100%,而应该是:
对允许自动化的范围给出可测试的正确性,对无法确认的情况明确拒绝,对错误保留足够证据定位原因。
问题九:为什么回答一个数字,还要附带数据时点?
因为指标值不是脱离时间存在的。
今天查询得到 86,明天得到 88,两次都可能正确。没有数据时点,用户无法判断:
- 是业务数据更新了;
- 是指标口径变化了;
- 是系统查到了旧数据;
- 还是查询真的出错了。
完整回答不应只有:
在建项目数是 86 个。
而应类似:
截至 8 月 19 日 6:04,某建设集团在当前业务范围内有 86 个在建项目。统计口径为按项目 ID 去重,数据来源为项目指标消费模型。
如果暂时拿不到稳定的 ETL 批次时间,可以使用消费数据中的业务记录最大更新时间,但必须准确命名,不能把"源业务记录更新时间"说成"数仓任务完成时间"。
数据时点有四个实际用途:
- 向业务解释两次查询为何不同;
- 发现业务系统已更新而 AI 消费层尚未更新的延迟;
- 让开发知道当时系统看到了哪批数据;
- 结合指标版本区分"数据变了"还是"口径变了"。
查询结果只是原料。一个可交付回答至少还需要主体、指标名称、数值、单位、数据时点、口径和来源。
问题十:结果如何复现和审计?
大模型生成的句子不一定每次逐字相同。生产系统真正需要复现的是:
- 当时识别成了什么意图;
- 生成了哪些工具参数;
- 参数如何被标准化;
- 使用了哪个指标定义版本;
- 查询了哪个数据时点;
- 执行了哪个 SQL 模板;
- 哪道校验通过或失败;
- 最终返回了什么结构化结果。
每次请求可以生成一个 trace_id,串起完整链路;每次工具调用再生成一个 audit_id,记录该工具的输入、输出和状态。
json
{
"trace_id": "trace-001",
"audit_id": "audit-001",
"tool": "query_kpi",
"metric": "active_project_count",
"normalized_subject_id": "sub-001",
"metric_registry_version": "1.0.0",
"sql_template_id": "active_project_count_v1",
"data_time": "2026-08-19 06:04:00",
"status": "ok"
}
这不会让模型变成确定性程序,却能让一次回答具备可回放的上下文。
回归测试也不应永远断言生产数据必须等于某个数字。生产数据会变化,更合理的测试是:
- 固定测试数据下结果是否符合预期;
- 未注册指标是否稳定拒绝;
- 无权限组织是否无法查询;
- 不支持历史时点时是否明确返回错误;
- 输出是否始终包含主体、时点、单位和来源。
可复现不是要求概率系统停止变化,而是把变化关进有版本、有输入、有输出、有记录的边界里。
问题十一:拒绝和报错,应该返回给谁看?
工具最好使用统一的机器响应结构:
json
{
"status": "denied",
"result_code": "SUBJECT_AMBIGUOUS",
"message": "查询主体无法唯一确定",
"data": null,
"trace_id": "trace-001",
"audit_id": "audit-001"
}
统一的是字段结构,不是所有错误的内容。
缺少主体、指标未注册、权限不足和数据库故障,必须有不同错误码和处理方式。同一个失败事件还需要形成两种展示。
给用户的是安全、可行动的信息:
"一建"对应多个组织,请从候选组织中选择。追踪编号:trace-001。
给开发和审计系统的是完整上下文:
- 原始参数和标准化参数;
- 失败发生在哪道校验;
- 指标注册表版本;
- 权限判断结果;
- SQL 模板 ID 或查询指纹;
- 数据库异常和堆栈;
trace_id与audit_id。
客户和开发看到的不是两套事实,而是同一事件的两种投影:
text
统一结构化事件
-> 用户可理解的提示
-> 开发可排查的审计记录
问题十二:出了错,AI 能担责吗?
不能。
模型没有组织身份,也不能承担法律和业务责任。"让 AI 担责"本身就是一个错误命题。
系统真正需要明确的是责任链:
- 谁批准指标口径;
- 谁维护数据消费模型;
- 谁配置用户权限;
- 谁决定哪些动作可以自动执行;
- 谁审批高风险写操作;
- 出现异常时由谁根据审计记录处理。
合理的责任分工是:
text
AI 负责理解、整理和提出候选动作;
确定性程序负责校验和限制;
有授权的人负责高风险决策。
对于只读问数,可以通过指标负责人、数据模型负责人和系统维护者划分责任。对于修改数据、解除预警、对外发送等高风险动作,AI 最多负责整理参数、证据和影响范围,最终执行必须进入人工审批。
这不是降低 AI 的价值,而是让它有条件进入真实流程。
四、回到最初的 AI 转型规划
如果现在重新汇报那份规划,我不会先讲模型、向量数据库或 Agent 框架,而会先讲四件事。
AI 如何理解
text
自然语言
-> 意图、主体、指标、时间
-> 标准化工具参数
模型处理模糊表达,规则负责补全和消歧。
AI 如何受控
text
输入契约
-> 指标注册表
-> 主体校验
-> 参数兼容性
-> 权限校验
-> 固定参数化 SQL
Agent 只能提出请求,不能给自己授权,也不能直接执行任意 SQL。
AI 如何可信
text
安全消费数据
-> 指标值
-> 数据时点
-> 口径、单位和来源
-> trace 与审计
不承诺永不犯错,但要求答案可解释、失败可拒绝、问题可定位。
AI 如何承担合适角色
text
脚本处理确定规则
AI 处理模糊入口
工具控制执行边界
人工承担高风险决策
这四件事才是 AI 数据项目能否落地的骨架。
结语
传统数据工程追求的是让数据沿着确定路线稳定流动,像铺铁轨。
AI 引入了一个新变量:用户表达是模糊的,模型判断是概率性的,执行结果却必须满足确定的业务规则。
我们不能把整条生产链路都交给模型,只能允许它在有限的岔路中做判断,并在每个关键路口设置检查、拒绝、记录和人工确认。
所以我仍然认同这个标题:
传统数据工程是在铺铁轨;AI 数据工程是在设计迷宫与护栏,给概率系统装上确定性的骨架。
但"迷宫与护栏"落到代码里并不神秘。它们可能只是一份 JSON Schema、一张指标表、一个安全视图、几段 Python 校验、一个固定 SQL 模板和一条审计记录。
抽象名词的意义,不是把简单技术说复杂,而是明确每个组件负责什么、不能做什么,以及出了问题该去哪里查。
当这些边界真正存在时,AI 才不再只是一个会说话的演示,而开始成为一个可以被验证、被限制、被审计的生产系统。
文中的受控工具、指标注册表与安全消费视图等设计,参考了开源企业 Copilot 项目的实现思路;业务名称和示例均已泛化。正文概念图由生成式图像模型辅助制作。