用LLM构建小说创作工作流:从设定到一致性自检
2026/8/29 9:38:23 网站建设 项目流程

在很长一段时间里,我写小说面临的最大问题不是“没灵感”,而是“灵感停留在脑子里,落不到稿子上”。大纲写了两章就开始崩,人物性格写着写着就飘了,场景之间的过渡生硬得像是幻灯片。接触大语言模型之后,我开始把它当成创作流程中的搭档,而不是资料查询工具,整套写作进度反而被盘活了。这篇文章不是讨论“AI 能不能写出伟大文学”,而是分享一套可复用的 LLM 辅助小说创作流程:从环境准备、接口封装、人物设定表、大纲生成、章节扩写,到一致性自检和常见坑点排查。无论你是写网文、短篇还是做互动叙事,下面这套思路都能直接迁移使用。

1. 背景与核心概念:LLM 辅助创作到底在解决什么问题

1.1 “LLMs Set My Fiction Free” 该怎么理解

这个标题可以拆成两层意思。第一层是字面的:大语言模型把小说创作者从繁琐的“码字劳动”中解放出来。以前写一个章节,可能要用两小时从头到尾打字;现在借助 LLM 辅助,先把章节的冲突、场景、视角、节奏想清楚,再交给模型生成初稿,最后做删改润色,效率确实提升很多。

第二层是更关键的:LLM 降低了长篇幅创作的“维持成本”。很多作者写着写着断更,不是因为编不出剧情,而是因为忘记了自己写过的设定。人物关系、伏笔位置、时间线、道具细节都在脑子里乱成一团。LLM 擅长处理和检索大量文本,只要我们把设定文档和章节摘要喂给它,它就能在生成新章节时保留前面的信息。从这个角度看,LLM 解放的不是“手”,而是“脑”:它让创作者不用再背着几十万字的记忆负担,可以专心做判断和创意决策。

1.2 LLM 能承担小说创作中的哪些环节

根据我自己的实践,下面这几个环节是 LLM 参与度最高、收益最明显的:

  • 世界观设定整理。把零散的想法写成结构化设定,比如地理、势力、能力体系、经济系统。
  • 剧情大纲生成。输入故事概念,输出三幕结构或章节级大纲。
  • 章节初稿扩写。根据大纲摘要和人物设定,生成一版可修改的正文初稿。
  • 对话润色与文风统一。保持人物说话习惯一致,减少“作者腔”。
  • 一致性自检。模型自己读一遍新章节,对照设定表找出矛盾点。
  • 灵感发散与卡文处理。给模型几个关键词,让它提供剧情转折方向,用于打破思路僵局。

这里要注意,LLM 的能力边界也很明显。它没有“长期记忆”,如果一次对话里塞不下整本小说的内容,它就会遗忘前文。它也没有稳定的审美判断,经常会生成“看似通顺但缺乏张力”的文本。它更不理解版权边界,如果我们不主动约束,它可能生成与已有作品高度相似的表达。所以,正确的使用方式是把 LLM 当成“协作伙伴”,而不是“自动写稿机”。

1.3 创作者的角色转换:从打字者变成编辑

引入 LLM 之后,我的创作角色从一个“纯打字者”变成了“编辑加导演”。我需要做的是:

  • 决定故事方向和冲突类型。
  • 审核模型输出是否符合人物逻辑。
  • 删掉模型生成的套话和冗余描写。
  • 把模型生成的多条候选路线合并成一条主线。

这种转换刚开始会有点不适应,尤其会担心“AI 写出来的文字还是我的作品吗”。我的经验是:最终成稿里,重要的剧情决策、人物弧光、情感爆发点都来自人工编辑,LLM 提供的是更快的“文本生成能力”和“信息组织能力”。只要人工审校介入充分,成品就能体现作者自己的创作意志。

2. 环境准备与模型选择

2.1 模型选择:API 还是本地部署

