W
AI-Wiki
SOURCE

数据库文档标注 摘要

文档概览

本文讨论的不是通用文档管理,而是 AI 问数、AI2SQL、N2SQL 一类场景里,如何把数据库表结构相关文档改造成更适合 RAG 检索的知识库内容。

作者的判断很明确:N2SQL 的精准度,重点已经不再只是依赖产品工具本身,而是依赖知识库内核的精准召回能力。所谓“精准”,并不是单纯把文本切得更细,或把 chunk 长度调到某个经验值,而是要让系统能完整召回一块知识内容

在数据库表文档中,这一点尤其重要,因为同一份文档里常同时混有多种信息:

  • 表说明
  • 业务说明
  • 字段说明
  • 关联关系
  • 常见查询
  • 数据样例

这些内容如果边界不清,切块后就很容易出现两类问题:

  • 切分过粗:单个 chunk token 太多,挤占大模型上下文窗口
  • 切分过细:业务语义和上下文关联被打散,导致召回不完整

因此,作者提出的重点不是继续纠缠 chunk 大小本身,而是通过结构化文档标注文档边界标记,让切分系统在分块时仍能保留完整业务语义。

关键事实

完整知识块比单纯调 chunk 更关键

作者开篇强调,知识库召回的基础是“完整知识内容”。这意味着:

  • 召回质量首先取决于知识块是否自洽、完整
  • 仅靠分块长度参数,无法解决业务语义被割裂的问题
  • 在 AI 问数领域,召回不完整会直接影响 SQL 生成准确率

也就是说,数据库文档分块的目标不是把文本机械切开,而是把“一个可以独立被理解和使用的知识单元”切出来。

表结构文档天然是多维信息混合体,必须明确边界

作者特别指出,表结构业务解读文档不是单一类型文本,而是多维内容叠加。对这种文档,如果没有清晰边界:

  • 检索时可能只命中字段说明,漏掉业务规则
  • 或只命中表说明,漏掉关联关系和样例
  • 即使召回了表名相关 chunk,也未必能保证语义完整

所以数据库文档不适合只写成连续自然语言描述,而要对不同信息类型进行显式分区。

三级标注体系的目标是提升召回完整性

文中提出三类互补的标注方法,可视作三级标注体系:

  1. 标题层级标注法
  2. 信息块标记法
  3. 语义分隔符方法

三者共同作用的目标是:

  • 让文档结构对人和机器都清晰
  • 为切块提供稳定的边界
  • 让召回结果尽可能落在完整模块上,而不是随机截断的段落上

结构化标注优于普通描述型文档

文章专门对比了改造前后的差异:

  • 改造前:普通描述型文档,自由流动、边界模糊,按表名召回时难以保证完整性
  • 改造后:表说明、字段信息、关联关系、业务特性、常见查询场景等被清晰分模块,并带有明确边界标记

作者给出的结论是,结构化标注能显著提高:

  1. 精准识别:更容易命中真正相关的信息块
  2. 完整性保障:降低知识被切散的问题
  3. 一致性维护:不同文档采用同样结构,后续扩展和维护更容易

可以用 LLM 做半自动批量转换

面对大量数据库文档,作者不建议纯手工改造,而是建议引入 LLM 做半自动流程:

  1. 先让 LLM 学习统一的标注模板
  2. LangChain 脚本实现批量文档转换
  3. 从本地读取待转换文档清单
  4. 解析文档内容并提交给 LLM
  5. 由 LLM 按模板输出结构化标注文档
  6. 再调用评估模型评估转换质量
  7. 最后生成评估报告

作者说明这类脚本逻辑并不复杂,但文中没有提供具体代码实现。

重要细节

标题层级标注法:最实用的基础结构

作者认为标题层级法“最实用”,核心是用 Markdown 标题建立稳定层级,例如:

  • 一级标题用于文档主题或表名
  • 二级标题用于主要分类
  • 三级标题用于次要分类

