Langgraph框架智能体实战开发

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

1. LangGraph 是什么

在这里插入图片描述
一句话解释
LangGraph 就是用来编排 AI 工作流的框架,让你可以把复杂的 AI 任务拆成一个个小步骤,然后像搭积木一样把它们连起来。

  • 为什么需要它?

假设你要做一个客服机器人:

  1. 先要判断用户问的是技术问题还是销售问题
  2. 技术问题交给技术 Agent 处理
  3. 销售问题交给销售 Agent 处理
  4. 如果答案质量不够好,还得循环改进

这种有分支、有循环、有状态的复杂流程,用普通代码写起来会很乱。LangGraph 就是专门解决这个问题的。

LangGraph vs LangChain
LangChain 是做零件的,LangGraph 是用这些零件搭建复杂系统的。

在这里插入图片描述

2. 环境准备

可参考上一篇:https://blog.csdn.net/Tracycoder/article/details/156943714?spm=1011.2415.3001.10575&sharefrom=mp_manage_link

3. 核心概念

LangGraph 就三个核心概念,搞懂了这三个,其他都是组合拳。

在这里插入图片描述

State(状态)

State 就是一个在所有节点间共享的数据结构。你可以把它想象成一个"快递包裹",每经过一个节点,就往里面加点东西或者修改点东西。

from typing_extensions import TypedDict

class MyState(TypedDict):
    count: int           # 计数器
    history: list[str]   # 历史记录

Node(节点)

Node 就是干活的。每个节点是一个 Python 函数,接收当前状态,返回需要更新的字段。

def my_node(state: MyState):
    # 读取当前状态
    current_count = state["count"]
    
    # 返回要更新的字段(不用返回所有字段!)
    return {
        "count": current_count + 1,
        "history": state["history"] + ["执行了一次"]
    }

Edge(边)

Edge 定义节点之间怎么连接,决定执行顺序。

# 普通边:A 执行完必定执行 B
workflow.add_edge("A", "B")

# 条件边:根据条件决定下一步
workflow.add_conditional_edges(
    "A",
    决策函数,
    {"条件1": "B", "条件2": "C"}
)

4. 第一个程序

让我们写一个最简单的计数器程序,理解状态是怎么流转的。

在这里插入图片描述

  • 完整代码
from langgraph.graph import StateGraph, START, END
from typing_extensions import TypedDict

# 1. 定义状态结构
class CounterState(TypedDict):
    count: int
    history: list[str]

# 2. 定义节点函数
def increment_node(state: CounterState):
    """计数器 +1"""
    new_count = state["count"] + 1
    message = f"count: {state['count']}{new_count}"
    
    return {
        "count": new_count,
        "history": state["history"] + [message]
    }

def double_node(state: CounterState):
    """计数器翻倍"""
    new_count = state["count"] * 2
    message = f"count: {state['count']}{new_count}"
    
    return {
        "count": new_count,
        "history": state["history"] + [message]
    }

def report_node(state: CounterState):
    """报告结果"""
    return {"history": state["history"] + [f"最终结果: {state['count']}"]}

# 3. 创建图
workflow = StateGraph(CounterState)

# 4. 添加节点
workflow.add_node("increment", increment_node)
workflow.add_node("double", double_node)
workflow.add_node("report", report_node)

# 5. 连接节点
workflow.add_edge(START, "increment")
workflow.add_edge("increment", "double")
workflow.add_edge("double", "report")
workflow.add_edge("report", END)

# 6. 编译
app = workflow.compile()

# 7. 运行
result = app.invoke({
    "count": 5,
    "history": ["开始执行"]
})

print("执行历史:")
for step in result["history"]:
    print(f"  - {step}")
print(f"\n最终计数: {result['count']}")
  • 运行结果
执行历史:
  - 开始执行
  - count: 56
  - count: 612
  - 最终结果: 12

最终计数: 12
  • 关键点
  1. 只返回需要更新的字段:节点不用返回完整的 state,只返回变化的部分
  2. 状态自动传递:每个节点执行完,更新后的状态自动传给下一个节点
  3. 必须编译:调用 compile() 后才能运行

5. Reducer 机制

为什么有时候状态会被覆盖,有时候会累加?
答案就是 Reducer。
在这里插入图片描述

  • 问题:状态被覆盖
    默认情况下,每个节点返回的值会覆盖之前的值:
class State(TypedDict):
    score: int  # 普通定义
# Node1 返回 {"score": 10}  → state["score"] = 10
# Node2 返回 {"score": 20}  → state["score"] = 20 (覆盖了!)
# 最终 score = 20
  • 解决方案:使用 Reducer
from typing import Annotated
import operator

class State(TypedDict):
    score: Annotated[int, operator.add]  # 使用 operator.add 作为 reducer

# Node1 返回 {"score": 10}  → state["score"] = 0 + 10 = 10
# Node2 返回 {"score": 20}  → state["score"] = 10 + 20 = 30
# 最终 score = 30 (累加!)
  • 实际例子:游戏得分
from langgraph.graph import StateGraph, START, END
from typing_extensions import TypedDict
from typing import Annotated
import operator

class ScoreState(TypedDict):
    player_name: str  # 普通字段,会被覆盖
    score: Annotated[int, operator.add]  # 累加
    actions: Annotated[list[str], operator.add]  # 列表也能累加

def level1_node(state):
    return {"score": 10, "actions": ["通关第一关"]}

def level2_node(state):
    return {"score": 20, "actions": ["通关第二关"]}

def level3_node(state):
    return {"score": 30, "actions": ["通关第三关"]}

# 构建图...
workflow = StateGraph(ScoreState)
workflow.add_node("level1", level1_node)
workflow.add_node("level2", level2_node)
workflow.add_node("level3", level3_node)
workflow.add_edge(START, "level1")
workflow.add_edge("level1", "level2")
workflow.add_edge("level2", "level3")
workflow.add_edge("level3", END)

app = workflow.compile()
result = app.invoke({"player_name": "小明", "score": 0, "actions": []})

print(f"玩家: {result['player_name']}")
print(f"总分: {result['score']}")  # 10+20+30=60
print(f"完成动作: {result['actions']}")
  • 常用 Reducer

在这里插入图片描述

6. 条件分支

在这里插入图片描述

  • 关键函数:add_conditional_edges
workflow.add_conditional_edges(
    "source_node",    # 从哪个节点出发
    routing_function, # 用什么函数决定路由
    {                 # 路由映射表
        "result1": "target_node1",
        "result2": "target_node2",
    }
)
  • 实际例子:智能客服路由
from langgraph.graph import StateGraph, START, END
from typing_extensions import TypedDict

class CustomerState(TypedDict):
    question: str
    category: str
    answer: str

# 分类节点
def classify_question(state: CustomerState):
    question = state["question"].lower()
    
    if "退款" in question or "退货" in question:
        category = "refund"
    elif "物流" in question or "配送" in question:
        category = "shipping"
    elif "产品" in question or "使用" in question:
        category = "product"
    else:
        category = "general"
    
    return {"category": category}

# 处理节点
def handle_refund(state):
    return {"answer": "退款专员:请提供订单号,3个工作日内处理。"}

def handle_shipping(state):
    return {"answer": "物流客服:您的包裹正在配送中。"}

def handle_product(state):
    return {"answer": "产品顾问:请查看说明书第3页。"}

def handle_general(state):
    return {"answer": "客服:感谢咨询,还有其他问题吗?"}

# 路由函数
def route_question(state: CustomerState) -> str:
    return state["category"]

# 构建图
workflow = StateGraph(CustomerState)

workflow.add_node("classify", classify_question)
workflow.add_node("refund", handle_refund)
workflow.add_node("shipping", handle_shipping)
workflow.add_node("product", handle_product)
workflow.add_node("general", handle_general)

workflow.add_edge(START, "classify")

# 关键:条件路由
workflow.add_conditional_edges(
    "classify",
    route_question,
    {
        "refund": "refund",
        "shipping": "shipping",
        "product": "product",
        "general": "general"
    }
)

# 所有分支都到 END
workflow.add_edge("refund", END)
workflow.add_edge("shipping", END)
workflow.add_edge("product", END)
workflow.add_edge("general", END)

app = workflow.compile()

# 测试
test_questions = [
    "我想申请退款",
    "我的快递到哪了?",
    "这个产品怎么使用?",
]

for q in test_questions:
    result = app.invoke({"question": q, "category": "", "answer": ""})
    print(f"问题: {q}")
    print(f"回答: {result['answer']}\n")