做 LLM 辅助创作,第一步是选择模型调用方式。目前市面上能提供大语言模型推理能力的渠道主要分为两类。

  • 云端 API:调用方把文本请求发给服务端,服务端返回生成结果。优点是使用简单、不需要本地 GPU 资源,适合快速开发和原型验证。
  • 本地部署:把开源模型下载到自己的机器上通过推理框架加载运行。优点是数据隐私好、没有按 token 计费的压力,适合长篇幅创作和敏感题材,但需要较好的显卡、显存和内存环境。

对大多数小说写作者来说,我建议先尝试云端 API,确认工作流能跑通,再根据成本、隐私和效果决定是否迁移到本地部署。实际操作中,无论选择哪家平台,只要模型服务兼容OpenAI Chat Completions这种 JSON 接口格式,就可以复用同一套代码逻辑,差别只在base_urlapi_keymodel参数。

2.2 开发环境准备

本文的示例代码使用 Python 编写,因为 Python 在处理 JSON 配置、文本文件、API 请求方面都很方便。你需要准备以下环境:

  • Python 3.9 或更高版本。
  • requests库,用于发起 HTTP 请求。
  • 一个可用的 LLM API 密钥,以及对应的接口地址和模型名称。
  • 本地目录里准备一个config.json,存放 API 配置,避免把密钥硬编码在代码中。

安装依赖只需要一条命令:

pip install requests

不建议为了这个项目一开始就安装大型深度学习依赖,等需要本地部署时再引入相应推理框架即可。

2.3 配置文件示例

新建config.json,内容结构如下:

{ "api_key": "your-api-key", "base_url": "https://your-endpoint.example.com", "model": "your-model-name", "temperature": 0.7, "max_tokens": 3000, "timeout": 120 }

这里需要注意,base_urlmodel都必须以你实际使用的服务为准,不同平台的字段要求会有差异。代码中我会统一从该配置读取参数,这样后续切换模型或平台时,只需要改配置文件,不需要改代码。

3. 核心原理:LLM 为什么能辅助长篇创作

3.1 从“文字接龙”到“创作外脑”

大语言模型的基本原理可以简化理解为:根据上文内容,预测下一个最可能出现的 token。这种能力经过大规模训练后,展现出惊人的文本连贯性和知识覆盖度。但在创作领域,它本质上依然是对海量文本中的写作模式进行“重组”和“续写”,而不是从真实体验出发进行原创。

理解这一点很重要。当我们让 LLM 写“雨夜里的追车戏”时,它给出的场景可能基于无数影视和小说里的经典桥段。它能给我们提供很好的“初稿素材”,但如果我们不做修改和组合,故事很容易显得套路化。所以,我会把模型生成的内容称为“素材草稿”,而不是“成品稿件”。

3.2 上下文窗口与记忆管理

每个 LLM 都有上下文长度限制,也就是一次请求里能容纳的 token 总数。超过限制后,要么接口报错,要么模型会忽略最前面的内容。对小说创作来说,这是最需要工程设计的问题。

比如一本 30 万字的小说,不可能一次全放进上下文。即便能放进,模型也很难在生成当前章节时精准引用 20 章之前埋下的伏笔。我的方案是做“分层记忆”:

  • 第一层:全局设定文档,包括世界观、人物设定、主线大纲,通常控制在 3000 到 5000 字符以内。
  • 第二层:近期摘要,把最近几章的剧情浓缩成一段话,每次生成新章节时带在请求里。
  • 第三层:当前章节正文,重点保证这一章内部的叙事连贯。

每次调用模型时,我们不需要让它记住所有内容,只要把“本次生成所需要的上下文”准备充分即可。这种思路在工程上叫检索增强式生成,在小说创作中就是给模型提供一份“现场编剧手册”。

3.3 Prompt 稳定性:为什么模型生成结果忽好忽坏

小说创作类的 Prompt 与问答类 Prompt 不一样。问答只需要给出明确指令,创作为了保持风格一致,需要更稳定的系统提示词。我们可以把系统提示词当作一部作品的“作者须知”,里面写清楚文风、视角、人称、节奏偏好、禁止事项等。

