
1. 万字长文生成的真实困境为什么你的大模型写到 2000 字就开始“水”如果你用过大模型写报告、白皮书或者小说大概率遇到过这个场景开头 800 字逻辑清晰、论据扎实写到 1500 字开始重复观点到 2000 字左右直接收尾留下一句“综上所述”草草了事。你明明在 prompt 里写了“请输出 8000 字”它却像没看见一样。这不是模型“不听话”而是它的输出长度在监督微调阶段就被“锁死”了。清华和智谱联合开源的 AgentWriter 项目正是冲着这个痛点来的。它把超长写作任务拆成“先规划、再逐段写”的 Agent 流程配合 LongWriter-6k 数据集训练出的 LongWriter 模型让现成的 LLM 也能稳定输出超过 10000 字的长文。这套方案适合谁写行业研究报告的、做长篇内容运营的、需要批量生成结构化文档的开发者以及想在自己机器上跑一套长文生成流水线的技术同学。我实测下来AgentWriter 的核心价值不在于“模型变大了”而在于它把长文生成从“一次性输出”变成了“可编排的流水线”。你不需要换一个 70B 的模型用 9B 的 LongWriter 配合正确的 Agent 调度就能拿到结构完整、段落连贯的万字输出。下面我从本地部署、模型接入、参数配置到一次完整的万字生成验证把这条路走通。2. AgentWriter 与 LongWriter 的协作机制长程写作代理到底怎么拆任务AgentWriter 不是一个单独的模型而是一套基于 Agent 的写作管道。它的核心思路来自论文里的 AgentWrite把“写一篇 10000 字的文章”这个超大任务分解成“规划阶段”和“逐段写作阶段”。规划阶段模型根据你的写作指令输出一个分段计划。每个子任务包含两个关键信息这一段的主要观点是什么以及大概写多少字。论文里给的约束是每段不少于 200 字、不超过 1000 字。这个粒度很关键——太短了模型会频繁切换上下文导致连贯性下降太长了又退回到“一次写太多写不动”的老问题。写作阶段模型拿到原始指令、完整的分段计划、以及前面已经写好的所有段落然后只输出当前这一段。注意它每次只写一段但能看到之前写过的全部内容。这就保证了段落之间的衔接不会断裂。你可以把它理解成“一个作者在写连载小说每写一章之前都会重读前面所有章节”。但光有 Agent 流程还不够。论文里做了一个对照实验同样的 AgentWrite 流程用在普通 LLM 上输出到 3000 字左右质量就开始下滑而用在经过 LongWriter-6k 数据集微调过的模型上输出到 10000 字以上仍然保持结构完整。LongWriter-6k 包含 6000 个训练样本输出长度从 2000 到 32000 词不等。正是这些长输出样本把模型在 SFT 阶段被限制住的“输出窗口”重新打开了。所以完整的方案是两层AgentWriter 负责“怎么拆、怎么串”LongWriter 负责“每一段能写得好、写得长”。两者缺一不可。你如果只做 Agent 拆分但用普通模型写到后面会明显感觉到段落质量下降你如果只用 LongWriter 但不做 Agent 拆分单次输出仍然受限于模型的最大生成长度。2.1 规划阶段的 Prompt 设计与分段策略规划阶段的 prompt 直接决定了后续写作的质量。论文里给出的模板要求模型输出固定格式的分段计划每行包含“第 N 段-主要观点xxx-字数xxx”。这个格式看起来简单但实际用的时候有几个坑。第一如果你不限制每段的字数范围模型会倾向于把计划拆得很碎比如每段 100 字结果写出来像流水账。论文里明确要求“每个子任务的段落不应少于 200 字且不超过 1000 字”这个约束要写进 prompt。第二规划阶段要确保所有子任务覆盖了写作指令的全部内容。我试过让模型规划一篇“2024 年 AI 编程助手市场分析”它只规划了产品对比漏掉了市场规模的估算。后来我在 prompt 里加了一句“确保每个子任务都清晰具体并且所有子任务覆盖了写作指令的全部内容”情况就好很多。第三规划阶段输出的分段数量要和你的目标总字数匹配。如果你要写 10000 字每段平均 600 字那大概需要 16 到 17 段。你可以在 prompt 里直接写“请规划 16 个段落”也可以让模型自己算。实测下来直接给段数更稳定。2.2 逐段写作阶段的上下文拼接与连贯性保障写作阶段的 prompt 需要拼接三部分内容原始写作指令、规划阶段生成的分段计划、以及之前已经生成的所有段落。这里有一个工程上的细节当已经写好的文本越来越长时prompt 的总长度会迅速膨胀。写到第 15 段的时候前面 14 段的内容可能已经超过 8000 字加上指令和计划整个 prompt 可能接近 10000 token。LongWriter 模型本身支持长上下文输入所以这个问题在模型层面不是瓶颈。但你在实际部署时要注意推理框架的 max_model_len 参数设置。如果你用 vLLM 部署默认的 max_model_len 可能只有 4096 或 8192需要手动调大。我一般设成 32768给输入和输出都留足空间。另一个细节是“已经写好的文本”要不要做截断。论文里的做法是完整拼接不截断。因为长文写作最怕的就是前后矛盾比如前面说“市场规模约 500 亿”后面写成“约 300 亿”。完整拼接虽然增加了 prompt 长度但换来了更好的一致性。如果你实在担心显存可以只保留最近 3 到 5 段但实测下来连贯性会打折扣。3. 本地部署与模型接入配置从环境准备到 settings 片段这一章给你一套可以直接复制的配置。我以 Linux CUDA 环境为例模型用 LongWriter-9B推理框架用 vLLM。如果你用 Windows建议走 WSL2原生 Windows 下 vLLM 的兼容性坑比较多。3.1 环境依赖与模型下载先创建虚拟环境安装基础依赖。Python 版本建议 3.10 或 3.113.12 在部分 CUDA 扩展上还有兼容问题。conda create -n longwriter python3.10 -y conda activate longwriter pip install torch2.3.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 pip install vllm0.5.4 pip install transformers4.43.3 pip install openai模型下载有两种方式。如果你网络条件允许直接从 Hugging Face 拉huggingface-cli download THUDM/LongWriter-9B-chat --local-dir ./LongWriter-9B-chat如果下载速度不理想也可以用 ModelScope 的镜像pip install modelscope python -c from modelscope import snapshot_download; snapshot_download(ZhipuAI/LongWriter-9B-chat, cache_dir./models)模型文件大概 18GB 左右9B 参数用 bfloat16 存储。你的显存至少要 24GB 才能比较舒服地跑起来。如果显存不够可以用 AWQ 量化版本但长文生成的质量会有轻微下降。3.2 vLLM 启动参数与 OpenAI 兼容接口配置vLLM 启动命令如下。关键参数是 max_model_len 和 gpu_memory_utilization。max_model_len 设成 32768给长 prompt 留空间gpu_memory_utilization 设成 0.9充分利用显存。python -m vllm.entrypoints.openai.api_server \ --model ./LongWriter-9B-chat \ --served-model-name longwriter-9b \ --max-model-len 32768 \ --gpu-memory-utilization 0.9 \ --port 8000 \ --dtype bfloat16启动成功后你会看到类似这样的输出INFO: Started server process [12345] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRLC to quit)这时候模型已经在本地的 8000 端口上提供了一个 OpenAI 兼容的 API。你可以用 curl 测试一下curl http://localhost:8000/v1/models返回的 JSON 里应该能看到longwriter-9b这个模型 ID。3.3 AgentWriter 的 settings 配置片段AgentWriter 的调度逻辑你可以自己写也可以用项目里提供的脚本。核心配置我整理成一个 JSON 片段你直接改路径和参数就能用{ model_config: { base_url: http://localhost:8000/v1, api_key: EMPTY, model_id: longwriter-9b, max_tokens: 4096, temperature: 0.7, top_p: 0.9 }, agent_config: { plan_max_segments: 20, min_words_per_segment: 200, max_words_per_segment: 1000, target_total_words: 10000, context_window: 32768 }, output_config: { save_dir: ./outputs, merge_segments: true, add_section_titles: true } }注意 api_key 这里填EMPTY就行因为 vLLM 本地服务默认不校验 key。如果你后面要换成 TaoToken 的 API 接入把 base_url 改成https://taotoken.net/apiapi_key 换成你在控制台申请的 Keymodel_id 换成对应的模型名称。TaoToken 的 API 文档在 https://taotoken.net/doc 有完整的参数说明接入方式和 OpenAI 完全兼容不需要改代码逻辑。4. 一次万字生成任务的完整验证从 prompt 到 12000 字输出配置跑通之后我们来实际生成一篇万字长文。我选的主题是“大模型长文本生成的技术演进与工程实践”目标总字数 10000 字规划 16 个段落。4.1 规划阶段的实际输出与检查规划阶段的 prompt 我这样写我需要你帮我将以下长篇写作指令分解为多个子任务。每个子任务将指导文章中一个段落的写作并应包括该段落的主要观点和字数要求。 写作指令如下 写一篇关于“大模型长文本生成的技术演进与工程实践”的深度文章目标总字数10000字面向有技术背景的开发者读者。文章需要覆盖长文本生成的问题背景、早期方案的局限性、AgentWrite 的分解思路、LongWriter 的训练数据构建、LongBench-Write 评估基准、实际部署中的工程挑战、以及未来可能的方向。 请按照以下格式进行分解每个子任务占一行 第一段-主要观点[详细描述段落的主要观点]-字数[字数要求例如400字] 第二段-主要观点[详细描述段落的主要观点]-字数[字数要求例如1000字] ... 确保每个子任务都清晰具体并且所有子任务覆盖了写作指令的全部内容。不要将子任务拆分得太细每个子任务的段落不应少于200字且不超过1000字。不要输出任何其他内容。模型返回的规划结果大概是这样的截取前几段第一段-主要观点介绍长文本生成在大模型应用中的重要性以及当前模型在输出长度上的实际限制引出核心问题-字数600字 第二段-主要观点分析长上下文模型在输入处理能力与输出生成能力之间的不对称性解释为什么100K输入不等于10K输出-字数700字 第三段-主要观点回顾早期解决长文本生成的尝试包括分段提示、滑动窗口、递归摘要等方法的局限性-字数800字 ... 第十六段-主要观点总结当前技术路线的核心洞察并给出对开发者的实践建议-字数500字检查一下16 段每段 500 到 800 字加起来大概 10000 字出头。覆盖了指令里要求的所有主题。格式也符合要求。这个规划可以直接进入写作阶段。4.2 逐段写作与拼接的完整流程写作阶段我用 Python 脚本调度。核心逻辑是循环 16 次每次把原始指令、完整规划、已写段落拼接成 prompt调用 API 生成当前段落。import json import openai client openai.OpenAI( base_urlhttp://localhost:8000/v1, api_keyEMPTY ) with open(plan.txt, r, encodingutf-8) as f: plan f.read() instruction 写一篇关于大模型长文本生成的技术演进与工程实践的深度文章... segments [] for i in range(1, 17): written \n\n.join(segments) if segments else 暂无 prompt f你是一位出色的写作助手。我将给你一个原始的写作指令和我计划的写作步骤。我还会提供我已经写好的文本。请帮我根据写作指令、写作步骤以及已经写好的文本继续写下一个段落。 写作指令 {instruction} 写作步骤 {plan} 已经写好的文本 {written} 请整合原始写作指令、写作步骤以及已经写好的文本现在继续写第{i}段的内容给我。如果需要你可以在开头添加一个小标题。记得只输出你写的段落不要重复已经写好的文本。 response client.chat.completions.create( modellongwriter-9b, messages[{role: user, content: prompt}], max_tokens4096, temperature0.7 ) segment response.choices[0].message.content segments.append(segment) print(f第 {i} 段完成当前累计字数{sum(len(s) for s in segments)})实际跑下来每段生成耗时大概 15 到 25 秒16 段总共 5 到 7 分钟。最终拼接后的全文我统计了一下中文字符数约 11800 字加上标点和英文术语总输出长度超过 12000 字。4.3 输出质量评估连贯性与结构完整性检查生成完之后我做了几个检查。第一看段落之间的衔接。第 3 段结尾讲“递归摘要方法在超过 5000 字后信息损失严重”第 4 段开头直接接“正是这种信息损失催生了基于 Agent 的分解思路”过渡自然没有断裂。第二看前后数据一致性。全文里我让模型提到了几次“LongWriter-6k 包含 6000 个样本”每次出现的数字都一致没有出现前后矛盾。第三看结构完整性。规划里的 16 个主题全部覆盖到了没有漏掉任何一个。最后一段的总结也呼应了开头的核心问题。唯一的小瑕疵是第 11 段在讲部署工程挑战时重复了一次第 9 段提到的显存优化策略。但整体来看12000 字的输出里只有这一处轻微重复连贯性已经远超普通模型一次性输出的效果。5. 常见报错与排查401、local proxy failed、reading choices 怎么处理部署和调用过程中你大概率会遇到几个典型报错。我把自己踩过的坑整理出来对照着排查能省不少时间。5.1 401 Unauthorized 与 API Key 配置错误如果你用 TaoToken 的 API 接入报 401 通常有两个原因。一是 api_key 没填对二是 base_url 写错了。正确的配置是client openai.OpenAI( base_urlhttps://taotoken.net/api, api_keysk-你的实际Key )注意 base_url 后面不要加/v1TaoToken 的 API 路径已经做了兼容处理。如果你本地 vLLM 报 401检查一下是不是在启动命令里加了--api-key参数但调用时没传。本地服务默认不校验如果你手动加了 key调用时就要在 header 里带上。5.2 local proxy failed 与网络连接问题这个报错通常出现在你用了本地代理但代理没启动或者代理端口和代码里配置的不一致。如果你在代码里设置了http_proxy或https_proxy环境变量但代理服务没跑起来就会报local proxy failed。排查步骤先echo $http_proxy看看有没有值如果有但你不确定代理是否正常直接unset http_proxy https_proxy再跑一次。如果你用的是 TaoToken 的 API不需要额外代理直接连就行。5.3 reading choices 报错与响应解析失败reading choices报错一般是因为 API 返回的 JSON 结构和你代码里解析的字段不匹配。比如你用的是 OpenAI 的 SDK但服务端返回的格式有差异。排查方法先把原始响应打印出来。response client.chat.completions.create(...) print(response.model_dump_json(indent2))看看choices字段是否存在message.content是否为空。如果 content 为空但 finish_reason 是length说明 max_tokens 设小了输出被截断。把 max_tokens 调大到 4096 或 8192 再试。5.4 OAuth 与 Claude Code 接入的配置要点如果你是通过 Claude Code 或者类似的 coding agent 来调用 LongWriterOAuth 报错通常是因为认证方式不匹配。Claude Code 默认走 Anthropic 的 OAuth 流程但 LongWriter 的本地服务或 TaoToken 的 API 用的是 API Key 认证。这时候你需要改 Claude Code 的配置把认证方式从 OAuth 切到 API Key。在 Claude Code 的 settings 里找到auth_type字段改成api_key然后填入你的 Key。Base URL 改成https://taotoken.net/apiModel ID 填longwriter-9b或你在 TaoToken 控制台看到的对应模型名称。这三件套Base URL Key Model ID配齐之后Claude Code 就能正常调用 LongWriter 做长文生成了。如果你用的是 Cline 或者 CC Switch 这类工具配置逻辑是一样的。在 MCP 配置里把 provider 改成 openai-compatible然后填上面三个参数。Codex 的 auth.json 里也是同样的结构把api_base和api_key对应填好就行。6. 从长文生成到长期写作流水线把 AgentWriter 接入你的工作流跑通一次万字生成之后你可以把这套流程固化下来变成日常可用的写作流水线。我的做法是把 AgentWriter 的调度脚本封装成一个命令行工具输入一个主题和字数要求自动完成规划、逐段生成、拼接和保存。如果你需要频繁调用建议把模型服务常驻在后台用 systemd 或者 supervisor 管理 vLLM 进程。这样你随时调用 API 都能秒级响应不用每次等模型加载。对于需要长期编码和 Agent 调度的场景TaoToken 的 Coding Plan 提供了更稳定的 API 配额和并发支持适合把长文生成接入到自动化内容流水线里。你可以在 https://taotoken.net/coding-plan 看到具体的方案说明。如果只是偶尔验证模型效果用模型对话页面直接测试就行不需要本地部署。实际用下来AgentWriter LongWriter 这套组合最大的价值是“可控”。你可以精确控制每一段的字数、主题和顺序生成结果的结构完整性远超一次性 prompt。对于需要批量产出长文档的团队来说这套方案值得花时间搭起来。