跳到主要内容 基于 LangGraph 构建多智能体电影脚本生成应用 | 极客日志
Python AI 算法
基于 LangGraph 构建多智能体电影脚本生成应用 本文介绍了基于 LangGraph 框架构建多智能体电影脚本生成应用的方法。通过定义 GraphState 管理状态,利用 create_scene、select_speaker、handle_dialogue 和 write 四个节点实现场景创建、角色切换、对话模拟及剧本导出。文章详细展示了环境配置、提示词设计、工作流编排及代码实现,并提供了优化建议,适用于构建复杂的 LLM 工作流应用。
LangGraph 是著名的大模型开发框架 LangChain 推出的用于构建基于复杂工作流的 LLM 应用的开发库。LangGraph 把任务的节点与关系用 Graph 结构来定义,以支持更多样、更复杂的应用场景,特别是:
实现包含循环、迭代等复杂工作流的高级 RAG 范式。
需要更灵活控制的 Agent 应用,如指定 Tool、增加人机交互等。
多智能体系统(Multi-Agent System)的构建。 本文分享一个如何使用 LangGraph 构建一个创作电影场景与脚本的多智能体应用。
关于多智能体系统 AI 智能体是一个基于大模型的具备自我感知、规划与行动能力的 AI 应用。而多智能体系统(MAS)顾名思义就是由多个 AI 智能体构成,通过相互关联与协作共同完成任务的智能体系统。
MAS 可以是每个智能体有自己独立的 LLM、提示词、Tools 或者其他自定义代码,用来与其他智能体协作;也可以是一个 LLM 在不同的提示下扮演不同的角色。例如在一个电影虚拟世界中,同一个 LLM 根据提示扮演不同的虚拟人物。
电影创作的多智能体应用目标 在这个应用中,我们的目标是实现一个能够自动创建电影场景、并能够代入场景中的多个角色,模拟对话生成台词脚本,最后输出剧本的 LangGraph 应用。在这个应用中,需要 AI 完成的任务是:
场景创建 :让 AI 根据简单输入创建一个电影场景与若干角色。
角色模拟与创作 :AI 模拟场景中的不同角色进行脚本创作,推动情节发展。
create_scene :用 AI 创作一个简单电影场景与若干角色。
select_speaker :AI 根据情节发展选择与切换人物角色,除非故事结束。
handle_dialogue :AI 模拟选择的人物角色,根据情节发展进行多轮对话。
write :完成后将会把整个电影场景与对话脚本输出成文件。
这里的多智能体体现在 AI 会根据提示扮演不同的角色,并根据情节发展做自主对话。其中角色的切换和扮演由 select_speaker 与 handle_dialogue 循环进行,直到满足结束条件(故事结束或者到达最大对话次数)。
环境配置与依赖 在开始编码之前,请确保已安装必要的 Python 库。建议创建一个虚拟环境以避免依赖冲突。
pip install langgraph langchain langchain-community langchain-openai python-docx ollama
此外,你需要本地运行 Ollama 服务并加载相应的模型,例如 qwen2 中文模型:
ollama pull qwen2
ollama run qwen2
核心代码实现
1. 导入依赖与模型调用 首先实现一个 LLM 调用方法。这里我们使用本地的 Ollama 加载 qwen2 中文模型,同时也预留了 OpenAI 接口的调用方式以便扩展。
from typing import Dict , TypedDict, Optional
from langgraph.graph import StateGraph, END
from langchain.output_parsers import CommaSeparatedListOutputParser
from langchain_community.llms import Ollama
from langchain_openai import ChatOpenAI
from docx import Document
llm_qwen = Ollama(model='qwen2' )
llm_openai = ChatOpenAI(model='gemini-pro' )
def llm (x ):
try :
response = llm_qwen.invoke(x)
return response
except Exception as e:
print (f"LLM Error: {e} " )
return ""
2. 定义提示词
创建场景 :根据输入的题材创建一个简单场景及相关角色。
提取角色 :也就是 AI 将要扮演的电影中的多个角色名称。
角色选择 :根据情节发展选择下一个登场的角色。
角色模拟 :让 AI 切换扮演电影中的不同角色进行对话并推动情节发展。
prompt_scene = "设计一个需要台词的电影场景,题材是:{}。简单描述故事的背景和角色名称,但不要设计台词。不超过 100 字。角色不超过 4 个。"
prompt_roles = "识别执行此电影场景所需的不同角色,仅以逗号分隔的列表形式输出最简单的角色名称,角色名称不要包含头衔或称呼、称号、昵称等。这是电影场景:{}"
prompt_select_speaker = """
请根据电影场景、已有对话内容,从以下角色中选出适合下一个说话的人。
如果没有对话内容,请选择一个角色开始对话。如果故事结束,输出 END。
-----------
{}
-----------
请仅输出以上的角色名称,名称必须完全匹配。
电影场景:
-----------
{}
-----------
当前对话内容:
-----------
{}
-----------
"""
prompt_speak = """
你现在是{},根据下面对话和场景,说出你的下一段台词。输出格式:
------
{}:台词
------
要求:
1. 台词与场景和角色的设定相符。
2. 台词能推动剧情发展。
3. 不要重复之前的台词。
到目前为止的对话内容:
----------
{}
----------
电影场景:
----------
{}
----------
"""
3. 定义 GraphState 这是 LangGraph 工作流运行中的共享状态数据,也就是各个流程节点之间交换的数据。通常把流程中需要共享的数据在此统一定义。这里我们做如下定义:
class GraphState (TypedDict ):
next_speaker: Optional [str ] = None
history: Optional [str ] = None
current_response: Optional [str ] = None
current_speaker: Optional [str ] = None
dialogues_left: Optional [int ] = None
scene: Optional [str ] = None
subject: Optional [str ] = None
roles: Optional [str ] = None
results: Optional [str ] = None
4. 节点函数实现
创建场景与角色 (create_scene) 根据设定主题生成一个电影场景与故事背景,并从场景中抽取出多个角色用于后续的 AI 模拟。角色存放到 roles,场景存放到 scene。
def create_scene (state ):
scene = llm(prompt_scene.format (state.get('subject' )))
actors = llm(prompt_roles.format (scene))
output_parser = CommaSeparatedListOutputParser()
roles = output_parser.parse(actors)
print (f"Scene created: {scene} " )
print (f"Actors created: {roles} \n" )
return {"scene" : scene, "roles" : roles}
角色选择 (select_speaker) 根据场景、对话历史、角色列表选择下一个合适对话的角色;如果输出为 END,表示故事与对话结束。
def select_speaker (state ):
speaker = state.get('current_speaker' )
scene = state.get('scene' )
summary = state.get('history' , '' ).strip()
roles = state.get('roles' )
next_speaker = llm(prompt_select_speaker.format (',' .join(roles), scene, summary))
if next_speaker == "END" :
return {"dialogues_left" : 0 }
return {"next_speaker" : next_speaker}
角色模拟对话 (handle_dialogue) LLM 根据提示词扮演不同的角色进行对话,对话需要基于场景与历史对话进行推理创作,完成后更改相关的 state 中信息。
def handle_dialogue (state ):
summary = state.get('history' , '' ).strip()
count = state.get('dialogues_left' )
next_speaker = state.get('next_speaker' , '' ).strip()
roles = state.get('roles' )
scene = state.get('scene' )
index = -1
for i, role in enumerate (roles):
if role in next_speaker:
index = i
break
if index == -1 :
return {"dialogues_left" : 0 }
prompt = prompt_speak.format (roles[index], roles[index], summary, scene)
argument = llm(prompt)
print (f"{argument} \n" )
return {
"history" : summary + '\n' + argument,
"current_speaker" : roles[index],
"current_response" : argument,
"dialogues_left" : count - 1
}
生成剧本文件 (write) 这是最后的任务节点,把之前的创作生成一个 word 文件保存。
def write (state ):
doc = Document()
scene = state['scene' ]
doc.add_heading('Scene' , level=1 )
doc.add_paragraph(scene)
roles = state['roles' ]
doc.add_heading('Roles' , level=1 )
doc.add_paragraph(', ' .join(roles))
history = state['history' ]
doc.add_heading('Dialogue History' , level=1 )
doc.add_paragraph(history)
doc.save('movie.docx' )
return {"results" : "剧本已生成" }
5. 定义 Workflow 所有的节点都已经准备完毕,现在我们可以定义应用的 workflow。定义一个 LangGraph 的工作流的典型步骤为:
增加节点 :将上面创建的场景与角色创建、角色选择、角色模拟、生成剧本等通过 add_node 加入。
增加边(节点关系) :按照之前设计的工作流进行连接,唯一需要注意的是流程结束条件的判断,需要使用条件函数来定义'条件边',使得流程在场景结束或者对话次数达到上限后终止。
编译工作流 :生成应用。
def check_end (state ):
return "end" if state.get("dialogues_left" ) == 0 else "continue"
workflow = StateGraph(GraphState)
workflow.set_entry_point("create_scene" )
workflow.add_node("create_scene" , create_scene)
workflow.add_node("select_speaker" , select_speaker)
workflow.add_node("handle_dialogue" , handle_dialogue)
workflow.add_node("write" , write)
workflow.add_edge('create_scene' , "select_speaker" )
workflow.add_conditional_edges(
"select_speaker" ,
check_end,
{
"continue" : "handle_dialogue" ,
"end" : "write"
}
)
workflow.add_conditional_edges(
"handle_dialogue" ,
check_end,
{
"continue" : "select_speaker" ,
"end" : "write"
}
)
workflow.add_edge('write' , END)
app = workflow.compile ()
6. 测试应用 现在我们可以简单测试这个自动生成电影场景与脚本的应用,看看效果如何,运行如下代码:
conversation = app.invoke({
'dialogues_left' : 20 ,
'next_speaker' : '' ,
'history' : '' ,
'current_response' : '' ,
'subject' : '一个关于三国的搞笑短视频' ,
}, {'recursion_limit' : 50 })
运行完成后,可以打开当前目录生成的 movie.docx 文件,查看创作的场景与台词脚本。
优化与注意事项
异常处理 :在 LLM 调用周围增加 try-except 块,防止因网络波动导致整个流程中断。
流式输出 :如果需要实时展示对话过程,可以使用 stream 接口替代 invoke。
Token 限制 :注意对话历史的长度,避免超过模型上下文窗口,必要时进行摘要压缩。
安全性 :对用户输入的题材进行过滤,防止注入恶意 Prompt。
总结 本文详细介绍了如何使用 LangGraph 构建一个多智能体电影脚本生成应用。通过定义状态图、节点和边,我们实现了场景创建、角色分配、对话模拟及文档输出的完整闭环。这种基于图的工作流模式非常适合处理具有循环、分支和状态依赖的复杂 AI 任务。开发者可以根据实际需求调整提示词逻辑或扩展更多的智能体角色,从而构建出更加智能化的应用场景。
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
Mermaid 预览与可视化编辑 基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online