如果发现同一个 Prompt 生成的结果时好时坏,通常原因有三个:

  • 模型对风格描述理解不稳定,需要更具体的正反示例。
  • 上下文里出现了无关信息,干扰了模型对主线的关注。
  • 温度参数过高,导致随机性过大。写大纲时建议temperature调低到 0.4 到 0.6,扩写正文可以调到 0.7 到 0.9。

在实际项目中,我倾向于为不同任务准备不同的 Prompt 模板,并且把模板保存为单独文件,这样既方便调试,也方便复用。

4. 完整实战:搭建一个小说创作辅助工作流

下面我们用 Python 实现一个可运行的小说创作辅助工具,功能包括:加载配置、调用模型接口、维护人物设定表、生成大纲、扩写章节、执行一致性自检。代码文件较多,建议按项目结构存放。

4.1 项目结构设计

fiction-assistant/ ├── config.json ├── main.py ├── requirements.txt ├── data/ │ ├── characters.json │ └── outline.json └── output/ ├── chapter_001.md └── chapter_002.md
  • config.json:API 配置。
  • main.py:主程序,包含接口封装与创作助手类。
  • data/characters.json:人物设定表。
  • data/outline.json:生成后的大纲文件。
  • output/:章节输出目录。

4.2 编写模型调用封装层

main.py中先实现一个通用的模型调用类。为了减少对特定 SDK 的依赖,直接使用requests请求兼容接口。

import json import requests class ChatClient: """兼容 OpenAI Chat Completions 接口的通用请求封装""" def __init__(self, api_key, base_url, model, timeout=120): self.api_key = api_key self.base_url = base_url.rstrip("/") self.model = model self.timeout = timeout def complete(self, messages, temperature=0.7, max_tokens=2000): url = self.base_url + "/chat/completions" headers = { "Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json", } payload = { "model": self.model, "messages": messages, "temperature": temperature, "max_tokens": max_tokens, } resp = requests.post(url, headers=headers, json=payload, timeout=self.timeout) resp.raise_for_status() data = resp.json() return data["choices"][0]["message"]["content"].strip()

如果使用 OpenAI 官方接口,base_url填官方地址即可;如果使用第三方兼容平台,则需要填入平台提供的base_url。整个项目只需要依赖这个封装类,后面所有功能模块都复用complete方法。

4.3 人物设定表的设计

人物设定表是保持长篇一致性的核心资产。我建议使用 JSON 保存,因为 JSON 结构清晰、便于程序读取和注入 Prompt。下面是一份简化示例,保存到data/characters.json

{ "角色": { "林晚": { "身份": "前考古队员,通晓古文字", "性格": "冷静、谨慎、不善表达情感", "外貌": "黑色短发,左眉有一道陈旧疤痕", "口头禅": "先看证据,再下结论", "核心动机": "寻找失踪导师的下落", "成长弧光": "从独来独往到学会信任同伴" }, "周野": { "身份": "地下市集的文物贩子", "性格": "圆滑、重义气、表面玩世不恭", "外貌": "高个子,笑起来有虎牙", "口头禅": "这行就是灰色地带", "核心动机": "替死去的妹妹讨回公道", "成长弧光": "从只信利益到愿意为同伴冒险" } }, "主要关系": "林晚与周野因同一件文物交易相识,两人互相戒备又不得不合作。", "禁止事项": "不要让林晚在非紧张情境中哭泣;不要安排周野主动坦白过去。" }

在实际项目中,这个文件可以写得很长,包含每个角色的背景故事、人际关系、当前状态等。关键在于:每次生成新章节前,都把这份设定表的关键部分注入到 Prompt 中。

4.4 实现小说创作助手类

接下来实现FictionAssistant类,包含人物设定加载、大纲生成、章节扩写、一致性自检四个方法。

