在实际 AI 应用开发中,单个大语言模型(LLM)往往难以独立完成复杂任务。真正的挑战在于如何将多个 LLM、工具调用、状态管理和条件判断串联成一个稳定、可控的智能工作流。LangGraph 正是为解决这类问题而生的 AI Agent 编排框架,它基于有向图结构,让开发者能够以代码方式定义和管理复杂的多步骤推理流程。

本文将以零基础视角,从 LangGraph 的核心概念入手,逐步搭建一个可运行的智能体(Agent),并深入讲解其状态管理、节点编排、条件路由等关键机制。无论你是希望了解 AI Agent 开发基础,还是需要在生产环境中部署可靠的多步推理系统,都能通过本文获得可复现的实践路径。

1. LangGraph 核心概念:为什么需要图编排

在传统 LangChain 中,Chain 提供了线性的任务串联能力,但对于需要循环、分支或动态调整流程的场景,Chain 的表达能力就显得不足。例如,一个客服 Agent 可能需要先理解用户意图,然后根据意图决定调用知识库检索、转人工或直接回答,甚至在某些条件下需要多次追问用户以澄清需求。这类场景正是 LangGraph 的设计目标。

LangGraph 将工作流抽象为有向图(Directed Graph),图中包含两种核心元素:节点(Node)和边(Edge)。节点代表一个执行单元(如调用 LLM、运行工具),边定义了节点之间的流转条件。通过这种模型,可以直观地描述多步、循环、条件判断等复杂逻辑。

与 LangChain 的主要区别在于:

  • LangChain 更适合线性、确定性的管道(Pipeline),例如文档加载 -> 分割 -> 向量化 -> 检索 -> 生成回答。
  • LangGraph 专为非线性、有状态、多代理(Multi-Agent)协作的工作流设计,例如模拟辩论、游戏对战、复杂决策等场景。

下面通过一个最小示例快速建立直观理解。假设我们要构建一个简易的对话 Agent,它根据用户输入决定直接回答或调用搜索工具:

from langgraph.graph import Graph
from langchain_core.messages import HumanMessage, AIMessage

# 定义两个节点:一个负责生成回答,一个负责调用搜索
def generate_response(state):
    return {"messages": [AIMessage(content="这是直接回答的内容")]}

def search_tool(state):
    return {"messages": [AIMessage(content="这是搜索后的结果")]}

# 创建图结构
graph = Graph()
graph.add_node("generate", generate_response)
graph.add_node("search", search_tool)
graph.set_entry_point("generate")

# 定义条件路由:如果用户输入包含"搜索"则跳转到搜索节点
def route_condition(state):
    last_message = state["messages"][-1]
    if "搜索" in last_message.content:
        return "search"
    return "generate"

graph.add_conditional_edges("generate", route_condition, {"search": "search", "generate": "generate"})
graph.add_edge("search", "generate")
graph.set_finish_point("generate")

# 编译并运行
app = graph.compile()
result = app.invoke({"messages": [HumanMessage(content="我想搜索天气预报")]})

这个示例虽然简单,但已经包含了图编排的核心要素。接下来我们将从环境准备开始,逐步构建一个功能完整的 AI Agent。

2. 环境准备与依赖配置

LangGraph 目前主要支持 Python 环境。建议使用 Python 3.8 或更高版本,以避免兼容性问题。

2.1 创建虚拟环境

使用虚拟环境可以隔离项目依赖,避免版本冲突:

# 创建项目目录
mkdir langgraph-agent-tutorial
cd langgraph-agent-tutorial

# 创建虚拟环境(可选,但强烈推荐)
python -m venv venv

# 激活虚拟环境
# Windows:
venv\Scripts\activate
# Linux/Mac:
source venv/bin/activate

2.2 安装核心依赖

LangGraph 的安装包根据功能需求有所区分。基础版包含图编排核心功能,完整版则额外集成 LangChain 生态工具:

# 安装 LangGraph 基础版
pip install langgraph

# 如果需要使用 LangChain 工具集,安装完整版
pip install "langgraph[all]"

