langgraph教程系列-01-为什么需要图-从链到图

本文是「LangGraph 教程系列」第 1 篇。写作时基于 langgraph 1.2.10、langchain 1.3.14、langchain-deepseek 1.1.0、Python 3.12+。配套代码仓库 https://github.com/wxj006007/deep-research-assistant ,本篇对应 tag v0

这个系列要做一件事,带你亲手把一个「深度研究助手」从 v0 养到 v5。它一开始只会直接回答问题,最后能联网检索、接受人类审批、跨会话记忆、多专家协作、部署成 API。

每一篇的节奏都一样,上一版撞了什么墙,这一版用 LangGraph 的哪个特性把墙拆掉。特性不是硬塞给你的,是助手自己「长」出来的需求。

今天是第 1 篇,我们先把 v0 跑起来。而且要跑两遍,一遍用链,一遍用图。跑完你就能亲手摸到「从链到图」的差异在哪。

一、你可能听过 LangChain 的 chain 写法

不需要你熟悉 LangChain,只需要知道一个背景。LangChain 是 LangGraph 的前代产品,同一家公司(LangChain Inc.)出品,它最出圈的抽象叫 chain(链),把提示词、模型、输出解析器像水管一样串起来,数据从左往右流。

这种写法有个正式名字叫 LCEL(LangChain Expression Language),核心就是一个管道符

python 复制代码
chain = prompt | llm | parser

优雅、直观、一行搞定。那为什么同一家公司后来又做了 LangGraph 呢,坦率的讲,因为链有一个天生的限制,数据只能单向流动,流程不能回头,也不能分叉。

先别急着理解这句话,我们把 v0 写出来,跑完再回头看。

二、v0,一个只会直接回答的研究助手

v0 的需求简单到不能再简单,用户提一个问题,助手直接回答。没有检索、没有反思、没有任何花活。

流程只有一步
#mermaid-svg-Tm8gGmOCZf9c4qT3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Tm8gGmOCZf9c4qT3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .error-icon{fill:#552222;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .marker.cross{stroke:#333333;}#mermaid-svg-Tm8gGmOCZf9c4qT3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Tm8gGmOCZf9c4qT3 p{margin:0;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .cluster-label text{fill:#333;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .cluster-label span{color:#333;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .cluster-label span p{background-color:transparent;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .label text,#mermaid-svg-Tm8gGmOCZf9c4qT3 span{fill:#333;color:#333;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .node rect,#mermaid-svg-Tm8gGmOCZf9c4qT3 .node circle,#mermaid-svg-Tm8gGmOCZf9c4qT3 .node ellipse,#mermaid-svg-Tm8gGmOCZf9c4qT3 .node polygon,#mermaid-svg-Tm8gGmOCZf9c4qT3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .rough-node .label text,#mermaid-svg-Tm8gGmOCZf9c4qT3 .node .label text,#mermaid-svg-Tm8gGmOCZf9c4qT3 .image-shape .label,#mermaid-svg-Tm8gGmOCZf9c4qT3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .rough-node .label,#mermaid-svg-Tm8gGmOCZf9c4qT3 .node .label,#mermaid-svg-Tm8gGmOCZf9c4qT3 .image-shape .label,#mermaid-svg-Tm8gGmOCZf9c4qT3 .icon-shape .label{text-align:center;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .node.clickable{cursor:pointer;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .arrowheadPath{fill:#333333;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Tm8gGmOCZf9c4qT3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Tm8gGmOCZf9c4qT3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Tm8gGmOCZf9c4qT3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .cluster text{fill:#333;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .cluster span{color:#333;}#mermaid-svg-Tm8gGmOCZf9c4qT3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Tm8gGmOCZf9c4qT3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .icon-shape,#mermaid-svg-Tm8gGmOCZf9c4qT3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .icon-shape p,#mermaid-svg-Tm8gGmOCZf9c4qT3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .icon-shape .label rect,#mermaid-svg-Tm8gGmOCZf9c4qT3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Tm8gGmOCZf9c4qT3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Tm8gGmOCZf9c4qT3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Tm8gGmOCZf9c4qT3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户问题
调用模型
回答

2.1 环境准备

bash 复制代码
pip install langgraph==1.2.10 langchain==1.3.14 langchain-deepseek==1.1.0 python-dotenv

模型用 DeepSeek(在 platform.deepseek.com 申请 API Key,写进 .envDEEPSEEK_API_KEY)。整个系列共用一个模型工厂

python 复制代码
# src/llm.py
from dotenv import load_dotenv
from langchain_deepseek import ChatDeepSeek

load_dotenv()

def get_llm(temperature: float = 0.0) -> ChatDeepSeek:
    return ChatDeepSeek(model="deepseek-chat", temperature=temperature)

2.2 用 LCEL 写 v0

python 复制代码
# src/v0_chain.py
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from src.llm import get_llm

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个研究助手,请直接、准确地回答用户的问题。"),
    ("human", "{question}"),
])

chain = prompt | get_llm() | StrOutputParser()

answer = chain.invoke({"question": "什么是 LangGraph?"})
print(answer)

