先把话说清楚:这篇东西不是那种"拿着ChatGPT随便聊两句、把对话整理成一个故事"的玩具教程。我要聊的是,怎么基于LLM设计一个有真实叙事能力的AI Agent,让它能稳定产出多章回、有因果推进、有人物弧光的故事,而不是一写就崩、一长就跑偏。
这个需求最近找我聊的人特别多。有人想拿AI做互动小说,有人想给自己的游戏批量生成支线剧情,还有人想做一个"AI陪你共同写小说"的创作助手。他们拿大模型的API直接对接之后,普遍遇到一个问题:单段生成很惊艳,多段生成就彻底失控。角色性格会变、时间线会乱、前文埋的伏笔后文根本不记得。这就是典型的"语言模型"和"叙事系统"之间的鸿沟。
我过去一年反复折腾过这类系统,从简单的"一个Prompt + 长上下文"到后来拆成多模块的Agent架构,踩了不少坑。这篇文章就把我沉淀下来的核心认知、架构思路和调优经验一次讲透,适合那些有一定工程基础、想真正把LLM用进叙事场景的人。
1. 先认清问题:LLM擅长的是"接句子",不是"讲故事"
我们得先接受一个反直觉的事实:哪怕是最顶尖的LLM,它的底层能力也只是"给定上文预测下文概率"。这个东西被用在对话、摘要、改写上非常好用,但故事不是"一串概率合理的句子",故事是一个有约束的系统。
1.1 语言模型对"局部合理性"的执着会毁掉"全局一致性"
LLM天然倾向于让当前这段输出在局部看起来通顺、合理、有文采。你和它说"主角是个胆小的会计",它在这一段里写得很到位,会描写他汗手、犹豫、不敢反抗。但到第三章,你的故事需要他爆发,LLM大概率会让他在一段对话里突然变成硬汉,因为"这一刻的戏剧张力"在局部看是合理的,LLM从概率上会倾向于选择更"带感"的表达,而不会去核对这个人物前80%的情节里是怎么行动的。
我刚开始做多章回生成时,用的是一个很粗暴的方案:把前文全塞进上下文,然后让模型续写。写到第5章之后,人物行为的一致性已经像换了个人设。这不是因为模型笨,而是因为局部连贯和全局一致是两种不同的任务,前者是LLM的强项,后者需要额外的系统约束。
1.2 故事的隐形结构:因果链、节拍、角色动机
要构建叙事能力,第一步是把"故事"拆解成LLM能够遵循的显性结构。我自己用下来最顺手的拆法是这样:
- 因果链:每一章事件必须是前一章事件的直接结果,同时要为后一章埋下种子。这是硬逻辑,不能靠模型自觉。
- 节拍:故事不是匀速前进的,需要有铺垫、升高、高潮、回落。每个节拍对应一种"叙事压力值"。
- 角色动机:每个主要角色在出场时要有一个"底层欲望"。后续所有行为都必须能被这个欲望解释,否则就是OOC(Out of Character,角色崩坏)。
工程上做的所有事,本质都是把这些隐形结构变成模型每轮生成时不可忽视的显式输入。
1.3 所以我们需要Agent,而不仅仅是一个Prompt
如果你只需要生成一个短篇,比如3000字以内,那确实靠精心写的Prompt就够。但一旦超过这个量级,单Prompt方案会同时撞上三堵墙:上下文窗口耗尽的墙、细节遗忘的墙、风格漂移的墙。
Agent的本质是把"生成"这个单一动作,拆成"规划-执行-检查-修正"多个环节。叙事Agent就是在生成故事时,先让一个模块做叙事设计,再让另一个模块把设计落地成文本,再让第三个模块检查落地质量。每个模块只做一件事,反而能避开LLM"什么都想干最后什么都干不干净"的毛病。
2. 架构上怎么拆:一个可落地的多模块叙事Agent设计
我踩过最贵的坑,就是把所有责任都压给一个超长Prompt。后来我参考了"编剧室"的工作方式——有人负责世界观、有人负责单集脚本、有人负责审稿——把这个流程映射成了Agent架构。下面是我目前在用的分工。
2.1 核心模块:Story Planner(故事规划器)
这个模块不生成正文,它只产出结构化叙事方案。每次动笔前,它需要输出一份JSON,包含:
- 当前章回的目标(这一章要让故事推进到什么状态)
- 冲突类型(人际冲突/环境冲突/内心冲突)
- 主要出场角色及他们在此章的情绪状态
- 本章要埋的伏笔和要回收的伏笔
为什么要单独拆出这个模块?因为LLM在"做计划"和"写正文"两种任务之间切换时,质量会互相拖累。你让模型既想情节又写具体句子,它很容易在句子里过度发挥,然后把计划偷偷改掉。拆开后,规划器只做纯逻辑推演,对格式稳定性要求极高,我给它的温度是0.2;写正文的模块温度是0.8。各司其职,效果立竿见影。
2.2 核心模块:Story Writer(故事落地器)
Writer负责把Planner的JSON方案转成可读的正文。它接收的东西不是"前文全文",而是经过处理的"前文摘要 + 角色状态 + 本章大纲"。
这里有个容易犯的错误:以为给全文越多越好。实际恰恰相反,上下文里的噪声会让Writers迷失重点。我给Writer的上下文一般控制在2400-3500个token,其中包含:
- 一段压缩后的故事摘要(覆盖全局因果链)
- 上一章的结尾段落(保证衔接感)
- 当前角色的状态卡(性格标签、当前情绪、当前目标)
- 本章的叙事节拍提示(铺垫-升高-高潮-回落里,本章处于哪个位置)
压缩摘要这个动作,最好单独用一个Summarizer模块来做,不能直接用Writer自己压缩。因为Writer在压缩时容易丢掉最能制造悬念的信息,它会把"主角发现戒指内侧刻着仇人的姓氏"这种关键细节在摘要里省掉,导致后文接不上。
2.3 核心模块:Continuity Checker(一致性检查器)
很多系统只做"生成",不做"校验"。但叙事场景里,校验模块才是质量的守门员。Continuity Checker拿到的输入是"角色状态卡 + 设定集 + 本章生成的正文",输出是一份问题清单。
它的检查项包括:
- 角色行为是否违背已设定的人格特质
- 时间线是否和全局年表冲突
- 是否重复使用了已经被用过的桥段
- 本章结尾是否留出了和后文衔接的钩子
这里有一个工程细节:Checker也必须是一个独立的LLM调用,而且要把温度调低,同时给它"挑刺优先"的System Prompt。如果让Writer自己检查自己,它会非常温柔,什么毛病都看不出来。我试过让同一个模型既写又审,它给我输出"这段描写很生动,无一致性问题",但我肉眼就能看出角色性别都变了。
2.4 三个模块之间的协作流:加权循环而不是一次性串行
初版架构我做成了一次性串行:Planner出方案,Writer写正文,Checker查问题。但很快发现,一次串行根本不够。
原因在于,Writer产出的正文往往和Planner的方案有偏差,而Checker发现的问题又会反过来要求调整方案。所以我改成了循环结构:Planner → Writer → Checker →(有问题则)回到Planner修正方案 → Writer重写 → Checker再查。循环上限设为3次,超过3次还没通过,就判定为"本章规划难度过高",强制触发降级策略:缩小本章目标,只推进一个核心事件,放弃次要支线。
这个循环看起来会增加API成本,但实际运行中,大约60%的章节在1-2轮内就能通过。真正需要3轮以上的,往往是规划器本身设计失误,这时候重写多少遍正文都没用,必须回到规划层。
3. 稳定性从哪来:状态管理、记忆系统和上下文控制
聊完了模块分工,接下来是数据层面的事。叙事Agent能不能稳定工作,一半取决于你怎么管理它的"记忆"和"当前状态"。
3.1 用状态机管理故事进度,而不是用"章节序号"
最简单的故事进度管理,是一个计数器:写到第几章了。但这个粒度太粗,无法支撑真正的叙事逻辑。我改成了状态机模型。
以最常见的"英雄之旅"结构做例子,我把进度定义为9个离散状态:
- Ordinary World(日常世界)
- Call to Adventure(冒险召唤)
- Refusal of the Call(拒绝召唤)
- Meeting the Mentor(遇见导师)
- Crossing the Threshold(跨越门槛)
- Tests, Allies, Enemies(试炼)
- Approach to the Inmost Cave(接近洞穴)
- Ordeal(磨难)
- Reward and Return(奖赏与回归)
Planning模块每次生成方案前,都要先读取当前处于哪个状态,然后本章的目标设置为"推进到下一个状态"。这样做最直接的好处是,故事不会在中间某个爽点上无限徘徊。没有状态机的约束时,LLM会特别喜欢反复写"角色陷入危险然后脱险"的桥段,因为这段戏剧张力高,局部概率大。有了状态机,你就能硬性打断它的重复冲动。
3.2 记忆分级:哪些进长期记忆,哪些只存在于本章
长期记忆是一个人设和因果链的总和,必须结构化存储,不能直接丢给模型让它自己记。我通常用JSON文件或数据库存三张表:
- Character Table:角色ID、名字、核心欲望、性格标签(固定5个)、当前目标、当前情绪、和其他角色的关系web。
- Plot Table:事件ID、所属章回、因果指向、涉及角色、是否已回收。
- Setting Table:世界观常量、不可破坏的物理/社会规则。
每章结束后,由一个Memory Updater模块读取本章正文,自动增量更新这三张表。这个更新动作极其关键,因为它决定了下一章Agent眼中的世界是什么样。我遇到过一种情况:角色已经在第五章死了,第六章Planner还把它安排在出场列表里。后来查出来是Memory Updater漏更新了这个角色的状态。所以Memory Updater输出后,必须加一道校验:核对本章正文里所有有明确状态的实体,是否在表里同步更新。
3.3 上下文压缩的时机和策略:增量摘要比全文摘要可靠
很多人做长故事,上下文不够用了就做一次摘要,把前文压缩一下接着写。但"一次性摘要"方案有个隐蔽缺陷:摘要本身会丢失大量细节,而丢失的细节恰恰是后面埋的伏笔和人物关系的暗线。
我现在的做法是增量摘要(Rolling Summary)。每次新增一章,只把"新章正文 + 已有摘要"放进Summarizer,生成一份"更新的摘要",然后丢弃旧的摘要文本。这样一来,摘要的压缩是渐进式的,早期重要信息会被反复保留(因为每次摘要都基于旧摘要生成),而早期的噪声会在几轮之后被自然淘汰。
成本上,增量摘要比全文摘要多几次调用,但换来的是长故事前30章的一致性明显提升。我之前做过实验:同一个设定,用全文摘要方案跑到第20章,角色关系图已经更新得面目全非;换增量摘要后,第30章依然沿用了第一章节埋下的核心冲突,这是质的差别。
4. 叙事质量的判断:不能靠感觉,要有可量化的检查维度
要做工程化的叙事系统,就必须解决"质量怎么判"的问题。以前我们靠人读,每章都通读一遍,一部长篇下来能累掉半条命。后来我把质量检查拆成了一个个可以自动化的子任务。
4.1 硬性指标:一致性、因果性、钩子留存
我给自动检查模块定义了三类硬性指标:
- 一致性(Consistency):本章出现的人名、地名、时间描述,是否和设定表冲突。这个是纯逻辑判断,让低温模型做精确引证。
- 因果性(Causality):本章至少要有两个事件能被上一章找到原因,同时本章至少要有一个事件成为后文的原因。我要求Planner在方案里显式声明因果关系,Checker按图索骥。
- 钩子留存(Hook Retention):本章的结尾是否提出了一个未解决的问题。如果Checker发现结尾的悬念已经被彻底解决,或者根本没有新的悬而未决项,就判定需要打回重写。
4.2 软性指标:情感弧线、张力曲线
硬性指标之上,还有一类软性指标,靠规则比较难量化,但仍然可以借助LLM做打分。我设计了一个带加权公式的评分卡:
- 情感张力:本章节情绪强度是否呈上升或下降趋势,而非一条平线。打分区间0-5。
- 节奏分布:是否有大段无冲突的平铺直叙。允许存在,但不能连续超过两章。
- 角色主动性:主要角色在事件中是"推动者"还是"被推着走"。一个叙事合格的章节里,至少有一个主要角色做出了改变局势的主动决策。
总分 = 0.4×一致性 + 0.3×因果性 + 0.3×主动决策指数。低于阈值的章节直接进入重写流程,而不是进入下一章。这套打分逻辑一开始我觉得太机械,但实际跑下来,它能挡住最糟糕的20%-30%的输出,剩下的人力精力去处理更微妙的叙事问题。
4.3 半自动检查:模型打分 + 人工抽检的双轨制
完全靠自动检查也有盲区,特别是涉及隐喻、主题表达这种高层次叙事要素,LLM的打分并不可靠。我的方案是双轨:每章自动跑硬性指标,每5章做一次人工抽检,重点看主题是否跑偏、人物弧光是否有递进。
人工抽检时我会格外留意一件事:模型是否把"信息的复述"当成了"剧情的推进"。比如主角反复在想同一个问题,虽然每次措辞不同,但认知状态没有变化。这种章节往往符合所有自动指标,但它是一段注水内容。回归工程角度,我最终认可了一个折中方案——允许每3章存在一段"缓冲章节",这种章节不推进主线,只用来展示人物关系和世界日常,但对它的要求是必须在结尾埋一个新事件种子。
5. 调优过程中最常见的四类事故与解决的思路
最后聊点实战中高频踩坑的事故类型。这些坑不是偶然,而是叙事Agnet系统的结构性问题,几乎每个做这方向的人都会遇到。
5.1 事故一:角色OOC(性格崩坏)与它的根治方案
先回归那个最常见的状况:一个谨慎胆小的人设,在某个高压场景下,LLM让他突然当众顶撞反派。原因是局部语境里"顶撞"是最有戏剧张力的选择,模型选择了局部最优。
我的根治方案是"角色行为准则预写"。给每个主要角色建一个行为规则卡,写清楚三件事:他在压力下的典型反应、他在舒适区的典型反应、他绝对不会做的事。这个规则卡每次生成时都会进上下文,并且它的优先级被放在比正文示例更高的位置。另外,一旦Checker在检查中识别出OOC行为,我会让Memory Updater在更新角色表时,主动记录这个行为偏差,并在后续对话中提醒模型修正,避免同一个角色连续两章行为漂移。
5.2 事故二:剧情原地打转,章节之间没有推进感
这个特别容易发生在没有状态机约束的版本里。模型反复让角色面临相似的困境,看起来情节紧凑,实际上在原地踏步。
处理办法前面提过:引入状态机。每个章节都必须把推进状态作为最高优先级目标。如果规划器给出的本章目标无法让状态前进,写入规则的逻辑会在路由层拦截,直接拒绝执行。
还有一种特殊情况:故事角色被"困"在某一个场景是剧情的需要,比如主角必须在一个密闭空间里待一整个晚上。这种情况下状态机无法前进,我不会硬改设定,而是在章节结束时安排一个认知层面的推进——比如角色做了一次关键决定,或者对处境有了全新的认知。认知变化也是一种推进,但必须显式记录到摘要里,否则下章就会被当作没有推进。
5.3 事故三:上下文被灌满脏数据,模型反而"变笨"
一个很反直觉的发现:当你试图把更多前文信息塞给Writer时,生成的文本质量不是单调上升的,而是呈倒U型曲线。上下文中过多类似的段落、过多对话记录,会让模型沉迷于模仿前文的句式,丧失对当前目标的执行力。
我的解决方法是加一层Semantic Router(语义路由)。它负责在Writer调用前,从记忆系统里检索和本章目标最相关的信息:人物状态、上一章结尾、相关伏笔;再把不相关的细节果断丢弃。与其说这是"尽量多记",不如说这是"尽量留精华"。上下文是稀缺资源,要把每一寸空间都用来服务当前叙事目标,而不是服务对过往历史的敬意。
5.4 事故四:风格漂移,长故事越写越不像原来那本书
风格漂移和角色OOC不太一样。OOC是角色性格出问题,风格漂移是整个文本的语气、词汇偏好、节奏感发生变化。常见表现:前三章是冷峻的悬疑风格,写到第十章开始无意识冒出大量网络热词和轻快口吻。
在工程上,我把"风格锚点"做成了系统级约束,独立于正文,不参与剧情逻辑,只影响输出文风。每一章生成前,交给Writer的不只是剧情方案,还要加上一段"风格参照段"(Style Anchor)。这段文本不长,约200-300字,来自第一章里最典型的三段描写。让Writer先吸收风格锚点,再写正文。同时,每次增量摘要时,我会把上文的风格描述单独提取出来,而不是混在情节摘要里。这样即使剧情摘要被压缩了,风格锚点仍然独立存在。
6. 如果想上手,第一步应该干什么
写了这么多架构和细节,最后给真正想学系统的人一个落地路线。
6.1 先做一个最小可用的单模块版本,再逐步拆分
我见到太多人一上来就要搭全套多Agent系统,结果管线太长,任何一个环节出错都不知道该调哪里。我的建议是分四步走:
第一步,用单Prompt生成一个完整短篇,5000-8000字。先感受一下LLM在纯生成模式下能稳定做到什么程度、在哪里崩。
第二步,加一个Plan前置:一个独立的调用负责写方案,一个负责写正文。这里你就已经能体会到"规划与执行分离"的力量。
第三步,加一个Checker做硬性校验,自动过滤明显的逻辑错误和OOC。这时你的故事从"看运气"变成"有质检"。
第四步,再把长期记忆、状态机、增量摘要加进去。这时的系统才称得上是Agent,因为它具备了持续的自我更新能力。
我最初就是被第二步启发的:同一个LLM,在单纯让它写正文 vs 让它先规划方案再写正文,前者的天马行空后者根本接不住。规划器把"灵感"变成了"蓝图",Writer要做的只是在蓝图里添加血肉,发挥反而更稳定。
6.2 记住一句话:好的叙事Agent,是不停地在给LLM维持一个"被约束的创作空间"
模型始终是那个擅长接句子的模型,它不会因为套上了"Agent"的名字就突然拥有真正的全局意识。Agent工程的价值,在于为模型构建一套外部的脚手架:状态机让它记住故事走到哪,角色卡让它记住人物是谁,Checker让它发现自己的盲区。每一次调用都在这个预设的约束网格里工作,它生成出来的东西自然就会越来越接近完整的叙事框架。
而你作为这个系统的设计者,真正的任务是理解"哪些约束必须铁腕执行、哪些地方又应该放给模型自由发挥"。这个平衡,是所有叙事Agent项目里最难把握、也最有意思的地方。调参调烦的时候,回来想想这篇的这几张表,路就会清晰很多。