第一部分:专门定义状态(类似于 state.py)
在这里,我们只关心数据结构长什么样,完全不引入任何大模型的代码。Python
from langgraph.graph import MessagesState
# 1. 定义状态类
# 继承 MessagesState 以获取历史对话功能,同时增加自定义字段
class AgentState(MessagesState):
user_intent: str # 记录用户意图
extracted_params: dict # 记录从对话中提取的结构化参数
retry_count: int # 记录发生错误的重试次数
第二部分:专门定义节点逻辑(类似于 nodes.py)
这里是纯粹的业务函数。它只需要声明自己接收 AgentState 作为参数,然后返回需要更新的字典即可。它不需要知道图(Graph)是怎么流转的。
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-3.5-turbo")
# 2. 定义独立的节点函数
def analyze_intent_node(state: AgentState):
"""专门负责分析意图的节点"""
messages = state["messages"]
# 这里可以有非常复杂的 LLM 调用、Prompt 拼接逻辑
# 为了演示,我们直接写死一个返回值
# 实际应用中,这里可能是 llm.with_structured_output().invoke(...)
intent = "query_weather"
# 只返回需要更新的状态字段
return {"user_intent": intent}
def generate_reply_node(state: AgentState):
"""专门负责生成最终回复的节点"""
intent = state.get("user_intent", "unknown")
# 结合当前状态中的自定义字段去调用 LLM
response = llm.invoke([
{"role": "system", "content": f"用户的意图是: {intent},请回复。"},
*state["messages"]
])
return {"messages": [response]}
第三部分:组装智能体图(类似于 graph.py)
在这里,我们将前面定义好的"状态类"和"节点函数"像拼图一样拼起来。
from langgraph.graph import StateGraph, START, END
# 3. 组装图
# 传入状态类
workflow = StateGraph(AgentState)
# 添加节点函数
workflow.add_node("intent_analyzer", analyze_intent_node)
workflow.add_node("reply_generator", generate_reply_node)
# 定义流转逻辑
workflow.add_edge(START, "intent_analyzer")
workflow.add_edge("intent_analyzer", "reply_generator")
workflow.add_edge("reply_generator", END)
# 编译运行
app = workflow.compile()
为什么要这样分开设计?
-
代码极度清晰 :如果您的智能体有 10 个节点,把逻辑全写在一起会变成一场灾难。分开后,您只需去
state.py看数据结构,去nodes.py修改 LLM 的提示词和解析逻辑。 -
方便独立测试 :因为节点就是普通的 Python 函数,您可以在不运行整个图的情况下,单独给
analyze_intent_node传入一个假造的AgentState字典进行单元测试。 -
团队协作:一个人可以负责设计状态数据结构,另一个人去专心调优 LLM 节点的 Prompt,互不干扰。