从自动补全到创作协作者:用RAG和Agent搭建LLM虚构写作工作流

发布时间:2026/8/30 23:43:33
从自动补全到创作协作者:用RAG和Agent搭建LLM虚构写作工作流 如果你是一个写作者或者正在帮写作者做工具这半年你大概率听过一个说法大语言模型LLM只是“高级自动补全”它写不出真正的小说。这个判断一半对一半错。对的地方在于如果你只是打开一个聊天窗口输入“帮我写一个悬疑故事开篇”绝大部分时候拿回来的是套路化的、漂浮在文本表面的东西。它没有肌理没有生活质感也没有人物真正挣扎的痕迹。错的地方在于这根本不是 LLM 用于虚构创作的正确姿势。真正的问题不是“模型能不能写小说”而是“你拿什么作为模型创作的外部支撑”。当你把角色设定、世界观档案、章节伏笔、时间线、人物关系图谱全部结构化地喂给模型把一次性“生成”变成多轮、多角色、带记忆的“协作流程”LLM 在虚构写作上的能力会发生质变。这篇文章想聊的不是“AI 写作有多神奇”而是一套可落地的 LLM 虚构写作工作流从模型选型、精度取舍到提示词架构、长篇一致性的解决方案再到用 RAG 和 Agent 把零散写作任务编排成流水线。所有技术点都能在本地或云端复现适合对 LLM 应用开发感兴趣、又想把技术能力用到内容创作上的读者。1. 这篇文章真正要解决的问题先说一个普遍的误区很多人尝试用 LLM 写小说第一反应是“让它一口气写完一个故事”。这个思路从根上就错了。LLM 的上下文窗口再大也有边界。一个十万字的网文需要至少三百万到五百万个 token目前任何主流模型都无法在单次对话里承载。更关键的是小说创作不是线性生成而是不断回溯、修改、推翻重来的过程。人物在第 30 章说了什么可能影响第 5 章某段对话的措辞一个伏笔在结尾要回收就必须在前 20 章埋下足够多的线索。这种非线性结构恰恰是单次生成式 AI 最不擅长的事情。所以这篇文章真正要解决的问题是如何把 LLM 从一个“一次性文本生成器”变成一个“有记忆、有设定、可编排的长期写作协作者”。围绕这个问题你会接触到三类技术组件模型与推理层选什么模型、用什么精度推理、本地跑还是调 API决定了创作的底线质量。记忆与知识层用 RAG 管理角色卡、世界观文档、旧章节内容解决长篇创作中“越写越偏”的问题。编排与自动化层用 Agent 把“生成新章节”“检查伏笔一致性”“提炼前情提要”等任务拆开让不同模型和工具协同工作。这三层搭建完成后你得到的不再是一个聊天窗口而是一个具备“创作上下文”的工作流。模型会记得你的设定会根据前期摘要推进情节会在你需要时帮你做一致性检查。这才是“LLMs Set My Fiction Free”真正的技术含义——它把你的精力从重复劳动中释放出来让你专注在创意决策上。2. 从“自动补全”到“创作协作者”LLM 虚构写作的核心原理要理解为什么 LLM 能用于虚构写作先要理解它的生成本质。LLM 本质上是一个极大规模的“下一 token 预测器”。给定前文它计算下一个最可能出现的 token 是什么。这句话有两层含义第一它确实不懂“情节张力”“人物弧光”这些抽象概念。它只是在海量文本中学到了“当故事出现某种模式时后面通常接什么”。但反过来想人类写作训练也有一部分是模式学习——比如“英雄之旅”的经典结构本质上也是一种被反复验证的模式。模型的优势在于它的模式库覆盖了远超个人经验的文本范围包括我们根本读不完的类型小说、文学批评、剧本和诗歌。第二因为生成过程是概率性的LLM 天然具备“发散”能力。同一个开头温度temperature调高一点它能给出完全不同走向的续写。这种发散性恰恰是传统写作软件给不了的程序员用自动化工具追求确定性而虚构创作有时需要“意外的惊喜”。真正让 LLM 从“自动补全”升级为“创作协作者”的是结构化上下文。来做个对比不给上下文直接问“写一个女孩在雨夜发现一封信”模型会给你一段通用场景描写大概率是“雨滴敲打着玻璃昏黄的灯光下她颤抖的手拆开了泛黄的信封”。给完整上下文“角色是 28 岁的法医她在整理已故母亲遗物时发现一封没有邮戳的信信里提到一个 20 年前的失踪案而失踪者是她的亲生父亲。她性格克制不轻易流露情绪雨夜让她想起童年的某个片段。”同一次续写质量会有明显差异。这不是玄学是 LLM 的概率条件机制在起作用提供的信息越精确、越具体模型输出的概率分布就越收窄到你想要的方向。在技术实现上这个“上下文”就是你注入给模型的 token。它可以是系统提示词system prompt可以先行的对话历史也可以是从向量数据库中检索出来的设定片段。理解了这一点后面 RAG 和 Agent 的设计就有了基础。2.1 关键参数对创作的影响虚构写作场景下有几个生成参数需要重点理解参数作用虚拟写作中的建议temperature控制随机性越大越发散创意发想阶段 0.8-1.0严格一致性检查阶段 0.2-0.4top_p核采样控制候选 token 范围一般保持默认或与 temperature 联动不优先调整max_tokens单次生成最大长度写章节正文 1500-3000写摘要 500 以内presence_penalty对已出现内容的惩罚减少重复章节生成建议 0.3-0.6避免车轱辘话frequency_penalty对高频 token 惩罚降低重复措辞0.3 左右即可太高会破坏文风统一性这些参数在不同推理引擎中定义略有差异但核心逻辑一致。实际项目中我建议你把“创意生成”和“一致性检查”这两类任务分开设计使用不同的参数组合而不是一套参数打天下。3. 环境准备选择模型与推理方案动手之前先解决“拿什么跑模型”的问题。从当前可用的方案看有三条路线路线一云端 API这是最省事的方案调用成熟厂商的文本生成接口按 token 计费。优点是模型能力强、维护省心缺点是需要联网数据会经过第三方服务对隐私敏感的内容创作不是最优选择。路线二本地部署开源模型用 Llama.cpp、Ollama、vLLM 等推理引擎在自有机器上运行开源权重模型。优点是完全离线、数据可控、可以深度定制采样参数缺点是硬件门槛较高模型能力相比顶级云端模型有差距。路线三混合方案用本地小模型做分类、摘要、格式整理等轻量任务用云端强模型做核心创作。这种方案工程复杂度高一些但成本控制灵活是我比较推荐在实际写作工作流中采用的方案。无论选择哪条路线都要先明确一个前提你的机器能跑多大参数的模型。3.1 精度问题FP16、FP32、BF16到底怎么选热搜词里反复出现“fp16、fp32、bf16”这是本地部署必须搞清楚的问题。简单解释FP32单精度浮点每个数值用 32 位存储精度最高但显存占用也最大。FP16半精度浮点每个数值用 16 位存储显存减半但对数值范围敏感某些场景会溢出。BF16Brain Floating Point同样是 16 位但指数位和 FP32 一样多只是尾数位更少。它对大范围数值更鲁棒训练和推理中越来越常见。对虚构写作来说精度影响的主要是生成质量和可运行性之间的取舍。精度相对显存占用常见问题适用场景FP32基准显存需求大推理慢小模型调试、CPU 推理一般不用于大规模生成FP16约为 FP32 一半大值溢出极小概率出现乱码GPU 显存适中时的常用选择BF16约为 FP32 一半尾数精度略低文本生成中几乎无感知大多数现代 GPU 推理的首选INT8/INT4 量化更小可能损失文笔细腻度情节逻辑偶发跳变显存紧张时的折中方案真实写作场景中的建议是如果显存足够跑 13B 以上模型优先 BF16跑 7B 以下模型FP16 和 BF16 差异不大如果显存不够先用量化方案跑通流程再考虑升级硬件。这里要特别提醒不要一上来就追求大模型。虚构写作的效果不只是模型规模决定的上下文管理、提示词质量、任务拆分同样重要。7B 模型配合良好的 RAG 工作流在很多场景下比裸跑一个 70B 模型更稳定。3.2 本地部署最小配置示例以 Ollama 为例一个最小的本地模型部署只需要两步# 安装 Ollama具体安装命令请参考官方文档 curl -fsSL https://ollama.com/install.sh | sh # 拉取一个适合写作的开源模型例如 Qwen2.5 7B ollama pull qwen2.5:7b # 启动交互式对话 ollama run qwen2.5:7b如果你的机器显存有限也可以用量化版本# 拉取 4bit 量化版本显存需求低很多 ollama pull qwen2.5:7b-instruct-q4_K_M在 Python 中调用本地模型生成一段小说草稿import ollama response ollama.chat( modelqwen2.5:7b, messages[ { role: system, content: 你是一位擅长都市悬疑小说的作者。你擅长用细节营造氛围对话简洁注重人物动作和感官描写。, }, { role: user, content: 林深第一次走进那家旧书店时雨刚停。书店老板递给他一本扉页写着不要读完的书。请续写这个场景400字。, }, ], options{ temperature: 0.9, top_p: 0.9, presence_penalty: 0.4, }, ) print(response[message][content])这段代码是完整的可以直接在装有 Ollama 的环境中运行。运行前需要安装 ollama 的 Python 库pip install ollama如果运行 OK你会看到模型基于你给定的场景生成一段风格相对统一的续写。注意 system 提示词里给出的信息直接影响了模型的语言风格——这正是“让模型进入小说家状态”的基础。4. 提示词架构如何让 LLM 成为一个“有自觉”的写作者环境准备好了下一步是提示词设计。很多人在这一步就放弃了因为他们把提示词理解成一句话“帮我写个故事。”真正的提示词工程不是这样它是一套有层级的“创作指令系统”。在虚构写作中我推荐至少拆分三层第一层系统提示词System Prompt——定义作者人格与铁律这一层模型每次对话都会看到。它负责回答一个问题“你是谁你写作时不可违背的原则是什么”一个示例你是一位擅长新本格推理的小说家风格受到日系推理文学影响注重日常场景中埋设异常细节。 你的写作铁律 1. 不使用“突然”“瞬间”等词制造廉价紧张感。 2. 每个角色说话时必须符合角色身份不允许所有人说一样的腔调。 3. 禁止解释性对白信息通过动作、环境、道具传递。 4. 段落之间以场景切换而非内心独白衔接。 5. 每章结尾必须留下至少一个未解释的细节。这个提示词的目的是把“风格约束”转成“生成参数”。没有约束模型会回归到平均化文本有了铁律输出会明显偏离平均值。第二层场景上下文——定义当前场景的所有可用信息这一层是动态变化的。每次生成新章节时你注入这一章相关的设定、人物状态、前情提要和目标冲突。它解决的问题是“模型在写这一章时需要知道什么”。第三层任务指令——定义这一次输出的目标格式是写 800 字草稿、50 字章节梗概、人物对话片段还是伏笔检查报告不同任务对应不同的输出结构。这在实际项目中的体现需要一套工程化的提示词管理方式可以把这些模板统一放在一个配置文件中。4.1 用配置文件管理提示词{ novel: { flash_fiction: { system: 你是新本格推理作家用日常场景制造违和感。禁止直接心理描写。, task: 根据以下场景设定创作一篇800字以内的微小说。要求包含一个耐人寻味的结尾。 }, chapter_outline: { system: 你是资深编辑擅长将复杂情节拆解为清晰的章节节奏。, task: 根据全书大纲和当前进度输出下一章的详细大纲包含场景列表、冲突升级点、伏笔安排。 }, consistency_check: { system: 你是严谨的设定管理员只从文本中提取事实不猜测。, task: 检查给定章节与设定文档之间的冲突点输出冲突清单并引用原文证据。 } } }在 Python 中可以这样加载和调用import json with open(prompts.json, r, encodingutf-8) as f: prompt_config json.load(f) def build_messages(task_name: str, user_content: str): template prompt_config[novel][task_name] return [ {role: system, content: template[system]}, {role: user, content: template[task] \n\n user_content}, ]有了这个组织方式你的提示词就像代码一样可以进行版本管理而不是散落在聊天记录里。这对长期创作项目尤其重要——一个小说写半年提示词一定会改很多轮。5. 长篇一致性用 RAG 解决“越写越偏”的核心难题长篇小说创作最头疼的问题是“自我一致性”。第 5 章设定主角左撇子第 23 章却写他用右手拿刀第 10 章说姑姑住在青岛第 40 章姑姑却在杭州出现。人类作者都会犯这类错误模型更严重。原因在于上下文窗口限制。当你写第 40 章时模型不可能始终记住第 10 章的细节。即使上下文窗口足够大从 50 万字文本里“注意”到关键设定对注意力机制也是巨大挑战——信息淹没在无关描述里模型分不清轻重。RAGRetrieval-Augmented Generation检索增强生成是解决这个问题的常用技术路线。核心思路是不把所有内容都塞进上下文而是把设定文档切碎、向量化、存进向量数据库生成前先根据当前任务检索最相关的片段只把这几段注入上下文。5.1 一个最小可用的写作记忆库假设你的小说有一个设定文档world.md内容包括人物卡、时间线、地点描述和设定规则。第一步是把它拆成小块并向量化from pathlib import Path from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.vectorstores import Chroma from langchain_community.embeddings import HuggingFaceEmbeddings # 1. 读取设定文档 text Path(world.md).read_text(encodingutf-8) # 2. 切块按章节和段落边界切避免切断完整设定 splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50, separators[\n## , \n### , \n\n, \n, 。, , ], ) chunks splitter.split_text(text) # 3. 向量化并存入 Chroma embedding HuggingFaceEmbeddings( model_nameBAAI/bge-small-zh-v1.5 ) vectorstore Chroma.from_texts( textschunks, embeddingembedding, persist_directory./novel_memory_db ) vectorstore.persist()注意几点切块粒度很重要。设定文档中的人物卡通常有完整信息切太碎会丢失上下文。用 500 字符配 50 字符重叠是一个可用起点需要根据文档结构微调。嵌入模型选中文能力好的。BGE 系列的中文效果相对稳妥但你也可以根据实际效果换其他嵌入模型。嵌入模型不决定生成质量但决定“检索到的东西对不对”。向量数据库可选 Chroma、FAISS、Milvus 等。单机项目用 Chroma 最简单没有额外服务依赖。5.2 生成时注入检索结果生成新章节时先从库里检索相关信息query 林深的左撇子习惯 和 母亲留下的信 当前人物状态 docs vectorstore.similarity_search(query, k4) context \n\n.join([doc.page_content for doc in docs]) user_content f本次需要沿用以下设定片段 ---设定开始--- {context} ---设定结束--- 请基于以上设定续写第41章3000字。这样每次生成的核心章节只会带上最相关的 4 段设定既不会超过上下文窗口又能保证关键设定不被淹没。从实践来看RAG 解决的是“静态知识”的一致性问题比如人物姓名、年龄、地点关系、时间线。但它解决不了“情节推进”的一致性——比如主角在第 20 章经历了某个重大转折之后的行为逻辑要建立在这个转折之上。针对后者更有效的做法是维护“章节摘要链”。6. 让创作自动化Agent 与编排的关键作用如果说 RAG 是“记忆层”Agent 就是“执行层”。在虚构写作项目中Agent 的价值不是替代你思考而是把重复性的、规则明确的创作子任务自动化。举个例子一个普通的章节生产流程包括根据大纲生成章节分镜chapter breakdown。根据分镜生成详细场景卡包括人物、地点、情绪、冲突目标。从设定库检索相关设定。生成章节正文。检查正文与设定的冲突。输出章节摘要并更新记忆库。更新伏笔追踪表。如果每一步都靠人工复制粘贴一个章节可能要折腾一到两小时。用 Agent 编排后这个流程可以在一个脚本里自动串联。6.1 理解 Agent 本质任务分解与工具调用LLM Agent 的核心是模型不再只是“说话”而是可以决定“调用什么工具”根据工具返回结果决定下一步动作。在写作场景工具可以包括向量数据库检索工具查设定、查前文摘要。文件读写工具读大纲、写章节、追加摘要。一致性检查工具返回冲突报告。外部模型调用工具用不同模型做不同子任务。6.2 一个最小流程示例下面的示例演示了如何用 LangChain 工具调用方式实现“先生成章节再做一致性检查”的流程。为了可控本例减小了工具数量只保留两个核心能力。from langchain_core.tools import tool from langchain_core.messages import HumanMessage from langchain_ollama import ChatOllama llm ChatOllama(modelqwen2.5:7b, temperature0.8) tool def search_memory(query: str) - str: 从设定库检索相关信息query为需要检索的内容描述 docs vectorstore.similarity_search(query, k3) return \n\n.join([d.page_content for d in docs]) tool def check_consistency(chapter_text: str) - str: 检查章节文本与设定库中的冲突返回冲突清单 docs vectorstore.similarity_search(chapter_text, k5) context \n\n.join([d.page_content for d in docs]) check_prompt f以下是设定文档片段 {context} 以下是章节正文 {chapter_text} 请检查正文与设定之间是否存在冲突输出冲突点列表。若无冲突输出无冲突。 resp llm.invoke([HumanMessage(contentcheck_prompt)]) return resp.content tools [search_memory, check_consistency] # 这里演示的是伪流程完整 Agent 循环需要处理 tool calling 的往返逻辑 # 实际开发时可以配合 LangGraph 或自写 while 循环来完成 user_request 写第41章林深根据信中的地址找到了父亲失踪前租住的房间 print(已发起创作任务, user_request)这个示例主要是为了展示工具定义的方式。实践中完整 Agent 循环会涉及模型输出 tool_call 消息、执行工具、将结果拼回消息列表后再继续生成。你可以用 LangGraph、AutoGen 或自己写循环实现。“LLM 应用为什么需要编排框架”是很多人的疑问。上面的流程就是答案当你要让模型在“检索—生成—检查—再生成”之间循环时如果没有编排层你需要自己管理多轮消息、工具结果拼接与控制流。编排框架提供的是标准化的处理逻辑。7. 推理引擎选型与本地运行调优本地跑 LLM 做虚构写作推理引擎的选择会直接影响体验。目前常用的有Ollama安装简单适合个人快速验证和原型开发。llama.cpp底层引擎支持量化格式多适合资源受限环境。vLLM吞吐量高适合服务化部署和并发请求场景。LM Studio图形界面友好适合不熟悉命令行的用户。如果你只是个人写作Ollama 足够。如果你要把写作服务部署给团队用vLLM 是更合适的服务化引擎。两者关注的维度不同前者是开箱即用后者是并发性能和显存管理。有一个细节值得留意同样一个模型在不同推理引擎下的采样参数实现可能有微小差异导致生成文本不同。更换推理引擎后你可能需要重新调一轮参数尤其是 temperature 和 penalty 参数。7.1 推理参数调优思路虚构写作的推理调优没有一个“万能参数组合”但有一个基本思路先固定模型和引擎用相同提示词做多组对照测试。每轮只改一个参数比如先定 temperature再调 presence_penalty。生成 5 到 10 段文本人工评估“可读性”“重复度”“设定遵从度”。记录下效果最好的参数组合作为后续默认配置。这个流程跟调机器学习超参数本质上一样只是目标函数变成了文本质量。建议在项目早期就把评估标准定好比如“角色说话不串味”可以量化为“评委能否从对话中辨认角色”。不用搞得很学术但要有标准。8. 常见问题与排查方法实际搭建 LLM 写作工作流时以下问题出现频率最高问题现象可能原因排查方式解决方案生成内容与设定明显矛盾检索召回不准确或设定文本被切碎打印检索到的文档片段检查是否包含关键设定调整切块粒度改用更大、更强的嵌入模型手动将核心设定固定注入系统提示词章节写得“很平”缺乏冲突和张力temperature 过低系统提示词缺少叙事节奏要求查看生成时使用的实际参数检查系统提示词是否明确要求“每章至少一个冲突升级点”适当提高 temperature在系统提示词中补充叙事约束角色说话都是一个腔调系统提示词缺少角色语言风格定义查看角色卡的描述粒度为每个角色单独维护语言风格片段作为 RAG 检索对象上下文被无关信息填满检索 k 值过大或 chunk 过长统计每轮注入的 token 数调低 k 值压缩设定文档保留高频使用信息本地推理显存不足OOM模型参数量超过显存查看推理引擎日志中的显存统计换更小的量化版本调低上下文长度换更小参数量模型生成过程出现乱码或数值异常精度选择不当或引擎 bug检查推理日志有没有 NaN 或 Inf切换 BF16/FP16升级推理引擎版本以上每个问题都不是孤立的技术 bug它们往往互相影响。建议从“上下文管理”入手排查多数写作质量问题都能追溯到这一层。9. 最佳实践与工程建议这套工作流要真正用于长篇小说创作还需要补充几个工程层面的规范。第一设定文档要结构化。不要把所有设定写成一个长 Markdown 文件。建议拆分为人物卡每个角色一个独立文件或一个独立标题层级。时间线以日期或章节编号为主线。世界观规则按领域分组比如魔法体系、科技水平、政治格局。伏笔追踪表记录伏笔出现章节、状态、回收计划。结构化越清晰RAG 的检索效果越好。模型能不能查到正确信息首先取决于你的资料库怎么组织。第二章节生成前必须有摘要。一个靠谱的流程是每次生成新章节前先从上一章生成 300 字以内的摘要把摘要和 RAG 检索到的设定一起注入上下文。整本书的长篇记忆由“摘要链 结构化设定库”共同承载而不是指望模型自己记住。第三生成内容一定要有人工环节。LLM 负责提供草稿、变体、检查和灵感卡片但最终的叙事决策权应该在写作者手里。至少做到生成内容不直接发布必须人工修订。关键情节线由人工决定模型只负责具体表达。重大转折点前后做一致性审核。第四注意数据安全与版权边界。如果你使用云端 API 处理未发表稿注意隐私条款如果作品涉及商业出版更要谨慎评估模型服务的条款和内容合规风险。本地部署可以避免很多数据外流问题但也要留意开源模型权重自身的许可协议。第五把提示词、摘要、设定文档纳入版本管理。写小说本质上是一个长期项目所有中间产物都值得进行版本追踪。用 Git 管理设定文档和生成脚本用标准化文件名管理章节正文。这样当你对模型做了一次调整导致后续章节风格突变时可以快速回滚到之前的版本。10. 写作工作流的下一步演进目前这套方案已经把“LLM 辅助虚构写作”从一次性聊天变成了一套有记忆、有检索、有自动检查的生产线。但仍有几个方向值得持续关注。更强的长文本理解能力。随着上下文窗口进一步扩大模型能直接阅读的章节数会增加但这不意味着 RAG 就不再需要——检索能帮助模型在长文本里“聚焦”重点而不是平均用力。多智能体协同写作。目前架构里是一个模型完成多个任务。更进一步的做法是让“大纲 Agent”“正文 Agent”“风格校对 Agent”各自使用不同的模型和提示词互相校验。这是 Agent 应用在内容生产领域比较有潜力的方向。多模态创作工作流。虚构创作不只是文字还包括场景概念图、角色立绘、地图、封面。如果 LLM 工作流能衔接绘画模型让文字生成的关键场景自动触发视觉素材生成整个创作链路会更完整。值得关注图像模型与文本模型在工程上的衔接方式。如果你想动手建议按以下路径推进先跑通单次章节生成用 Ollama 加一个好用的开源模型解决“质感和风格”问题。加入 RAG用向量库管理设定解决“一致性”问题。加入摘要链解决“长篇小说越写越失控”的问题。最后再上 Agent 编排解决“流程自动化”的问题。不要一上来就搭一个复杂的 Agent 系统。写作这件事内容质量永远是第一位的技术是为内容服务的。先把模型的输出调到能看、能用再逐步升级工程架构。现在最值得做的一件事是把你正在写的那个故事的大纲、人物卡和世界观文档整理成结构化文本然后喂给模型让它先写一个章节试稿。你会发现当模型不再是在黑暗里“猜”你的故事时它给出的文本会远远超出你的预期。而这种体验大概就是“LLMs Set My Fiction Free”的真正含义。