7. 循环流程

有时候一次处理不够好,需要反复迭代优化。LangGraph 天然支持循环。

在这里插入图片描述

  • 关键点
  1. 条件边可以指回前面的节点:形成循环
  2. 必须有退出条件:否则会无限循环
  3. 建议设置最大迭代次数:兜底保护
  • 实际例子:答案优化器
from langgraph.graph import StateGraph, START, END
from typing_extensions import TypedDict
from typing import Annotated
import operator

class ImprovementState(TypedDict):
    question: str
    answer: str
    quality_score: float
    iteration: Annotated[int, operator.add]
    max_iterations: int

def generate_answer(state: ImprovementState):
    """生成或改进答案"""
    iteration = state.get("iteration", 0)
    
    # 模拟答案逐步改进
    if iteration == 0:
        answer = "简单回答:这是一个基础答案。"
        score = 0.5
    elif iteration == 1:
        answer = "改进回答:这是一个更详细的答案,包含更多信息。"
        score = 0.7
    else:
        answer = "完善回答:经过仔细思考的完整答案,包含背景和建议。"
        score = 0.9
    
    print(f"第{iteration + 1}次生成 (质量: {score})")
    
    return {
        "answer": answer,
        "quality_score": score,
        "iteration": 1  # 每次+1(因为有 reducer)
    }

def check_quality(state):
    """检查质量,不更新状态"""
    return state

def should_continue(state: ImprovementState) -> str:
    """决定是继续还是结束"""
    if state["quality_score"] >= 0.8:
        return "done"
    elif state["iteration"] >= state["max_iterations"]:
        return "done"
    else:
        return "improve"

# 构建带循环的图
workflow = StateGraph(ImprovementState)

workflow.add_node("generate", generate_answer)
workflow.add_node("check", check_quality)

workflow.add_edge(START, "generate")
workflow.add_edge("generate", "check")

# 关键:条件边可以循环回去
workflow.add_conditional_edges(
    "check",
    should_continue,
    {
        "improve": "generate",  # 循环
        "done": END             # 结束
    }
)

app = workflow.compile()

result = app.invoke({
    "question": "什么是人工智能?",
    "answer": "",
    "quality_score": 0.0,
    "iteration": 0,
    "max_iterations": 5
})

print(f"\n最终答案: {result['answer']}")
print(f"最终质量: {result['quality_score']}")
print(f"迭代次数: {result['iteration']}")

8. 消息历史管理

做聊天机器人最头疼的是消息历史管理。LangGraph 提供了专门的工具来解决这个问题。
核心:MessagesState 和 add_messages

from langgraph.graph import MessagesState
from langgraph.graph.message import add_messages
from langchain_core.messages import HumanMessage, AIMessage

# 方式1:继承 MessagesState
class ChatState(MessagesState):
    user_name: str
    conversation_count: int

# 方式2:手动定义
class ChatState(TypedDict):
    messages: Annotated[list[BaseMessage], add_messages]
    user_name: str

add_messages 是一个智能 Reducer,它会:

  • 自动追加新消息
  • 处理消息 ID 去重
  • 保持消息顺序

聊天机器人代码

import os
from langgraph.graph import StateGraph, START, END, MessagesState
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
from langchain_community.llms import Tongyi

os.environ["DASHSCOPE_API_KEY"] = "your-key"

llm = Tongyi(model="qwen-plus", temperature=0.7)

class ChatState(MessagesState):
    user_name: str
    conversation_count: int

def chatbot_node(state: ChatState):
    """聊天节点"""
    # 构建系统提示
    system_prompt = f"""你是一个友好的AI助手。
用户名: {state.get('user_name', '用户')}
当前是第 {state.get('conversation_count', 0)} 轮对话。"""
    
    messages = [SystemMessage(content=system_prompt)] + state["messages"]
    response = llm.invoke(messages)
    
    return {
        "messages": [AIMessage(content=response)],
        "conversation_count": state.get("conversation_count", 0) + 1
    }

# 构建图
workflow = StateGraph(ChatState)
workflow.add_node("chatbot", chatbot_node)
workflow.add_edge(START, "chatbot")
workflow.add_edge("chatbot", END)
app = workflow.compile()