文中给出的约束包括:

  • 一级标题 #:仅用于文档主题
  • 二级标题 ##:用于主要分类
  • 三级标题 ###:用于次要分类
  • 标题要简洁明了,建议 3 到 7 个字

这类结构的价值在于,切块器即使不理解全部业务语义,也能先依赖标题层级做较稳妥的初级切分。

信息块标记法:用显式开始/结束标记保护边界

第二种方法是在结构区块前后加入明确标记,例如字段说明开始、字段说明结束、示例数据开始、示例数据结束。

作者建议使用 HTML 注释作为标记,原因是:

  • 不影响文档可读性
  • 标记名可以简洁明确
  • 开始与结束成对出现,便于机器识别完整区块

这实际上是在给文档边界标记提供机器可识别的“硬边界”,比只靠自然段落分隔更稳定。

语义分隔符方法:适合进一步切出特定区块

第三种方法是使用文档中不会自然出现的特殊分隔符,例如:

  • ---字段区块---
  • ---字段区块结束---
  • ---规则区块---
  • ---规则区块结束---

实施要求有两个:

  • 选择不会在正文自然出现的分隔符,避免误判
  • 保持风格统一,不能一篇文档一个规则

它的价值在于,当标题层级还不够细时,可以继续把同一章节中的不同语义块拆出来。

account_acc 案例展示了“完整表文档块”如何设计

文中给出了一份较完整的会计科目表示例,表名为 account_acc。从这个案例可以看出,作者不是只做形式上的标题美化,而是把一个数据表相关知识拆成多个明确的业务模块。

案例中的最外层使用了整表级边界:

  • table-doc-begin
  • table-doc-end

在整表范围内,至少包含以下模块:

  • 表说明
  • 业务用途
  • 表数据样例解读
  • 字段说明
  • 关联关系

其中“表说明”被单独包裹在 table-desc-begintable-desc-end 之间;“业务用途”被包裹在 business-usage-beginbusiness-usage-end 之间;“表数据样例解读”被放在 data-sample-begindata-sample-end 之间;“字段说明”使用 field-chunk-beginfield-chunk-end;“关联关系”使用 relations-beginrelations-end

这说明作者的设计重点是:不仅要把一张表的文档写全,还要让每种信息类型拥有独立、稳定、可切分、可召回的边界。

account_acc 案例中的具体事实

案例中保留了若干非常具体的业务信息,这些信息恰恰是结构化标注后更容易被完整召回的内容:

  • [account_acc](/wiki/it/ai/entity/account_acc) 是“会计科目表”
  • 该表用于存储企业财务会计科目信息
  • 它被描述为整个财务系统核算体系的核心基础数据表
  • 它为资产清算、预算管理等业务模块提供科目引用支持

业务用途部分列出三类用途:

  • 会计科目管理:定义科目代码、名称、层级结构、余额方向等
  • 业务数据关联:供其他业务模块关联和查询会计科目
  • 报表生成:在财务报表生成过程中获取会计科目信息

样例数据中给出了具体字段和值:

  • 第一行示例:id=166372830500000001entid=1accountcode=1001accountname=现金shortname=现金parentcode=0
  • 第二行示例:id=166372830500000002entid=1accountcode=1001.001accountname=现金_人民币shortname=现金_人民币parentcode=1001

字段说明里给出的细节包括:

  • id 是主键,用于唯一标识一个会计科目
  • entid 关联企业维度表,默认全部都是 1,目前该字段没有启用
  • accountcode 是核算科目编码,为每个会计科目的唯一编码,示例格式为 1001.001,用于快速定位和区分不同科目

关联关系部分还给出边界条件:

  • 该表被定义为业务模型中的主表
  • id 虽然是唯一主键,但“没有被任何表引用”
  • 其他表通常存储的是 accountcode,并通过它关联本表以获取会计科目名称

这些信息说明,结构化标注的价值不只是“更整齐”,而是能保留主键、外键、默认值、未启用状态、引用方式、业务位置等对 SQL 生成非常重要的细节。