AI 数据字典自动生成:从字段注释到业务口径的智能补全
AI 数据字典自动生成:从字段注释到业务口径的智能补全
接手的表有 200 个字段,注释全是 NULL?让 AI 帮你从字段名和数据值里推断含义,自动补齐数据字典。
一、数据字典是最大的技术债
如果你在大厂做过数据开发,一定经历过这个噩梦:接了个需求要用某张表,打开一看——200 个字段,150 个没有注释,剩下 50 个的注释是"用户标识""金额""状态"这种说了等于没说的废话。
更要命的是业务口径。"活跃用户"在这张表里是"过去 7 天有登录",在另一张表里是"过去 30 天有下单"。没有一个统一的数据字典,每次写 SQL 都要先花半天搞清楚"这个字段到底是什么意思"。
数据字典的维护在大多数团队里都是"重要但不紧急"的事。开发忙着上线,谁会专门花时间去补 50 张表的注释?结果就是技术债越滚越大。
AI 数据字典的思路是:不要指望人手动填,让 AI 从数据的"表面信息"和"内在规律"自动推断字段的业务含义。
为什么三层推断要分步走而不是让 AI 一步完成?如果你直接把"表名+字段名+200 行样例数据"扔给 LLM 让它生成注释,它的成功率大概只有 60%。不是 LLM 不行,而是你把太多不确定性一次塞给了它。分三步走的好处是每一步的输入都是确定的、可验证的。字段名解析是纯规则——
order_amt_7d一定拆成[order, amt, 7d],零概率出错。数据值推断是统计——唯一值 95% 就是 ID,枚举值在 {0,1} 就是布尔,准确率 85% 以上。前两步已经把"这个字段大概率是什么"确定了 80%,第三步 LLM 只是把结论润色成自然语言。这种规则+统计+AI的三段式 pipeline,准确率能到 90% 以上,比一步到位的 LLM 高 30 个百分点。这就是为什么在 AI 落地场景里,永远不要让 LLM 从零开始——先给它确定性信号,再让它发挥语言能力。
二、字段名解析:从命名约定推断含义
第一步最简单但最有效——从字段名本身提取信息。大多数团队的字段命名是有规律的,只是没人整理成文档:
import re from collections import defaultdict class FieldNameAnalyzer: """ 从字段名自动推断字段含义 利用命名约定(驼峰/下划线/前缀)反向推导 """ # 常见缩写映射表 —— 这是领域知识,需要持续积累 ABBREVIATIONS = { 'amt': '金额(amount)', 'cnt': '计数(count)', 'qty': '数量(quantity)', 'dt': '日期(date)', 'ts': '时间戳(timestamp)', 'uid': '用户ID(user_id)', 'sku': '商品编码(stock keeping unit)', 'cvr': '转化率(conversion rate)', 'ctr': '点击率(click through rate)', 'uv': '独立访客(unique visitor)', 'pv': '页面浏览(page view)', 'gmv': '成交总额(gross merchandise volume)', 'arpu': '每用户平均收入(average revenue per user)', 'dau': '日活跃用户(daily active users)', 'mau': '月活跃用户(monthly active users)', } # 常见前后缀含义 PREFIX_SUFFIX = { '_id': '标识符/主键', '_name': '名称', '_time': '时间', '_date': '日期', '_status': '状态', '_type': '类型', '_flag': '布尔标记(0/1)', '_rate': '比率/比例', '_ratio': '比率', '_rank': '排名', 'is_': '是否(布尔值)', 'has_': '是否有(布尔值)', 'total_': '总计', 'avg_': '平均值', 'max_': '最大值', 'min_': '最小值', } def parse_field_name(self, field_name): """ 解析单个字段名,拆成有意义的部分 比如 parse_field_name('total_order_amt_7d') → ['total', '总计'], ['order', '订单'], ['amt', '金额'], ['7d', '近7天'] """ tokens = [] # 按驼峰或下划线拆分 # 处理驼峰:orderStatus → ['order', 'status'] words = re.findall(r'[A-Z]?[a-z]+|[A-Z]+(?=[A-Z][a-z]|\d|\b)', field_name) # 处理下划线:order_status → ['order', 'status'] if len(words) <= 1: words = field_name.split('_') for word in words: word_lower = word.lower() # 先查缩写表 if word_lower in self.ABBREVIATIONS: tokens.append((word, self.ABBREVIATIONS[word_lower])) else: tokens.append((word, word)) return tokens def analyze_table_fields(self, table_fields): """ 分析整张表的字段,发现命名模式 比如所有以 _7d 结尾的字段都是"近7天"指标 """ results = {} patterns = defaultdict(list) for field_name in table_fields: tokens = self.parse_field_name(field_name) results[field_name] = { 'tokens': tokens, 'description': ' | '.join(f'{t}→{d}' for t, d in tokens) } # 发现模式:查找共享后缀的字段组 suffix = field_name.split('_')[-1] if '_' in field_name else '' if suffix.isdigit() or suffix.endswith('d'): patterns[suffix].append(field_name) return results, dict(patterns) # 使用示例 # analyzer = FieldNameAnalyzer() # fields = ['order_amt_7d', 'order_amt_30d', 'user_cnt_7d', 'cvr_7d', 'is_paid_user'] # results, patterns = analyzer.analyze_table_fields(fields) # for field, info in results.items(): # print(f"{field}: {info['description']}") # 输出: # order_amt_7d: order→order | amt→金额(amount) | 7d→7d # order_amt_30d: order→order | amt→金额(amount) | 30d→30d # 发现模式: {'7d': ['order_amt_7d', 'user_cnt_7d', 'cvr_7d'], ...}为什么缩写映射表是"活的领域知识"而不是一劳永逸的字典?同一个缩写在不同业务线可能有不同含义——你从支付团队学到的
amt是"金额",但到了广告团队amt可能是"amount(投放量)"。如果你把缩写映射表写死在代码里,换一个团队就得重写一份。更好的做法是把映射表做成可配置的 YAML 文件,每个业务线维护自己的缩写映射。更关键的是,PREFIX_SUFFIX里的模式发现——你通过分析整张表发现了所有以_7d结尾的字段是一组"近 7 天"指标,这个发现本身比任何预设的映射表都更有价值。AI 的价值不在于它背下了多少缩写,而在于它从一张新表里自动发现模式的能力。
三、数据值推断:从实际数据反推含义
光看字段名不够,有些字段名太简洁或者命名不规范。这时候就要看数据本身来推断。
import pandas as pd import numpy as np from datetime import datetime class DataValueAnalyzer: """ 从数据值推断字段的语义类型 核心思路:不同类型的数据有不同的分布特征 - ID 类:唯一值多,接近行数 - 枚举类:几个固定值反复出现 - 时间类:数字在合理范围内(时间戳/日期) - 金额类:正数,有小数点 """ # 常见枚举值的语义映射 ENUM_MAPPINGS = { frozenset([0, 1]): '布尔标记(是/否)', frozenset([0, 1, 2]): '三态状态(待处理/进行中/已完成)', frozenset([0, 1, 2, 3]): '四态状态(未开始/进行中/已完成/已取消)', } def infer_field_type(self, series, field_name): """ 根据数据分布推断字段的语义类型 """ series_clean = series.dropna() if len(series_clean) == 0: return {'type': '空字段', 'confidence': 1.0} result = {'field_name': field_name} # 唯一值占比 —— ID 类字段的核心特征 unique_ratio = series_clean.nunique() / len(series_clean) if unique_ratio > 0.9: result['type'] = 'ID标识符' result['confidence'] = min(unique_ratio, 1.0) result['note'] = f'{series_clean.nunique()} 个唯一值' # 枚举类:唯一值少,重复多 elif unique_ratio < 0.05 and series_clean.nunique() <= 20: unique_vals = sorted(series_clean.unique()) result['type'] = '枚举/状态码' result['values'] = unique_vals # 尝试匹配已知的枚举模式 val_set = frozenset(unique_vals[:4]) # 取前4个看模式 for pattern, meaning in self.ENUM_MAPPINGS.items(): if val_set.issubset(pattern) or pattern.issubset(val_set): result['semantic_hint'] = meaning break result['confidence'] = 0.85 # 时间戳:10 位且都在合理范围 elif ( series_clean.dtype in ['int64', 'float64'] and series_clean.min() > 1000000000 and series_clean.max() < 2000000000 and field_name.endswith(('_time', '_ts', 'timestamp')) ): result['type'] = 'Unix时间戳' result['confidence'] = 0.9 # 用 min/max 的 datetime 作为提示,方便人确认 result['time_range'] = f"{datetime.fromtimestamp(series_clean.min())} ~ {datetime.fromtimestamp(series_clean.max())}" result['note'] = result['time_range'] # 比率类:值在 0~1 之间 elif series_clean.between(0, 1).all() and field_name.endswith(('_rate', '_ratio')): result['type'] = '比率/概率' result['confidence'] = 0.8 else: result['type'] = '数值/文本' result['confidence'] = 0.3 return result # 使用示例 # analyzer = DataValueAnalyzer() # 用户状态字段:值全是 0/1/2 # s1 = pd.Series([0, 1, 2, 1, 0, 2, 1, 0, 0, 1] * 100) # result = analyzer.infer_field_type(s1, 'user_status') # print(result) # → {'type': '枚举/状态码', 'values': [0, 1, 2], # 'semantic_hint': '三态状态(待处理/进行中/已完成)', 'confidence': 0.85}为什么"数据值推断"的置信度不是 100%?一个字段的值全是 0 和 1,你推断它是布尔标记——但实际可能是"是否 VIP"(布尔),也可能是"支付方式(0=微信 1=支付宝)"(枚举)。一个字段的唯一值占比 95%,你推断它是 ID——但实际可能是一个高度随机化的设备指纹字段,不是业务 ID。数据值推断的本质是模式匹配,而模式匹配永远有误判的情况。这就是为什么
infer_field_type返回的每个结果都带了confidence字段——0.85 表示"大概率对但需要人看一眼"。反过来说,如果一个推断工具告诉你 100% 确定,那才是真的不靠谱。信任度评分是 AI 系统对自己的"诚实度"——高置信度的自动接受,低置信度的提交人工审核,这个"人机协同"的机制比"全自动"更可靠。
四、AI 综合推理:把碎片信息拼成完整描述
字段名解析给了一些线索,数据值推断给了另一些线索。但要把这些线索拼成一句完整准确的字段描述,还得靠 LLM。
def generate_field_description(field_name, field_type, analysis_result, context): """ 用 LLM 将分析结果转为自然语言描述 输入:字段名 + 分析结果 + 表级上下文 输出:自然的字段注释 + 业务口径说明 """ prompt = f"""你是资深数据分析师。请为以下数据库字段生成标准的字段注释。 ## 字段信息 - 字段名:{field_name} - 字段类型(数据库):{field_type} - 自动分析结果:{analysis_result} ## 表级上下文 {context} ## 输出要求 请按以下格式输出: 1. 字段注释(20字以内,一句说清楚) 2. 业务口径(这句话在业务上怎么理解的) 3. 取值范围说明(如果是枚举,列出各值的含义) ## 示例 字段名:order_amt_7d 输出: 1. 近7天订单金额 - 滚动窗口汇总 2. 口径:过去7个自然日(含当日)内用户完成的订单总金额(含运费,不含退款) 3. 取值范围:浮点数 ≥ 0,单位为元 """ # 实际使用时调用 LLM API # response = call_llm_api(prompt) return prompt这套流程整合起来的数据字典生成 pipeline:
为什么 LLM 在这一层只做"润色"而不做"发现"?这是整个 pipeline 架构最关键的决策。如果让 LLM 负责"推断字段含义",它就会靠"经验"来猜——看到
user_type就猜"用户类型,0=普通 1=VIP"——但这个枚举值映射是完全错误的,因为你们公司的 user_type 可能是 0=个人 1=企业。LLM 在"推断"任务上不可靠的原因在于:它依赖的是训练数据里的普遍模式,而不是你公司数据库里的实际数据分布。所以正确的职责分配是:让规则和统计做推断(因为它们依赖事实),让 LLM 做润色(因为它擅长语言组织)。规则告诉 LLM "这个字段是布尔类型、值域 0/1",LLM 把它写成"是否标记,0=否 1=是"——推断是规则做的,文字是 LLM 写的。混淆了这两个角色,整个系统的准确率就会从 90% 掉到 60%。
踩坑提醒
不要用生产库的全量数据跑数据值推断——
DataValueAnalyzer.infer_field_type需要扫描字段的所有值来计算唯一值占比,在生产库的千万级表上跑一次SELECT DISTINCT可能直接把库拖慢。正确做法是:只抽样(比如 10000 行)做推断,然后把结果标记为"基于抽样"而非"全量准确"。对于抽样结果置信度低的字段,再用离线表的全量数据跑一次,不要在生产主库上跑分析查询。字段名解析无法处理中文字段名和拼音混合命名——如果你的 Hive 表里有
用户年龄、zhifu_jine、order_金额这种混搭命名,FieldNameAnalyzer的正则拆分会全部失效。这种情况目前只能用 LLM 直译(把中文片段送给 LLM 翻译),但准确率会降到 60% 左右。团队里规范字段命名语言(要么全英文,要么全中文拼音转写)比写一个万能的解析器更实际。数据字典生成之后必须配"过期检测"——你花两天跑完了 50 张表的字典,三个月后 20% 的表结构变了(加了字段、改了类型、删了旧字段),但你没有任何机制检测这些变化。最简单的方案是加一个定时任务:每天对核心表的 DDL 做一次快照,对比昨天的快照,有变化就触发字典更新——不需要人工盯着表结构变化。
五、总结
AI 自动生成数据字典的思路是"先让机器做 80%,人复核 20%"。机器擅长从字段名规律和数据分布里找线索,但最终的语义理解和业务口径确认,还是需要人来把关。
实际落地时建议分步走:
- 先从核心表开始:挑 3-5 张最重要、使用最频繁的表,先跑一遍验证效果
- 建立缩写/枚举知识库:把团队内部的命名约定沉淀下来,AI 推断会越来越准
- 保持 human-in-the-loop:AI 生成的结果标记置信度,置信度低的主动提示人工审核
数据字典做好了,新人 onbarding 效率直接提升 50%——再也不用追着老员工问"这个字段啥意思"了。
本文由朱大喜原创,如果你也被数据字典问题折腾过,欢迎评论区吐槽~