class FictionAssistant: def __init__(self, client: ChatClient): self.client = client self.character_sheet = None self.outline = None def load_characters(self, path="data/characters.json"): with open(path, "r", encoding="utf-8") as f: self.character_sheet = json.load(f) def load_outline(self, path="data/outline.json"): with open(path, "r", encoding="utf-8") as f: self.outline = json.load(f) def save_json(self, data, path): with open(path, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) def generate_outline(self, concept, style="简洁网文风"): system_prompt = ( "你是一名资深小说策划编辑,擅长把创意概念转化为结构清晰、冲突明确的长篇大纲。" "输出 JSON 格式,包含 story_name、logline、characters、chapters 四个字段。" "chapters 是列表,每个元素包含 chapter_title 和 chapter_brief。" "不要输出 JSON 之外的任何文字。" ) user_prompt = f"故事概念:{concept}\n目标文风:{style}" result = self.client.complete( [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ], temperature=0.5, max_tokens=2500, ) outline = self._parse_json(result) self.outline = outline return outline def expand_chapter(self, chapter_no, chapter_title, chapter_brief): if self.character_sheet is None: raise ValueError("请先加载人物设定表:load_characters()") if self.outline is None: raise ValueError("请先加载或生成大纲:load_outline() / generate_outline()") system_prompt = ( "你是小说续写助手。你只负责根据给定大纲、人物设定和本章摘要求输出正文。\n" "要求:\n" "1. 保持人物性格、行为逻辑和说话习惯一致。\n" "2. 避免重复用词和空泛描写。\n" "3. 不要擅自引入尚未出现的世界观设定。\n" "4. 叙事视角、人称以现有正文为准。\n" "5. 直接输出正文,不要解释写作思路。" ) user_prompt = f"""请扩写小说章节。 人物设定: {json.dumps(self.character_sheet, ensure_ascii=False, indent=2)} 章节信息: - 章节序号:{chapter_no} - 章节标题:{chapter_title} - 本章摘要:{chapter_brief} 请输出正文:""" return self.client.complete( [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ], temperature=0.8, max_tokens=3000, ) def consistency_check(self, chapter_text): if self.character_sheet is None: raise ValueError("请先加载人物设定表") system_prompt = ( "你是一名小说编辑质检助手。请对照人物设定检查章节正文," "找出人物性格、外貌、关系、行为逻辑不一致的地方," "并用简洁的清单形式输出问题。如果没有问题,返回:一致性良好。" ) user_prompt = f"人物设定:\n{json.dumps(self.character_sheet, ensure_ascii=False, indent=2)}\n\n章节正文:\n{chapter_text}" return self.client.complete( [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ], temperature=0.2, max_tokens=1200, ) @staticmethod def _parse_json(text): start = text.find("{") end = text.rfind("}") if start == -1 or end == -1: raise ValueError("模型输出中没有找到 JSON 对象") return json.loads(text[start:end + 1])

_parse_json的作用是容错。有些模型可能返回带前后说明文字的 JSON,直接用json.loads会失败,所以我们先裁剪出最外层{...}再解析。如果仍然失败,可以把原始输出打印出来查看。

4.5 编写主程序入口

main.py中追加main函数,用来串联整个流程。

def load_config(path="config.json"): with open(path, "r", encoding="utf-8") as f: return json.load(f) def main(): config = load_config() client = ChatClient( api_key=config["api_key"], base_url=config["base_url"], model=config["model"], timeout=config.get("timeout", 120), ) assistant = FictionAssistant(client) assistant.load_characters() concept = ( "都市奇幻题材:考古队员林晚在一次文物鉴定中,偶然发现一件能映照使用者内心的古镜。" "这件古镜被地下文物贩子周野盯上,两人被迫合作,去追查古镜背后隐藏的失落古城。" ) outline = assistant.generate_outline(concept=concept, style="悬疑冒险风格") assistant.save_json(outline, "data/outline.json") print("大纲生成完毕:", outline["story_name"]) for idx, chap in enumerate(outline["chapters"][:2], start=1): text = assistant.expand_chapter( chapter_no=idx, chapter_title=chap["chapter_title"], chapter_brief=chap["chapter_brief"], ) report = assistant.consistency_check(text) print(f"第 {idx} 章一致性检查:") print(report) with open(f"output/chapter_{idx:03d}.md", "w", encoding="utf-8") as f: f.write(f"# 第 {idx} 章:{chap['chapter_title']}\n\n{text}\n") if __name__ == "__main__": main()

