数据库文档标注 摘要
文档概览
本文讨论的不是通用文档管理,而是 AI 问数、AI2SQL、N2SQL 一类场景里,如何把数据库表结构相关文档改造成更适合 RAG 检索的知识库内容。
作者的判断很明确:N2SQL 的精准度,重点已经不再只是依赖产品工具本身,而是依赖知识库内核的精准召回能力。所谓“精准”,并不是单纯把文本切得更细,或把 chunk 长度调到某个经验值,而是要让系统能完整召回一块知识内容。
在数据库表文档中,这一点尤其重要,因为同一份文档里常同时混有多种信息:
- 表说明
- 业务说明
- 字段说明
- 关联关系
- 常见查询
- 数据样例
这些内容如果边界不清,切块后就很容易出现两类问题:
- 切分过粗:单个 chunk token 太多,挤占大模型上下文窗口
- 切分过细:业务语义和上下文关联被打散,导致召回不完整
因此,作者提出的重点不是继续纠缠 chunk 大小本身,而是通过结构化文档标注与文档边界标记,让切分系统在分块时仍能保留完整业务语义。
关键事实
完整知识块比单纯调 chunk 更关键
作者开篇强调,知识库召回的基础是“完整知识内容”。这意味着:
- 召回质量首先取决于知识块是否自洽、完整
- 仅靠分块长度参数,无法解决业务语义被割裂的问题
- 在 AI 问数领域,召回不完整会直接影响 SQL 生成准确率
也就是说,数据库文档分块的目标不是把文本机械切开,而是把“一个可以独立被理解和使用的知识单元”切出来。
表结构文档天然是多维信息混合体,必须明确边界
作者特别指出,表结构业务解读文档不是单一类型文本,而是多维内容叠加。对这种文档,如果没有清晰边界:
- 检索时可能只命中字段说明,漏掉业务规则
- 或只命中表说明,漏掉关联关系和样例
- 即使召回了表名相关 chunk,也未必能保证语义完整
所以数据库文档不适合只写成连续自然语言描述,而要对不同信息类型进行显式分区。
三级标注体系的目标是提升召回完整性
文中提出三类互补的标注方法,可视作三级标注体系:
- 标题层级标注法
- 信息块标记法
- 语义分隔符方法
三者共同作用的目标是:
- 让文档结构对人和机器都清晰
- 为切块提供稳定的边界
- 让召回结果尽可能落在完整模块上,而不是随机截断的段落上
结构化标注优于普通描述型文档
文章专门对比了改造前后的差异:
- 改造前:普通描述型文档,自由流动、边界模糊,按表名召回时难以保证完整性
- 改造后:表说明、字段信息、关联关系、业务特性、常见查询场景等被清晰分模块,并带有明确边界标记
作者给出的结论是,结构化标注能显著提高:
- 精准识别:更容易命中真正相关的信息块
- 完整性保障:降低知识被切散的问题
- 一致性维护:不同文档采用同样结构,后续扩展和维护更容易
可以用 LLM 做半自动批量转换
面对大量数据库文档,作者不建议纯手工改造,而是建议引入 LLM 做半自动流程:
- 先让 LLM 学习统一的标注模板
- 用 LangChain 脚本实现批量文档转换
- 从本地读取待转换文档清单
- 解析文档内容并提交给 LLM
- 由 LLM 按模板输出结构化标注文档
- 再调用评估模型评估转换质量
- 最后生成评估报告
作者说明这类脚本逻辑并不复杂,但文中没有提供具体代码实现。
重要细节
标题层级标注法:最实用的基础结构
作者认为标题层级法“最实用”,核心是用 Markdown 标题建立稳定层级,例如:
- 一级标题用于文档主题或表名
- 二级标题用于主要分类
- 三级标题用于次要分类
文中给出的约束包括:
- 一级标题
#:仅用于文档主题 - 二级标题
##:用于主要分类 - 三级标题
###:用于次要分类 - 标题要简洁明了,建议 3 到 7 个字
这类结构的价值在于,切块器即使不理解全部业务语义,也能先依赖标题层级做较稳妥的初级切分。
信息块标记法:用显式开始/结束标记保护边界
第二种方法是在结构区块前后加入明确标记,例如字段说明开始、字段说明结束、示例数据开始、示例数据结束。
作者建议使用 HTML 注释作为标记,原因是:
- 不影响文档可读性
- 标记名可以简洁明确
- 开始与结束成对出现,便于机器识别完整区块
这实际上是在给文档边界标记提供机器可识别的“硬边界”,比只靠自然段落分隔更稳定。
语义分隔符方法:适合进一步切出特定区块
第三种方法是使用文档中不会自然出现的特殊分隔符,例如:
---字段区块------字段区块结束------规则区块------规则区块结束---
实施要求有两个:
- 选择不会在正文自然出现的分隔符,避免误判
- 保持风格统一,不能一篇文档一个规则
它的价值在于,当标题层级还不够细时,可以继续把同一章节中的不同语义块拆出来。
account_acc 案例展示了“完整表文档块”如何设计
文中给出了一份较完整的会计科目表示例,表名为 account_acc。从这个案例可以看出,作者不是只做形式上的标题美化,而是把一个数据表相关知识拆成多个明确的业务模块。
案例中的最外层使用了整表级边界:
table-doc-begintable-doc-end
在整表范围内,至少包含以下模块:
- 表说明
- 业务用途
- 表数据样例解读
- 字段说明
- 关联关系
其中“表说明”被单独包裹在 table-desc-begin 与 table-desc-end 之间;“业务用途”被包裹在 business-usage-begin 与 business-usage-end 之间;“表数据样例解读”被放在 data-sample-begin 与 data-sample-end 之间;“字段说明”使用 field-chunk-begin 与 field-chunk-end;“关联关系”使用 relations-begin 与 relations-end。
这说明作者的设计重点是:不仅要把一张表的文档写全,还要让每种信息类型拥有独立、稳定、可切分、可召回的边界。
account_acc 案例中的具体事实
案例中保留了若干非常具体的业务信息,这些信息恰恰是结构化标注后更容易被完整召回的内容:
[account_acc](/wiki/it/ai/entity/account_acc)是“会计科目表”- 该表用于存储企业财务会计科目信息
- 它被描述为整个财务系统核算体系的核心基础数据表
- 它为资产清算、预算管理等业务模块提供科目引用支持
业务用途部分列出三类用途:
- 会计科目管理:定义科目代码、名称、层级结构、余额方向等
- 业务数据关联:供其他业务模块关联和查询会计科目
- 报表生成:在财务报表生成过程中获取会计科目信息
样例数据中给出了具体字段和值:
- 第一行示例:
id=166372830500000001,entid=1,accountcode=1001,accountname=现金,shortname=现金,parentcode=0 - 第二行示例:
id=166372830500000002,entid=1,accountcode=1001.001,accountname=现金_人民币,shortname=现金_人民币,parentcode=1001
字段说明里给出的细节包括:
id是主键,用于唯一标识一个会计科目entid关联企业维度表,默认全部都是 1,目前该字段没有启用accountcode是核算科目编码,为每个会计科目的唯一编码,示例格式为1001.001,用于快速定位和区分不同科目
关联关系部分还给出边界条件:
- 该表被定义为业务模型中的主表
id虽然是唯一主键,但“没有被任何表引用”- 其他表通常存储的是
accountcode,并通过它关联本表以获取会计科目名称
这些信息说明,结构化标注的价值不只是“更整齐”,而是能保留主键、外键、默认值、未启用状态、引用方式、业务位置等对 SQL 生成非常重要的细节。