一文读懂LangChain/LangGraph:从智能体构建到复杂工作流编排
引言:为什么需要LangChain与LangGraph?
在人工智能应用开发领域,大语言模型(LLM)展现出了惊人的潜力,但直接将其集成到生产系统中却面临诸多挑战:如何管理上下文?如何连接外部工具和数据?如何构建多步骤、有状态的复杂推理流程?这正是LangChain和LangGraph诞生的背景。
LangChain是一个用于开发由语言模型驱动的应用程序的框架,它通过“链”(Chains)的概念,将模型调用、提示模板、记忆、工具调用等组件标准化并串联起来。而LangGraph则是在LangChain基础上,专为构建有状态、多参与者的智能体(Agent)和复杂工作流而设计的库。它引入了图(Graph)的概念,使得开发者能够以可视化、可调试的方式编排LLM、工具和人类决策。
简单来说,LangChain解决了“如何让LLM做一件事”的问题,而LangGraph解决了“如何让LLM和一系列工具协作完成一连串复杂任务”的问题。
核心概念快速扫盲
在深入之前,我们先厘清几个关键术语:
- 链(Chain):LangChain的核心抽象。一个链将多个组件(如模型、提示词、解析器)按固定顺序组合,执行一个特定任务。例如,一个“问答链”可能包含:接收用户问题 -> 检索相关文档 -> 构造提示词 -> 调用LLM -> 解析答案。
- 智能体(Agent):一个由LLM驱动的自主决策系统。它配备了一系列工具(如搜索、计算、API调用),并能根据目标动态决定下一步使用哪个工具。LangChain提供了多种Agent执行器。
- 图(Graph):LangGraph的核心。图由节点(Nodes)和边(Edges)组成。节点代表一个执行步骤(如调用LLM、运行工具),边定义了节点之间的流转条件。这允许构建循环、分支和并行执行路径。
- 状态(State):LangGraph中贯穿整个图执行过程的共享数据上下文。它定义了每个节点可以读取和修改哪些信息,是构建有状态应用的关键。
LangChain 核心组件与实战
让我们通过一个简单的代码示例,快速感受LangChain如何工作。假设我们要构建一个根据公司名称查询其简介并总结的链。
# 示例:使用LangChain构建一个简单的查询-总结链fromlangchain_openaiimportChatOpenAIfromlangchain_core.promptsimportChatPromptTemplatefromlangchain_core.output_parsersimportStrOutputParserfromlangchain_community.utilitiesimportWikipediaAPIWrapper# 1. 初始化组件llm=ChatOpenAI(model="gpt-4o-mini")wikipedia=WikipediaAPIWrapper()output_parser=StrOutputParser()# 2. 构建提示词模板prompt=ChatPromptTemplate.from_messages([("system","你是一个商业分析助手。请根据提供的公司信息,用一段话总结该公司的核心业务与特点。"),("user","公司信息:{company_info}\n\n请开始总结:")])# 3. 构建并运行链# 这是一个简单的链:获取信息 -> 格式化提示词 -> 调用LLM -> 解析输出defcompany_summary_chain(company_name:str):# 第一步:获取信息(可视为一个节点)company_info=wikipedia.run(company_name)# 第二步和第三步:通过LangChain LCEL语法组合chain=prompt|llm|output_parser# 执行链summary=chain.invoke({"company_info":company_info})returnsummary# 使用链result=company_summary_chain("OpenAI")print(result)这个例子展示了LangChain的“链式”思维。然而,当任务变得复杂,需要根据上一步的结果动态决定下一步行动时(例如,LLM判断信息不足,需要先调用搜索工具),简单的链就显得力不从心。这时就需要智能体,而LangGraph为构建健壮的智能体提供了更强大的范式。
LangGraph:用图编排复杂工作流
LangGraph 将工作流视为一个有向图。它的强大之处在于可以轻松处理:
- 循环:智能体思考 -> 执行工具 -> 观察结果 -> 继续思考,直到任务完成。
- 分支:根据LLM或条件判断,决定下一步走向哪个节点。
- 状态管理:自动维护对话历史、工具执行结果等上下文。
让我们构建一个简单的研究助手智能体,它可以根据一个问题,自动决定是直接回答,还是需要先搜索维基百科。
# 示例:使用LangGraph构建一个带条件分支的研究助手fromtypingimportTypedDict,Annotatedimportoperatorfromlanggraph.graphimportStateGraph,ENDfromlangchain_openaiimportChatOpenAIfromlangchain_community.toolsimportWikipediaQueryRunfromlangchain_community.utilitiesimportWikipediaAPIWrapper# 1. 定义状态结构classAgentState(TypedDict):question:stranswer:strneeds_search:boolsearch_result:str# 2. 初始化工具和模型llm=ChatOpenAI(model="gpt-4o-mini",temperature=0)wiki_tool=WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())# 3. 定义节点函数defdecide_route(state:AgentState):"""决策节点:判断是否需要搜索"""# 这里可以设计更复杂的逻辑,例如让LLM判断问题是否需要事实核查simple_questions=["什么是人工智能?","你是谁?"]ifstate["question"]insimple_questions:return{"needs_search":False}else:return{"needs_search":True}defcall_llm_directly(state:AgentState):"""直接回答节点"""messages=[("system","你是一个乐于助人的助手。直接回答用户问题。"),("user",state["question"])]response=llm.invoke(messages)return{"answer":response.content}defsearch_and_answer(state:AgentState):"""搜索并回答节点"""# 执行搜索search_result=wiki_tool.run(state["question"])# 基于搜索结果构造提示词messages=[("system","你是一个严谨的研究助手。请基于以下搜索结果为用户的问题提供一个准确的回答。"),("user",f"问题:{state['question']}\n\n搜索结果:{search_result}")]response=llm.invoke(messages)return{"search_result":search_result,"answer":response.content}# 4. 构建图workflow=StateGraph(AgentState)# 添加节点workflow.add_node("decide",decide_route)workflow.add_node("direct_answer",call_llm_directly)workflow.add_node("search_answer",search_and_answer)# 设置入口点workflow.set_entry_point("decide")# 根据条件创建边workflow.add_conditional_edges("decide",# 下一个节点由 `decide_route` 函数返回的 `needs_search` 值决定lambdax:"direct_answer"ifnotx.get("needs_search",True)else"search_answer")workflow.add_edge("direct_answer",END)workflow.add_edge("search_answer",END)# 编译图app=workflow.compile()# 5. 执行图initial_state={"question":"特斯拉汽车公司是哪一年成立的?","answer":"","needs_search":None,"search_result":""}final_state=app.invoke(initial_state)print(f"最终答案:{final_state['answer']}")通过这个流程图,可以清晰地看到LangGraph的工作方式:
渲染错误:Mermaid 渲染失败: Parse error on line 2: ...用户问题”] --> B{“决策节点\n(decide_route)”} -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'
LangChain vs LangGraph:如何选择?
| 特性 | LangChain | LangGraph |
|---|---|---|
| 核心范式 | 链(Chain) | 图(Graph) |
| 状态管理 | 通过Memory组件,相对独立 | 内置、显式、强类型的状态管理,贯穿全图 |
| 流程控制 | 线性或有限分支(通过Router) | 任意复杂的循环、分支、并行、人工干预 |
| 适用场景 | 相对固定、线性的任务(文档问答、文本总结、简单提取) | 复杂、有状态、多步骤的智能体和工作流(自主研究、多工具协作、审批流程) |
| 调试与可视化 | 可通过回调查看日志 | 原生支持将工作流图可视化,执行过程可追溯 |
选择建议:
- 如果你的应用是线性的、无状态的或状态简单(例如,聊天机器人单轮响应、文档处理流水线),从LangChain开始更直接。
- 如果你需要构建具备长期记忆、能使用多种工具、任务步骤动态可变的智能体,或者业务逻辑本身就是一个复杂的流程图(例如,客服工单处理、数据分析管道),那么LangGraph是你的不二之选。它提供的结构和可控性对于复杂应用至关重要。
