基于LangChain与LLM构建多AI角色协同叙事系统实战指南

发布时间:2026/8/3 8:33:18
基于LangChain与LLM构建多AI角色协同叙事系统实战指南 最近在开发一个AI角色互动项目时遇到了一个非常有趣的需求如何让多个具备不同性格和背景的AI角色比如“胖橘”、“虎哥”、“熊猫道长”在一个连贯的叙事中互动并共同应对一个外部挑战比如“河马大姐的冤魂”。这不仅仅是简单的多轮对话更涉及到角色记忆、性格一致性、剧情推进和协同决策等复杂问题。本文将围绕如何构建一个多AI角色协同叙事系统展开从核心概念、架构设计到具体的代码实现与调试提供一个完整的实战教程。无论你是想开发AI剧本杀、互动小说还是复杂的游戏NPC系统都能从本文中找到可复用的方案。我们将使用目前主流的LangChain框架结合大型语言模型LLM来落地实现。1. 核心概念与项目背景在开始编码之前我们需要明确几个关键概念这有助于理解整个系统的设计思路。1.1 什么是多AI角色叙事系统传统的人机对话通常是一对一的。而多AI角色叙事系统则模拟了一个“虚拟房间”其中有多个拥有独立身份、记忆和目标的AI智能体Agent。它们不仅能与用户玩家对话更能彼此之间进行互动根据剧情发展做出符合自身人设的反应共同推动故事线前进。在本项目中“胖橘”、“虎哥”、“熊猫道长”就是三个这样的智能体。1.2 智能体Agent的核心要素一个合格的叙事智能体需要具备以下要素身份Identity明确的名字、性格、背景故事。例如“熊猫道长”可能是一位仙风道骨但贪吃的修道者。记忆Memory分为短期记忆当前会话的上下文和长期记忆角色固有的知识、过往经历。记忆是保证角色言行一致性的关键。目标Goal在当前场景或章节中的目的。例如“查明河马大姐冤魂的真相”。行动Action根据观察其他角色的话、剧情描述和自身目标决定要说什么或做什么。工具Tools角色可以调用的外部能力如“掐指一算”、“搜寻线索”等在LangChain中通常对应一个可执行函数。1.3 叙事驱动与状态管理系统需要一个“导演”或“世界规则”来管理全局状态、描述场景变化、引入外部事件如“冤魂现身”并裁决角色行动的结果。这个驱动机制负责将分散的角色行动编织成一个连贯的故事。2. 环境准备与依赖说明我们将使用Python和LangChain框架来实现。LangChain提供了构建智能体所需的大量组件能极大简化开发流程。2.1 基础环境操作系统Windows 10/11, macOS 或 Linux (Ubuntu 20.04)Python版本3.8 或 3.9建议3.9兼容性最好包管理工具pip2.2 核心依赖库创建一个新的项目目录并通过requirements.txt文件管理依赖。# requirements.txt langchain0.1.0 langchain-openai0.0.5 openai1.6.0 python-dotenv安装命令pip install -r requirements.txt2.3 配置API密钥本项目需要调用大语言模型API以OpenAI GPT为例。你需要准备一个有效的API Key。 在项目根目录创建.env文件来安全存储密钥# .env OPENAI_API_KEY你的sk-xxx密钥然后在代码中通过python-dotenv加载。3. 系统架构设计与原理拆解在动手写代码前先看下系统的核心架构这能帮助我们理解各个模块如何协作。[ 用户/导演输入 ] | v [ 叙事引擎 (Narrative Engine) ] | (管理剧情状态分发事件) v [ 角色智能体池 (Agent Pool) ] | |--- [ 智能体A: 胖橘 ] - [记忆] [决策链] |--- [ 智能体B: 虎哥 ] - [记忆] [决策链] |--- [ 智能体C: 熊猫道长 ] - [决策链] [工具] | v [ 行动协调与输出 ] | v [ 更新全局及个体记忆 ]3.1 叙事引擎Narrative Engine这是系统的大脑负责初始化创建所有角色智能体加载初始剧情。回合管理决定当前该哪个角色行动或是处理外部事件。状态更新根据角色行动的结果更新“世界状态”如“房间温度降低”、“找到关键道具”。剧情推进在适当时机注入新事件推动故事发展如“河马大姐的冤魂突然发出哭声”。3.2 角色智能体Character Agent每个角色是langchain.agents.AgentExecutor的一个实例但经过了高度定制。其核心是一个决策链LLMChain该链的提示词Prompt融合了角色的身份、当前记忆、观察到的信息以及可用的工具。3.3 记忆系统Memory System这是实现角色“不跑偏”和剧情连贯性的核心技术。我们采用两层记忆对话记忆ConversationMemory使用ConversationBufferWindowMemory保存最近N轮角色间的直接对话确保上下文连贯。摘要记忆SummaryMemory更高级的技术。当对话记忆达到一定长度后让LLM对之前发生的剧情进行总结将摘要存入角色的长期记忆。这样既能保留关键信息又避免了上下文过长导致的Token超限和成本增加。4. 完整实战构建“AI胖橘虎哥历险记”接下来我们一步步实现这个多角色叙事系统。4.1 项目结构初始化创建如下目录和文件ai_adventure/ ├── .env ├── requirements.txt ├── main.py ├── narrative_engine.py ├── agents/ │ ├── __init__.py │ ├── base_agent.py │ ├── fat_orange_agent.py │ ├── tiger_bro_agent.py │ └── panda_taoist_agent.py └── prompts/ └── character_prompts.py4.2 定义角色提示词与工具在prompts/character_prompts.py中我们为每个角色精心设计系统提示词这是塑造性格的关键。# prompts/character_prompts.py FAT_ORANGE_PROMPT 你是一只名叫“胖橘”的猫妖性格慵懒、贪吃但关键时刻很讲义气。你修炼了五百年能说人话喜欢晒太阳和吃小鱼干。 你目前和虎哥、熊猫道长一起被卷入河马大姐冤魂的事件中。 你的长期记忆{long_term_memory} 最近的对话记录{recent_conversation} 当前场景描述{current_scene} 其他角色的上一轮行动{other_actions} 请以胖橘的身份和口吻思考并行动。你可以 1. 与其他角色对话。 2. 描述你的动作例如伸个懒腰竖起耳朵听。 3. 使用你的妖力感知异常如果需要。 请输出你的行动 TIGER_BRO_PROMPT 你是“虎哥”一只性格豪爽、冲动、但内心善良的虎妖。你是团队里的武力担当讨厌弯弯绕绕。 你的长期记忆{long_term_memory} 最近的对话记录{recent_conversation} 当前场景描述{current_scene} 其他角色的上一轮行动{other_actions} 请以虎哥的身份和口吻思考并行动。你可以 1. 与其他角色对话。 2. 提出直接的行动方案比如“我们直接冲进去”。 3. 展示威慑或保护同伴的动作。 请输出你的行动 PANDA_TAOIST_PROMPT 你是“熊猫道长”一位道法高深但嗜竹如命的熊猫道士。你知识渊博处事冷静喜欢引经据典但肚子饿的时候会注意力不集中。 你拥有以下法器/工具 - 罗盘可以探测灵体方位。 - 符咒可以暂时驱散或安抚怨灵。 - 八卦镜可以反射邪气揭示真相。 你的长期记忆{long_term_memory} 最近的对话记录{recent_conversation} 当前场景描述{current_scene} 其他角色的上一轮行动{other_actions} 请以熊猫道长的身份和口吻思考并行动。你可以 1. 与其他角色对话。 2. 提出基于道法的分析或建议。 3. 使用你的法器罗盘、符咒、八卦镜。当你想使用时请明确说出“使用罗盘探测”或“祭出符咒”。 请输出你的行动 4.3 实现基础智能体类在agents/base_agent.py中我们创建一个所有角色智能体的基类封装记忆和决策链的初始化。# agents/base_agent.py from langchain.memory import ConversationBufferWindowMemory, ConversationSummaryMemory from langchain_openai import ChatOpenAI from langchain.chains import LLMChain import os from dotenv import load_dotenv load_dotenv() class BaseCharacterAgent: def __init__(self, name, system_prompt, llmNone, window_size5): self.name name self.llm llm or ChatOpenAI(modelgpt-3.5-turbo, temperature0.7, api_keyos.getenv(OPENAI_API_KEY)) # 短期记忆保存最近N轮对话 self.short_term_memory ConversationBufferWindowMemory(memory_keyrecent_conversation, kwindow_size, input_keyinput) # 长期记忆使用摘要记忆定期总结 self.long_term_memory ConversationSummaryMemory(llmself.llm, memory_keylong_term_memory, input_keyinput) # 创建决策链 self.chain LLMChain( llmself.llm, promptsystem_prompt, # 这里传入的是已经格式化好的PromptTemplate memoryself.short_term_memory, verboseFalse # 调试时可设为True ) def get_memory_context(self): 获取整合后的记忆上下文用于填充Prompt # 这里简化处理实际应用中需要更精细地拼接长短期记忆 long_term self.long_term_memory.load_memory_variables({}).get(long_term_memory, 暂无长期记忆。) short_term self.short_term_memory.load_memory_variables({}).get(recent_conversation, 暂无近期对话。) return { long_term_memory: long_term, recent_conversation: short_term } def act(self, scene_description, other_actions): 角色执行一次行动 # 准备链的输入 memory_ctx self.get_memory_context() input_dict { current_scene: scene_description, other_actions: other_actions, **memory_ctx } # 调用LLM生成行动 response self.chain.run(input_dict) # 将本次行动存入短期记忆 self.short_term_memory.save_context({input: f场景{scene_description}}, {output: response}) return response def summarize_memory(self): 触发一次长期记忆摘要 # 获取当前短期记忆的完整内容 buffer self.short_term_memory.chat_memory.messages if len(buffer) 10: # 当对话轮次较多时触发摘要 summary_input \n.join([f{msg.type}: {msg.content} for msg in buffer[-20:]]) # 取最近20条消息总结 self.long_term_memory.save_context({input: 以下是一段剧情摘要请求}, {output: summary_input}) # 清空或部分清空短期记忆避免重复此处策略可根据需要调整 # self.short_term_memory.clear()4.4 实现具体角色智能体以“熊猫道长”为例展示如何集成工具。在agents/panda_taoist_agent.py中# agents/panda_taoist_agent.py from langchain.agents import Tool, AgentExecutor, create_react_agent from langchain import hub from .base_agent import BaseCharacterAgent class PandaTaoistAgent(BaseCharacterAgent): def __init__(self, llmNone): # 先初始化基础部分 from prompts.character_prompts import PANDA_TAOIST_PROMPT from langchain.prompts import PromptTemplate prompt_template PromptTemplate.from_template(PANDA_TAOIST_PROMPT) super().__init__(name熊猫道长, system_promptprompt_template, llmllm) # 定义专属工具 self.tools [ Tool( name罗盘探测, funcself._use_compass, description使用罗盘探测周围灵体的方位和强度。输入应为探测目标如‘探测冤魂方位’。 ), Tool( name绘制符咒, funcself._draw_talisman, description绘制一张镇灵或驱邪符咒。输入应为符咒类型如‘绘制镇灵符’。 ), ] # 创建支持工具的ReAct智能体 react_prompt hub.pull(hwchase17/react) self.agent create_react_agent(llmself.llm, toolsself.tools, promptreact_prompt) self.agent_executor AgentExecutor(agentself.agent, toolsself.tools, verboseFalse, handle_parsing_errorsTrue) def _use_compass(self, query): 模拟罗盘探测的工具函数 # 这里可以连接更复杂的逻辑或外部API return f“{self.name}取出古朴罗盘指针剧烈颤动后指向西北角罗盘显示阴气最重之处在西北方距离约十步怨念极深。” def _draw_talisman(self, query): 模拟绘制符咒的工具函数 return f“{self.name}屏息凝神以指代笔凌空画出一道金光闪闪的‘镇灵符’符成此符可暂时安定怨灵但需贴于其执念之物上。” def act(self, scene_description, other_actions): 重写act方法让智能体能够选择使用工具 memory_ctx self.get_memory_context() # 构建一个包含场景、记忆和其他角色行动的综合性输入 agent_input f 当前场景{scene_description} 你的长期记忆摘要{memory_ctx[long_term_memory]} 近期对话{memory_ctx[recent_conversation]} 其他角色刚才的行动{other_actions} 请以‘熊猫道长’的身份思考并行动你可以说话、做动作也可以使用你的法器罗盘、符咒。 try: response self.agent_executor.invoke({input: agent_input}) action response[output] except Exception as e: action f“{self.name}似乎有些走神摸了摸肚子嗯...待贫道思索片刻。” self.short_term_memory.save_context({input: agent_input}, {output: action}) return action4.5 实现叙事引擎在narrative_engine.py中创建串联一切的核心引擎。# narrative_engine.py from agents.fat_orange_agent import FatOrangeAgent from agents.tiger_bro_agent import TigerBroAgent from agents.panda_taoist_agent import PandaTaoistAgent class NarrativeEngine: def __init__(self): self.llm ChatOpenAI(modelgpt-3.5-turbo, temperature0.7) self.characters { “胖橘”: FatOrangeAgent(llmself.llm), “虎哥”: TigerBroAgent(llmself.llm), “熊猫道长”: PandaTaoistAgent(llmself.llm) } self.scene “你们三人站在河马大姐生前居住的破旧木屋前。夜幕降临屋内传来若有若无的哭泣声。门扉无风自动吱呀作响。” self.turn_order [“胖橘”, “虎哥”, “熊猫道长”] # 简单的回合顺序 self.current_turn 0 self.history [] def describe_scene(self): 向所有角色描述当前场景 return self.scene def play_turn(self): 进行一个回合 character_name self.turn_order[self.current_turn] character self.characters[character_name] # 获取其他角色上一回合的行动从历史记录中 other_actions “” if self.history: other_actions “; “.join([f“{name}: {action}” for name, action in self.history[-3:]]) # 取最近3条历史 print(f“\n {character_name}的回合 ”) print(f“场景{self.scene}”) if other_actions: print(f“其他行动{other_actions}”) # 角色行动 action character.act(self.scene, other_actions) print(f“{character_name}: {action}”) # 记录历史 self.history.append((character_name, action)) # 更新回合 self.current_turn (self.current_turn 1) % len(self.turn_order) # 简单的事件触发逻辑示例根据行动可能改变场景 if “罗盘” in action and “西北” in action: self.scene “在熊猫道长罗盘的指引下你们的目光聚焦在木屋西北角的一个老旧梳妆台上。哭泣声似乎变得更清晰了。” print(f“\n【剧情推进】场景更新为{self.scene}”) return action def run(self, max_turns10): 运行故事 print(“故事开始...“) print(self.describe_scene()) for i in range(max_turns): self.play_turn() # 每5回合让角色总结一次记忆 if i % 5 4: for char in self.characters.values(): char.summarize_memory() print(“\n 当前章节结束 )4.6 主程序与运行最后在main.py中启动整个故事。# main.py from narrative_engine import NarrativeEngine if __name__ __main__: engine NarrativeEngine() engine.run(max_turns12)4.7 运行结果示例运行python main.py你将看到类似下面的输出每次运行因LLM随机性会不同故事开始... 你们三人站在河马大姐生前居住的破旧木屋前。夜幕降临屋内传来若有若无的哭泣声。门扉无风自动吱呀作响。 胖橘的回合 场景你们三人站在河马大姐生前居住的破旧木屋前。夜幕降临屋内传来若有若无的哭泣声。门扉无风自动吱呀作响。 胖橘: 炸毛尾巴竖起来喵呜这地方让本喵浑身不舒服...虎哥你块头大你走前面 虎哥的回合 场景你们三人站在河马大姐生前居住的破旧木屋前。夜幕降临屋内传来若有若无的哭泣声。门扉无风自动吱呀作响。 其他行动胖橘: 炸毛尾巴竖起来喵呜这地方让本喵浑身不舒服...虎哥你块头大你走前面 虎哥: 一拍胸脯怕什么跟紧我胖橘你别怂道长你看好周围说着虎哥上前一步警惕地推开半掩的木门 熊猫道长的回合 场景你们三人站在河马大姐生前居住的破旧木屋前。夜幕降临屋内传来若有若无的哭泣声。门扉无风自动吱呀作响。 其他行动胖橘: 炸毛尾巴竖起来喵呜这地方让本喵浑身不舒服...虎哥你块头大你走前面; 虎哥: 一拍胸脯怕什么跟紧我胖橘你别怂道长你看好周围说着虎哥上前一步警惕地推开半掩的木门 熊猫道长: 捋了捋胡须从袖中取出罗盘且慢让贫道先探探虚实。使用罗盘探测。 【剧情推进】场景更新为在熊猫道长罗盘的指引下你们的目光聚焦在木屋西北角的一个老旧梳妆台上。哭泣声似乎变得更清晰了。 ...5. 常见问题与调试技巧在开发此类系统时你可能会遇到以下问题5.1 角色行为偏离OOC现象角色说话不符合预设性格如“熊猫道长”突然说网络流行语。原因系统提示词不够强或被对话历史中的其他信息带偏。解决在系统提示词开头用更强烈的语句定义角色例如“你必须始终以[角色名]的身份和口吻回应严格遵守其性格设定...”。在每次调用时都将角色身份描述作为输入的一部分。使用更高性能的LLM如GPT-4其遵循指令的能力更强。5.2 上下文长度与记忆丢失现象故事进行到后面角色忘记了之前的重要情节。原因对话缓冲区有长度限制Token数超限后旧信息被丢弃。解决采用记忆摘要技术如上文BaseCharacterAgent.summarize_memory所示。实现一个关键事实提取器定期让LLM从对话中提取关键人物、地点、事件、目标并将其作为“核心记忆”单独存储和注入提示词。使用支持更长上下文的模型或API。5.3 剧情陷入循环或停滞现象角色间对话来回重复无法推动剧情。原因缺乏外部事件驱动或“导演”干预。解决增强叙事引擎的干预能力。当检测到对话轮次过多无进展时引擎强制插入一个新事件描述如“突然梳妆台的镜子裂开一道缝一股寒气涌出”。为角色设定更明确的阶段性目标并在提示词中强调。引入一个“旁白”或“导演”角色由另一个LLM实例驱动专门负责推进剧情。5.4 工具调用不准确现象智能体该用工具时不用或乱用工具。原因工具描述不够清晰或ReAct提示词未优化。解决为每个工具编写精确、无歧义的description。在给智能体的输入中明确提醒“你可以使用以下工具...”。使用更先进的智能体框架如LangGraph它可以定义更复杂的决策流程。6. 最佳实践与进阶优化建议6.1 工程化与配置管理角色配置化将角色的姓名、提示词、工具列表等存入JSON或YAML配置文件便于管理和创建新角色。状态持久化将游戏状态场景、角色记忆、历史保存到数据库如SQLite、Redis支持存档/读档。异步处理如果角色较多使用异步IOasyncio并行处理多个角色的“思考”过程提升响应速度。6.2 提升叙事质量分层记忆系统实现短期对话、中期场景、长期角色关系、世界观的多层记忆结构。情感与关系建模为角色添加对其他人物的“好感度”、“信任度”等隐藏属性这些属性会影响其对话倾向。剧情树与分支预先设计关键剧情节点和分支选择让叙事引擎根据角色的集体决策引导故事走向不同结局。6.3 性能与成本优化本地模型对于对响应速度要求高或需控制成本的场景可考虑使用本地部署的轻量级LLM如Qwen、ChatGLM等通过LangChain集成。缓存机制对常见的场景描述、角色固定回应进行缓存减少重复调用LLM。Token精打细算定期清理记忆缓冲区优化提示词模板移除冗余信息。构建一个生动的多AI角色叙事系统就像执导一部微电影你需要好的“演员”强提示词定义的智能体、清晰的“剧本”叙事引擎与状态管理和灵活的“调度”记忆与工具系统。本文提供的框架是一个坚实的起点你可以在此基础上扩展出更复杂的世界观、更细腻的角色互动和更引人入胜的剧情。动手试试吧创造出属于你的“胖橘和虎哥和熊猫道长”的精彩冒险如果在实现过程中遇到具体问题欢迎在评论区交流探讨。