# 多轮对话
state = {"messages": [], "user_name": "小明", "conversation_count": 0}

conversations = [
    "你好,我是小明",
    "我刚才说我叫什么?",
    "帮我推荐一本Python书"
]

for user_input in conversations:
    print(f"用户: {user_input}")
    state["messages"].append(HumanMessage(content=user_input))
    result = app.invoke(state)
    state = result
    print(f"助手: {result['messages'][-1].content}\n")

9. ReAct Agent

ReAct = Reasoning + Acting,是目前最流行的 Agent 架构。

在这里插入图片描述

工作流程

  1. Thought:AI 思考需要做什么
  2. Action:选择并调用工具
  3. Observation:观察工具返回结果
  4. 重复:直到任务完成

使用预置组件

LangGraph 提供了现成的组件:

from langgraph.prebuilt import create_react_agent, ToolNode
from langchain_community.chat_models import ChatTongyi
from langchain_core.tools import tool
from langchain_core.messages import SystemMessage

@tool
def add(a: float, b: float) -> float:
    """两数相加"""
    return a + b

@tool
def multiply(a: float, b: float) -> float:
    """两数相乘"""
    return a * b

# 创建模型
llm = ChatTongyi(model="qwen-plus", temperature=0.7)

# 创建 ReAct Agent
agent = create_react_agent(
    model=llm,
    tools=[add, multiply],
    messages_modifier=SystemMessage(content="你是一个数学计算助手。")
)

# 使用
result = agent.invoke({
    "messages": [{"role": "user", "content": "计算 (10 + 5) × 3"}]
})
print(result["messages"][-1].content)

手动实现 ReAct Agent

如果需要完全控制,可以手动实现:

from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode
from typing import Annotated, Literal
from typing_extensions import TypedDict

class AgentState(TypedDict):
    messages: Annotated[list, add_messages]
    iterations: Annotated[int, operator.add]

# 工具定义...
tools = [python_executor, text_analyzer]
tool_node = ToolNode(tools)

# 模型绑定工具
llm_with_tools = llm.bind_tools(tools)

def call_model(state: AgentState):
    """调用模型决策"""
    messages = state["messages"]
    response = llm_with_tools.invoke(messages)
    return {"messages": [response], "iterations": 1}

def should_continue(state) -> Literal["tools", "__end__"]:
    """判断是否需要调用工具"""
    last_message = state["messages"][-1]
    if hasattr(last_message, "tool_calls") and last_message.tool_calls:
        return "tools"
    return "__end__"

# 构建图
workflow = StateGraph(AgentState)
workflow.add_node("agent", call_model)
workflow.add_node("tools", tool_node)

workflow.add_edge(START, "agent")
workflow.add_conditional_edges("agent", should_continue, {
    "tools": "tools",
    "__end__": END
})
workflow.add_edge("tools", "agent")

app = workflow.compile()

10. 多Agent协作

当任务复杂时,一个 Agent 搞不定,需要多个专业 Agent 配合。

Supervisor 模式

在这里插入图片描述
核心思想:一个 Supervisor 负责调度,多个专业 Agent 负责执行。

  • 代码示例:
from langgraph.graph import StateGraph, START, END
from langchain_core.messages import HumanMessage, AIMessage, BaseMessage
from langchain_core.tools import tool
from typing import Annotated, Literal, Sequence
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages

# 1. 定义状态
class SupervisorState(TypedDict):
    messages: Annotated[Sequence[BaseMessage], add_messages]
    next: str  # 下一个执行的 Agent

# 2. 定义各种工具
@tool
def check_system_status(component: str) -> str:
    """检查系统组件状态"""
    status = {"database": "正常", "server": "负载高", "api": "正常"}
    return status.get(component, "未知")

@tool
def get_product_info(product_name: str) -> str:
    """获取产品信息"""
    products = {
        "基础版": "99元/月,基础功能",
        "专业版": "299元/月,高级功能",
    }
    return products.get(product_name, "产品不存在")

@tool
def query_invoice(order_id: str) -> str:
    """查询发票"""
    invoices = {"ORD001": "已开具", "ORD002": "处理中"}
    return invoices.get(order_id, "订单不存在")