三个组件,一根管道,数据从 prompt 流到 llm 再流到 parser,一路向右,绝不回头。对 v0 这种「一步到位」的需求,链是完美的。

2.3 用 LangGraph 写 v0

现在用 LangGraph 把完全相同的功能再写一遍。LangGraph 的世界里只有三件套

  • State(状态),一个共享的数据结构,流程中所有步骤都读写它
  • Node(节点),一个普通的 Python 函数,读 state、干一件事、返回要更新的字段
  • Edge(边),声明节点之间谁先谁后
python 复制代码
# src/v0_graph.py
from typing import TypedDict
from langgraph.graph import END, START, StateGraph
from src.llm import get_llm

# State:整个图共享的状态
class ResearchState(TypedDict):
    question: str
    answer: str

# Node:一个普通函数就是一个节点
def answer_node(state: ResearchState) -> dict:
    llm = get_llm()
    response = llm.invoke([
        ("system", "你是一个研究助手,请直接、准确地回答用户的问题。"),
        ("human", state["question"]),
    ])
    # 节点只返回"要更新的字段",LangGraph 负责合并进 state
    return {"answer": response.content}

# Edge:把节点连成图
builder = StateGraph(ResearchState)
builder.add_node("answer", answer_node)
builder.add_edge(START, "answer")
builder.add_edge("answer", END)
graph = builder.compile()

result = graph.invoke({"question": "什么是 LangGraph?"})
print(result["answer"])

跑一下,两个版本的输出几乎一样,但代码量明显变多了。可能有小伙伴纳闷,这不是退步吗?

三、图到底比链强在哪

3.1 先承认,v0 用图是杀鸡用牛刀

对单步流程,图写法确实更啰嗦。LCEL 一行管道,LangGraph 要定义 State、写节点函数、连边、编译,四道工序。这一点我不打算辩护,说实话,如果你的需求永远停留在单步问答,用链一点问题没有。

但注意两个写法的结构性差异

维度 链(LCEL) 图(LangGraph)
数据传递 上一步的输出直接塞给下一步 所有步骤读写同一个共享 State
流程形状 一条直线,方向固定 任意拓扑,边可以指向任何节点
流程控制 结构即控制,写死在管道里 边是显式声明的,可以按条件选择
回到之前的步骤 不可能 加一条回指的边就行

链的「输出直接喂给下一步」听着省事,代价是第 3 步只能看到第 2 步的输出,看不到第 1 步的。图的共享 State 不一样,任何节点都能看到之前所有节点留下的信息。

链的「结构即控制」呢,流程在写代码时就定死了。图把边做成显式声明,那结果会怎样呢,下一步去哪,可以在运行时根据 State 决定。这就是下一篇要讲的条件边。

3.2 三种范式,链、循环、图

把流程组织方式抽象一下,其实是三个层次
#mermaid-svg-hVL3afjDJpcrWCrI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-hVL3afjDJpcrWCrI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hVL3afjDJpcrWCrI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hVL3afjDJpcrWCrI .error-icon{fill:#552222;}#mermaid-svg-hVL3afjDJpcrWCrI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hVL3afjDJpcrWCrI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hVL3afjDJpcrWCrI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hVL3afjDJpcrWCrI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hVL3afjDJpcrWCrI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hVL3afjDJpcrWCrI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hVL3afjDJpcrWCrI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hVL3afjDJpcrWCrI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hVL3afjDJpcrWCrI .marker.cross{stroke:#333333;}#mermaid-svg-hVL3afjDJpcrWCrI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hVL3afjDJpcrWCrI p{margin:0;}#mermaid-svg-hVL3afjDJpcrWCrI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hVL3afjDJpcrWCrI .cluster-label text{fill:#333;}#mermaid-svg-hVL3afjDJpcrWCrI .cluster-label span{color:#333;}#mermaid-svg-hVL3afjDJpcrWCrI .cluster-label span p{background-color:transparent;}#mermaid-svg-hVL3afjDJpcrWCrI .label text,#mermaid-svg-hVL3afjDJpcrWCrI span{fill:#333;color:#333;}#mermaid-svg-hVL3afjDJpcrWCrI .node rect,#mermaid-svg-hVL3afjDJpcrWCrI .node circle,#mermaid-svg-hVL3afjDJpcrWCrI .node ellipse,#mermaid-svg-hVL3afjDJpcrWCrI .node polygon,#mermaid-svg-hVL3afjDJpcrWCrI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hVL3afjDJpcrWCrI .rough-node .label text,#mermaid-svg-hVL3afjDJpcrWCrI .node .label text,#mermaid-svg-hVL3afjDJpcrWCrI .image-shape .label,#mermaid-svg-hVL3afjDJpcrWCrI .icon-shape .label{text-anchor:middle;}#mermaid-svg-hVL3afjDJpcrWCrI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hVL3afjDJpcrWCrI .rough-node .label,#mermaid-svg-hVL3afjDJpcrWCrI .node .label,#mermaid-svg-hVL3afjDJpcrWCrI .image-shape .label,#mermaid-svg-hVL3afjDJpcrWCrI .icon-shape .label{text-align:center;}#mermaid-svg-hVL3afjDJpcrWCrI .node.clickable{cursor:pointer;}#mermaid-svg-hVL3afjDJpcrWCrI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hVL3afjDJpcrWCrI .arrowheadPath{fill:#333333;}#mermaid-svg-hVL3afjDJpcrWCrI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hVL3afjDJpcrWCrI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hVL3afjDJpcrWCrI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hVL3afjDJpcrWCrI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hVL3afjDJpcrWCrI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hVL3afjDJpcrWCrI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hVL3afjDJpcrWCrI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hVL3afjDJpcrWCrI .cluster text{fill:#333;}#mermaid-svg-hVL3afjDJpcrWCrI .cluster span{color:#333;}#mermaid-svg-hVL3afjDJpcrWCrI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-hVL3afjDJpcrWCrI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hVL3afjDJpcrWCrI rect.text{fill:none;stroke-width:0;}#mermaid-svg-hVL3afjDJpcrWCrI .icon-shape,#mermaid-svg-hVL3afjDJpcrWCrI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hVL3afjDJpcrWCrI .icon-shape p,#mermaid-svg-hVL3afjDJpcrWCrI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hVL3afjDJpcrWCrI .icon-shape .label rect,#mermaid-svg-hVL3afjDJpcrWCrI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hVL3afjDJpcrWCrI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hVL3afjDJpcrWCrI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hVL3afjDJpcrWCrI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 图:任意拓扑
节点A
路由
节点B
节点C
节点D
循环:能回头


