之前有个做内容的朋友跟我聊,说他们团队每个月固定要产出几份深度行业报告,单篇两万字起步,从资料收集到初稿成文再到反复校对,几个人轮流扑上去,一周时间说没就没。我听完当时的判断是:这个场景不是靠增加人力就能消化的,它真正缺的是一条能把资料整理、内容规划、逐段生成、质量检查串起来的自动化写作流。正好我在类似项目里搭过一套方案,从 RAG 检索到全局 Linting 一路跑通,中途踩了不少坑,今天把架构思路和落地细节完整拆开讲。
这条自动化写作流能做什么,一句话概括:把几万字长文的生产,从“人肉堆时间”变成“流程可控的流水线作业”。RAG 负责让模型“有据可依”,Linting 负责让生成结果“不越界”。它适合谁?内容团队批量出报告、个人写技术白皮书、做课程讲义整理,甚至网站批量文章生产,都可以参考这套思路。当然,它不是一个开箱即用的成品,需要你愿意投入一点基建时间,但投产之后的生产效率提升,绝对值回票价。
1. 为什么长文写作必须先聊架构
1.1 长文生产的三个常见失控点
写长文和写短文完全是两码事。写一篇几百字的短文,你脑子能装下全部要点,写完扫一眼就能判断质量。但一篇几万字的内容,信息量是分散的,越往后写越容易失控。我在几个长文项目里遇到的典型问题,基本能归成三类。
第一是资料整合失控。几万字内容往往要从十几份甚至几十份来源里抽素材,人工整理本身就要花大量时间,而且整理出来的东西大概率是一堆剪贴板碎片,没有结构,更谈不上互相印证。第二是内容一致性失控。人写长文,状态好的时候前面写得好,状态差的时候后面就飘了;模型生成也一样,不同批次生成的段落可能风格不一、术语不一、前后观点还可能打架。第三是质量检查失控。几万字逐字读一遍就要占掉大半天,漏检错漏是常事,更别提重复段落、术语不统一、数字对不上这种细碎问题。
这三个问题单独拿出来,好像都能靠“多招个人”“多花点时间”缓解,但放到一个需要稳定产出的流程里,这样做的成本高且不可控。所以我后来想明白一件事:长文生产必须像软件工程一样做架构拆分,把资料、生成、校验当成独立模块,各自负责自己的一摊,通过清晰的接口协作,而不是指望某个环节一刀切解决所有问题。
1.2 为什么是 RAG 加 Linting 的组合
RAG 的完整说法是 Retrieval-Augmented Generation,检索增强生成。它解决的是“素材从哪里来”的问题。不接 RAG 的时候,模型只能靠训练时的记忆写东西,长文里非常容易出现事实性偏差,尤其是数据、引用、背景描述这些细节,模型经常一本正经地编。接上 RAG 之后,可以先把资料做成向量化索引,按需检索,把相关内容作为上下文喂给模型,生成的每个章节,至少有一部分内容是有来源的,而不是凭空猜测。
Linting 原本是编程领域的词,指用工具对源代码做静态检查,找出潜在问题。后来我越做越觉得,几万字的文章本质上也是一份“超长代码”:它有结构、有格式、有命名规范,也会出现重复、不一致、越界的问题。把 Lint 的思想用到文本上,相当于给自动生成的内容加了一道兜底保护。先让模型大胆写,写完用规则去查,查出来有问题就走修复流程。生成和校验分开,还有个额外好处:校验规则是确定的,生成结果却是概率性的,不能让可能出错的模型去自证自己写得没问题,那样它会带着同样的偏见一路错下去。
一句话总结我的核心思路:不要试图让模型一次生成几万字,而是让它在一个受控的流水线里,每次只生成一段,但确保每一段都有资料依据、都有规则校验、都能被追踪。
1.3 五层架构总览
按我实践下来的经验,一条自动化写作流可以拆成五层。每一层的职责边界尽量清晰,层与层之间只通过标准化的数据格式通信。
| 层次 | 职责 | 关键要点 |
|---|---|---|
| 数据层 | 资料采集、清洗、分块、向量化 | 资料质量决定生成质量的上限 |
| 规划层 | 生成全局大纲、章节要点 | 大纲是长文一致性的骨架 |
| 检索层 | RAG 召回相关片段并注入上下文 | 控制信息密度,避免上下文爆炸 |
| 生成层 | 按章节逐段生成,维护全局状态 | 温度、上下文摘要、章节约束 |
| 校验层 | Linting 检查、问题反馈、自动修复 | 规则分级,错误阻断,警告提示 |
这套五层结构不依赖什么重型框架,很多环节用脚本就能编排起来。工程化的关键不是工具多高级,而是每层之间有固定的输入输出协议。比如数据层输出的是带 metadata 的文本块,检索层返回的是带相关性分数的片段列表,生成层产出的是带章节标识的 Markdown 文本,校验层反馈的是规则命中的结构化结果。只要这些协议稳定,任何一层单独替换升级,其他层都不需要跟着动。
2. RAG 检索细节:决定内容上限的关卡
2.1 资料清洗与分块策略
很多人图省事,把资料抓下来直接扔进向量库,结果检索出来全是噪音,生成内容质量跟着崩。资料入库前一定要做清洗,这个环节看起来不起眼,但对最终效果影响极大。我常用的清洗流程包括:去广告与去页脚页眉、统一正文编码、识别并修复乱码字符、剔除与主题无关的段落(比如文章底部的推荐阅读、版权声明),再把图片的替代文本和图表标题提取成纯文本,补充到对应段落里。
清洗完了还需要做质量排序。给每份资料打一个可信度分数:来源相对权威、发布时间新的资料分数高;来源不明的个人博客、转载痕迹明显的页面,分数就压一压。这个分数在检索结果融合时会作为加权因子,避免模型优先引用低质量来源的内容。资料量少的时候可以人工打标,量一上来就得靠规则自动打标,比如按站点类型、作者历史记录、引用次数来估。
分块这块,我强烈建议做结构感知分块,而不是按固定字符数硬切。分块太粗,整篇报告作为一个向量存进去,检索匹配到的是大而全的模糊内容,信息密度太低;分块太细,上下文被切断,检索出来的片段没有来龙去脉,模型看不懂。我常用的策略是按章节语义分块,每个块控制在 512 到 1024 个 token,块与块之间预留少量重叠。这个数字和向量编码器的输入长度上限、模型的上下文窗口、检索精度的平衡都有关系,512 到 1024 基本能兼顾“保留完整语义”和“精确匹配”。
实操中还有一个容易踩的坑:分块要避开表格和列表的中间位置。表格从中间断开,向量化之后语义就丢了;列表从中间断开,检索出来的片段可能只有半句话。我的做法是先做结构解析,把标题、表格、列表识别出来,再基于这些结构边界切块,宁可让块大小浮动,也比固定长度硬切更好。
2.2 混合检索与重排:为什么不能只靠向量
向量检索的本质是把文本映射到高维空间,用距离衡量相似度。它擅长处理“语义相似但字面不同”的查询,比如你搜索“行业市场份额变化”,它能召回“该赛道销量占比趋势”这种表述相近但用词不同的内容。但向量检索也有短板:它在关键词精确匹配上不一定可靠,尤其当资料里出现大量编号、术语缩写时,向量距离往往分不清细微差别。
所以我实际用的是混合检索,也就是向量召回加关键词召回并行,再做结果融合。具体操作是:对同一个检索 query,向量召回取回 top 20,关键词召回取回 top 20,然后把两路结果按分数加权排序,最后取 top 5 到 top 8 作为注入上下文。这样既保住语义相关性,也保住术语精确性。权重怎么设?我一般先跑一组样本,对比纯向量、纯关键词、混合三种模式的命中情况再调,没有固定参数,关键词权重通常落在 0.3 附近。
检索完还要加一步重排。向量和关键词召回算的是粗粒度相关性,重排这一步会用更精细的匹配模型,对候选片段逐条打分排序。这一步能明显改善注入内容的质量,因为粗召回里总会有一些“看起来像但实际不相关”的内容混进来,重排可以把它压下去。用不用重排,差别在最开始体感不明显,但放到几万字的长文里,累积效应很大。
提醒一句:检索不是一次搞定。我会针对一个大纲章节做多路检索,比如把章节标题、章节要点、该章节涉及的关键实体分别作为 query 检索,再合并去重。这样召回的内容覆盖度更高,生成时不容易漏关键信息。
2.3 上下文注入:不是塞得越多越好
模型生成每一段内容,上下文窗口终归有限。你不能把检索回来的所有资料全塞进去,那样不仅烧钱,还会稀释重点,模型反而抓不住核心。我的经验是把注入内容分成三块:全局约束、章节要求、检索片段。
全局约束包含全文的受众定位、语气风格、术语表、必须出现的核心结论,这块内容比较小,全程携带。章节要求是当前正在生成章节的标题、要点列表、与其他章节的关系。检索片段是真正从资料库召回的相关内容,我会限制在 1500 到 2500 字以内,按关系分数排序截断。三块加起来尽量控制在模型舒适的处理范围之内,既给足信息,又不至于让模型被无关内容干扰。
一个常见的认知误区是“为了怕模型没素材,就多塞一点”。实测下来,上下文里塞太多弱相关内容,模型会把其中不太重要的信息也当成重点写出来,反而跑偏。宁可少而精,把最相关的片段喂进去,再靠多轮生成和检查来补齐遗漏。
3. 生成调度:大纲驱动与分段写作
3.1 大纲生成与校验:先有施工图再动工
在生成正文之前,我会先让模型产出全局大纲。大纲的粒度要到三级标题甚至四级标题,每个叶子节点还要带上“本章要回答的问题”“可能涉及的资料关键词”“预期篇幅”。这一步相当于给整个写作流画了一张施工图,后面的生成任务全部围绕这张图展开。没有大纲直接开写,最容易出现的情况是章节之间逻辑脱节,写到一半才发现前面漏了重要主题,再回头补非常痛苦。
大纲生成之后不能直接开写,我会先过一遍规则检查:标题编号是否连续、是否有重复标题、各章篇幅是否匀称、是否漏掉了资料库里的关键主题。发现漏掉的章节,就补充检索词再生成一次。大纲阶段多花半小时,正文生成阶段能省好几个小时。因为很多一致性问题,比如术语不统一、前后结构重复,在源头其实是大纲埋下的雷。大纲阶段就把这些雷排掉,后面才不会炸。
3.2 分段生成:一次只写一个章节
分段生成的核心思路很简单:每次调用只生成当前章节,不一次生成整篇。每一章的输入由几部分组成:全局约束、上一章摘要、当前章标题、当前章要点、检索上下文、输出格式要求。其中上一章摘要非常关键,它让章节之间有基本的衔接,又不会让上下文越滚越长。摘要我一般让模型用 100 到 150 字概括上一章的核心内容,作为下一章的前置信息。
为什么要用摘要而不是把上一章全文接过来?道理很朴素。写几万字如果每章都把前面所有内容拼进上下文,token 消耗会一路飙升,而且超长上下文中模型对早期内容的注意力会分散,你希望它引用第一章某段内容的时候,它往往已经忘得差不多。摘要相当于给模型一张“前情提要”,让它在写当前章的时候知道上一章讲了什么,又不会被远古历史干扰。
温度参数我也单列一下。生成正文阶段,我一般把温度设在 0.3 到 0.5 之间,太低容易显得机械,太高容易飞。大纲阶段可以再低一点,让模型更稳定地输出结构。检索进来的资料如果信息量充足,温度稍微调高一点问题不大;如果资料稀疏,温度还高的话,模型容易自己发挥编内容。
3.3 局部重跑与日志追踪
分段生成还有一个很大的好处:出错了可以局部重跑。坏了哪章修哪章,不需要整篇推倒重来。我实际跑的时候,会把每一章的输入和输出都存成日志,带章节标识、检索结果、生成结果、Lint 结果。这样一旦某章出了问题,能快速定位是检索问题、提示词问题,还是模型偶发问题。
日志示例大概是这样的:
{ "chapter_id": "03-02", "chapter_title": "市场规模与增速分析", "retrieval_queries": ["市场规模", "同比增速", "行业空间"], "retrieved_fragments_count": 7, "generated_text_length": 2341, "lint_result": { "error_count": 0, "warning_count": 2, "warnings": ["第三章与第二章背景段落相似度较高"] }, "retry_count": 1 }有了这种结构化日志,整个流程的可观测性就上来了。文本生成模型本质上有一点随机性,偶尔抽风是正常的,关键是能快速定位抽风发生在哪一环、原因是信息不足还是提示词不清。这一点对后期优化整个流水线价值很大,比通篇读文章找毛病高效得多。
4. 全局 Linting:给长文加一道质量围栏
4.1 文本 Lint 到底检查什么
把代码世界的 Lint 思路搬到文本世界,第一步要梳理清楚检查项。我维护了一套自己的文本检查规则集,主要覆盖四类。
结构规则负责查标题层级是否连续、章节编号是否正确、段落长度是否过短或过长、列表符号是否统一。内容规则负责查关键术语是否在对应章节出现、核心结论是否被重复、前后文的同一指标数字是否一致。格式规则负责查全半角符号混用、多余空行、标题前后空行不一致等排版问题。安全规则负责查违禁词、敏感主题和明显的主题漂移。
这些规则的实现难度差别很大。像全半角、空行这类规则,纯字符处理就能搞定。但数字一致性检查就要稍微费点劲,比如“2.5亿”这个值在后文不能变成“25000万”,需要把数值归一化后做全篇对齐比较。术语一致性检查也类似,得先维护一份项目术语表,再对全文做词形匹配。下面用表格整理一下:
| 规则类别 | 检查项示例 | 严重级别 |
|---|---|---|
| 结构规则 | 标题编号断裂、无编号列表混用 | Error |
| 内容规则 | 同一指标前后数字不一致、结论重复 | Error |
| 格式规则 | 全角逗号混用、多余空行 | Warning |
| 安全规则 | 违禁词命中、主题漂移 | Error |
4.2 规则分级与错误反馈闭环
Lint 规则一定要分级。我的设计是 Error 和 Warning 两级。
Error 级别规则命中时,流水线会暂停当前章节的下一步操作,把具体问题、涉及文本片段、修改建议一起返回给重写模块。模型拿到这些反馈后做局部改写,改完再过一遍 Lint,直到通过或者达到最大重试次数。Warning 级别只记录到报告里,不阻断生成,避免流程被小问题卡死。这个分级非常关键,如果所有问题都阻断,一篇几万字的长文会频繁暂停,跑完可能比人写还慢;如果所有问题都不阻断,质量问题又会累积到最后爆发。
自动修复的方式也要分场景。格式类问题,比如全半角、多空行,直接做确定性规则替换就行,不需要模型介入,快且便宜。语义类问题,比如段落重复、论述不完整,则需要把 Lint 反馈写给模型让它重写。这两类修复的路径要分开,节省算力也节省时间。
4.3 修复重跑与最终报告
每一章从生成到过 Lint,如果中间出现 Error,就进入修复重跑循环。我设置的最大重试次数一般是 3 次。超过 3 次还不能通过,就把这章标记为“待人工处理”,让流水线继续跑后面的章节,不要因为一章卡住导致整篇停滞。最后所有章节跑完后,会聚合生成一份质量报告,列出每一章的问题数、剩余 Warning 清单、以及哪些章节需要人工复核。
这份 Lint 报告对人工审核非常有用。几万字的内容不可能逐字读,有了报告,审核的人只需要重点关注那些命中 Error 的重写章节和剩余 Warning 较多的地方,效率会高很多。自动化生产的内容不怕有问题,怕的是问题藏在暗处发现不了;Lint 报告相当于把所有的风险点摊开放在桌面上。
5. 最小可用系统:全流程跑通实操记录
5.1 技术选型:能省则省,先跑通再升级
很多朋友一听这种系统,第一反应是上重型框架。我的建议相反:先尽量用轻量方案把最小闭环跑通,再逐步升级。
向量库在一开始数据量不大时,用嵌入式本地方案就够了,不需要一上来就上分布式服务。任务调度用简单的队列脚本就能串起来,不一定非要引入复杂的作业编排系统。模型调用也先用统一封装,后面要换更强的模型,只改一层模型适配代码就行。整套系统最核心的是流程分层和各层之间的数据协议,这些一旦定好,底层工具换掉不影响大局。
选型遵循三个原则即可:单机跑得动、模块能解耦、接口保持简单。等到单机实在顶不住性能压力,或者并发调用量上来了,再针对具体的瓶颈模块做替换,完全来得及。
5.2 流水线调度与失败处理
我把整个写作流跑成一个状态机,每个章节独立流转。状态大致是:待处理、生成中、Lint 检查、修复中、已完成、待人工。某个章节在任意环节失败,都会走失败处理逻辑,而不是直接中断全局。
失败处理分两个级别。模型调用超时或返回异常这类基础设施故障,用指数退避重试,最多三次,三次后标记待人工。Lint 检查反复不通过这类业务问题,直接走最大重试逻辑,不浪费无谓的资源。整个流水线支持断点续跑:日志里记着每个章节当前处于什么状态,中途中断了,重启后从最后一个未完成章节接着跑,而不是从头开始。
并发控制同样要设计。我一般会设置一个全局并发上限,防止同时发起太多模型调用导致接口限流。尤其是在批量跑十几章内容时,串行跑虽然稳但慢,并发放开后虽然快但容易触发限流。折中做法是控制并发数为两到三个,每章之间错峰发起,配合重试机制,整体速度很稳,也不会被限流卡死。
5.3 成本估算与跑通前的小规模验证
长文生成跑通之前,最好先做一次小规模验证,不要直接拿整篇几万字开跑。我会先用一章的体量,比如 2000 字左右的章节,从资料清洗、检索、生成到 Lint 全链路跑一遍,估算单章的成本和耗时,再按照章节数量推算全篇预算。
成本的大头在模型生成阶段。中文内容加上检索上下文,实测下来生成一章节内容,token 消耗往往是正文篇幅的两到三倍。很多新接入的人一上来就惊讶“怎么 token 花得这么快”,其实都是上下文里携带的信息在消耗。如果预算紧张,可以先在规划层和大纲阶段用相对轻量的模型,正文阶段再调用强模型,整体开支能压缩不少。
跑通验证之后,再分批次把章节喂进流水线。这样即使中间某一章出问题,损失也只限于当前批次,不会出现跑了一整晚、早上一看整个流程在开头就挂掉的惨剧。
6. 常见问题与排错实录
6.1 检索内容太散,写出来偏题
现象:章节生成内容跟标题相关,但明显跑偏,关键信息没写进去,杂七杂八的信息倒写了不少。
排查思路:先看检索环节返回的片段,是不是前几个片段都不够贴题。再检查章节要点是否写清楚,要点越模糊,模型发挥空间越大,也越容易偏。最后看上下文注入是否塞了太多弱相关片段。
解决措施:检索那边改成多路检索加重排,章节标题、要点、关键实体分别查,合并后重排。提示词里明确要求“若资料中未覆盖某要点,须明确说明资料不足”,把模型自由发挥的闸门关小一点。实测下来,这两步对偏题问题的改善非常明显。
6.2 章节之间重复内容多、术语漂移
现象:第三章和第四章出现大段内容重复,都在介绍同一个背景概念。同一件事前文叫“数字化内容”,后文叫“电子化内容”,读起来像换了个人在写。
排查思路:重复内容多,往往是上一章摘要没传到位,或者两章检索到的资料重叠度高。术语漂移则大概率是全局约束里的术语表没有发挥约束作用,模型没有强制遵守固定表达。
解决措施:在全局约束中放一份强制术语表,Lint 规则里增加术语一致性检查,发现偏离就给 Error 提示让模型重写。重复检测方面,对相邻章节做向量相似度比对,相似度过高就触发重写。另外把上一章摘要写得更具体一点,告诉模型“这部分已经讲过了,不要再展开”,也能有效压制重复。
6.3 Lint 误报太多,人工反而不想用
现象:Lint 系统上线后,规则误报率很高,动不动就把正常句子判成违规,团队干脆不再看 Lint 报告,等于系统白做。
排查思路:大多数误报来自规则太死。比如“核心结论不能重复”这条,如果把所有出现关键词的段落都判为重复,那肯定误伤。规则需要更精细的判定逻辑,比如先定位真正的核心结论句,再查它是否在不同章节中以大段相似形式出现。
解决措施:做一段人工校正数据集,把预期判为正常的内容跑一遍回归,凡是误报多的规则就降级或改逻辑。同时把规则的开关和阈值做成配置文件,不同项目可以调,避免一套规则打天下。Lint 系统要让人愿意用,首先不能给人添不必要的麻烦。
6.4 长文生成速度太慢、预算超支
现象:整篇跑下来耗时远超预期,或者消耗额度高得吓人,只能中途放弃。
排查思路:看日志,定位时间都花在哪个环节。如果模型调用环节占大头,考虑是否每章都塞了太多上下文;如果重试环节频繁,考虑是不是 Lint 规则过严导致反复重写。
解决措施:先按单章成本做预估算,把预期成本摆出来再决定是否继续。速度方面,可以并发跑章节,但控制好并发上限。预算方面,大纲阶段用轻量模型,正文阶段才用强模型;另外把重试次数从 3 次降到 2 次,少刷无效请求。
6.5 模型输出格式偶尔损坏
现象:大多数章节输出正常,偶尔某章漏了标题,或者列表符号混乱,或者 Markdown 结构断掉。
排查思路:这类问题属于模型输出稳定性问题,尤其在长上下文生成时更容易出现。上下文越长,模型越可能在格式上“手抖”。
解决措施:生成后立刻做格式校验,发现关键标题缺失或结构断掉,直接让模型重写对应片段,不流到下一步。同时对输出做后处理,例如自动补全章节编号、规范化列表符号。偶尔手工微调不可避免,但整体占比控制住就问题不大。
7. 一点心得与扩展方向
这套系统从搭起来到稳定跑通,我最大的体会是:技术不是核心,流程分层的习惯才是。最开始我也忍不住想一次性把 prompt 写得又长又全,希望模型一步到位生成高质量长文,但实测崩过几轮之后才明白,几万字的生产必须靠拆解和校验来兜底。每一个环节单独看起来都不复杂,组合起来却能消解掉长文生产里最折磨人的随机性。
后续如果还想继续扩展,我会优先做两个方向。一个是把 Lint 检查从“事后查”往“事前拦”推进,比如在多路检索结果进来之前就先过滤一波低质量片段,让生成环节一开始就拿到底子更好的素材。另一个是接一个人工审核节点,让所有被打上“待人工”标记的章节进一个统一工作台,配合 Lint 报告快速处理,而不是在文档里来回翻找。自动化写作流的价值,不是彻底取代人,而是把人的精力只留给人最擅长的判断和决策。