# 3. 定义专业 Agent 节点
def tech_agent_node(state):
    """技术支持 Agent"""
    last_message = state["messages"][-1].content
    
    if "系统" in last_message or "服务器" in last_message:
        result = check_system_status.invoke({"component": "server"})
        response = f"【技术支持】系统状态: {result}"
    else:
        response = "【技术支持】请描述您遇到的技术问题。"
    
    return {"messages": [AIMessage(content=response)]}

def sales_agent_node(state):
    """销售 Agent"""
    last_message = state["messages"][-1].content
    
    for product in ["基础版", "专业版"]:
        if product in last_message:
            info = get_product_info.invoke({"product_name": product})
            return {"messages": [AIMessage(content=f"【销售顾问】{info}")]}
    
    return {"messages": [AIMessage(content="【销售顾问】我们有基础版和专业版,需要了解哪个?")]}

def billing_agent_node(state):
    """账单 Agent"""
    return {"messages": [AIMessage(content="【账单专员】请提供订单号,我来查询。")]}

# 4. Supervisor 节点
def supervisor_node(state):
    """Supervisor:决定下一步"""
    last_message = state["messages"][-1].content
    
    tech_keywords = ["错误", "bug", "崩溃", "系统", "服务器"]
    sales_keywords = ["价格", "购买", "产品", "套餐"]
    billing_keywords = ["发票", "支付", "退款", "账单"]
    
    if any(kw in last_message for kw in tech_keywords):
        next_agent = "tech_support"
    elif any(kw in last_message for kw in sales_keywords):
        next_agent = "sales"
    elif any(kw in last_message for kw in billing_keywords):
        next_agent = "billing"
    else:
        next_agent = "sales"  # 默认销售
    
    return {"next": next_agent}

# 5. 路由函数
def route_after_supervisor(state) -> Literal["tech_support", "sales", "billing"]:
    return state["next"]

# 6. 构建图
workflow = StateGraph(SupervisorState)

workflow.add_node("supervisor", supervisor_node)
workflow.add_node("tech_support", tech_agent_node)
workflow.add_node("sales", sales_agent_node)
workflow.add_node("billing", billing_agent_node)

workflow.add_edge(START, "supervisor")
workflow.add_conditional_edges(
    "supervisor",
    route_after_supervisor,
    {
        "tech_support": "tech_support",
        "sales": "sales",
        "billing": "billing",
    }
)
workflow.add_edge("tech_support", END)
workflow.add_edge("sales", END)
workflow.add_edge("billing", END)

app = workflow.compile()

# 7. 测试
test_cases = [
    "我遇到500错误,系统无法访问",
    "专业版多少钱?",
    "我需要查发票",
]

for query in test_cases:
    print(f"用户: {query}")
    result = app.invoke({"messages": [HumanMessage(content=query)], "next": ""})
    for msg in result["messages"]:
        if isinstance(msg, AIMessage):
            print(msg.content)
    print()

11. 一些注意事项

持久化和记忆

使用 checkpointer 保存对话状态:

from langgraph.checkpoint.memory import MemorySaver

memory = MemorySaver()
app = workflow.compile(checkpointer=memory)

# 使用 thread_id 管理不同会话
config = {"configurable": {"thread_id": "user_123"}}
result = app.invoke(state, config=config)

流式输出

实现打字机效果:
# 方式1:使用 streaming=True 的模型
llm = Tongyi(model="qwen-plus", streaming=True)

for chunk in llm.stream(messages):
    print(chunk, end="", flush=True)

# 方式2:使用 app.stream()
for event in app.stream(initial_state):
    print(event)

子图(Subgraph)

复杂系统可以拆成多个子图:
# 定义子图
sub_workflow = StateGraph(SubState)
# ... 添加节点和边
sub_graph = sub_workflow.compile()

# 在主图中使用子图作为节点
main_workflow.add_node("sub_process", sub_graph)

Human-in-the-Loop

让人类参与决策:
# 设置断点
app = workflow.compile(
    checkpointer=memory,
    interrupt_before=["sensitive_action"]
)

# 运行到断点会暂停
result = app.invoke(state, config)

# 人工审核后继续
app.invoke(None, config)  # 继续执行

调试技巧

# 可视化图结构
print(app.get_graph().draw_mermaid())

# 打印执行过程
for event in app.stream(state, stream_mode="debug"):
    print(event)

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TracyCoder123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值