1. 为什么数据分析平台里的“数据治理”会卡住整条链路
1.1 从“有数可用”到“数据可信”的真实距离
今年我一直在折腾一个内部的数据分析平台,框架上用了 LLM + Data Multi-Agents,前端接 BI 看板,后端接数仓。刚开始跑演示时,所有人都很兴奋:你跟 Agent 说一句“帮我看看华东区 6 月销售额环比”,它自己会选表、写 SQL、做图、给结论,体验确实像有个数据分析师坐在旁边。
可一上真实业务数据,问题就全冒出来了。华东区 6 月销售额算出来是 2 亿,可业务口同事说他们系统里看是 1.85 亿。两边都用了同一个数仓,为什么结果不一致?查了半天,发现销售订单表里“金额”字段被上游 CRM 同步任务改过口径,原来含税,现在不含税;更隐蔽的是,有部分门店数据在 5 月底做过一次迁移,老数据里的维度编码根本没有同步到新编码体系,Agent 用旧编码去匹配区域,结果丢了一大批订单。
这一下让我彻底意识到:LLM 再聪明,Multi-Agents 再多,它也只是在你的数据上面跑。数据本身不可信,任何智能化的分析都是空中楼阁。所以这个系列的第二篇,我没有马上去聊怎么做更炫酷的 Agent 能力,而是扎进数据治理——这件事做不好,后面所有环节都会被放大拖累。
数据治理这个词在企业里已经提了很多年,但在传统 CDH/DW 语境里,它更多是偏管理侧的词语:元数据管理系统、质量稽核、血缘追踪、权限审批,往往是 IT 部门被动响应,业务部门感知不到价值。而当 LLM 把数据分析从一个“工具”变成“数字员工”之后,数据治理的颗粒度、时效性、执行方式全都变了。Agent 每次答题前都得判断“哪个表是对的”“哪个字段是可信的”“我能不能看这部分数据”,这就是把治理能力从后台管理直接推到了分析入口。
1.2 LLM 时代,治理问题会被放大而不是消失
有人问过我:既然 Agent 都能理解自然语言了,是不是治理规则也可以用自然语言直接告诉它,就不用建复杂的治理系统了?这是一个非常危险的误区。
LLM 确实能“理解”很多规则描述,但理解不等于执行。你告诉 Agent “取数时注意过滤掉测试环境的数据”,它下一步还是会全表扫描,因为它不知道哪个库是测试环境,也不知道“环境”这个语义在表结构里该如何表达。更麻烦的是,Agent 会一本正经地编造一个它认为合理的条件。这恰恰是数据治理要解决的问题:把组织里的数据约定、业务口径、权限边界,变成 Agent 可识别、可推理、可校验的机器知识,而不是让 Agent 靠“猜”来碰运气。
我们平台当时踩过一个典型的坑:让 Agent 直接去做跨部门报表分析,结果它把不同系统中“用户数”的定义混用了。有的系统统计的是注册用户,有的统计的是去重后的登录用户,有的统计的是有购买行为的活跃用户。单看每个系统都没错,但 Agent 取数时如果只看字段名,三个“用户数”会被当作同一个指标,最终得出一份完全错误的留存结论。这已经不是 LLM 幻觉问题,而是企业数据语义治理不到位的问题。大模型只是忠实地把混乱的语义关系“翻译”成了更严谨的 SQL,把原来隐性的脏乱差变成了显性的错误结论罢了。
所以,在这一篇里,我想把数据治理拆开,讲清楚在 LLM + Multi-Agents 架构下,治理到底要治理什么,哪些技术点值得优先投入,哪些环节最容易翻车。题目里写“数据治理之一”,是因为治理本身包罗甚多,一篇写不完,这篇先聚焦元数据、质量、血缘、访问控制这四个最直接影响 Agent 分析结果的方向。
2. Data Multi-Agents 下的治理架构,我为什么这么拆
2.1 多智能体不是堆角色,而是“数据流分工”
很多团队一听说 Multi-Agents,就常规性地拆一堆 Agent:数据接入 Agent、指标问答 Agent、报表生成 Agent、异常预测 Agent……每个名字听着都合理,但跑起来就会陷入互相掣肘。比如指标问答 Agent 需要调用数据接入 Agent 拿数,但数据接入 Agent 只管连通性,不管质量,拿到一半脏数据就丢给上层 Agent,上层 Agent 又没感知,自然出错。
我现在的做法是:把多智能体理解成一条数据流水线,每个环节都有明确的责任和输出标准。所有 Agent 并行不悖,但数据流是严格串起来的——先治理、后分析、再呈现。治理类的 Agent 不是挂在边上的临时工,而是所有分析 Agent 前方的质检站。
基于这个思路,我的治理链路里定义了四个基础角色:
- 元数据 Agent:负责回答“这个表/字段是什么、准不准、怎么理解”。
- 质量 Agent:负责回答“这批数据现在能不能用、有什么异常”。
- 血缘 Agent:负责回答“这个字段是从哪来的、中间经过哪些加工”。
- 安全 Agent:负责回答“当前用户能看哪些数据、字段要怎么脱敏”。
这四个角色在平台上并不一定都是独立服务,更多时候是共享同一个治理底座,对外提供 API。上层 Planner Agent 收到用户问题后,会先并行调用这四个治理 Agent 做一次“数据健康度体检”,体检通过之后才进入取数和分析阶段。
2.2 “治理 Agent 四件套”的内部关系与协作机制
一开始我也以为,这四个 Agent 各干各的就行。后来发现,它们之间的关系必须靠一套共享知识库来打通,否则每个 Agent 只掌握局部信息,做出的判断经常自相矛盾。
举个真实例子。某报表里的“销售额”字段,在元数据 Agent 里定义是“不含税、已回款”,在质量 Agent 里发现近三天空值率上升到 30%,在血缘 Agent 里追溯到是因为上游接口改了字段映射。这几个信息如果分散在三个系统里,Planner Agent 无法形成整体判断。所以我在设计上增加了一个事件总线,由每个 Agent 在发现异常时向总线推事件,其他 Agent 订阅事件更新自己的状态。元数据 Agent 发现字段口径变更,马上会通知质量 Agent 重新启用“口径变更期间数据不可信”的标记;质量 Agent 发现异常,会触发血缘 Agent 去定位根因链路。
这样一来,四件套就不是四个孤岛,而是一张动态更新的知识网。上层分析 Agent 在写 SQL 之前,只要问一次“这个字段当前状态是什么”,就能拿到一个综合结论,而不是只看单一 Agent 的结果。
下面是我简化过的类型定义(示例伪代码):
@dataclass class FieldHealth: field_name: str semantic_type: str # 例如: monetary_value / user_id / date quality_score: float # 0-1 completeness: float consistency: bool lineage_verified: bool access_level: str # 例如: public / restricted / pii last_verified_at: datetime def health_check(field: str, user_context: dict) -> FieldHealth: md = metadata_agent.get_field_semantics(field) qa = quality_agent.evaluate(field, window="7d") lin = lineage_agent.trace(field) sa = security_agent.check(field, user_context) return FieldHealth(...)这段伪代码代表了数据流水线中“治理前置”的最小单元。实际系统里,我会再包一层缓存,避免 Agent 每回答一个问题都把全链路跑一遍。
2.3 为什么不用单一“超级 Agent”一把梭
也见过有人用一个大模型 Agent 把元数据、质量、权限判断全部写在 prompt 里,让模型自己推理。短期 demo 确实够用,长期维护是噩梦。
- 上下文窗口有限:企业级数据治理动辄涉及上千张表、几万个字段,prompt 根本塞不下。
- 知识更新困难:治理规则是经常变的,每次变更都改 prompt,不可控,而且模型容易被旧知识误导。
- 责任边界模糊:出了问题后,你很难确认是模型忘了质量规则,还是压根没调用质量检测工具。分开成不同 Agent,每次决策和行为都有日志,排查时能精确到某一条链路。
- 成本控制更优:只有需要深度分析时,才让轻量模型做路由,重任务再交给大模型,避免所有问题都打大模型接口。
从团队协作角度看,拆成独立 Agent 也更贴近组织分工。元数据治理这件事往往由一个数据平台组负责,质量稽核由另一个数据质量组负责,权限安全由基础架构组负责。系统模块和团队职责对齐,后续沟通成本低很多。
3. 核心环节落地实录:元数据、质量、血缘、权限
3.1 元数据 Agent:让 Agent 真正“看懂”字段,而不是猜字段
做数据分析的人都有个直觉:看到“sales_amt”就能猜出是销售额,看到“crt_time”就能猜出是创建时间。但这种直觉对 LLM 来说既可靠又不可靠。可靠的是它确实能根据命名做出高概率推断;不可靠的是,一旦字段命名不规范,或者同一个缩写在不同表里含义不同,猜错一次就是全盘错。
我设计元数据 Agent 时,思路不是让它去猜,而是让它把“猜”的过程变成“查证”的过程。具体分三步:
- 字段语义注册:每个表在接入平台时,通过自动化脚本解析 DDL、数据字典、历史 SQL 脚本,生成字段级别的描述,并同步人工维护的业务术语表。比如
amt字段在字典里登记为“订单实付金额(含运费,不含退款)”,user_id登记为“用户体系主键,仅包含注册用户”。 - 语义冲突检测:元数据 Agent 会定期扫描所有表,把所有字段名相似但语义描述不一致的字段找出来,形成“疑似同义不同义”清单。这个工作靠规则能完成一半,另一半要借助 LLM 做语义聚类。例如
user_cnt、uv_cnt、user_num本质都可能是“用户数/访客数”,Agent 让模型给出相似度评分,再结合字段备注和样例数据自动打标。 - 语义注入模板:当上层分析 Agent 准备针对某个字段写 SQL 时,元数据 Agent 会把该字段的完整语义描述、取值示例、单位/口径、有效期这些信息拼装成一小段“字段说明书”,动态注入到分析 Agent 的 prompt 里。
这个机制解决了一个很实际的问题:LLM 的统计能力很强,但业务口径不是统计出来的,是业务定义出来的。比如“GMV”在不同公司有完全不同的算法,你无法靠模型悟出来,只能依赖元数据去约束。
3.2 质量 Agent:用自然语言配置校验规则,而不是写死脚本
数据质量规则如果全部交给工程师写 Python/SQL,那治理这件事就会变成永远排不上期的需求桶。所以我给质量 Agent 加了一层自然语言规则翻译器。业务人员或数据分析师可以直接提交这样一句话:
“订单表里的 order_date 不能为空,如果最近一天缺失率超过 5%,请在报告里标记告警。”
质量 Agent 会把这句话解析成一个可执行的质检任务:扫描目标表,计算 order_date 的空值率,跑批频率设置为天级,产出结构化报告。这套能力依赖一个小模型做意图解析,再加上模板映射,并不需要太复杂的模型。
我在实际平台里落地的质检项大概有这几类:
| 质检类型 | 自然语言示例 | 底层实现逻辑 |
|---|---|---|
| 完整性 | “订单金额字段不能为空” | 计算空值率,超过阈值告警 |
| 唯一性 | “订单号不允许重复” | 查重复记录数,输出重复明细 |
| 有效性 | “省份字段必须在标准字典内” | 枚举匹配,检查非法值占比 |
| 及时性 | “昨天的数据今天早上8点前必须到位” | 比较分区表最新可用分区时间与当前时间 |
| 波动性 | “销售额环比波动超过30%要提示” | 对比当日值与历史均值,计算 Z-Score |
| 口径一致性 | “本月销售额与上月统计口径必须一致” | 比对元数据版本号,发现变更则触发复核 |
值得注意的是,质量 Agent 的告警结果不是只给运维人员看,它必须反向影响分析 Agent。在我们的流水线里,如果质量 Agent 给某张表打上“质量分低于 0.6”的标签,上层 Agent 取数时就会自动被拦截,并生成一个解释说明:“由于该表数据近 3 日缺失率达到 15%,本次分析结果置信度较低,建议先检查上游同步任务。”这样的提示,比直接在报告里给出错误数字要有价值得多。
3.3 血缘 Agent:可追溯才不会让错误“背锅”
数据分析里最尴尬的场景是,报告做出来,业务方问“这个数哪来的?”。如果回答“从宽表里查的”,就是没治理。血缘 Agent 的目标,是让每一个产出指标都可以沿着加工链路回溯到源头表,同时让每次加工变化都被记录。
血缘追踪在实现上分两个层面:
- 静态血缘:由 SQL 解析器自动解析数仓的 ETL 作业,识别每张表 SELECT 了哪些上游表、JOIN 了哪些键、做了哪些转换。这类血缘图比较稳定,适合做资产地图。
- 动态血缘:在查询分析阶段,实时记录 Agent 提交的 SQL 访问了哪些表、哪些字段,以及命中了什么质量标签。这一步能让平台知道“用户刚刚的查询依赖了已经被质量胶囊标记的字段”,从而在返回结果时主动附加可信度提示。
动态血缘我还用来做性能治理。某次 Agent 写了一个带笛卡尔积的查询,跑了一个多小时。血缘追踪显示它错误 JOIN 了两张没有关联键的大表。定位后,我在查询日志里加了一条识别规则:如果 Agent 生成的 SQL 出现 1:N 关联且关联字段不是血缘图中的外键关系,就会被安全拦截并退回重写。
血缘不仅仅是事后审计,更应该作为 Agent 选表时的重要参考。当系统里存在两张表都含有“销售额”字段时,血缘 Agent 会告诉上层 Agent:一张是 ODS 层原始表,一张是经过统一定义的 DWS 层指标表,分析场景应该优先选后者。这个“推荐优先取数路径”的能力,极大减少了 Agent 在不合适的表上乱取数的频率。
3.4 安全 Agent:数据脱敏与权限校验是最后一道闸
权限问题在 LLM 场景里比传统 BI 更敏感。传统 BI 里每个用户有固定菜单,他不会也不容易写出跨权限的 SQL。但 LLM 生成 SQL 时,压根不知道你是谁,你有多大权限。如果不在 Agent 层加一道强校验,很容易出现普通用户查询数据时把用户的手机号、身份证号等敏感字段带出来。
我实现的安全 Agent 分两级:
第一级,在取数前做字段级权限判断。用户发起查询后,安全 Agent 根据用户的角色列表判断:该用户能不能访问这张表?能不能访问这些敏感字段?如果不能,直接把字段从 Agent 可见的 schema 中去掉,甚至替换成脱敏后的仿真数据。对 LLM 来说,看不到的字段和值,就不会平白无故出现在 SQL 里。
第二级,在 SQL 执行后做结果集守卫。即使前面的过滤已经生效,用户也有可能利用 Agent 漏洞构造恶意查询(或者说模型误生成),把敏感字段带出来。这一级我会在查询结果返回前扫描每一列,如果结果集中出现手机号、邮箱、身份证等格式数据,而当前用户没有相应权限,则自动对该列做掩码处理,并在报告里标注“敏感字段已脱敏”。
这里有个容易忽略的细节:LLM 在生成 SQL 时,如果你只是简单给它一个 schema,它很容易在 JOIN 时把不相关的表也带进来,从而意外访问到无权限字段。我在实际开发中,会根据用户权限表,提前过滤掉用户不可见的表名和字段名,从物理上缩小 Agent 的决策空间。权限表本身也放在治理平台上,定期与统一身份认证系统同步。
以下是一段简化的权限过滤逻辑:
def filter_schema_for_user(user_id: str, raw_schema: list[Table]) -> list[Table]: allowed_tables = access_control.get_user_tables(user_id) visible_schema = [] for table in raw_schema: if table.name not in allowed_tables: continue allowed_cols = access_control.get_user_columns(user_id, table.name) visible_columns = [col for col in table.columns if col.name in allowed_cols] visible_schema.append(Table(table.name, visible_columns)) return visible_schema在跑了一段时间后,我发现安全 Agent 的价值不仅在于防泄露,它还能反哺质量。安全 Agent 的访问日志记录了每个用户高频查询哪些字段,这些“真实需求热度”可以反馈给元数据 Agent,帮助确定哪些字段应该优先补全语义描述。治理能力之间不再是割裂的,而是互相生长。
4. 实际运行中经常翻车的 6 个坑
4.1 脏数据把 Agent “带偏”,但报告依旧很漂亮
最典型的翻车现象是:Agent 基于一份质量很差的表生成了汇报,图表、摘要、结论什么都有,业务方差点拿去开周会。原因不是 Agent 不聪明,而是质量 Agent 当时没有配置那张表的完整性校验。等业务方发现问题时,已经晚了。
后来我规定:所有进入分析广度的表,如果没有质量评分,一律视为“未知质量”,不允许直接用于正式报告,只能用于探索。同时增加“快照对比”功能,Agent 生成结果时会同时展示“如果使用昨日数据会怎样”,通过两个版本对比提醒用户数据时效性问题。
4.2 元数据语义过时,Agent 越用越“自信”
还有一次,业务方把“销售额”的口径从“含税”调整为“不含税”,但元数据 Agent 里的描述还是旧口径。Agent 写 SQL 时完全没意识到口径变化,出来的数据全错了。这个问题的根源在于业务变更没有同步到治理平台。
解决方法是每次数据加工任务更新后,自动下发变更事件给元数据 Agent,并触发一个“流通预警”流程。如果某张表存在口径变更,所有相关字段的健康状态降级,分析 Agent 必须重新确认。这个机制跑起来以后,错误结论少了很多。
4.3 血缘断裂导致链路无法追溯
有一次某张核心表的字段源头被替换了,但血缘系统里没有记录,新旧字段的数据格式不一致,Agent 用旧字段去关联新表,导致最终结果多出大量 NULL。后来我在血缘解析上增加了“反向溯源校验”:每次查询执行完,反向检查结果集中每一列是否与血缘图中的字段路径一致,不一致就报警。
还有一个小经验:SQL 解析不要只解析标准语法,还要能处理各种方言,比如一些数据库的 limit 语法、开窗函数写法、replace into 等。血缘 Agent 的解析引擎一开始漏掉了很多非标准语法,导致图谱不完整。后来我换了一个具备方言扩展能力的解析库,并补充了一批历史任务日志进行验证,血缘覆盖率才提到 95% 以上。
4.4 权限校验和 Agent 推理之间的“递归打架”
安全 Agent 要在 SQL 执行前拦截,但它拦截条件越复杂,Agent 就越容易写出绕过的 SQL。比如我们限制只能查询最近 3 个月数据,Agent 可能通过拼时间函数绕过分区过滤,把历史数据全查出来。这不是安全 Agent 不够强,而是它没有把约束条件直接传递给 SQL 生成层。
正确的做法分两层:第一层,在 schema 注入阶段就把时间分区裁剪条件写死在表定义中,Agent 只能看到“分区表(近90天)”,而不是完整表;第二层,在执行层再校验一次查询的扫描分区数量。如果 Agent 试图扫描全分区,立刻熔断。这样既保留了 Agent 的灵活性,又卡住了底线。
4.5 治理 Agent 自身变成性能瓶颈
起初我让治理 Agent 在每次查询前都做一次全量往返,结果 SQL 还没写,元数据、质量、血缘三个 Agent 的 API 调用已经花了 4 秒多。加上大模型推理时间,整个回答过程逼近 20 秒,体验很差。
后来我加了治理指标缓存,把每个表的元数据版本、质量评分、血缘路径按小时粒度缓存到 Redis 里。只有表发生变更或者质量告警时,才会强制刷新。实测下来,缓存命中时治理检查整体耗时压到了 100ms 级别,对上层 Agent 几乎无感知。
4.6 LLM 生成列名和治理字典不匹配
最让我头疼的是 Agent 从列名里猜不出语义,比如一个字段叫a_0001,元数据字典里明明有解释,但 Agent 没有主动去查,直接用这个字段名拼 SQL,导致结果出来全是看不懂的内容。这个问题靠 prompt 调优无法根治,后来我在 Agent 的输出解析里加了一步“列语义校验”:当 SQL 涉及的字段不在可见 schema 列表中时,强制要求 Agent 重新生成,并将字段语义信息反馈回去。相当于给 Agent 装了一个“查字典”的强制动作。
5. 一些个人收尾建议,给正在做同类事情的朋友
数据治理和 LLM 分析平台的结合,说到底还是一个工程问题,不是某一个模型能单独解决的。我自己做下来最深的体会是:先别急着把概念铺得太大,拆出三四个核心治理能力,让它们真正在每个查询链路里起作用,比建一个“完整数据治理中台”更实际。
刚开始我只实现了元数据和质量这两个 Agent,血缘和安全都是后面才补的。事实证明,先跑通这两个,已经能拦下 70% 的错误。再加上血缘追溯和权限校验后,整体正确率才有了质的提升。所以如果你也在做一个 LLM + Multi-Agents 的数据分析项目,建议遵循这个优先级:元数据 -> 质量 -> 血缘 -> 权限,每一步都要跟真实业务场景绑定验证,不要光做内部 Demo。
再分享一个小技巧:所有治理 Agent 的决策记录都必须落库,包含时间、输入 schema、规则命中、输出结论。这不是为了出报告,而是为了将来排查问题时有据可依。我踩过好几次坑,都是因为没有日志,出了问题只能靠人肉猜。有了记录,复盘的效率翻倍。
下一篇我打算继续写数据治理之二,重点放到监控告警、数据分级分类,以及如何通过用户反馈闭环持续提升治理规则的质量。也欢迎在评论区聊聊你项目里遇到的数据治理难题,尤其是 Agent 被脏数据坑过哪些奇奇怪怪的方式,咱们一起攒点经验。