Markdown 知识库怎么分段?按标题保存层级并避免切断代码块

用 markdown-it-py 按顶层标题切分 Markdown,记录标题路径与源行位置,完整保留代码围栏;通过拼回原文验证,并说明超长段与嵌套标题的限制。

Markdown 知识库分段时,直接按每行开头的井号切割,可能把代码里的注释误当标题。先用 Markdown 解析器识别真正的标题,再按其源行位置截取原文,能保留标题层级,并避免在围栏代码块中间设置标题边界。

下面实现按顶层标题分段,输出每段的标题路径、源文件和行区间。输入是人为构造的 Markdown,已在 Windows、Python 3.11.15、markdown-it-py 4.2.0 环境运行;没有测量检索命中率,也没有连接大模型。

Markdown 知识库怎么分段?按标题保存层级并避免切断代码块

为什么要看解析结果,而不是正则匹配

CommonMark 规范区分 ATX 标题、Setext 标题与围栏代码块。同样的 # 出现在 Python 围栏内,是代码内容;标题也可以采用文字下一行加等号的形式,单看井号会漏掉后一种。

markdown-it-py 用法说明原文先解析为 token,再交给渲染器。本例直接读取 token:heading_open 的 tag 表示 h1、h2 等级,map 给出源行位置,level 用于区分嵌套层次。

本例只把 token.level == 0 的标题设为边界;列表和块引用中的标题仍留在所属段落中。标题路径按标题等级维护:遇到同级或更高层标题时,先移除原路径中对应层级,再加当前标题。

运行按标题切分的完整代码

安装 python -m pip install markdown-it-py==4.2.0。将代码保存为 split_markdown.py,执行 python split_markdown.py。程序创建演示文件,随后保存 markdown_chunks.json。

源文本开头有一段来源说明,因此第一段没有标题路径;后面包含一级、二级和三级标题,代码围栏内特意放入一条以井号开头的注释,检查它是否被误切。

from pathlib import Path
import json
from markdown_it import MarkdownIt

sample='''来源:人为构造的 Markdown 文档。

# AI 知识库示例
这里只描述解析结构,不提供真实业务答案。

## 安装
记录安装版本,并保留原始文档。

## 运行
```python
# 这行是代码注释,不是文档标题
print("验证输入")
```
代码必须与上面的运行说明保存在同一段。

### 结果检查
核对代码块完整,并保留当前标题层级。
'''
Path('sample_knowledge.md').write_text(sample,encoding='utf-8')

def split_by_headings(text,source_file):
    lines=text.splitlines(keepends=True)
    tokens=MarkdownIt('commonmark').parse(text)
    headings=[]
    stack=[]
    for index,token in enumerate(tokens):
        if token.type=='heading_open' and token.level==0:
            level=int(token.tag[1:])
            title=tokens[index+1].content
            stack=[item for item in stack if item[0]<level]
            stack.append((level,title))
            headings.append((token.map[0],[x[1] for x in stack]))
    boundaries=[(0,[])] if not headings or headings[0][0]>0 else []
    boundaries.extend(headings)
    result=[]
    for i,(start,path) in enumerate(boundaries):
        end=boundaries[i+1][0] if i+1<len(boundaries) else len(lines)
        content=''.join(lines[start:end])
        if content.strip():
            result.append({'chunk_id':len(result),'heading_path':path,'start_line':start+1,
                           'end_line':end,'source_file':source_file,'markdown':content})
    return result

chunks=split_by_headings(sample,'sample_knowledge.md')
assert ''.join(x['markdown'] for x in chunks)==sample
assert len(chunks)==5
run=next(x for x in chunks if x['heading_path']==['AI 知识库示例','运行'])
assert run['markdown'].count('```')==2
assert '# 这行是代码注释' in run['markdown']
assert not any('这行是代码注释' in title for x in chunks for title in x['heading_path'])
assert chunks[-1]['heading_path']==['AI 知识库示例','运行','结果检查']
Path('markdown_chunks.json').write_text(json.dumps(chunks,ensure_ascii=False,indent=2),encoding='utf-8')
print('chunks:',len(chunks))
print('reconstruction_matches_source:',True)
print('code_fence_intact:',True)
print('last_heading_path:',chunks[-1]['heading_path'])
print('saved: markdown_chunks.json')

本地运行输出

chunks: 5
reconstruction_matches_source: True
code_fence_intact: True
last_heading_path: ['AI 知识库示例', '运行', '结果检查']
saved: markdown_chunks.json

怎样验证层级和代码完整

运行得到 5 段。把每段 markdown 按顺序拼回后,与演示原文完全一致;“运行”这一段包含代码的起止围栏和后面的操作说明,注释“# 这行是代码注释,不是文档标题”没有生成额外标题路径。

打开 markdown_chunks.json,检查“结果检查”的 heading_path 是否为“AI 知识库示例 → 运行 → 结果检查”,再按 start_line、end_line 回到原文件核对。行号从 1 开始,end_line 是本段最后一行;token.map 的内部行索引从 0 开始,代码已经完成转换。

chunk_id 只表示本次解析中的段序号。向持久知识库写入时,应与文档 ID 和版本一起保存,不能把不同文档的 chunk_id=0 当作同一条记录。

接入自己的 Markdown 文件

  1. 保留 split_by_headings 函数,删除创建 sample 的部分,改为读取实际的 UTF-8 文件。
  2. 调用 split_by_headings(text, '实际文件名.md'),写出对应 JSON;保留原文件供行号核对。
  3. 先检查无标题文档、标题跳级和开头说明。在没有标题时,非空文本会作为一个整体返回,标题路径为空。
  4. 检查你使用的语法。本文采用 CommonMark 模式,特定平台的扩展表格、数学公式或自定义语法需要相应解析支持,再验证边界。
  5. 用真实检索问题检查某段是否包含回答所需的上下文,再决定加入父标题说明或调整二次切分。

标题切分仍有哪些限制

本例没有设置 token 长度上限,一个标题下的长文仍可能超过下游输入限制。若要二次切分,应在解析后的完整块之间设置边界;不能对已经保存的字符串按固定字符数硬截,再假定代码围栏仍完整。

单个代码块本身过长时,优先判断是否可以单独存放代码、用摘要和链接引用原块;确实要拆开时,保留语言、函数上下文和有效围栏,并重新做完整性检查。本文没有实现这种二次切分。

本例保留原始 Markdown,标题路径也使用标题内联源文;它不是把标题渲染成纯文本的清洗器。空白独立段可能被跳过,演示中的拼回一致结论不能推广成任意输入都逐字不变。

按标题切好就一定更容易检索吗?

不一定。章节结构完整,只证明材料边界更容易核对;问题可能需要跨节信息,也可能被一个很长的段落淹没。用固定问题检查召回片段是否包含答案依据,再比较不同分段方式;本文的代码完整性测试不是检索效果测试。

Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/31900.html

赞 (0)
AI小管家的头像AI小管家
网页怎么整理成 AI 知识库资料?用 Trafilatura 提取正文并保留来源
上一篇 2小时前
EPUB 怎么接入 AI 阅读工具?按 spine 提取正文并保留章节来源
下一篇 2小时前

相关推荐

联系我们

联系我们

1

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信
关注微信
分享本页
返回顶部