从 2000 字散架到万字连贯:AgentWriter 的 plan/write 管道怎么跑通
如果你最近在折腾长文生成,大概率刷到过清华和智谱联合开源的 AgentWriter(LongWriter)——它用 plan/write 两阶段把一次生成 2000 字就崩的任务,拆成每段 200–1000 字的子任务,再带着前文逐段续写,硬是把现成 LLM 的输出拉到 10000 字以上。但真把仓库 clone 下来跑,很多人卡在同一个地方:plan 阶段能出结果,write 阶段续到第三段就开始丢上下文、请求超时、或者干脆报通道错误。这篇就从这条管道的实际配置切入,把 Base URL 怎么填、Key 怎么拿、plan 和 write 怎么分别验证讲清楚。TaoToken 在这里只做一件事:提供可用的 Key 和 Base URL,plan/write 的任务分解逻辑完全按论文 prompt 原样保留。官网入口先放这里:https://taotoken.net/?utm_source=taotoken_aicg_blog_end
一、原问题与场景:为什么 write 阶段总断片
AgentWriter 的核心思路不复杂。plan 阶段让模型把用户的长篇写作指令拆成若干子任务,每个子任务对应一个段落,明确主要观点和字数要求,约束是每段不少于 200 字、不超过 1000 字。write 阶段则是循环:第 n 段生成时,把原始指令、完整写作计划、以及已经写好的 n-1 段文本一起塞进 prompt,让模型接着写第 n 段,并且只输出新段落、不重复前文。
问题就出在这个循环上。假设一篇 10000 字的文章被拆成 15 段,write 阶段就要发起 15 次请求,每次请求的上下文都在增长——到后面几段,prompt 里已经累积了七八千字的已写文本。这条多轮循环每走一步都在消耗 Token,如果模型通道不稳定、Base URL 配错、或者 Key 权限不对,整条管道根本跑不满:可能 plan 能过,write 第一段能过,第二段开始 401,或者请求发出去半天不返回,脚本直接超时退出。
更隐蔽的问题是 Base URL 的写法。很多教程里写的是带/v1的地址,但 AgentWriter 脚本里如果用的是 OpenAI 兼容接口,填错路径会导致请求打到错误端点,返回 404 或者莫名其妙的空响应。这不是模型能力问题,是通道配置问题。
二、TaoToken 前置:拿 Key、填 Base URL
在改 AgentWriter 脚本之前,先把模型通道准备好。打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 注册账号,然后在控制台创建一个 API Key。这个 Key 就是后面脚本里要填的凭证。
关键点在于 Base URL 的写法。AgentWriter 脚本里通常有一个base_url或者api_base参数,填的是:
https://taotoken.net/api注意两点:不带/v1,不加任何 UTM 参数。有些 OpenAI SDK 会自动在 base_url 后面拼/v1/chat/completions,所以 base_url 本身不要带版本路径。如果你用的是 requests 直接发请求,那完整的请求地址就是https://taotoken.net/api/v1/chat/completions,但脚本配置项里只填到/api这一层。
Key 的获取和 Base URL 的确认,都在控制台的 API Keys 页面完成。如果你在配置过程中遇到 401 或 404,优先检查这两个值:Key 是否复制完整(有没有多余空格)、Base URL 是否误加了/v1或末尾斜杠。
三、可复制配置:plan 和 write 的脚本改动点
AgentWriter 原仓库的脚本结构一般是这样的:一个plan.py或agent_write.py,里面定义了两个函数——generate_plan()和write_paragraph()。你要改的只有模型调用那一层,prompt 模板原样保留。
以 OpenAI Python SDK 为例,改动集中在客户端初始化:
from openai import OpenAI client = OpenAI( api_key="YOUR_API_KEY", base_url="https://taotoken.net/api" ) def call_llm(prompt: str, model: str = "MODEL_ID") -> str: response = client.chat.completions.create( model=model, messages=[{"role": "user", "content": prompt}], temperature=0.7, max_tokens=4096 ) return response.choices[0].message.contentplan 阶段的 prompt 按论文原样:
我需要你帮我将以下长篇写作指令分解为多个子任务。每个子任务将指导文章中一个段落的写作,并应包括该段落的主要观点和字数要求。 写作指令如下: {用户指令} 请按照以下格式进行分解,每个子任务占一行: 第一段 - 主要观点:[详细描述段落的主要观点] - 字数: [字数要求,例如,400字] 第二段 - 主要观点:[详细描述段落的主要观点] - 字数: [字数要求,例如,1000字] ... 确保每个子任务都清晰具体,并且所有子任务覆盖了写作指令的全部内容。不要将子任务拆分得太细;每个子任务的段落不应少于200字且不超过1000字。不要输出任何其他内容。write 阶段的 prompt 同样保留:
你是一位出色的写作助手。我将给你一个原始的写作指令和我计划的写作步骤。我还会提供我已经写好的文本。请帮我根据写作指令、写作步骤以及已经写好的文本继续写下一个段落。 写作指令: {用户指令} 写作步骤: {第一步生成的写作计划} 已经写好的文本: {之前生成的(n-1)个段落} 请整合原始写作指令、写作步骤以及已经写好的文本,现在继续写{第n段的计划,即写作计划中的第n行}给我。如果需要,你可以在开头添加一个小标题。记得只输出你写的段落,不要重复已经写好的文本。循环逻辑就是:先调一次call_llm(plan_prompt)拿到分段计划,解析出每段的主要观点和字数;然后 for 循环每一段,把已写文本拼进 write_prompt,调call_llm(write_prompt),把返回结果追加到written_text里。每次请求都走同一个 client,也就是同一个 Base URL 和 Key。
四、验证请求:先单跑 plan,再连写三段
配置改完不要直接跑整篇。分两步验证。
第一步,单跑 plan。把用户指令设成一个明确的长文题目,比如“写一篇关于大模型长文本生成技术演进的综述,目标 8000 字”。调一次 plan,看返回的子任务列表是不是按格式拆成了多行,每行有没有主要观点和字数,字数是否落在 200–1000 区间。如果 plan 返回的是乱七八糟的散文而不是结构化列表,说明模型没按 prompt 走,可以检查 temperature 是不是太高,或者换一个指令遵循能力更强的模型 ID。
第二步,让 write 连续续写三段。不要一次跑完 15 段,先手动循环三次,每次打印出当前请求的 prompt 长度和返回内容。确认三件事:每次请求的 prompt 里确实包含了前 n-1 段的已写文本;请求是从 TaoToken 通道出去的(可以在控制台看调用记录);第三段返回的内容没有和前两段重复,上下文是连贯的。如果第三段开始出现“重复前文”或者“忘记计划”的情况,大概率是 prompt 拼接时把已写文本截断了,或者 max_tokens 设得太小导致返回被截。
验证通过后,再把循环跑满。这时候如果中间某一段失败,脚本要有重试逻辑,不要直接退出——因为长文生成本来就是多轮请求,偶发超时是正常的。
五、本篇常见错排查
报错 401 Unauthorized:Key 没填对,或者 Key 被禁用。去 API Keys 页面重新创建一个,确认复制时没有带空格。如果用的是环境变量,检查echo $OPENAI_API_KEY是不是空的。
报错 404 Not Found:Base URL 写错了。最常见的是填成了https://taotoken.net/api/v1,多加了/v1。改成https://taotoken.net/api即可。另外检查脚本里有没有硬编码的旧地址没改干净。
plan 能过但 write 超时:write 阶段的 prompt 随着段数增加会越来越长,到后面几段可能超过模型的上下文窗口。解决办法是在拼接已写文本时做截断,只保留最近 n-1 段,或者把 max_tokens 调小一点,让每次返回的段落短一些。另外确认请求超时时间设得够长,长文生成单次请求 60 秒以上是正常的。
write 返回内容重复前文:prompt 里“不要重复已经写好的文本”这句没生效,或者已写文本的拼接格式让模型误以为要续写而不是新写。检查 write_prompt 里{之前生成的(n-1)个段落}是不是真的填进去了,以及段落之间有没有用明确的分隔符隔开。
控制台看不到调用记录:请求根本没发到 TaoToken。检查脚本里的 client 是不是真的用了你改的 base_url,有些仓库会在多个地方初始化 client,改了一处漏了另一处。
六、配通之后:从单篇验证到长期跑管道
AgentWriter 这条管道的特点是请求次数多、上下文累积快,单篇万字长文跑下来就是十几次甚至几十次模型调用。如果你只是偶尔复现一下论文效果,按上面的配置跑通就行。但如果你打算把这条管道接到自己的写作工具里、或者批量生成长文,那每次手动改脚本、盯着控制台看调用量就不太现实了。
长期跑的话,建议把 Key 和 Base URL 统一放到环境变量或者配置文件里,脚本里不要硬编码。另外可以在 write 循环里加一个简单的日志,记录每次请求的 prompt 长度和返回长度,方便排查是哪一段开始出问题。模型 ID 也可以根据 plan 和 write 的不同需求分开配——plan 阶段需要指令遵循强,write 阶段需要长文本连贯性好,不一定用同一个模型。
如果你在配置过程中卡在 Key 或者 Base URL 上,直接去 API Keys 页面重新生成一个,对照接入文档确认地址格式。需要验证模型通不通,可以在模型对话页面发一条测试请求,看能不能正常返回。长期做编码类 Agent 或者需要稳定跑管道的,可以了解一下 Coding Plan 的额度方案,比单次按量更适合高频调用场景。
通道配通只是第一步,plan/write 的 prompt 和循环逻辑才是 AgentWriter 的核心。把这两层分开调,长文生成的成功率会高很多。