ChatGLM3处理长文档会断片——分段喂进去能保住前后关系吗?

文章导读
分段喂进去能保住一部分前后关系,但不会自动把整篇文档的指代、定义和章节结构接回来。ChatGLM3 在长输入里出现断片,通常表现为前面提过的人名、编号、条件在后续回答里丢失,或者把不同段落的事实串在一起。要判断分段是否有效,先把漏答位置变成可记录的现象,再固定问题集比较切分方式,最后用重叠、摘要衔接和片段编号约束把流程固化。上下文窗口、模型版本和接入方式不同,具体参数需要结合环境确认。
📋 目录
  1. 先复现一次断片并记下发生位置
  2. 用同一批问题测三种切分策略
  3. 给相邻片段加重叠或摘要衔接
  4. 在提示里要求模型引用片段编号
  5. 把有效组合固化成脚本参数
A A

分段喂进去能保住一部分前后关系,但不会自动把整篇文档的指代、定义和章节结构接回来。ChatGLM3 在长输入里出现断片,通常表现为前面提过的人名、编号、条件在后续回答里丢失,或者把不同段落的事实串在一起。要判断分段是否有效,先把漏答位置变成可记录的现象,再固定问题集比较切分方式,最后用重叠、摘要衔接和片段编号约束把流程固化。上下文窗口、模型版本和接入方式不同,具体参数需要结合环境确认。

ChatGLM3 对长文档断片通常不是单点故障,而是上下文窗口、切分边界和提示约束共同作用。可以先复现漏答位置,再固定问题集比较按字数切、按章节切和带重叠切。通常按章节切加少量重叠更稳,把上一段摘要作为下一段前缀,并在提示中要求回答引用片段编号。若衔接处事实被切断且无法通过重叠恢复,需要回退到人工核对或检索片段,不要默认模型一定接得上。

先复现一次断片并记下发生位置

不要一上来就改切分参数。先构造一份可核对输入:在文档不同位置放入事实点,例如项目代号、金额口径、生效日期、否定条件、单位、章节归属。每个事实点都要能在原文里直接找到,不能依赖模型常识。然后按自然段或固定窗口切分,逐段喂给 ChatGLM3,并在每轮后追问同一个问题。记录模型从第几段开始漏答、答错或把 A 段事实安到 B 段上。复现步骤要能重复:同一份文档、同一组问题、同一温度或采样设置、同一片段顺序。表格可以先留空,跑完再填。

轮次/片段编号片段起止或章节放入的可核对事实点提问模型回答摘要事实点是否保留首次异常位置备注
chunk-01第 1 章项目代号、生效日期待填待填待填待填待填
chunk-02第 2 章金额口径、否定条件待填待填待填待填待填
chunk-03第 3 章单位、章节归属待填待填待填待填待填

用同一批问题测三种切分策略

问题集固定后,只改变切分方式。按字数切通常实现简单,但容易把定义和引用它的句子拆开;按章节切更贴近语义边界,但单章过长时仍可能触发长上下文衰减;带重叠切是在相邻片段边界保留一段重复文本,用冗余换衔接。比较时记录三个指标:事实点保留数量、答错但语气肯定的次数、把其他片段事实混入当前回答的串话次数。不要把回答更长当成更好。若某策略只是在重复原文,而没有让事实点被正确引用,它对长文档问答没有帮助。

策略切分依据事实点保留数串话次数边界丢失表现适用场景
按字数切固定字符窗口待填待填定义与引用被拆开结构弱、格式杂的文本
按章节切标题、空行或章节标记待填待填超长章节仍断片有清晰层级的文档
带重叠切章节或窗口加重叠区待填待填重叠不足时仍丢边界衔接强、指代多的文档

给相邻片段加重叠或摘要衔接

重叠字数不要凭感觉设。可以先从一个到两个完整句子的长度开始,例如 120 到 200 字量级,再结合文档句子长度、片段长度上限和模型可用上下文调整。重叠太大,片段数量上升,重复内容会挤占上下文;重叠太小,跨段指代和表格表头仍可能被切断。如果原文有表格,尽量让表头随每个数据片段重复出现。摘要衔接用于相隔较远的片段:把上一段摘要作为下一段前缀,提示模型只把摘要当背景,不当作新事实来源。可以先用下面的骨架,把占位替换为实际内容。

