From 750af43ff39f107b4832c9981f1df8323d0105a2 Mon Sep 17 00:00:00 2001 From: jonathang4 Date: Thu, 11 Sep 2025 20:44:14 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E5=BA=A6=E6=99=BA=E8=83=BD=E4=BD=93?= =?UTF-8?q?=20=E8=AE=B0=E5=BF=86=E5=AD=98=E5=82=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agent/build_bible.py | 2 +- agent/episode_create.py | 2 +- agent/scheduler.py | 3 +- agent/script_analysis.py | 2 +- agent/strategic_planning.py | 2 +- doc/节点结构图.png | Bin 0 -> 31217 bytes graph/test_agent_graph_1.py | 294 ++++++++++++++++++++++++------- tools/database/mongodb_memory.py | 30 ---- 8 files changed, 239 insertions(+), 96 deletions(-) create mode 100644 doc/节点结构图.png delete mode 100644 tools/database/mongodb_memory.py diff --git a/agent/build_bible.py b/agent/build_bible.py index d7a297c..212819d 100644 --- a/agent/build_bible.py +++ b/agent/build_bible.py @@ -35,7 +35,7 @@ DefaultAgentPrompt = f""" def create_agent_prompt(prompt, SchedulerList): """创建代理提示词的辅助函数""" if not SchedulerList or len(SchedulerList) == 0: return prompt - node_list = [f"{node.name}:{node.desc}" for node in SchedulerList] + node_list = [f"{node['name']}:{node['desc']}" for node in SchedulerList] return f""" {prompt} \n 下面返回数据中node字段的取值范围列表([{{名称:描述}}]),请根据你的分析结果选择一个节点名称返回: diff --git a/agent/episode_create.py b/agent/episode_create.py index 21c406c..b01b847 100644 --- a/agent/episode_create.py +++ b/agent/episode_create.py @@ -35,7 +35,7 @@ DefaultAgentPrompt = f""" def create_agent_prompt(prompt, SchedulerList): """创建代理提示词的辅助函数""" if not SchedulerList or len(SchedulerList) == 0: return prompt - node_list = [f"{node.name}:{node.desc}" for node in SchedulerList] + node_list = [f"{node['name']}:{node['desc']}" for node in SchedulerList] return f""" {prompt} \n 下面返回数据中node字段的取值范围列表([{{名称:描述}}]),请根据你的分析结果选择一个节点名称返回: diff --git a/agent/scheduler.py b/agent/scheduler.py index 5e87ddd..f064777 100644 --- a/agent/scheduler.py +++ b/agent/scheduler.py @@ -97,6 +97,7 @@ DefaultAgentPrompt = f""" "status": "当前阶段的状态",//取值范围在上述 status的描述中 不可写其他值 "agent":'',//分析后得出由哪个智能体继续任务,此处为智能体名称;如果需要继续与用户交互或仅需要回复用户则为空字符串 "message":'',//回复给用户的内容 + "retry_count":0,//重试次数 "node":'',//下一个节点名称 }} @@ -105,7 +106,7 @@ DefaultAgentPrompt = f""" def create_agent_prompt(prompt, SchedulerList): """创建代理提示词的辅助函数""" if not SchedulerList or len(SchedulerList) == 0: return prompt - node_list = [f"{node.name}:{node.desc}" for node in SchedulerList] + node_list = [f"{node['name']}:{node['desc']}" for node in SchedulerList] return f""" {prompt} \n 下面返回数据中node字段的取值范围列表([{{名称:描述}}]),请根据你的分析结果选择一个节点名称返回: diff --git a/agent/script_analysis.py b/agent/script_analysis.py index 37cff34..a359f9a 100644 --- a/agent/script_analysis.py +++ b/agent/script_analysis.py @@ -35,7 +35,7 @@ DefaultAgentPrompt = f""" def create_agent_prompt(prompt, SchedulerList): """创建代理提示词的辅助函数""" if not SchedulerList or len(SchedulerList) == 0: return prompt - node_list = [f"{node.name}:{node.desc}" for node in SchedulerList] + node_list = [f"{node['name']}:{node['desc']}" for node in SchedulerList] return f""" {prompt} \n 下面返回数据中node字段的取值范围列表([{{名称:描述}}]),请根据你的分析结果选择一个节点名称返回: diff --git a/agent/strategic_planning.py b/agent/strategic_planning.py index e145e87..a2e29b4 100644 --- a/agent/strategic_planning.py +++ b/agent/strategic_planning.py @@ -35,7 +35,7 @@ DefaultAgentPrompt = f""" def create_agent_prompt(prompt, SchedulerList): """创建代理提示词的辅助函数""" if not SchedulerList or len(SchedulerList) == 0: return prompt - node_list = [f"{node.name}:{node.desc}" for node in SchedulerList] + node_list = [f"{node['name']}:{node['desc']}" for node in SchedulerList] return f""" {prompt} \n 下面返回数据中node字段的取值范围列表([{{名称:描述}}]),请根据你的分析结果选择一个节点名称返回: diff --git a/doc/节点结构图.png b/doc/节点结构图.png new file mode 100644 index 0000000000000000000000000000000000000000..1727f0b11fd7191db68971523c218e36ca72744d GIT binary patch literal 31217 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 F{tx3-y+8l} literal 0 HcmV?d00001 diff --git a/graph/test_agent_graph_1.py b/graph/test_agent_graph_1.py index b29ba02..e5eef46 100644 --- a/graph/test_agent_graph_1.py +++ b/graph/test_agent_graph_1.py @@ -4,15 +4,23 @@ """ from typing import TypedDict, Annotated, Dict, Any, List, TypedDict, Optional + +from langgraph.graph.state import RunnableConfig from agent.scheduler import SchedulerAgent from agent.build_bible import BuildBibleAgent from agent.episode_create import EpisodeCreateAgent from agent.script_analysis import ScriptAnalysisAgent from agent.strategic_planning import StrategicPlanningAgent +from langchain_core.messages import AnyMessage,HumanMessage from langgraph.graph import StateGraph, START, END from utils.logger import get_logger import operator +import json +import config + +from tools.database.mongo import client # type: ignore +from langgraph.checkpoint.mongodb import MongoDBSaver logger = get_logger(__name__) @@ -24,7 +32,8 @@ def replace_value(old_val, new_val): # 状态类型定义 class InputState(TypedDict): """工作流输入状态""" - input_data: Annotated[Dict[str, Any], operator.add] + input_data: Annotated[list[AnyMessage], operator.add] + from_type: Annotated[str, replace_value] session_id: Annotated[str, replace_value] class OutputState(TypedDict): @@ -32,6 +41,7 @@ class OutputState(TypedDict): session_id: Annotated[str, replace_value] status: Annotated[str, replace_value] error: Annotated[str, replace_value] + agent_message: Annotated[str, replace_value] # 智能体回复 class NodeInfo(TypedDict): """工作流信息""" @@ -45,12 +55,17 @@ class NodeInfo(TypedDict): class ScriptwriterState(TypedDict, total=False): """智能编剧工作流整体状态""" # 输入数据 - input_data: Annotated[Dict[str, Any], operator.add] + input_data: Annotated[list[HumanMessage], operator.add] session_id: Annotated[str, replace_value] + from_type: Annotated[str, replace_value] # 本次请求来着哪里 [user, agent] # 节点间状态 - node_info: NodeInfo - + next_node: Annotated[str, replace_value] # 下一个节点 + workflow_step: Annotated[str, replace_value] # 阶段名称 [wait_for_input,script_analysis,strategic_planning,build_bible,episode_create_loop, finish] + workflow_status: Annotated[str, replace_value] # 当前阶段的状态 [waiting,running,failed,completed] + workflow_reason: Annotated[str, replace_value] # 失败原因 + workflow_retry_count: Annotated[int, replace_value] # 重试次数 + # 中间状态 agent_script_id: Annotated[str, replace_value] # 剧本ID 包括原文 agent_plan: Annotated[Dict[str, Any], replace_value] #剧本计划 @@ -58,6 +73,7 @@ class ScriptwriterState(TypedDict, total=False): episode_list: Annotated[List, replace_value] # 章节列表 完成状态、产出章节id # 输出数据 + agent_message: Annotated[str, replace_value] # 智能体回复 status: Annotated[str, replace_value] error: Annotated[str, replace_value] @@ -76,77 +92,105 @@ class ScriptwriterGraph: def __init__(self): """初始化工作流图""" self.graph = None + self.memory = MongoDBSaver(client, db_name=config.MONGO_CHECKPOINT_DB_NAME) self._build_graph() def node_router(self, state: ScriptwriterState) -> str: - next_node = state.get("node", '') - if next_node: - return next_node - else: - return END + """节点路由函数""" + print(f'node_router state {state}') + next_node = state.get("next_node", 'pause_node') + # 修复:当 next_node 为空字符串时,设置默认值 + if not next_node: + next_node = 'pause_node' # 设置为暂停节点 + print(f'node_router next_node {next_node}') + return next_node def _build_graph(self) -> None: """构建工作流图""" try: # 创建智能体 + print("创建智能体") # 调度智能体 - schedulerAgent = SchedulerAgent( + self.schedulerAgent = SchedulerAgent( tools=[], SchedulerList=[ { - "scheduler_node": "调度智能体节点", - "script_analysis_node": "原始剧本分析节点", - "strategic_planning_node": "确立改编目标节点", - "build_bible_node": "剧本圣经构建节点", - "episode_create_node": "单集创作节点", - "end_node": "结束节点,任务失败终止时使用,结束后整个工作流将停止" + "name": "scheduler_node", + "desc": "调度智能体节点", + }, + { + "name": "script_analysis_node", + "desc": "原始剧本分析节点", + }, + { + "name": "strategic_planning_node", + "desc": "确立改编目标节点", + }, + { + "name": "build_bible_node", + "desc": "剧本圣经构建节点", + }, + { + "name": "episode_create_node", + "desc": "单集创作节点", + }, + { + "name": "end_node", + "desc": "结束节点,任务失败终止时使用,结束后整个工作流将停止" } ] ) - scriptAnalysisAgent = ScriptAnalysisAgent( + self.scriptAnalysisAgent = ScriptAnalysisAgent( tools=[], SchedulerList=[ { - "scheduler_node": "调度智能体节点", + "name": "scheduler_node", + "desc": "调度智能体节点", } ] ) - strategicPlanningAgent = StrategicPlanningAgent( + self.strategicPlanningAgent = StrategicPlanningAgent( tools=[], SchedulerList=[ { - "scheduler_node": "调度智能体节点", + "name": "scheduler_node", + "desc": "调度智能体节点", } ] ) - buildBibleAgent = BuildBibleAgent( + self.buildBibleAgent = BuildBibleAgent( tools=[], SchedulerList=[ { - "scheduler_node": "调度智能体节点", + "name": "scheduler_node", + "desc": "调度智能体节点", } ] ) - episodeCreate = EpisodeCreateAgent( + self.episodeCreate = EpisodeCreateAgent( tools=[], SchedulerList=[ { - "scheduler_node": "调度智能体节点", + "name": "scheduler_node", + "desc": "调度智能体节点", } ] ) # 创建状态图 + logger.info("创建状态图") workflow = StateGraph(ScriptwriterState, input_schema=InputState, output_schema=OutputState) # 添加节点 + logger.info("添加节点") workflow.add_node("scheduler_node", self.scheduler_node) workflow.add_node("script_analysis_node", self.script_analysis_node) workflow.add_node("strategic_planning_node", self.strategic_planning_node) workflow.add_node("build_bible_node", self.build_bible_node) workflow.add_node("episode_create_node", self.episode_create_node) workflow.add_node("end_node", self.end_node) + workflow.add_node("pause_node", self.pause_node) # 添加边 workflow.set_entry_point("scheduler_node") @@ -167,13 +211,14 @@ class ScriptwriterGraph: "episode_create_node": "episode_create_node", # 用户确认和暂停逻辑在这里处理,不需要单独的边 "end_node": "end_node", + "pause_node": "pause_node", } ) workflow.add_edge("end_node", END) # 编译图 - self.graph = workflow.compile() + self.graph = workflow.compile(checkpointer=self.memory) logger.info("工作流图构建完成") except Exception as e: @@ -182,10 +227,44 @@ class ScriptwriterGraph: # --- 定义图中的节点 --- async def scheduler_node(self, state: ScriptwriterState)-> ScriptwriterState: - """第一步:初步沟通,请求剧本""" - session_id = state.get("session_id", "") - - return {} + """调度节点""" + try: + session_id = state.get("session_id", "") + from_type = state.get("from_type", "") + input_data = state.get("input_data", []) + logger.info(f"调度节点 {session_id} 输入参数: {input_data} from_type:{from_type}") + reslut = await self.schedulerAgent.ainvoke(state) + ai_message_str = reslut['messages'][-1].content + ai_message = json.loads(ai_message_str) + logger.info(f"调度节点结果: {ai_message}") + step:str = ai_message.get('step', '') + status:str = ai_message.get('status', '') + next_agent:str = ai_message.get('agent', '') + return_message:str = ai_message.get('message', '') + retry_count:int = int(ai_message.get('retry_count', '0')) + next_node:str = ai_message.get('node', 'pause_node') + if next_node == 'scheduler_node': + # 返回自身 代表暂停 + print(f"调度节点 暂停等待") + return { + "agent_message": return_message, + } + else: + return { + "next_node":next_node, + "workflow_step":step, + "workflow_status":status, + # "workflow_reason":return_message, + "workflow_retry_count":retry_count, + "agent_message":return_message, + } + except Exception as e: + return { + "next_node":'end_node', + "agent_message": "执行失败", + "error": str(e) or '系统错误,工作流已终止', + 'status':'failed', + } async def script_analysis_node(self, state: ScriptwriterState)-> ScriptwriterState: """第二步:诊断分析与资产评估""" @@ -218,20 +297,32 @@ class ScriptwriterGraph: episode_list = [] return {"episode_list": episode_list} + async def pause_node(self, state: ScriptwriterState)-> ScriptwriterState: + """ 暂停节点 处理并完成所有数据状态 """ + print(f"langgraph 暂停等待") + return { + "session_id": state.get("session_id", ""), + "status": state.get('status', ''), + "error": state.get('error', ''), + "agent_message": state.get('agent_message', '') + } + async def end_node(self, state: ScriptwriterState)-> OutputState: """ 结束节点 处理并完成所有数据状态 """ print(f"langgraph 所有任务完成") return { "session_id": state.get("session_id", ""), - "status": "", - "error": "", + "status": state.get('status', ''), + "error": state.get('error', ''), + "agent_message": state.get('agent_message', ''), } - async def run(self, input_data: Dict[str, Any]) -> OutputState: + async def run(self, session_id: str, input_data: list[AnyMessage], thread_id: str|None = None) -> OutputState: """运行工作流 - Args: + session_id: 会话ID input_data: 输入数据 + thread_id: 线程ID Returns: 工作流执行结果 @@ -239,44 +330,102 @@ class ScriptwriterGraph: try: logger.info("开始运行智能编剧工作流") - # # 初始化状态 - # initial_state: InputState = { - # 'input_data': input_data, - # 'session_id': input_data.get('session_id', ''), - # 'max_iterations': input_data.get('max_iterations', 3), - # 'batch_info': input_data.get('batch_info', {}) - # } + # 配置包含线程 ID + config:RunnableConfig = {"configurable": {"thread_id": thread_id}} + # 初始化状态 + initial_state: InputState = { + 'input_data': input_data, + 'session_id': session_id, + 'from_type': 'user', + } - # # 运行工作流 - # if self.graph is None: - # raise RuntimeError("工作流图未正确初始化") + # 运行工作流 + if self.graph is None: + raise RuntimeError("工作流图未正确初始化") - # result = await self.graph.ainvoke(initial_state) + result = await self.graph.ainvoke( + initial_state, + config, + # stream_mode='values' + ) # logger.info(f"工作流执行结果: {result}") - # if not result: - # raise ValueError("工作流执行结果为空") - # # 保存到记忆 - # self.memory.save_workflow_result(result) + if not result: + raise ValueError("工作流执行结果为空") - # # 构造输出状态 - # output_result: OutputState = { - # 'script': result.get('script'), - # 'adjustment': result.get('adjustment'), - # 'error': result.get('error'), - # 'iteration_count': result.get('iteration_count', 0) - # } - output_result:OutputState = { - 'session_id': "", - 'status': 'completed', - 'error': '', + # 构造输出状态 + output_result: OutputState = { + 'session_id': result.get('session_id', ''), + 'status': result.get('status', ''), + 'error': result.get('error', ''), + 'agent_message': result.get('agent_message', ''), } logger.info("智能编剧工作流运行完成") return output_result except Exception as e: logger.error(f"运行工作流失败: {e}") + import traceback + traceback.print_exc() raise + + async def get_checkpoint_history(self, thread_id: str): + """获取检查点历史""" + config:RunnableConfig = {"configurable": {"thread_id": thread_id}} + + try: + history_generator = self.memory.list(config, limit=10) + + print("正在获取检查点历史...") + + # 使用列表推导式或 for 循环来收集所有检查点 + history = list(history_generator) + + print(f"找到 {len(history)} 个检查点:") + + for i, checkpoint_tuple in enumerate(history): + # checkpoint_tuple 包含 config, checkpoint, metadata 等属性 + # print(f" - ID: {checkpoint_tuple}") + checkpoint_data = checkpoint_tuple.checkpoint + metadata = checkpoint_tuple.metadata + print(f"检查点 {i+1}:") + print(f" - ID: {checkpoint_data.get('id', 'N/A')}") + print(f" - 状态: {checkpoint_data.get('channel_values', {})}") + print(f" - 元数据: {metadata}") + print("-" * 50) + + except Exception as e: + print(f"获取历史记录时出错: {e}") + + def resume_from_checkpoint(self, thread_id: str, checkpoint_id: str): + """从检查点恢复执行""" + config:RunnableConfig = {"configurable": {"thread_id": thread_id}} + + if checkpoint_id: + config["configurable"]["checkpoint_id"] = checkpoint_id + + try: + # 获取 CheckpointTuple 对象 + checkpoint_tuple = self.memory.get_tuple(config) + + if checkpoint_tuple: + # 直接通过属性访问,而不是解包 + checkpoint_data = checkpoint_tuple.checkpoint + metadata = checkpoint_tuple.metadata + print(f"从检查点恢复:") + print(f" - 检查点 ID: {checkpoint_data.get('id', 'N/A')}") + print(f" - 状态: {checkpoint_data.get('channel_values', {})}") + print(f" - 元数据: {metadata}") + return checkpoint_data.get('channel_values', {}) + else: + print(f"未找到线程 {thread_id} 的检查点") + return None + + except Exception as e: + print(f"恢复检查点时出错: {e}") + return None + + def get_graph_visualization(self) -> str: """获取工作流图的可视化表示 @@ -285,8 +434,31 @@ class ScriptwriterGraph: """ try: if self.graph: - return str(self.graph) + with open('graph_visualization.png', 'wb') as f: + f.write(self.graph.get_graph().draw_mermaid_png()) + print("图片已保存为 graph_visualization.png") return "工作流图未初始化" except Exception as e: logger.error(f"获取图可视化失败: {e}") - return f"获取图可视化失败: {e}" \ No newline at end of file + return f"获取图可视化失败: {e}" + + +if __name__ == "__main__": + import asyncio + + async def main(): + print("测试") + graph = ScriptwriterGraph() + print("创建完成") + # graph.get_graph_visualization() + # print("可视化完成") + # 运行工作流 + session_id = "68c2c2915e5746343301ef71" + result = await graph.run( + session_id, + [HumanMessage(content="你好编剧,我想写小说!")], + session_id + ) + print(f"最终结果: {result}") + + asyncio.run(main()) diff --git a/tools/database/mongodb_memory.py b/tools/database/mongodb_memory.py deleted file mode 100644 index 362f929..0000000 --- a/tools/database/mongodb_memory.py +++ /dev/null @@ -1,30 +0,0 @@ -"""工作流记忆管理模块 - -该模块负责管理智能编剧系统工作流的记忆存储和检索。 -""" - -import sys -import os -from typing import Dict, Any, List, Optional -from datetime import datetime -import json -from database import client # type: ignore -from langgraph.checkpoint.mongodb import MongoDBSaver - -# 添加项目根目录到路径 -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))) -from agentgraph.utils.logger import get_logger - -logger = get_logger(__name__) - -DB_NAME = "langgraph_memory_db" - -class WorkflowMemory: - """工作流记忆管理类 - - 负责管理工作流执行过程中的状态存储、检索和历史记录。 - """ - - def __init__(self): - """初始化工作流记忆管理器""" - self.memory = MongoDBSaver(client, db_name=DB_NAME)