执行
满意?
结束
链:单向流水线
步骤1
步骤2
步骤3

链能表达「依次做」,循环能表达「不满意就重做」,图能表达依次做、按条件分叉、不满意重做的任意组合。你想想看,链和循环其实都只是图的特例。

一个真正的 Agent 需要什么,判断、分叉、重试、回头反思,这些全是图才有的能力。LangChain Inc. 自己从 chain 走到 graph 的产品演进,就是「从链到图」这个标题的最佳注脚,先用链把「把 LLM 组件串起来」这件事做顺,然后发现严肃的 Agent 场景需要的是图。

顺带一提,如果你把「节点」换成「工序」,这个演进你在历史课本里见过。手工作坊是一条链,一个人从头做到尾。流水线引入了固定分工的循环。现代供应链则是一张动态调度的图。生产组织方式的演进和 Agent 编排方式的演进,走的是同一条路,复杂度到了,结构就得升级。

3.3 那 v0 为什么还要用图写一遍

因为 v0 不会停在 v0。

我们的研究助手马上要面对的现实是,事实型问题和分析型问题该用不同的回答策略(v0.1,流程要分叉),检索结果要累积不能覆盖(v0.2,状态要合并),答案不满意要回头重查(v1,流程要循环)。

如果 v0 用链写,每次进化都要推翻重来。用图写,每次进化只是加节点、加边,State、Node、Edge 三件套不变。这就是我们在最简单的版本就切换到 LangGraph 的原因,不是 v0 需要图,是 v1、v2、v3 需要。

四、本篇小结

回到「从链到图」这块,这篇其实就讲了一件事。链是 prompt | llm | parser 式的单向流水线,适合定死的流程。图用 State、Node、Edge 三件套描述任意拓扑,链和循环都只是它的特例,Agent 需要的判断、分叉、重试,只有图能表达。另外有一个约定值得你现在就记牢,节点不修改 state,只返回要更新的字段,合并交给框架。第 3 篇讲 reducer 时,它是主角。

v0 已经能跑了,但它有个毛病。不管你问「LangGraph 是哪家公司开源的」还是「为什么 Agent 框架都在转向图结构」,它都用同一套提示词硬答,事实型问题答得啰嗦,分析型问题答得草率。

流程需要分叉。下一篇,条件边登场。

赞或收藏 关注 我们下次再见

相关推荐
梦想的颜色5 小时前
2026 AI Agent 工程师完整技术图谱|从面试题「什么是本体 Ontology」切入,附精选面试题库
面试·知识图谱·langgraph·aiagent·大模型面试·本体·2026 面试真题
一只小bit8 小时前
Agent 动态调控:模型、工具、提示词、输出、流模式
机器学习·langchain·llm·人机交互·langgraph
喜欢的名字被抢了9 小时前
langgraph教程系列-02-让流程分叉-条件边与路由
教程·langgraph
_itgo18 小时前
LangGraph 主要3 种核心模式
ai·langchain·langgraph
糖果店的幽灵19 小时前
人已经用 WorkBuddy 找工作拿了面试,你还在一份份手工改简历
人工智能·面试·职场和发展·langgraph
Esaka_Forever1 天前
为什么要学习 LangGraph,LangChain 与 LangGraph 的适用边界
langgraph
人间凡尔赛2 天前
2026多智能体系统深度解析:从GPT-5.6 Ultra到开源框架,构建你的Agent军团
ai·agent·多智能体·langgraph·crewai·gpt-5.6
Esaka_Forever2 天前
StateGraph —— LangGraph 的核心基石
langgraph
uncle_ll3 天前
LangGraph 深度解析:用图结构构建下一代智能代理与多智能体系统
langchain·llm·agent·graph·langgraph