上一段摘要:{prev_summary}
当前片段编号:{chunk_id}
当前片段正文:
{chunk_text}
回答约束:只依据当前片段和上一段摘要;摘要与当前片段冲突时,以当前片段为准;无法确定时写“无法从给定片段确定”。

验证方式:同一问题分别跑无摘要衔接和有摘要衔接,检查跨段指代是否恢复、是否引入摘要中不存在的新事实。若摘要本身写错,错误会传播到后续片段,所以摘要也要保留可核对的片段编号。

ChatGLM3处理长文档会断片——分段喂进去能保住前后关系吗?

在提示里要求模型引用片段编号

让模型引用片段编号,是为了判断它到底用了哪段上下文,而不是只看答案像不像。提示里要同时给出片段格式、回答约束和不确定表达。片段编号建议稳定且短,例如 chunk-01、chunk-02,不要用会随排序变化的序号。可以要求每个事实句后带 [chunk-xx],如果多个片段冲突,先列冲突再给倾向。下面是一个可直接改写的提示骨架。

你只能使用下面带编号的片段回答。
片段格式:
[chunk-01] 正文...
[chunk-02] 正文...

回答要求:
1. 每个事实后标注来源片段编号,如 [chunk-02]。
2. 不要使用片段之外的常识补全。
3. 如果多个片段冲突,列出冲突点,不要强行合并。
4. 如果片段没有提供答案,写“无法从给定片段确定”。
5. 如果答案需要跨片段推断,说明使用了哪些片段编号。

检查时不要只看最终答案。把回答里出现的片段编号和原文事实点对照:编号是否真实存在、事实是否来自该片段、有没有把摘要当成片段。若模型频繁不标编号,先降低单片段长度或减少片段数量,再检查提示是否太长。

把有效组合固化成脚本参数

把有效组合写进脚本参数,下一次处理长文档才能重复。参数至少包括切分方式、片段长度、重叠量、片段数量上限,以及是否启用摘要衔接。数量上限不是越大越好,超过模型可用上下文后,后面的片段可能被挤压;可以先设一个保守值,再用复现问题集验证。脚本骨架不绑定具体 SDK,调用 ChatGLM3 的部分替换成你的客户端即可。

CHUNK_MODE = 'section'      # char / section / overlap
MAX_CHARS = 1800            # 单片片段长度占位符
OVERLAP = 160               # 重叠量占位符
MAX_CHUNKS = 12             # 片段数量上限占位符
USE_SUMMARY = True
SUMMARY_MAX_CHARS = 200

def split_doc(text):
    if CHUNK_MODE == 'char':
        return split_by_chars(text, MAX_CHARS)
    if CHUNK_MODE == 'section':
        return split_by_sections(text, MAX_CHARS)
    return split_with_overlap(text, MAX_CHARS, OVERLAP)

def ask_model(chunk_id, chunk_text, prev_summary):
    prompt = build_prompt(chunk_id, chunk_text, prev_summary if USE_SUMMARY else '')
    return call_chatglm(prompt)

chunks = split_doc(raw_text)[:MAX_CHUNKS]
prev_summary = ''
for i, chunk in enumerate(chunks, 1):
    chunk_id = 'chunk-%02d' % i
    answer = ask_model(chunk_id, chunk, prev_summary)
    log_jsonl(chunk_id, answer)
    prev_summary = summarize(answer) if USE_SUMMARY else ''

端到端验证方式:用第一步的同一批问题和同一份文档跑脚本,保留每次请求的片段编号、片段正文哈希、提示版本和模型回答,输出到 jsonl 或日志文件。验证时逐条检查事实点是否被正确片段支撑、串话次数有没有变化、无法确定时是否如实表达。若按章节切加重叠仍无法恢复关键衔接,不要继续加长上下文硬试;需要把关键段落作为检索片段单独注入,或退回人工核对。