AgentDepthAgent 工程学习指南
输入概念或问题,检索正文和待编写目录。
第一次学习?从 L0 总览开始 →

分块、元数据与权限继承

我的学习 →

正在读取学习记录…

分块是把长文档拆成能单独检索的小段。元数据是伴随正文的说明,如文档编号、版本、字符位置和可见范围。块越短越容易定位,条件与结论也越容易被拆开;重叠让相邻块共享一部分文字,减少边界处的信息断裂。

用退款资料观察分块

退款资料依次说明期限、生鲜例外和申请所需订单号。程序按固定字符窗口切分,相邻窗口重叠6个字符,同时把店铺和可见范围复制到每个块。

原文共32字符。size=18表示每块最多18字符,overlap=6表示相邻块共享6字符,因此每次前进12字符。文档是A店铺的私有资料,版本1;切成三块后,来源身份与权限也会各自存入三条记录。

保存完整程序并运行

先在 PowerShell 创建并进入学习目录:

Bash
mkdir "F:\学习\l5-chunking" -Force
cd "F:\学习\l5-chunking"

保存下面的文件后,在这个终端运行 dir 核对文件名,再执行本节给出的运行命令。

新建学习目录,例如 F:\学习\l5-chunking。将下面代码完整保存为 chunking_demo.py;代码包含固定资料和运行入口,保存后就能执行。

先在 PowerShell 创建并进入学习目录:

Bash
mkdir "F:\学习\l5-chunking" -Force
cd "F:\学习\l5-chunking"

保存下面的文件后,在这个终端运行 dir 核对文件名,再执行本节给出的运行命令。

Python
"""切分退款规则,保留重叠、字符位置与文档权限。"""
DOCUMENT = {"id": "refund-A", "version": 1, "tenant": "A",
            "visibility": "private",
            "text": "退款通常三至五个工作日到账。生鲜商品除外。申请退款需要订单编号。"}

def split_document(doc, size=18, overlap=6):
    if type(size) is not int or type(overlap) is not int:
        raise ValueError("窗口与重叠必须是整数")
    if size <= 0 or not 0 <= overlap < size:
        raise ValueError("重叠应小于正数窗口")
    chunks = []
    # 每次前进size-overlap个字符,相邻块保留共同文字。
    for start in range(0, len(doc["text"]), size - overlap):
        end = min(start + size, len(doc["text"]))
        chunks.append({
            "id": f"{doc['id']}:v{doc['version']}:{start}",
            "doc_id": doc["id"], "version": doc["version"],
            "tenant": doc["tenant"], "visibility": doc["visibility"],
            "start": start, "end": end, "text": doc["text"][start:end],
        })
        if end == len(doc["text"]):
            break
    return chunks

def main():
    for chunk in split_document(DOCUMENT):
        print(f"{chunk['id']} [{chunk['start']}:{chunk['end']}] "
              f"{chunk['tenant']}/{chunk['visibility']}:{chunk['text']}")

if __name__ == "__main__":
    main()

在保存文件的文件夹地址栏输入 powershell 并回车,再执行:

macOS:先在 Python 篇的学习文件夹中打开终端,执行 source .venv/bin/activate。再输入 cd ,把当前例子的文件夹拖入终端并回车,运行下面的命令。

Bash
cd "F:\学习\l5-chunking"
ls
python -X utf8 chunking_demo.py

使用其他目录时,把第一行换成自己的路径。dir 应能列出 chunking_demo.py。终端输出如下:

TXT
refund-A:v1:0 [0:18] A/private:退款通常三至五个工作日到账。生鲜商品
refund-A:v1:12 [12:30] A/private:账。生鲜商品除外。申请退款需要订单编
refund-A:v1:24 [24:32] A/private:款需要订单编号。

编号中的v1说明来源版本,最后的数字是该块在原文中的起点。区间采用左闭右开规则,例如[0:18]包含第0到17个字符。每个块都保持A/private,切小后不会突然变成公开资料。相邻块共享6字符,能在输出中逐字对上。

为什么需要位置、重叠与权限

split_document先检查窗口与重叠,再以size-overlap为步长移动窗口。每次记录原文起止位置和正文,同时继承文档元数据。最后一个窗口到达末尾就停止,避免额外生成只包含尾部重叠的小块。

固定字符窗口容易切在句子中间,重叠只能降低断裂概率。退款例外条件若比重叠更长,仍可能离开主条款。段落切分可保留句子结构;递归切分先按标题、段落、句子拆,过长部分再按长度拆;语义切分根据主题变化决定边界,需要检查是否把条件和结论放在一起。