# 常用辅助库
pip install python-dotenv  # 环境变量管理

2.3 配置大模型访问权限

LangGraph 本身不提供 LLM,需要接入第三方模型服务。以下以 OpenAI 和 Ollama(本地部署)为例:

OpenAI API 配置

  1. 获取 API Key:访问 OpenAI Platform 创建 API Key。
  2. 在项目根目录创建 .env 文件:
# .env 文件内容
OPENAI_API_KEY=你的实际API密钥
  1. 在代码中加载配置:
from dotenv import load_dotenv
import os

load_dotenv()
openai_api_key = os.getenv("OPENAI_API_KEY")

Ollama 本地模型配置

如果你希望使用本地部署的模型,可以安装 Ollama:

# 安装 Ollama(参考 https://ollama.ai/)
curl -fsSL https://ollama.ai/install.sh | sh

# 拉取常用模型(如 Llama 3.1)
ollama pull llama3.1

# 验证模型运行
ollama run llama3.1

2.4 验证环境

创建一个简单的验证脚本,确保所有组件正常工作:

# test_environment.py
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
import os

load_dotenv()

# 测试 OpenAI 连接
llm = ChatOpenAI(model="gpt-3.5-turbo", api_key=os.getenv("OPENAI_API_KEY"))
response = llm.invoke("请用一句话回答:LangGraph 的主要作用是什么?")
print("OpenAI 连接测试:", response.content)

# 测试 LangGraph 导入
from langgraph.graph import Graph
print("LangGraph 导入成功")

运行此脚本应能正常输出模型回答和导入成功信息。如果遇到 API 连接错误,请检查网络环境、API Key 配置和余额状态。

3. 构建第一个 LangGraph Agent:对话助手

现在我们将构建一个具备工具调用能力的对话助手。这个 Agent 能够根据用户问题决定是否需要查询网络信息,并组织最终回答。

3.1 定义状态结构

LangGraph 使用状态(State)对象在节点间传递数据。我们首先定义状态的结构:

from typing import Dict, Any, List, Annotated
import operator
from langchain_core.messages import BaseMessage, HumanMessage
from langgraph.graph import StateGraph, END

# 定义状态类型
class AgentState(TypedDict):
    messages: Annotated[List[BaseMessage], operator.add]  # 自动累加消息
    needs_search: bool  # 是否需要搜索
    search_result: str  # 搜索结果

这里使用了 Annotated operator.add 来自动处理消息列表的追加操作,避免手动合并。

3.2 创建工具节点

工具节点负责执行具体操作,如调用搜索引擎、查询数据库等。以下模拟一个搜索工具:

import requests

def search_web(query: str) -> str:
    """模拟网络搜索功能"""
    # 实际项目中可替换为真实搜索引擎 API
    print(f"执行搜索: {query}")
    # 这里返回模拟结果
    return f"关于'{query}'的搜索结果:当前天气晴,温度25℃"

def tool_node(state: AgentState) -> Dict[str, Any]:
    """工具调用节点"""
    last_message = state["messages"][-1]
    
    # 提取搜索关键词(实际项目可用LLM提取)
    search_query = last_message.content
    
    # 执行搜索
    result = search_web(search_query)
    
    return {
        "messages": [HumanMessage(content=f"搜索结果: {result}")],
        "search_result": result
    }

3.3 创建LLM决策节点

这个节点负责分析用户输入,决定下一步行动:

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

# 初始化LLM
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

def llm_decision_node(state: AgentState) -> Dict[str, Any]:
    """LLM决策节点:判断是否需要搜索"""
    
    # 构建决策提示词
    prompt = ChatPromptTemplate.from_messages([
        ("system", """你是一个助手,需要判断用户问题是否需要实时信息。
        如果问题涉及实时数据、最新事件或需要最新信息,回答"需要搜索"。
        如果问题是一般性、常识性或不需要最新信息,回答"直接回答"。
        
        只返回"需要搜索"或"直接回答",不要额外解释。"""),
        ("human", "{question}")
    ])
    
    last_message = state["messages"][-1]
    chain = prompt | llm
    decision = chain.invoke({"question": last_message.content})
    
    needs_search = "需要搜索" in decision.content
    return {"needs_search": needs_search}

