重构 Agent:引入 LangGraph StateGraph 替代手写 FC 循环

架构变更:
- 新增 app/services/agent/ 模块(state/prompts/graph/tools)
- 7 个工具从 _tool_xxx 方法提取为 @tool 装饰器函数
- 构建 assistant + ToolNode 的 ReAct 图
- agent_dispatcher.py 改为薄壳入口,支持 USE_LANGGRAPH 开关
- MemorySaver checkpoint 持久化对话(thread_id=wechat-{user_id})
- 新增依赖:langchain-core, langchain-openai, langgraph

向后兼容:
- USE_LANGGRAPH=false 可切回旧版 FC 循环
- LangGraph 初始化失败自动降级到 Legacy 模式
- 企微图片处理/VLM分析逻辑不变
This commit is contained in:
2026-03-25 13:52:55 +08:00
parent d1aa14bb23
commit 8156f54004
11 changed files with 809 additions and 668 deletions

View File

@@ -0,0 +1,70 @@
"""
LangGraph StateGraph 构建
构建 assistant + ToolNode 的 ReAct 图,接入 Dashscope Qwen。
"""
from typing import Optional
from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import ToolNode, tools_condition
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI
from app.config import settings
from app.utils.logger import logger
from .state import AgentState
from .tools import all_tools
from .prompts import SYSTEM_PROMPT
def _create_llm():
"""创建 LLM 客户端(通过 Dashscope OpenAI 兼容接口对接 Qwen"""
return ChatOpenAI(
model=settings.agent.model,
base_url=settings.agent.vlm_base_url,
api_key=settings.agent.vlm_api_key,
timeout=settings.agent.timeout,
)
def build_agent_graph(checkpointer=None):
"""构建并编译 Agent 图
Args:
checkpointer: LangGraph checkpointer 实例None=不持久化)
Returns:
编译后的 CompiledGraph
"""
llm = _create_llm()
llm_with_tools = llm.bind_tools(all_tools)
def assistant(state: AgentState):
"""LLM 推理节点:接收消息 + 系统提示,返回回复或工具调用"""
system_msg = {"role": "system", "content": SYSTEM_PROMPT}
response = llm_with_tools.invoke([system_msg] + state["messages"])
return {"messages": [response]}
# 构建图
builder = StateGraph(AgentState)
# 两个核心节点
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(all_tools))
# 边START → assistant → (条件) → tools 或 END
builder.add_edge(START, "assistant")
builder.add_conditional_edges("assistant", tools_condition)
builder.add_edge("tools", "assistant")
# 编译
graph = builder.compile(checkpointer=checkpointer)
logger.info("LangGraph Agent 图已编译")
return graph
def create_default_graph():
"""创建带内存 checkpoint 的默认图(开发用)"""
checkpointer = MemorySaver()
return build_agent_graph(checkpointer=checkpointer)