4.6 运行与验证

运行前请先创建output目录,并确保data/characters.json存在。

python main.py

如果 API 配置正确,会看到类似下面的输出:

大纲生成完毕:古镜迷城 第 1 章一致性检查: 一致性良好。 第 2 章一致性检查: 1. 第 2 章中林晚说话时语气比设定更热情,建议检查是否符合前期克制风格。 2. “周野的虎牙”描写出现次数偏多,可以考虑适当减少。

这里要说明,实际输出内容由模型决定,不同模型、不同 Prompt 的结果会有差异。这个流程的目的是把创作过程中的“生成、检查、保存”变成可重复执行的操作,而不是追求某一次的具体输出。

4.7 结果说明与文件归档

章节生成后会保存到output/chapter_001.mdoutput/chapter_002.md。每个文件包含章节标题和正文。一致性检查结果目前只是打印在终端,实际项目中可以把检查结果也保存到文件,例如output/check_report.md,方便统一处理。

建议每次批量生成后,把人物设定表和最新章节一起提交到 Git 仓库。这样一旦发现某一次生成导致剧情走向失控,可以快速回退到之前的版本。

5. 常见问题与排查思路

在使用 LLM 辅助写小说的过程中,最常遇到的问题并不是模型“不会写”,而是“写得前后不一致”。下面整理一份高频问题清单。

问题现象可能原因排查与解决思路
人物性格前后不一致,林晚突然变得爱哭上下文过长后模型遗忘了早期设定在每次生成前注入人物设定表,并将设定表放在 Prompt 靠前位置
剧情走向频繁偏离大纲大纲没有在每次请求中都携带生成章节时把当前主线目标、本章任务加入 Prompt
文风漂移,一会像网文一会像翻译腔系统提示词中的风格描述不够具体加入正反风格示例,明确禁止使用的句式
模型输出大量套话和空泛描写温度过低导致模型选择最常见表达适当调高 temperature,并在 Prompt 中要求“避免陈词滥调”
返回内容无法解析为 JSON模型在 JSON 前后添加了说明文字使用正则裁剪 JSON 片段,并用json.loads解析后再做类型检查
一次请求内容太长导致报错上下文超出模型窗口限制把旧章节摘要化,只保留关键伏笔和状态
模型生成了不合规或不合适的内容系统提示词没有定义底线在系统提示词中明确禁止事项,例如“不要生成血腥暴力内容”
接口偶尔超时生成 token 数过多或服务端繁忙设置超时时间、拆分长章节、增加重试机制

排查这类问题,我建议先看日志。最简单的方式是在ChatClient.complete里打印请求和响应的 token 数量,便于判断是不是上下文超限。也可以把每次 Prompt 保存到文件里检查,看看是不是某个字段拼接错误导致模型理解偏了。

6. 工程化与创作管理建议

6.1 建立分层记忆体系

长篇小说创作的核心难点在于信息管理。我建议把项目信息分为三个层级来管理:

  • 世界设定层:世界观、势力、地理、科技水平、魔法规则等,不经常变动。
  • 故事线层:主线大纲、每卷目标、章节摘要,随创作进度滚动更新。
  • 细节档案层:每个角色的完整背景、伏笔记录、时间线事件表。

每次调用模型时,先决定本次写作需要哪一层的信息,然后组合成 Prompt。不需要把所有档案都塞进去,信息越多反而可能干扰模型对当前章节重点的把握。

6.2 使用版本管理保存创作过程

对于长篇写作,强烈建议用 Git 管理文稿和设定文档。具体做法是:

  • 每个章节一个 Markdown 文件。
  • 人物设定表作为一个独立 JSON 文件,每次修改后提交一次。
  • 大纲的每次调整单独提交,保留变更历史。