3.4 创建回答生成节点

基于决策结果和可用信息生成最终回答:

def response_node(state: AgentState) -> Dict[str, Any]:
    """回答生成节点"""
    
    messages = state["messages"]
    has_search_result = state.get("search_result")
    
    if has_search_result:
        # 结合搜索结果生成回答
        prompt = ChatPromptTemplate.from_messages([
            ("system", "请根据以下搜索结果和对话历史回答用户问题。"),
            *messages[:-1],
            ("human", f"搜索结果: {has_search_result}\n用户问题: {messages[-1].content}")
        ])
    else:
        # 直接基于对话历史回答
        prompt = ChatPromptTemplate.from_messages([
            ("system", "请直接回答用户问题。"),
            *messages
        ])
    
    chain = prompt | llm
    response = chain.invoke({})
    
    return {"messages": [response]}

3.5 构建图工作流

将各个节点连接成完整的工作流:

def create_agent_workflow() -> StateGraph:
    """创建Agent工作流"""
    
    # 创建图构建器
    workflow = StateGraph(AgentState)
    
    # 添加节点
    workflow.add_node("llm_decision", llm_decision_node)
    workflow.add_node("tool_call", tool_node)
    workflow.add_node("generate_response", response_node)
    
    # 设置入口点
    workflow.set_entry_point("llm_decision")
    
    # 定义条件路由
    def route_after_decision(state: AgentState) -> str:
        if state.get("needs_search"):
            return "tool_call"
        return "generate_response"
    
    workflow.add_conditional_edges(
        "llm_decision",
        route_after_decision,
        {
            "tool_call": "tool_call",
            "generate_response": "generate_response"
        }
    )
    
    # 连接边
    workflow.add_edge("tool_call", "generate_response")
    workflow.add_edge("generate_response", END)
    
    return workflow.compile()

# 编译Agent
agent = create_agent_workflow()

3.6 测试运行

现在可以测试这个完整的 Agent:

# 测试不需要搜索的问题
result1 = agent.invoke({
    "messages": [HumanMessage(content="Python是什么编程语言?")]
})
print("测试1 - 一般问题:")
print(result1["messages"][-1].content)

# 测试需要搜索的问题
result2 = agent.invoke({
    "messages": [HumanMessage(content="今天北京的天气怎么样?")]
})
print("\n测试2 - 需要实时信息的问题:")
print(result2["messages"][-1].content)

这个基础 Agent 已经具备了决策、工具调用和回答生成的完整能力。在实际项目中,你可以根据需要扩展更多工具和决策逻辑。

4. 高级特性:状态管理、持久化与多Agent协作

4.1 深入理解状态管理

LangGraph 的状态管理是其核心优势之一。上面的示例使用了简单的字典状态,但在复杂场景中可能需要更精细的控制。

自定义状态更新器

from typing import Sequence

def update_messages(existing: Sequence[BaseMessage], new: Sequence[BaseMessage]) -> Sequence[BaseMessage]:
    """自定义消息更新逻辑"""
    # 过滤掉系统消息,只保留用户和AI消息
    filtered_existing = [msg for msg in existing if msg.type in ["human", "ai"]]
    return [*filtered_existing, *new]

class AdvancedAgentState(TypedDict):
    messages: Annotated[Sequence[BaseMessage], update_messages]
    conversation_id: str
    turn_count: int
    user_intent: str

状态检查点

def should_create_checkpoint(state: AdvancedAgentState) -> bool:
    """决定何时创建状态检查点"""
    return state["turn_count"] % 5 == 0  # 每5轮对话创建检查点

4.2 工作流持久化

对于长时间运行或需要恢复的 Agent,持久化是必备功能:

import pickle
from datetime import datetime

