ChatGPT Work 处理长文档拆分与结果合并的工程化方法

文章导读
处理超长文档时,常见做法是把文档切成多个片段,逐段交给 ChatGPT 处理,再把结果拼回。但难点不在“切”,而在“切完还能拼得回”:如果切点切断语义、合并时不去重、失败后从头再来,返工成本会很快超过手工处理。本文给出一套可落地的分片、合并、校验和重跑流程,适合合同、报告等需要稳定输出的长文本批次处理。
📋 目录
  1. 定义分片粒度与重叠策略
  2. 编写分片调用循环
  3. 合并结果并处理边界内容
  4. 检查合并后文档的完整性
  5. 失败重跑与局部修复
A A

处理超长文档时,常见做法是把文档切成多个片段,逐段交给 ChatGPT 处理,再把结果拼回。但难点不在“切”,而在“切完还能拼得回”:如果切点切断语义、合并时不去重、失败后从头再来,返工成本会很快超过手工处理。本文给出一套可落地的分片、合并、校验和重跑流程,适合合同、报告等需要稳定输出的长文本批次处理。

长文档处理应采用“段落或固定长度切分 + 少量重叠 + 逐段调用 + 结果合并 + 完整性校验”的流程。适用场景:单次调用有输入长度限制的文本生成任务。操作动作:设置分片大小与 overlap,逐段调用并保存中间结果。验证方式:对比原文关键句和段落数,确认无遗漏。风险边界:切分可能切断语义,合并需去重,接口错误需用断点续传降低返工成本。

定义分片粒度与重叠策略

分片粒度需要根据模型单次输入上限决定,常见做法是“优先按段落,兜底按固定长度”,并在相邻片段之间设置少量 overlap(重叠区域)。按段落切保证语义完整,适合合同、报告这种有章节层次的文件;如果某一段过长,就按句子或固定长度继续切。固定长度切分简单可控,但容易把一句话从中间切开,因此要在每个片段末尾多留出一段 overlap,比如包含前一个片段最后 1-2 个句子。这样模型在生成当前片段时仍能看到句子的起始上下文,结果更连贯。

overlap 的具体长度没有通用值,需要根据内容语言和模型量级调整。中文可先尝试 100-300 字符,英文可以按句子数。设置过小失去上下文,过大浪费输入空间。建议先用两个片段做小样,观察边界是否出现“前言不搭后语”或重复输出,再决定最终值。

编写分片调用循环

分片策略定好后,需要写一个循环逐段调用模型,并把每段结果单独保存。单独保存是为了让后续合并和断点续传可用,同时也方便定位是哪个片段出错。下面是一段通用接入骨架,读取文件、按固定长度切分、调用接口、写入片段文件。实际使用时把 call_model 替换成你自己的接口封装即可。

ChatGPT Work 处理长文档拆分与结果合并的工程化方法
import pathlib

def call_model(fragment: str) -> str:
    # 替换为实际模型接口调用,例如 requests.post(...)
    return '模型返回文本'

def split_text(text: str, chunk_size: int = 1500, overlap: int = 200):
    chunks = []
    start = 0
    while start < len(text):
        end = start + chunk_size
        chunks.append(text[start:end])
        start = end - overlap
    return chunks

source = pathlib.Path('report.txt').read_text(encoding='utf-8')
chunks = split_text(source)

for index, chunk in enumerate(chunks):
    result = call_model(chunk)
    pathlib.Path(f'output/{index}.txt').write_text(result, encoding='utf-8')

这个例子是按固定长度切,实际项目中建议在 split_text 里加入“优先在段落边界处切”的判断,避免切在句号中间。另外,每个片段的输入还可以附带一个简短提示词,告诉模型当前片段来自长文档的第几部分,让输出更稳定。

合并结果并处理边界内容

把各片段结果按顺序拼起来不算完,因为 overlap 会让相邻结果出现重复开头或结尾。合并时要先去重再拼接。一种做法是:检查当前片段开头是否与已合并文本结尾有重复,找到最长的公共重叠部分,然后从重复处之后开始拼接。这样能消除因 overlap 产生的重复内容。

ChatGPT Work 处理长文档拆分与结果合并的工程化方法
def find_overlap(left: str, right: str) -> int:
    limit = min(len(left), len(right))
    for n in range(limit, 0, -1):
        if left[-n:] == right[:n]:
            return n
    return 0

def merge_results(files):
    merged = ''
    for file in files:
        text = pathlib.Path(file).read_text(encoding='utf-8')
        if not merged:
            merged = text
        else:
            n = find_overlap(merged, text)
            merged += text[n:]
    return merged

如果模型在片段边界额外输出了相同的过渡句,这种去重方式也能兜住。但要注意,如果原文本身存在自然重复,可能误删。所以建议先设定一个最小重叠阈值,比如 20 个字符或一个完整句子,只有超过阈值才去重。

检查合并后文档的完整性

去重拼接后,需要检查是否有片段被漏掉或生成结果不完整。最直接的方式是拿原文和合并结果做对比:统计两者字数或段落数,通常能发现大段缺失;再用关键句抽样式检查,定位到具体内容。下面的校验脚本思路会从原文每个段落提取前 20 个字符作为关键键,然后看这些键是否都出现在合并结果中。

source_paras = source.split('\n\n')
expected_keys = [p[:20] for p in source_paras if p.strip()]
merged_paras = merged.split('\n\n')
merged_keys = [p[:20] for p in merged_paras if p.strip()]
missing = [k for k in expected_keys if k not in merged_keys]
print('缺失段落首句数量:', len(missing))

如果模型对原文做了改写,关键句不一定逐字出现。这时可以把比较条件改成“前 20 个字符中是否有超过一半字符在结果段落中出现”,或者只比较数字、合同编号等强特征。字数对比只能作为兜底检查,关键句或关键实体才是定位问题的有效方式。

ChatGPT Work 处理长文档拆分与结果合并的工程化方法

失败重跑与局部修复

长文档分片数量一多,接口超时、限流或返回异常几乎必然出现。如果每次失败都要从第一段重跑,成本很高。对策是每成功处理一个片段,就把片段编号写入进度文件;重跑时先加载进度,只处理未完成的片段。下面是一段断点续传的伪代码思路。

import json, pathlib

done = set()
progress_file = pathlib.Path('progress.json')
if progress_file.exists():
    done = set(json.loads(progress_file.read_text()))

for index, chunk in enumerate(chunks):
    if index in done:
        continue
    try:
        result = call_model(chunk)
        pathlib.Path(f'output/{index}.txt').write_text(result, encoding='utf-8')
        done.add(index)
        progress_file.write_text(json.dumps(list(done)), encoding='utf-8')
    except Exception:
        print(f'片段 {index} 处理失败,稍后重试')

除了断点续传,局部修复还有一层含义:如果合并后发现某段输出明显不对,只需重新生成对应片段,再跑一次合并即可,不需要重新处理整篇文档。建议在保存片段时加上源文件版本号和片段序号,方便定位和替换。