这样做的价值在于:你可以随时知道“剧情是在哪一版发生了转折”,也可以对比不同模型的输出效果,甚至在 AI 生成内容出现问题时回滚到安全版本。版本管理不只是开发者的习惯,也是内容创作者保持作品可控性的重要手段。

6.3 版权与内容合规提醒

使用 LLM 生成小说内容时,需要注意几个问题:

  • 不同平台对 AI 生成内容的版权归属有不同的规定,发布前务必阅读并遵守相关平台条款。
  • 不要在 Prompt 中要求模型模仿在世作家的风格,也不要让模型直接续写别人的作品,避免侵权风险。
  • AI 生成内容不代表可以免去人工审核。涉及事实性信息、出版规范、敏感题材时,创作者必须自行把关。
  • 如果作品计划用于商业出版,建议保留人工修改和创作的记录,并咨询专业法律意见。

这些都只能在“合法授权、测试环境验证、资料备份”的框架下进行。创作过程越透明、越有据可查,后续潜在的争议就越少。

6.4 控制 API 成本与调用频率

生成小说需要大量 token,成本控制不能忽视。常用手段包括:

  • 使用模型返回的usage字段统计 token 数。
  • 对章节生成结果做缓存,避免重复请求。
  • 把“大纲生成”和“章节扩写”拆开,分别使用不同的模型和参数,而不是所有任务都用最强模型。
  • 设置单次批处理的最大章节数,一次性任务不要无限制跑下去。

示例中ChatClient目前没有打印 token 统计,如果你需要,可以在接口返回中读取data["usage"],然后写入日志文件。这些数据能帮你判断哪类请求消耗最大。

# 在 ChatClient.complete 中追加一个可选日志 if data.get("usage"): print("prompt_tokens:", data["usage"].get("prompt_tokens"), "completion_tokens:", data["usage"].get("completion_tokens"))

6.5 把创作流程拆成可复用的流水线

当项目变大后,可以把流程进一步模块化。比如从FictionAssistant中拆出OutlineGeneratorChapterWriterConsistencyChecker三个类,各自只负责一个任务。这样便于针对不同模型做 A/B 测试,也便于以后接入新的模型能力。

一条典型的流水线可以长这样:

  1. 创作者撰写或确认故事概念。
  2. 大纲生成器产出粗纲。
  3. 人工调整大纲,确认每章目标。
  4. 章节扩写器逐章生成初稿。
  5. 一致性检查器发现问题。
  6. 人工修改后在设定的“禁止事项”中加入新规则。
  7. 定期把新状态写回人物设定表和主线大纲。

这个循环跑上一段时间后,设定文件会越来越完善,模型输出的质量也会因为 Prompt 更精确而明显提升。

7. 总结与下一步学习

这篇文章围绕“用 LLM 解放小说创作”这条主线,介绍了一套从环境准备、提示词设计、代码封装到长篇小说信息管理的完整方案。你可以看到,LLM 在这里承担的角色不是“作者”,而是“外脑加初稿生成器”。真正让故事有价值、有情感、有风格的,仍然是作者对人物和世界的主观判断。

下一步你可以从三个方向继续深入:一是优化 Prompt 模板,例如加入“一句话概括本章目标”“本章必须出现的伏笔”等结构化字段,让生成结果更可控;二是研究适合本地部署的开源模型,在隐私和成本之间找到平衡;三是尝试做多角色互动式创作,比如用不同模型分别扮演不同人物,让对话更自然。

如果你打算把这套流程真正用在一个长篇项目里,我的建议是先开一本短篇练手。目标不是写出惊天大作,而是把“设定表维护、章节生成、一致性自检、人工修改”这套循环跑熟。等这种节奏变成肌肉记忆之后,再回到更复杂的长篇创作,你会明显感觉到:LLM 确实把写作从“一字一字苦熬”变成了“从多个可能中做选择”,而选择权始终在你手里。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询