def save_workflow_state(agent, state: dict, filepath: str):
    """保存工作流状态"""
    checkpoint = {
        "timestamp": datetime.now().isoformat(),
        "state": state,
        "agent_config": agent.get_graph().to_json()
    }
    
    with open(filepath, 'wb') as f:
        pickle.dump(checkpoint, f)

def load_workflow_state(filepath: str) -> dict:
    """加载工作流状态"""
    with open(filepath, 'rb') as f:
        checkpoint = pickle.load(f)
    
    return checkpoint["state"]

# 使用示例
current_state = agent.get_state()  # 获取当前状态
save_workflow_state(agent, current_state, "checkpoint.pkl")

# 恢复状态
loaded_state = load_workflow_state("checkpoint.pkl")
agent.invoke(loaded_state)  # 从检查点继续执行

4.3 多Agent协作系统

LangGraph 真正强大的地方在于构建多Agent系统。以下是一个简单的双Agent协作示例:

class MultiAgentState(TypedDict):
    messages: Annotated[List[BaseMessage], operator.add]
    current_agent: str  # 当前活跃的Agent
    specialist_type: str  # 专家类型

def create_specialist_agent(role: str, expertise: str):
    """创建专业Agent"""
    def specialist_node(state: MultiAgentState):
        prompt = ChatPromptTemplate.from_messages([
            ("system", f"你是{role},擅长{expertise}。请基于你的专业知识回答问题。"),
            *state["messages"]
        ])
        
        response = (prompt | llm).invoke({})
        return {"messages": [response]}
    
    return specialist_node

def coordinator_agent(state: MultiAgentState):
    """协调员Agent:决定由哪个专家处理"""
    question = state["messages"][-1].content
    
    prompt = ChatPromptTemplate.from_messages([
        ("system", """根据用户问题判断需要哪个领域的专家:
        - 如果涉及技术、编程,选择"技术专家"
        - 如果涉及商业、市场,选择"商业专家"
        - 如果是通用问题,选择"通用助手"
        
        只返回专家类型。"""),
        ("human", question)
    ])
    
    decision = (prompt | llm).invoke({})
    specialist_type = decision.content.strip()
    
    return {
        "current_agent": specialist_type,
        "specialist_type": specialist_type
    }

# 构建多Agent工作流
def create_multi_agent_system():
    workflow = StateGraph(MultiAgentState)
    
    # 添加节点
    workflow.add_node("coordinator", coordinator_agent)
    workflow.add_node("tech_specialist", create_specialist_agent("技术专家", "编程、算法、系统设计"))
    workflow.add_node("business_specialist", create_specialist_agent("商业专家", "市场分析、商业模式、战略规划"))
    workflow.add_node("general_assistant", create_specialist_agent("通用助手", "日常问题解答"))
    
    workflow.set_entry_point("coordinator")
    
    # 动态路由到不同专家
    def route_to_specialist(state: MultiAgentState):
        specialist_type = state["specialist_type"]
        return {
            "技术专家": "tech_specialist",
            "商业专家": "business_specialist",
            "通用助手": "general_assistant"
        }.get(specialist_type, "general_assistant")
    
    workflow.add_conditional_edges(
        "coordinator", 
        route_to_specialist,
        {
            "tech_specialist": "tech_specialist",
            "business_specialist": "business_specialist", 
            "general_assistant": "general_assistant"
        }
    )
    
    workflow.add_edge("tech_specialist", END)
    workflow.add_edge("business_specialist", END)
    workflow.add_edge("general_assistant", END)
    
    return workflow.compile()

multi_agent = create_multi_agent_system()

这种架构允许构建复杂的专家系统,每个 Agent 专注于特定领域,由协调员动态分配任务。

5. 生产环境部署与优化建议

5.1 性能优化策略

异步执行

import asyncio

async def process_concurrent_requests(requests: List[str], agent):
    """并发处理多个请求"""
    tasks = [agent.ainvoke({"messages": [HumanMessage(content=req)]}) for req in requests]
    results = await asyncio.gather(*tasks)
    return results

