简介这是一套基于Gemini大模型构建的AI长篇小说生成器工程面向网络小说作者、AI写作爱好者与NLP开发者用于解决长篇创作中剧情连贯性差、人物设定易崩坏、伏笔管理困难等痛点。包内共30个文件体量仅106KB以20个Python源码文件为核心涵盖从设定到审校的完整流水线实现小说设定工坊、智能章节生成、状态追踪系统、语义检索引擎、自动审校机制等模块另附示例配置、文本说明与Markdown文档方便快速跑通与二次开发。目前已有139人学习下载。通过这套代码读者可掌握基于向量检索的长程上下文一致性维护方法以及多阶段生成保障剧情连贯的设计思路可视化工作台提供全流程GUI操作配置、生成、审校一体化非常适合作为大模型落地写作工具的学习样板或改造基础也适合用于毕业设计或工程实践参考。1. 百万字网文生成器Gemini长上下文让一键产出不再是噱头有个朋友用各家大模型写了一部两百万字的玄幻小说结果读起来像流水账主角名字改了三次第十章境界设定和第二十章完全对不上。这不是模型不行而是把超长篇小说当成了继续写这一个动作。真正能跑通百万字生成的项目核心不是把几十万字一次性塞进上下文而是拆成大纲→分卷→分章→修订四段流水线用状态文件把单章上下文控制在可管理量级。Gemini的长上下文窗口是关键支撑——它让单章生成时能看到足够长的历史摘要但绝不意味着把全部正文喂进去。所谓小白文终结者重点不是让文笔变华丽而是让逻辑链条不再断裂。这篇笔记写给两类人想用AI批量产出长篇内容的写手和想搭一套文本生成pipeline的开发者。照着做能跑出一部结构完整、前后一致的超长篇初稿。2. 拆解生成链路从大纲到百万字的四段流水线超长篇小说生成不能追求一次调用出全书这个判断由token量级决定。中文一字的token成本大约在1.5到2个token百万字小说折算下来是150万到200万token已经超过Gemini单次100万token的窗口上限。即便未来窗口继续放大把几百万字塞进一次生成也不是好方案模型对越靠前的信息遗忘越严重生成的稳定性和成本都不划算。常见做法是把链路拆成四段段与段之间用JSON状态文件衔接每段只做一件事前一段的输出是后一段的输入。2.1 第一段大纲与世界观约束让模型先立骨架大纲是不能省的。直接让模型写一本小说得到的是没有约束的自由发挥前20章可能很出彩写到100章世界观就开始自相矛盾。我一般会让模型输出结构化JSON大纲而不是Markdown文档因为JSON可以直接进入后续每一章的prompt不需要解析转换。import google.generativeai as genai import json genai.configure(api_keyYOUR_API_KEY) model genai.GenerativeModel(gemini-1.5-pro) prompt 你是一名资深网文主编。请为以下设定生成创作大纲只输出JSON { title: 书名, genre: 类型, world_view: 世界观的硬性边界不超过200字, power_system: [境界名称与说明], volume_plan: [{volume: 1, chapters: 80, arc: 本卷核心冲突}], main_characters: [{name: 角色名, role: 主角/配角/反派, growth: 成长线}] } 题材现代修真 主角IT工程师意外穿越到修真世界靠编程思维破解功法。 要求世界观必须有无法逾越的边界主角成长分四段每段对应一卷。 resp model.generate_content(prompt) text resp.text.strip() markers [json, ] for marker in markers: if text.startswith(marker): text text[len(marker):].strip() if text.endswith(): text text[:-3].strip() break data json.loads(text) print(json.dumps(data, ensure_asciiFalse, indent2))这段代码的逻辑是先用自然语言描述小说设定迫使Gemini输出符合字段约束的JSON再由json模块解析成Python字典。这里有两个关键点一是硬性边界四个字它能防止模型把世界观写成无所不能的万能设定二是成长分四段它决定后续分卷节奏。每次生成大纲后我会人工读一遍world_view和power_system这一步别省——大纲错了后面一百万字都白跑。2.2 第二段分卷分章生成用状态文件保住上下文四段流水线里第二章到最后一章都在重复同一个模式读状态文件、组装prompt、生成章节、更新状态文件。状态文件是整个项目的地基它把模型从无状态对话变成有状态生成器。{ book: { title: 代码修仙, current_chapter: 312, total_chars: 920000 }, recent_summary: 主角在第310章突破金丹中期获得青莲剑正在前往南疆秘境。, characters: { 林尘: { last_seen_chapter: 312, cultivation: 金丹中期, items: [青莲剑, 储物戒], goal: 找到南疆秘境中的紫府 } }, timeline: { day: 187, season: 秋, note: 距离宗门大比还有43天 } }注意这个文件里没有正文只有状态。每章生成前把状态文件转成几百字的prompt前缀生成完再调用一个更新函数把新章节里发生的变化写回去。这样做的好处是不管写到第300章还是第800章模型每次看到的都是摘要人物卡时间线而不是堆叠的正文。Gemini长上下文在这里的正确用法是单章生成时把最近几章的摘要甚至少量原文放进来让文风连续而不是把全部章节放进来。2.3 第三段动态人物卡与设定库让角色跟得上剧情人物卡不能一劳永逸。主角第100章突破金丹第300章化神如果人物卡还写着练气期那生成出来的对话和行为全是错位的。每次章节生成之后要做一次状态更新。这一步可以用规则脚本也可以用便宜的模型二次调用常见做法是后者update_prompt f 你是小说设定管理员。阅读以下章节正文更新JSON中的角色状态 - 修为、物品、人际关系发生变化时更新没变化的不动 - 新增角色要补充条目 - 死亡或离开的角色标记 status: gone不要删除。 当前人物卡 {json.dumps(state[characters], ensure_asciiFalse)} 章节正文节选 {chapter_text[:3000]} 只输出更新后的JSON。 resp cheap_model.generate_content(update_prompt) new_characters json.loads(resp.text.strip().strip()) state[characters] new_characters这个更新函数的核心逻辑是有变化才更新没变化不要动否则跑几百轮之后人物卡会被模型瞎编的内容污染。参数上我一般把节选正文控制在3000字以内既够模型判断变化又不会让更新成本失控。这里用便宜模型就够——信息抽取任务不需要Gemini主力模型来跑省下来的预算留给正文生成。2.4 生成后自动门禁字数、实体与AI腔检查每一章生成完不要急着进下一章先过一道自动门禁。门禁可以很简单第一字数低于3000的章节直接打回重写第二把生成的文本和人物卡里的已知人名做一次扫描凡是出现林尘写作林辰这类音近字替换的触发警告第三检测末尾有没有未完待续这类带AI腔的结尾。def check_chapter(text, state): problems [] if len(text) 3000: problems.append(字数不足3000) for name in state[characters]: for alt in get_similar_names(name): # 预置别名表或编辑距离计算 if alt in text and alt ! name: problems.append(f疑似实体漂移{name} - {alt}) if text.rstrip().endswith((未完待续, 敬请期待)): problems.append(带AI腔结尾) return problemsget_similar_names可以是一个预置的别名表也可以从文本里抽候选后用编辑距离计算。最常见的翻车是林尘和林辰、萧炎和肖炎这种同音替换出现一次就要人工确认。门禁通过之后才允许把章节写进正文库并更新人物卡。这一道门禁拦住的问题后面第5章会展开细讲。3. Gemini长上下文窗口的正确打开方式上下文管理与预算分配Gemini这款大模型的上下文长度是它最出圈的能力之一但上下文长不意味着可以把所有历史都塞进去。每多塞一个token生成阶段就要多计算一次注意力费用和延迟都随上下文长度上涨。我见过不少项目把最近30章正文全部喂进去结果API账单翻了几倍生成速度却慢到没法用。管理上下文本质是管理预算而不是测试模型的极限。3.1 100万token不是无限先把量级算清楚先算一笔账。中文千字大约等于1500到2000个token实测会因为标点和专有名词浮动。按平均值估算一部百万字小说换算下来是150万到200万token任何单次生成窗口都装不下全部正文。即便只喂最近20章每章4000字也要12万到16万token加上prompt后一次调用费用就很可观。生成阶段输入内容预估token单章生成大纲摘要 人物卡 时间线 最近1章摘要2000 - 6000人物卡更新最近1章正文节选3000字4000 - 6000卷末摘要整卷10万字15万 - 20万全文一致性扫描分卷扫描按卷划分这张表是我实际在项目里用的分配方式单章生成只花小几千token把大头留给卷末摘要。卷末摘要要把30章内容压缩成500字剧情摘要这一步是对上下文长度要求最高的环节正好用上Gemini长窗口的能力。分配逻辑很明确长上下文应该用在收拢信息而不是堆叠信息。生成时喂太多原文模型会过度模仿最近的文风造成风格漂移喂一篇精心压缩的摘要模型反而能抓住主线。大模型LLM做摘要比做创作稳定得多这一点已被很多人验证过。3.2 滚动窗口策略摘要替代原文三步完成滚动窗口的核心很简单——永远不要让历史正文堆积。每写完一个固定批次比如10章就触发一次摘要更新把旧的摘要和最近10章正文合并压缩成新摘要再淘汰掉原文。def roll_summary(state, model): old_summary state[recent_summary] recent_text load_recent_chapters(state[book][title], 10) prompt f 你是小说编辑。把下面两部分合并成一份500字以内的剧情摘要 1. 已有的旧摘要如果信息仍然重要保留 2. 最近10章正文的剧情推进。 输出要求 - 只写事实不写评价 - 必须保留所有存续人物的名字与状态变化 - 必须保留时间线推进第X天/季节/年份 - 丢弃与主线无关的支线细节。 旧摘要 {old_summary} 最近10章 {recent_text} resp model.generate_content(prompt) state[recent_summary] resp.text.strip() save_state(state)参数说明旧摘要一定不能丢直接拿10章原文替换旧摘要会让更早的伏笔全部作废摘要长度我给500字压得太短会丢掉人物名太长后续prompt成本又上来了。更新摘要用的模型可以是Gemini主力模型如果你只是想压缩文本用免费大模型API也够用能省不少预算。我习惯在这里做一个权衡摘要质量决定后续几十章的连贯性不值得为了省钱用太弱的模型但也不至于动用旗舰。3.3 参数调优temperature、top_p与频次惩罚怎么配超长篇生成和单篇短文生成的参数策略完全不同。短文可以把temperature拉到0.9追求惊喜长文拉到这么高五章之后剧情就脱缰了。下面这组参数是我在Gemini上常用的起点参数调优有点玄学但这几条是可复制的规律参数推荐值说明temperature0.7 - 0.8保持一定随机性但不过于激进top_p0.9配合temperature做核采样top_k40限制候选范围减少生僻词frequency_penalty0.3 - 0.6抑制重复句式太高会破坏语句连贯presence_penalty0 - 0.3鼓励谈论新内容但不要过高一个容易踩的坑是同时调高temperature和top_p。这两个参数是配合使用的一个调高了另一个得往回收否则输出会开始无逻辑跳跃。我一般固定top_p在0.9只用temperature和frequency_penalty做微调。如果发现连续几章出现XX说道、心中一惊这类高频重复先加frequency_penalty而不是降temperature如果发现剧情开始胡编乱造再降temperature。另外如果出于隐私考虑把整套生成器迁到企业大模型私有化部署方案或者本地模型参数不能照搬。开源模型对temperature的响应和Gemini不一样一般要比Gemini低0.1到0.2才稳定。我的习惯是每换一个底层模型先拿历史prompt回放20次肉眼比较输出重复率和跳跃程度再定参数。这套验证方法也适合每次Gemini更新模型版本之后。4. 把生成器跑起来最小可复现的命令与参数模板前面两章讲了原理和配置这一章给一套能直接跑起来的最小工程。我的建议是先跑通单章再跑批量不要一开始就追求完整工程化。以下代码都以Python为例Gemini的官方SDK是google-generativeai包。4.1 环境准备与API接入密钥、模型与常见报错pip install google-generativeai安装完之后密钥处理是第一个坑。很多人以为必须先到网页版完成Gemini登录才能拿到API权限实际上网页版和API是两条独立路径——你在网页版聊得再多代码里没配置API密钥一样报错。正确做法是在Google AI Studio申请API密钥然后通过环境变量注入不要硬编码进代码。export GEMINI_API_KEY你的密钥import google.generativeai as genai import os genai.configure(api_keyos.environ[GEMINI_API_KEY]) model genai.GenerativeModel(gemini-1.5-pro)一个非常常见的报错是403或PERMISSION_DENIED。先检查环境变量有没有真的读进来再检查密钥是否有效。模型名写错会报404不同账号可用的模型名可以在SDK的list_models接口里查不要凭记忆敲。for m in genai.list_models(): if generateContent in m.supported_generation_methods: print(m.name)这个list_models调用很实用它会把当前账号可用的生成模型全部列出来。配置好这一步后面所有工作都建立在这一个model对象上。4.2 单章生成的最小闭环状态注入与结果落盘import json STATE_PATH book/state.json def load_state(): with open(STATE_PATH, encodingutf-8) as f: return json.load(f) def save_state(state): with open(STATE_PATH, w, encodingutf-8) as f: json.dump(state, f, ensure_asciiFalse, indent2) def generate_chapter(state, chapter_no): prompt f 你是网文作者现在要写第{chapter_no}章。 写作要求 1. 遵循故事状态推进至少一个关键事件 2. 保持与前文一致的语气 3. 结尾留一个悬念 4. 不要复述前文内容。 故事状态 {json.dumps(state, ensure_asciiFalse)} resp model.generate_content(prompt) return resp.text state load_state() chapter_no state[book][current_chapter] 1 text generate_chapter(state, chapter_no) print(f第{chapter_no}章生成完毕长度{len(text)}字)逻辑说明load_state和save_state是整套管线的地基每章开始前读状态、结束后写状态异常崩溃时最多损失一章。generate_chapter把状态文件整个序列化进prompt这比只塞摘要更简单跑通之后再按第3章的滚动窗口方案优化。参数上chapter_no来自state而不是外部传入可以避免重复生成同一章。这里要注意max_output_tokens的默认值。Gemini默认输出长度可能只有4096写长章容易截断。生成超长文本时我一般显式设置到8192以上model genai.GenerativeModel( gemini-1.5-pro, generation_config{ temperature: 0.8, top_p: 0.9, max_output_tokens: 8192, } )8192个token大约能输出6000到8000个汉字对一篇网文章节够用。如果你的设定是每章一万字以上就拆成两段生成不要指望一次输出两万字——超长输出在末尾几乎必然开始复读。4.3 批量生成与断点续写让它自己跑一晚上单章跑通后批量只是加一层循环。但直接写一个for循环从头跑到尾会很脆弱API偶尔限流、网络偶发错误、生成出来的内容解析失败任何一个中断都会让整批报废。所以批量生成的每一章都要独立落盘、独立计数并且支持断点续跑。断点续跑相当于给批量生成留了一颗后悔药崩了从上一章接着跑就行。import time def batch_generate(start_chapter, end_chapter): state load_state() for ch in range(start_chapter, end_chapter 1): try: text generate_chapter(state, ch) state[book][current_chapter] ch state[book][chapters] state[book].get(chapters, {}) state[book][chapters][str(ch)] text save_state(state) print(f完成第{ch}章) except Exception as e: print(f第{ch}章失败{e}) break time.sleep(1) # 简单限速断点续跑的关键在于每章成功就立刻更新current_chapter和正文存盘下次从current_chapter1开始。time.sleep(1)是给API限流留的缓冲如果账号配额高可以调到0.3但遇到429限流错误时需要改成指数退避重试。for retry in range(4): try: text generate_chapter(state, ch) break except Exception: wait 2 ** retry print(f重试等待{wait}秒) time.sleep(wait)指数退避的逻辑是每次失败后等待时间翻倍给服务端恢复的时间。连续重试四次都失败就放弃这一章记录断点退出人工排查后再续跑。这样跑一晚上能稳定产出几十章不会因为一次网络抖动丢掉全部进度。5. 避坑与排查超长篇生成最容易翻车的5个地方这套流水线我前后搭过三轮踩坑踩出来的经验比看文档学到的多。下面五个坑几乎每个百万字级别项目都会遇到按出现频率从高到低排序每条都按现象、原因、解决的顺序说清楚。5.1 实体漂移主角名字从林尘变成林辰现象第30章前主角叫林尘第80章开始偶尔出现林辰后期同一个段落里两个名字混着用。网文里这种同音字替换很常见因为模型对人物名的记忆是语义层面的不是字面层面的。原因状态文件里虽然有人物卡但生成prompt时没有把人物名的正确写法强调出来。当上下文里混入某个错误写法模型会把它当成合法候选继续使用。解决在状态文件里单独维护一个golden_names清单每章生成前原样注入prompt并要求以下人名必须严格按此写法林尘、萧炎、李青莲。同时跑自动扫描把文本里和golden_names编辑距离为1的词全部标出来供人工确认。我见过最稳妥的做法是每10章做一次全局替换检查把林辰批量替换回林尘之前先人工确认别盲替。5.2 时间线错乱三天前的事和三个月前的事同时发生现象主角第40章刚说距离宗门大比还有43天第41章就写三个月后宗门大比如期举行。或者刚突破境界下一章又退回旧境界。原因时间信息没有显式建模。模型没有当前时刻的概念每次生成都靠上下文里的时间词推算一旦章节间隔拉长就会错位。解决状态文件里加一个timeline字段每次生成前在prompt开头写死当前时间第187天秋季距离宗门大比43天。并且要求模型每章首句先声明时间比如三天后。生成后跑一个简单正则把第\d天\d年后之类的时间词抽出来和时间线比对偏差超过阈值就告警。这个方法成本几乎为零但能拦下大部分时间线bug。5.3 上下文溢出API直接报错或者生成内容前言不搭后语现象跑了几百章之后突然某次调用返回错误或者更隐蔽的情况是没有报错但生成的内容开始重复上一章的段落。原因状态文件里塞的东西越来越多或者摘要更新不及时导致单次请求的token总量超过模型限制。很多人忽略的是输出长度也计入总tokenmax_output_tokens设置过大时同一请求的总额会爆掉。解决先看报错码如果提示context_length就把recent_summary长度砍半或者把最近3章正文从prompt里去掉只留摘要。另外把输入长度和输出上限做成预算函数输入超过一定token时自动降低输出上限。养成每100章清理一次状态文件的习惯把已经完结的支线人物从人物卡移到archive字段。5.4 风格漂移前20章是轻松日常100章后变成苦大仇深现象同一本书前20章读着像搞笑文第100章突然变成黑暗流人物说话的语气全变了。文风是逐章渐变漂移的不拿前几章对比很难发现。原因每章prompt只重复了状态信息没有重复风格约束。模型会受最近生成文本的影响逐渐偏离最初设定这是大模型生成长文本的固有问题。解决大纲阶段就把风格锚点写死例如对话占比40%叙述视角第三人称限知禁用华丽辞藻堆砌每章至少一个笑点。把这段锚点原样复制进每一章的prompt不要嫌啰嗦。每次批量生成30章之后取第1章和第30章各500字做一次相似度比对如果风格差异过大说明锚点没有生效回到参数层调温度。5.5 AI味过重满屏嘴角微微上扬和心中一惊现象连续生成几十章后高频词和句式开始重复读者一眼看出是AI写的。最典型的是段尾总结句比如这一刻他明白了什么叫做责任。原因这是大模型的高频token偏好加上长文本生成时模型倾向于保守复现安全句式。temperature太低会加剧这个问题完全依赖prompt禁止效果也有限。解决维护一个禁用或少用短语表生成后用脚本统计出现次数超过阈值就标记。同时在prompt里给出正面示例而不是负面清单——告诉模型参考这段文风比不要写有效得多。frequency_penalty调到0.5左右对句式重复有明显抑制作用但要小心太高的惩罚会让对话生硬断裂这个参数需要配合人工抽检来定。6. 验证百万字成果一致性检查脚本与人工抽检策略生成一百万字的初稿只是第一步真正决定这本书能不能看的是验证环节。很多人会在这里犯一个错误把全文喂给Gemini让它整体检查一遍费用高不说模型对百万字文本的校验能力并不靠谱。更稳的做法是拆成可执行的检查脚本再加上固定的人工抽检节奏。模型内部是个黑匣子一致性必须靠外部工具兜底。6.1 实体漂移扫描三分钟找出全书人名错乱import re names [林尘, 萧炎, 李青莲, 紫府] wrong_variants {林辰: 林尘, 肖炎: 萧炎} path book/chapters/ for ch in range(1, 313): try: text open(f{path}chapter_{ch}.txt, encodingutf-8).read() except FileNotFoundError: continue for wrong, right in wrong_variants.items(): hits re.findall(wrong, text) if hits: print(f第{ch}章{wrong}出现{len(hits)}次应为{right})这段脚本的原理是预置一份错误写法→正确写法的映射表逐章扫描并输出位置。运行成本极低可以挂在每天批量生成之后的自动流程里。注意映射表要持续维护每次发现新的错写就加一条跑得越久越准。6.2 人工抽检的三个锚点脚本能拦住客观错误拦不住剧情是否合理这种主观问题。我习惯每10章抽1章只读开头500字回答三个问题上一章的悬念有没有被接上人物行动是否符合人物卡里的性格时间线和境界是否和状态文件一致抽检发现的问题回写到状态文件里作为下一章生成的修正指令而不是回头修改已生成的章节——回头改百万字成本太高不现实。这套验证方法的边界很清楚它能保证一致性不能保证文学性。百万字AI小说目前的上限是结构完整、可读的初稿要变成能发布的作品还需要人工修订。我吃过没做校验的亏第一部生成器写到60万字时发现主角已经换了三个门派后来再也不敢省这道工序。验证脚本不是为了证明模型厉害是为了让后续修订有据可查。养成每10章抽检一次的习惯比依赖任何模型的自我检查都可靠。希望帮到你。本文还有配套的精品资源点击获取