窗口增大通常提供更完整的语境,也会带来更多无关文字;重叠增大增加索引体积和重复候选。演示以字符计数,模型请求还需按对应分词器统计Token。图片、代码和表格适合保留结构,不宜机械拆成同一字符窗口。

权限继承后,检索仍需按当前用户权限筛选每个块。资料变更时,用doc_id定位旧块,以版本区分新旧,再更新索引;引用时利用start/end回到原文。可追溯位置能帮助检查答案依据,不能自动证明分块保住了业务含义。

退款原文:0 到末尾;块0:位置0:18,A/private;块1:位置12:30,A/private;相邻块共享6字符;块2:位置24:末尾,A/private
窗口移动12字符,重叠6字符,每块继承相同权限。

改一处输入,核对机制变化

将函数默认 overlap=6 改为 overlap=0,运行 python -X utf8 chunking_demo.py。窗口前进18字符,只得到[0:18]和[18:32]两块,权限仍是A/private。恢复6并重跑,回到三块和6字符重叠。字符区间决定切片位置,权限继承不随窗口变化。

运行配套检查

把下面测试保存为 test_chunking_demo.py,与主程序放在同一目录。5项测试检查上面的实际处理结果,也覆盖重叠、权限、位置和非法窗口。

查看测试代码:test_chunking_demo.py
Python
"""核对分块、元数据与权限继承的正常结果与具体边界。"""
import unittest
import chunking_demo as demo


class DemoTests(unittest.TestCase):
    def test_every_chunk_inherits_permissions(self):
        # 每个块都继承原文的可见范围。
        for chunk in demo.split_document(demo.DOCUMENT):
            self.assertEqual((chunk["tenant"], chunk["visibility"]), ("A", "private"))
            self.assertEqual(chunk["doc_id"], "refund-A")

    def test_offsets_point_to_original_text(self):
        # 位置记录能回到原文取出相同文字。
        for chunk in demo.split_document(demo.DOCUMENT):
            self.assertEqual(chunk["text"], demo.DOCUMENT["text"][chunk["start"]:chunk["end"]])

    def test_overlap_is_preserved(self):
        # 相邻块确实共享六个字符。
        chunks = demo.split_document(demo.DOCUMENT)
        for left, right in zip(chunks, chunks[1:]):
            self.assertEqual(left["text"][-6:], right["text"][:6])

    def test_invalid_window_rejected(self):
        # 非法窗口拒绝运行,避免零步长。
        for size, overlap in ((0, 0), (4, 4), (4, -1), (True, 0)):
            with self.assertRaises(ValueError):
                demo.split_document(demo.DOCUMENT, size, overlap)

    def test_empty_and_short_documents(self):
        # 空文档没有块,短文档只有一块。
        self.assertEqual(demo.split_document(dict(demo.DOCUMENT, text="")), [])
        self.assertEqual(len(demo.split_document(dict(demo.DOCUMENT, text="退款"))), 1)


if __name__ == "__main__":
    unittest.main()
Bash
python -X utf8 -m unittest -v test_chunking_demo.py

测试名称后应显示 ok,末尾是 Ran 5 tests 和 OK。

运行时怎样定位问题

看到的情况检查与操作
找不到 chunking_demo.py运行 dir,切换到保存主程序的目录
切分一直不结束overlap必须小于size,步长要为正数
检索块丢了生鲜例外按完整条款切分或增加邻接块,核对原文位置
私有原文出现公开块切分时复制权限,检查每块tenant与visibility

每个块有了可追溯身份,索引才能按编号更新和删除。

参考阅读:LlamaIndex 源码与检索流程。阅读时可以沿文档、节点、索引和检索入口对照这里的数据流。

课后练习展开习题收起习题

4 道练习 · 客观题规则评分 · 其余题目待批改

正在读取本地记录…

不调用模型,不执行代码。简答、代码和场景题仅保存答案并展示自查标准,不生成评分、徽章或认证。输入内容不会自动保存,请点击提交;不要填写个人信息或密钥。

练习 1 / 4 · v1.0.1

选择题 · 先判断边界

关于“分块、元数据与权限继承”,哪种做法更稳妥?

对应知识点:L5-chunking

选择一个答案
练习 2 / 4 · v1.0.0

简答题 · 解释失败

写出一个失败输入并说明记录字段。

对应知识点:L5-chunking

练习 3 / 4 · v1.0.0

代码题 · 边界测试

补充正常、边界、失败三组输入和预期结果。

对应知识点:L5-chunking

练习 4 / 4 · v1.0.0

场景题 · 结果未知

证据不足时如何设计状态和人工接管?

对应知识点:L5-chunking

下一篇:索引、增量更新与删除。

图片查看器
100%

文章配图 · 按 Esc 关闭