Appearance
LangChain 与 LangGraph
LangChain 是 AI 应用开发最广泛使用的框架,LangGraph 在此基础上支持有状态的 Agent 编排。本页讲解核心架构、组件和两者的关系,帮助开发者选型并快速上手。
先说应不应该用框架
这一章讲框架,但我想先说一件容易被忽略的事:用框架不是必须的,也不总是对的。
如果你刚学完 Agent 基础原理,代码里已经有了一个能跑的 Agent Loop,这时候加 LangChain 可能只是在给自己加一层你还不熟悉的抽象,出了问题更难排查。
框架的价值,在你已经理解了它帮你隐藏了什么之后才会真正体现出来。如果你还不清楚 Tool Calling 怎么工作、消息历史为什么要手动管理、状态在哪里维护,建议先把手写版的 Agent Loop 做稳,再来看框架。
带着这个前提来理解 LangChain 和 LangGraph,会更有收获。
LangChain 是什么
LangChain 是一个帮你组织"模型调用链路"的框架。它的核心抽象是这几个:
Model / LLM:对各家模型 API 的统一封装,换模型不用改调用代码。
Prompt Template:把静态字符串变成带变量的模板,方便复用和测试不同版本的 prompt。
Chain:把多个步骤串成一个可执行的链,输出自动作为下一步的输入。
Memory:对话历史的管理抽象,包括全量保留、固定窗口、摘要压缩等。
Agent:基于工具调用的 Agent 封装,内置多种推理策略(ReAct、Function Calling 等)。
Retriever:检索器抽象,向量库、BM25、混合检索都可以接进来。
如果你已经做过 RAG 知识库,LangChain 的一个典型使用场景是把"检索 + 生成"这条链路封装成一个 RetrievalQA 或 ConversationalRetrievalChain,省去手动拼 prompt 和管理上下文的步骤:
python
from langchain_openai import ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain.chains import RetrievalQA
llm = ChatOpenAI(model="gpt-4o")
embeddings = OpenAIEmbeddings()
vectorstore = Chroma(embedding_function=embeddings, persist_directory="./db")
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
retriever=vectorstore.as_retriever(search_kwargs={"k": 3}),
return_source_documents=True
)
result = qa_chain.invoke({"query": "申请退款需要多少天?"})
print(result["result"])
print(result["source_documents"])十几行代码就跑通了一个带检索的问答链。对比手写版,少了 embedding 调用、向量检索、prompt 拼装这些步骤。
LangChain 适合的场景:
- 快速搭 RAG 原型,不想从零实现检索 + 生成链路
- 需要支持多个模型切换,想用统一接口
- 需要标准的 Memory 实现,不想自己处理消息历史
LangChain 不适合的场景:
- 你还不清楚框架底层在做什么,调试困难时无从下手
- 你的 Agent 逻辑很特殊,需要精细控制每一步,框架反而是束缚
- 项目对包依赖很敏感,LangChain 的依赖树比较重
LangChain 的生态很大,但它并不总是最好的选择。手写版的 Tool Calling + 消息管理,在很多场景里更透明、更容易维护。
LangGraph 是什么
LangGraph 是 LangChain 团队在 LangChain 之上做的 Agent 编排框架,专门处理"有状态、多步、可恢复"的 Agent 流程。
它的核心模型是图(Graph):把 Agent 的执行流程建模成节点和边,每个节点是一个处理步骤,边定义了步骤之间的跳转逻辑(包括条件分支)。
为什么要引入图结构?因为普通的 while 循环处理不好这几种情况:
- 条件分支:根据当前状态决定下一步是继续搜索、还是直接输出结论,还是请求用户确认
- 循环:某些步骤需要重复执行(比如搜索失败要重试)
- 持久化中断:Agent 执行到一半,可以暂停、存档,等待人工审批,再继续
- 并行分支:多个子任务可以并行跑,等所有结果回来再合并
LangGraph 把这些控制流需求变成了显式的图结构,而不是混在 if/else 和 while 里。
LangGraph 的基本用法
LangGraph 里最关键的概念是 StateGraph:定义一个带有共享状态的图,每个节点都可以读写这个状态。
用 Research Agent 的场景来看一个最小例子:
python
from typing import TypedDict, Annotated
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, END
import operator
# 1. 定义状态结构
class ResearchState(TypedDict):
question: str
search_results: Annotated[list[str], operator.add] # 支持追加
final_answer: str
steps_taken: int
# 2. 定义节点函数
llm = ChatOpenAI(model="gpt-4o")
def search_node(state: ResearchState) -> dict:
"""执行搜索"""
# 实际场景里调用真实搜索工具
results = [f"搜索 '{state['question']}' 的结果(模拟)"]
return {
"search_results": results,
"steps_taken": state["steps_taken"] + 1
}
def summarize_node(state: ResearchState) -> dict:
"""整合结果,生成结论"""
context = "\n".join(state["search_results"])
response = llm.invoke(
f"基于以下搜索结果,回答问题:{state['question']}\n\n{context}"
)
return {"final_answer": response.content}
def should_continue(state: ResearchState) -> str:
"""条件分支:决定下一步"""
if state["steps_taken"] >= 3:
return "summarize" # 步数够了,生成结论
if len(state["search_results"]) == 0:
return "search" # 还没搜,继续搜
return "summarize" # 默认进入总结
# 3. 构建图
graph = StateGraph(ResearchState)
graph.add_node("search", search_node)
graph.add_node("summarize", summarize_node)
graph.set_entry_point("search")
# 条件跳转:search 结束后,根据 should_continue 决定下一步
graph.add_conditional_edges(
"search",
should_continue,
{
"search": "search", # 继续搜索
"summarize": "summarize" # 进入总结
}
)
graph.add_edge("summarize", END)
app = graph.compile()
# 4. 执行
result = app.invoke({
"question": "2025 年最主流的前端框架是什么?",
"search_results": [],
"final_answer": "",
"steps_taken": 0
})
print(result["final_answer"])这个例子比实际用到的功能简单,但结构是真实的:状态定义、节点函数、条件边、入口设定。
LangGraph 的关键能力在 add_conditional_edges:根据当前状态选择走哪条边,这比手写 if/else 更结构化,也更容易可视化和调试。
人工审批(Human-in-the-Loop)
LangGraph 的 interrupt 机制让 Agent 可以在执行到某个敏感节点时暂停,等待人工确认再继续。这个能力在纯手写版 Agent Loop 里很难优雅地实现。
python
from langgraph.checkpoint.memory import MemorySaver
from langgraph.types import interrupt
def risky_action_node(state: ResearchState) -> dict:
"""需要人工确认的高风险操作"""
# 暂停执行,把问题抛给人工
approval = interrupt({
"message": "即将执行以下操作,请确认",
"action": state.get("planned_action")
})
if approval["approved"]:
# 执行操作
return {"result": "操作已执行"}
else:
return {"result": "操作已取消"}
# 编译时加入 checkpointer
checkpointer = MemorySaver()
app = graph.compile(
checkpointer=checkpointer,
interrupt_before=["risky_action_node"] # 到这个节点前暂停
)
# 第一次运行:会在 risky_action_node 前暂停
thread_id = {"configurable": {"thread_id": "task-001"}}
result = app.invoke(initial_state, config=thread_id)
# result 里包含待确认的信息
# 人工审批后,恢复执行
resume_result = app.invoke(
{"approved": True},
config=thread_id
)这个模式对需要"让人类保持控制"的 Agent 系统非常关键。Agent 发现要做一个高风险动作(比如发邮件、修改数据库),可以先暂停,展示给用户确认,而不是直接执行。
手写 vs LangChain vs LangGraph 对比
| 场景 | 建议选择 |
|---|---|
| 学习阶段,搞清楚 Tool Calling 怎么工作 | 手写 |
| 快速搭 RAG 原型,用标准 Retriever | LangChain |
| 做多步 Agent,需要条件分支和状态管理 | LangGraph |
| Agent 需要暂停、等待人工审批、恢复执行 | LangGraph |
| 线上系统对可维护性和依赖稳定性要求高 | 手写 + 适当借用 |
不存在"LangGraph 比手写版更好"这个说法。LangGraph 的优势是在图结构清晰、需要 checkpoint 和 Human-in-the-Loop 的场景。如果你的 Agent 是一个直接跑到底的简单循环,手写版更透明,出了问题更好查。
LangGraph 和 LangChain 的关系
LangGraph 是 LangChain 生态里的一个独立包(langgraph),但不依赖 LangChain 就能用。两者的关系是:
- LangChain 提供了模型封装、Prompt Template、Memory、Retriever 等"积木"
- LangGraph 提供了编排这些积木的"图结构框架"
- 你可以单独用 LangGraph + 原生 OpenAI SDK,不用 LangChain 那一层
实际项目里比较常见的组合:用 LangGraph 做流程编排,用 LangChain 的 Retriever 做检索,模型调用直接用各家官方 SDK。每层选最熟悉的工具。
和 Agent 基础原理的关系
Agent 基础原理里的 Plan → Act → Observe 循环,在 LangGraph 里的对应关系:
- Plan 阶段:模型决定下一步的逻辑,封装成一个节点
- Act 阶段:工具执行节点
- Observe 阶段:处理工具结果的节点,根据结果决定走哪条边
- 终止条件:
conditional_edges里包含END分支
LangGraph 没有改变 Agent 的本质逻辑,只是把这个逻辑用图的方式表达出来,让分支、循环和中断变得更显式、更可测试。
如果你已经理解了手写 Agent Loop 的工作方式,看 LangGraph 的图定义会很自然——它其实就是把你脑子里已经有的状态机,用代码显式地画了出来。
进一步了解 Agent 的推理模式,可以继续阅读 CoT 与 ReAct;如果你想深入理解 LangGraph 支持的多 Agent 架构,可以参考 多 Agent 协调。