diff --git a/backend/app/main_graph/nodes/finalize.py b/backend/app/main_graph/nodes/finalize.py new file mode 100644 index 0000000..b342f71 --- /dev/null +++ b/backend/app/main_graph/nodes/finalize.py @@ -0,0 +1,54 @@ +""" +Finalize 节点 - 轻量后处理 +""" + +from typing import Dict, Any, Optional +from langchain_core.runnables.config import RunnableConfig +from langchain_core.messages import AIMessage + +from backend.app.main_graph.state import AgentState +from backend.app.logger import info + + +async def finalize_node(state: AgentState, config: Optional[RunnableConfig] = None) -> Dict[str, Any]: + """ + Finalize 节点:轻量后处理 + + 职责: + 1. 从 messages 中提取最后一条 AIMessage.content 作为最终回复 + 2. 汇总元数据:步数、使用的工具、停止原因 + 3. 如果 final_reply 为空且有 stop_reason,生成说明文本 + """ + info("[Finalize] 执行后处理") + + # 获取最终回复 + final_reply = "" + for msg in reversed(state.messages): + if isinstance(msg, AIMessage) and msg.content: + final_reply = msg.content + break + + # 获取停止原因 + stop_reason = getattr(state, "stop_reason", "") + if not final_reply and stop_reason: + # 如果没有回复但有停止原因,生成说明 + reason_messages = { + "loop_detected": "[系统] 检测到工具调用循环,已终止。", + "max_steps": "[系统] 已达到最大步数限制。", + } + final_reply = reason_messages.get(stop_reason, f"[系统] 已终止,原因: {stop_reason}") + + # 汇总元数据 + metadata = { + "steps_taken": getattr(state, "current_step", 0), + "tools_used": getattr(state, "tools_used", []), + "stop_reason": stop_reason, + "llm_calls": getattr(state, "llm_calls", 0), + } + + info(f"[Finalize] 完成 - steps: {metadata['steps_taken']}, tools: {len(metadata['tools_used'])}") + + return { + "final_reply": final_reply, + "metadata": metadata, + }