Grok 4.5 如果单次输出长度有限,要生成长文档,就得先把输入切成多个短块逐块请求,再把结果合并。这里的关键不是把文本平均切开,而是让切分点落在段落或章节边界上,同时在每块请求时把前面已生成的内容摘要或结尾带回去,否则后一段会失去上下文,生成方向容易偏。
适用场景:需要一次性生成超长文档,但模型单次输出长度受限。操作动作:以自然段落或标题为切分点,拆分为多个输入块;逐块请求时把前一块的末尾两句追加到 prompt;合并时检测重置部分并删除。验证方式:用 Python 统计每块长度、检查段落顺序,再人工抽查衔接处。风险边界:模型输出可能重复或遗漏,重叠窗口需要根据实际内容调整,本方案不依赖 Grok 4.5 的特定参数,可平移到其他生成模型。
确定分块边界与段落关系
文本切分的第一原则是:不要在句子中间切。一句话一旦被切开,后一块开头就失去了主语或谓语,模型只能靠猜。常见的做法是依据文档里的标题层级和空行来分段。标题通常以 Markdown 的 # 开头,或者像“第 1 章”“一、”这样的中文编号;段落之间一般有至少一个空行。可以用正则把标题行和空行位置找出来,再决定从哪里切。
import re
def find_split_points(text):
# 匹配以 # 开头的标题,或常见的章节编号行
pattern = re.compile(r'^#{1,6}\s+.*$|^第[0-9一二三四五六七八九十]+[章节].*$|^[一二三四五六七八九十]+、.*$', re.M)
split_points = []
for m in pattern.finditer(text):
split_points.append(m.start())
return split_points
# 使用示例
text = open('input.md', encoding='utf-8').read()
print(find_split_points(text))拿到这些位置后,就可以把它们作为候选切分边界。匹配到标题行时,把标题保留在前一块的末尾,或者直接作为后一块的开头,方便后续拼接时识别章节结构。如果文本里没有明显标题,就按空行分段,把连续空行当成分隔符。
按标题或语义切分输入
下面这个函数把长文本拆成一个列表,每个元素是一个内容块。切分逻辑是:遇到标题行就开启新块,把标题包含在后一块里;普通行追加到当前块。最后打印每块的字符数,方便判断是否超出模型单次输出长度。
import re
def split_into_blocks(text):
# 按行拆分,识别标题行
lines = text.splitlines()
blocks = []
current = []
for line in lines:
if re.match(r'^#{1,6}\s+.*$|^第[0-9一二三四五六七八九十]+[章节].*$', line):
if current:
blocks.append('\n'.join(current))
current = []
current.append(line)
if current:
blocks.append('\n'.join(current))
# 输出每块字符数
for i, block in enumerate(blocks):
print(f'块 {i+1}: {len(block)} 字符')
return blocks
blocks = split_into_blocks(long_text)如果文本本身没有标题,也可以改成按段落数量或字符数上限切分,但那种切法很容易把段落从中间断开。先按标题切,再对过长的块二次按空行切,是一种更稳的顺序。
逐块请求并保留上下文提示
每个分块生成时,不能只把当前块的内容丢给模型。Grok 4.5 的上下文窗口虽然有长度,但后面生成的块看不到前面已经生成的内容,所以要在 prompt 里显式带上前一节的结尾。最简单的做法是把前一节最后两句话附在 prompt 开头,再让模型接着写。
prev_tail = ""
for i, block in enumerate(blocks):
if i == 0:
prompt = block + "\n\n请根据以上内容继续写作下一部分。"
else:
prompt = f"上一节结尾:{prev_tail}\n\n当前章节标题:{block}\n\n请从上一节结尾处继续展开,不要重复开头。"
# 调用 Grok 4.5 接口,伪代码示意
resp = grok_generate(prompt)
output_texts.append(resp)
# 取当前输出的最后两句作为下一轮的提示
sentences = resp.split('。')
prev_tail = '。'.join(sentences[-2:]) + '。' if len(sentences) >= 2 else resp注意这里的 grok_generate 只是占位,实际使用时换成对应 SDK 或 HTTP 请求即可。如果模型输出结果里已经带上了上一节结尾的重复内容,下一步合并时再清理。
合并时去除重复开头结尾
模型在承接上一段提示时,可能会把上一节的最后一句原样复述一遍,用来衔接。这样合并前必须先找到前后两块的重叠部分,去掉后一块开头与前一节结尾重复的内容。下面是一个简单的重叠检测函数:它比较前一节的尾部子串与后一节的头部子串,找出最长的一个匹配,然后截断后一节。
def trim_overlap(prev_text, next_text, min_overlap=10):
# 取 prev_text 尾部最长 200 字符作为搜索范围
tail = prev_text[-200:]
max_len = min(len(tail), len(next_text))
for n in range(max_len, min_overlap - 1, -1):
if tail[-n:] == next_text[:n]:
return prev_text + next_text[n:]
return prev_text + next_text
# 合并所有块
merged = blocks[0]
for block in blocks[1:]:
# 先用生成的文本替换原块,实际项目中这里的 block 应该是模型输出
merged = trim_overlap(merged, block)这个函数假设重叠部分至少 10 个字符,避免把恰好相同的标点误删。实际使用时,如果模型重复的内容超过 200 字符,可以把搜索范围调大。如果发现删完的边界出现残句,就要考虑把前一节的结尾提示改短一些,或者减少重叠窗口。
检查合并后的连贯性
合并完成后,需要确认没有产生断裂或乱序。先检查段落顺序是否和原输入一致,再检查总字数是否合理。下面这段脚本会统计合并后每个自然段的长度,并检查段落顺序是否按原始块顺序排列。
import re
def check_coherence(merged_text):
paragraphs = [p.strip() for p in re.split(r'\n\s*\n', merged_text) if p.strip()]
print(f'总段落数: {len(paragraphs)}')
for idx, para in enumerate(paragraphs):
print(f'段落 {idx+1}: {len(para)} 字')
# 检查是否有明显短句孤立,比如只有几个字
short = [i+1 for i, p in enumerate(paragraphs) if len(p) < 10]
if short:
print(f'注意:段落过短,序号 {short}')
else:
print('没有发现过短段落')
check_coherence(merged)如果你是在命令行里操作,也可以直接用 wc -m 看总字数,再用 grep -n '^第\|^#' merged.txt 看章节标题的位置。人工抽查的重点放在每两块交接的前后各一两句话:如果读起来断裂,说明提示词里的上下文不够,需要回看上一节结尾的截取方式。