AI 元数据管理:自动给表和字段打标签,让数据目录活起来
公司数据仓库 3000 张表,新人问"用户流失数据在哪",老员工答"你去 dw.dm_user_churn_v3 看看"—— 这种对话每天都在发生。表名和字段名是给机器读的,不是给人理解的。AI 打标签,就是给这些冷冰冰的表名和字段名配上一个"人话翻译器"。
一、元数据目录的"冷尸"困局
提起数据目录,很多团队都有类似经历:老板说搞数据治理,Data Catalog 搭起来,第一批表手工录入了 200 条描述,然后……就没人更新了。三个月后,新增的 80 张表没有描述,改过的 30 个字段注释还停留在旧版本。元数据目录变成了"建完就死"的数字坟场。
问题出在手工业模式。手工录入元数据描述有三个致命缺陷:
一是人不够。3000 张表 × 平均 20 个字段 = 6 万个字段。靠 3 个数据开发手写注释,写到退休也写不完。
二是定义不一致。张三觉得 status 是"订单状态",李四觉得是"记录有效状态"。同一张表被不同人描述,用词五花八门,搜索时根本找不到。
三是不会更新。表结构变更了(加了个字段、改了类型),手工注释不会跟着改,积攒半年就面目全非了。
AI 打标签的作用就在这里:自动化地、一致地、持续地给每个表和字段贴上语义标签,让数据目录从"静态档案"变成"活地图"。
二、AI 打标签的三级分类体系
先定义清楚"打标签"具体打什么。套用百度谷歌的富媒体搜索思路,给表和字段打三层标签:
graph TD
A[元数据标签体系] –> B[L1: 业务域标签]
A –> C[L2: 数据特征标签]
A –> D[L3: 质量与血缘标签]
B –> B1["用户域 / 订单域 / 商品域 / 物流域"]
B –> B2["PII / 金融 / 医疗 等合规标签"]
C –> C1["主键 / 外键 / 维度 / 指标"]
C –> C2["枚举类 / ID类 / 时间类 / 文本类"]
D –> D1["时效性: 实时 / 准实时 / T+1"]
D –> D2["热度: 过去30天查询次数"]
D –> D3["血缘: 上游表 / 下游表"]
style B fill:#f96,stroke:#333,stroke-width:2px
style C fill:#69f,stroke:#333,stroke-width:2px
style D fill:#9f9,stroke:#333,stroke-width:2px
L1 业务域标签回答"这表是干嘛的"。AI 通过表名、字段名和少量数据样本来推断表属于哪个业务域。orders 表里有 user_id、amount、pay_time,明显是订单域。
L2 数据特征标签回答"这字段怎么用"。字段是维度还是指标,是 ID 还是分类值,这个标签对 BI 自动建模极有用 —- AI 知道 user_id 是维度、amount 是指标,写 SQL 时就能自动做 GROUP BY user_id, SUM(amount)。
L3 质量与血缘标签回答"这表可信吗"。更新频率、数据量、热度、血缘链路,这些是运维向的信息,帮人快速评估"这张表值不值得用"。
三、自动标签生成的技术实现
标签的生成分两步走:离线批量打标和在线增量更新。
离线打标的核心是用 LLM 批量处理元数据。对每张表,构造一个包含表名、字段列表、字段类型和少量数据样本的 Prompt:
import json
from typing import List, Dict
def generate_table_tags(
table_name: str,
columns: List[Dict],
sample_data: Dict[str, List],
llm_client
) -> Dict:
"""
用 AI 给一张表和它的所有字段自动打标签
设计意图:不给模型全量数据,只传字段名+类型+脱敏样本值。
模型综合这三个维度信息,输出结构化的标签 JSON,
供数据目录前端展示和搜索索引使用。
"""
# 构造字段描述摘要
columns_desc = []
for col in columns:
col_name = col["name"]
col_type = col["type"]
# 取前 3 条样本值并脱敏
samples = [str(v)[:20] + ("…" if len(str(v)) > 20 else "")
for v in sample_data.get(col_name, [])[:3]]
columns_desc.append(
f"- {col_name} ({col_type}): 样本={samples}"
)
prompt = f"""你是数据治理专家。请为下表生成标签:
表名: {table_name}
字段信息:
{chr(10).join(columns_desc)}
为以下三类标签返回 JSON:
1. business_tags: 业务域标签,如"用户域"、"订单域"、"商品域"、"物流域"、"营销域"
2. field_tags: 每个字段的数据特征标签,从以下选项组合:
– 角色: "主键"、"外键"、"维度"、"指标"、"时间戳"、"分区键"
– 内容: "ID类"、"名称类"、"金额类"、"日期类"、"枚举类"、"文本类"
– 敏感度: "PII"、"非敏感"、"待确认"
3. quality_tags: 从字段类型和命名推断的质量标签
– 时效预估: "实时/准实时/T+1"
– 置信度: 0-1
输出格式:
{{
"table": {{
"business_tags": ["订单域"],
"description": "一句话业务描述"
}},
"fields": {{
"user_id": {{
"tags": ["维度", "ID类", "PII"],
"description": "用户唯一标识"
}},
"amount": {{
"tags": ["指标", "金额类", "非敏感"],
"description": "订单金额"
}}
}}
}}
"""
response = llm_client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"}
)
return json.loads(response.choices[0].message.content)
四、从标签到搜索:让数据目录真正可用
打完标签只是第一步,真正让数据目录"活"起来的是基于标签的语义搜索。
传统数据目录搜索只能做字段名精确匹配。你搜"活跃用户",如果字段叫 active_users 能搜到;如果叫 user_active_flag 或者 is_user_alive,大概率搜不到。AI 标签解决了这个"同义异名"问题 —- user_active_flag 和 is_user_alive 都被打上了"用户活跃度"的业务标签,语义搜索能命中。
def semantic_search(query: str, tagged_tables: List[Dict]) -> List[Dict]:
"""
基于标签的语义搜索:让用户用自然语言找到表
设计意图:传统搜索靠字段名匹配,同义词和近义词完全覆盖不到。
引入标签后,搜索匹配的是"业务语义"而非"字段字面",
用户搜"活跃用户"能命中有 "user_active_flag" 字段且被打上"用户活跃度"标签的表。
"""
# Step 1: 将用户的搜索意图也转为标签
intent_prompt = f"""用户搜索: "{query}"
请返回匹配的业务域标签和字段特征标签。
例如: 搜"活跃用户" → 业务标签:["用户域"], 字段标签:["活跃度","用户行为"]"""
# Step 2: 用标签做交集匹配
# 计算每张表的标签与搜索意图标签的重合度
scored_tables = []
for table in tagged_tables:
score = 0
# 业务域标签匹配
score += len(set(table["business_tags"]) & set(search_tags))
# 字段标签匹配
for field_tags in table["field_tags"].values():
score += len(set(field_tags) & set(field_tags_target))
scored_tables.append((table["name"], score))
# Step 3: 按匹配度排序返回
scored_tables.sort(key=lambda x: x[1], reverse=True)
return [t for t, _ in scored_tables[:10]]
这其实在模仿一个"老员工介绍表"的过程:老员工脑子里有一张"表→做什么用"的映射,用户问什么,他能快速联想出对应的表。AI 标签就是把这张映射外化、结构化。
五、总结
元数据目录最怕"建完就死"。手工录入解决不了增量问题,建得再好看也是摆设。AI 打标签让标注这件事从"手工填写"变成"自动生成+人工校验",解决了人力和时效性两个核心痛点。
落地路线建议:先从 50 张核心表做起,验证标签准确率 > 90% 再推广;标签体系不用太复杂,L1 业务域 + L2 字段特征就够用了;最后一定要接上搜索接口,标签是为搜索服务的,打完标签没人搜等于白干。