# 使用示例
async def main():
    requests = ["问题1", "问题2", "问题3"]
    results = await process_concurrent_requests(requests, agent)
    for result in results:
        print(result["messages"][-1].content)

# asyncio.run(main())

缓存机制

from functools import lru_cache
from langchain.cache import InMemoryCache
from langchain.globals import set_llm_cache

# 启用LLM缓存
set_llm_cache(InMemoryCache())

# 自定义工具结果缓存
@lru_cache(maxsize=100)
def cached_search_web(query: str) -> str:
    return search_web(query)

5.2 监控与日志

完善的监控是生产系统的生命线:

import logging
from datetime import datetime

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('agent_operations.log'),
        logging.StreamHandler()
    ]
)

logger = logging.getLogger("langgraph_agent")

def instrumented_node(node_func):
    """为节点添加监控装饰器"""
    def wrapper(state: dict):
        start_time = datetime.now()
        logger.info(f"开始执行节点: {node_func.__name__}")
        
        try:
            result = node_func(state)
            duration = (datetime.now() - start_time).total_seconds()
            logger.info(f"节点执行成功: {node_func.__name__}, 耗时: {duration:.2f}s")
            return result
        except Exception as e:
            logger.error(f"节点执行失败: {node_func.__name__}, 错误: {str(e)}")
            raise
    
    return wrapper

# 使用监控装饰器
@instrumented_node
def monitored_decision_node(state: AgentState):
    return llm_decision_node(state)

5.3 错误处理与重试机制

节点级错误处理

from tenacity import retry, stop_after_attempt, wait_exponential

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
def robust_llm_call(prompt):
    """带重试的LLM调用"""
    return llm.invoke(prompt)

def error_handling_node(state: AgentState):
    """具备错误处理能力的节点"""
    try:
        return llm_decision_node(state)
    except Exception as e:
        logger.error(f"决策节点失败: {e}")
        # 返回降级响应
        return {
            "messages": [AIMessage(content="系统暂时繁忙,请稍后重试")],
            "needs_search": False
        }

5.4 安全与权限控制

输入验证

import re

def validate_user_input(message: str) -> bool:
    """验证用户输入安全性"""
    # 检查长度
    if len(message) > 1000:
        return False
    
    # 检查潜在危险模式
    dangerous_patterns = [
        r"系统命令.*执行",
        r"文件.*删除",
        r"数据库.*清空"
    ]
    
    for pattern in dangerous_patterns:
        if re.search(pattern, message, re.IGNORECASE):
            return False
    
    return True

def safe_invoke(agent, user_input: str):
    """安全的Agent调用封装"""
    if not validate_user_input(user_input):
        return {"messages": [AIMessage(content="输入内容不符合安全要求")]}
    
    return agent.invoke({"messages": [HumanMessage(content=user_input)]})

6. 常见问题排查与调试技巧

6.1 典型错误场景及解决方案

问题现象 可能原因 检查点 解决方案
图编译失败 节点未正确定义或连接 检查节点名称拼写、边连接顺序 使用 graph.get_graph().print_ascii() 可视化图结构
状态更新异常 状态注解类型不匹配 验证 Annotated 注解的正确性 确保使用 operator.add 或自定义更新函数
LLM调用超时 API密钥错误、网络问题 检查API密钥、网络连接 配置超时参数、添加重试机制
工具调用失败 工具函数参数不匹配 验证工具输入输出格式 添加类型注解和参数验证
内存使用过高 状态累积过大 监控状态对象大小 定期清理历史消息、实现检查点机制

6.2 调试工具与技术

可视化工作流

def visualize_workflow(agent):
    """生成工作流可视化"""
    graph = agent.get_graph()
    
    # 打印ASCII图示
    print("工作流结构:")
    graph.print_ascii()
    
    # 生成Mermaid图(需支持的环境)
    try:
        mermaid_code = graph.get_graph().draw_mermaid()
        print("\nMermaid 图代码:")
        print(mermaid_code)
    except:
        print("Mermaid 可视化不可用")

# 使用示例
visualize_workflow(agent)

状态检查工具

def debug_state_transition(initial_state, agent, max_steps=10):
    """逐步调试状态转换"""
    current_state = initial_state
    step = 0
    
    while step < max_steps:
        print(f"\n--- 步骤 {step} ---")
        print("当前状态:", {k: v for k, v in current_state.items() if k != 'messages'})
        print("最新消息:", current_state['messages'][-1].content if current_state['messages'] else "无")
        
        try:
            next_state = agent.invoke(current_state)
            if next_state == current_state:
                print("状态稳定,可能到达终点")
                break
            current_state = next_state
            step += 1
        except Exception as e:
            print(f"执行错误: {e}")
            break
    
    return current_state

6.3 性能分析工具

import cProfile
import pstats
from io import StringIO

def profile_agent_performance(agent, test_input, output_file='profile_results.txt'):
    """分析Agent性能瓶颈"""
    profiler = cProfile.Profile()
    
    def run_agent():
        return agent.invoke({"messages": [HumanMessage(content=test_input)]})
    
    profiler.enable()
    result = run_agent()
    profiler.disable()
    
    # 保存分析结果
    s = StringIO()
    ps = pstats.Stats(profiler, stream=s).sort_stats('cumulative')
    ps.print_stats()
    
    with open(output_file, 'w') as f:
        f.write(s.getvalue())
    
    print(f"性能分析结果已保存到: {output_file}")
    return result

7. 扩展学习路径与最佳实践

7.1 渐进式学习路线

  1. 基础掌握 (1-2周):

    • 理解图编排概念
    • 掌握状态管理机制
    • 构建简单对话Agent
  2. 中级应用 (2-4周):

    • 集成真实工具(API、数据库)
    • 实现复杂条件逻辑
    • 添加持久化支持
  3. 高级架构 (1-2月):

    • 设计多Agent系统
    • 优化性能与可靠性
    • 部署生产环境

7.2 项目实践建议

从小开始,迭代开发

  • 第一版:实现核心工作流,硬编码简单逻辑
  • 第二版:添加工具集成,完善错误处理
  • 第三版:引入LLM决策,实现动态路由
  • 第四版:添加监控、缓存、安全机制

测试策略

import pytest

def test_agent_decision_logic():
    """测试Agent决策逻辑"""
    agent = create_agent_workflow()
    
    # 测试需要搜索的场景
    result = agent.invoke({"messages": [HumanMessage(content="今天天气如何?")]})
    assert "搜索" in str(result) or "天气" in str(result)
    
    # 测试直接回答的场景  
    result = agent.invoke({"messages": [HumanMessage(content="什么是Python?")]})
    assert "编程" in str(result) or "语言" in str(result)

def test_error_handling():
    """测试错误处理"""
    agent = create_agent_workflow()
    
    # 测试空输入
    result = agent.invoke({"messages": [HumanMessage(content="")]})
    assert len(result["messages"]) > 0

7.3 生产环境检查清单

在将 LangGraph Agent 部署到生产环境前,确保完成以下检查:

  • [ ] 环境变量配置正确(API密钥、数据库连接等)
  • [ ] 错误处理机制完备(网络异常、API限制等)
  • [ ] 日志系统就绪(操作日志、错误日志、性能日志)
  • [ ] 监控告警配置(响应时间、错误率、资源使用)
  • [ ] 安全措施到位(输入验证、权限控制、数据脱敏)
  • [ ] 性能测试通过(并发能力、内存使用、响应时间)
  • [ ] 备份恢复方案(状态持久化、检查点机制)
  • [ ] 文档更新完成(API文档、运维手册、故障处理流程)

LangGraph 为复杂 AI Agent 开发提供了强大的编排能力,但真正的挑战在于如何将技术能力转化为稳定可靠的业务价值。建议从具体业务场景出发,先解决一个小而具体的问题,再逐步扩展能力边界。每个成功的 Agent 系统都是通过不断迭代、测试和优化逐步成熟的。

更多推荐