资讯动态

LangGraph提示链实战:从状态管理到可运行代码

发布时间:2026/10/20 21:41:12 来源:尧图企业网站定制
1. 提示链是什么以及为什么是 LangGraph 的第一课做 LLM 应用这两年我最大的感受是拼 prompt 不如拼流程。很多人拿到需求就开始调 prompt试图让一个大模型在一条消息里完成从分析、构思、写作到润色的所有工作。结果模型经常“一本正经地胡说八道”或者给你一个结构松散、没有重点的长文。问题其实不在模型不够聪明而在你让一次生成承担了太多职责。LangGraph 的 Prompt Chaining提示链恰恰就是从这个问题切入的基础模式。它把复杂任务拆成多个顺序执行的节点每个节点只负责一件具体的事前一个节点的输出作为后一个节点的输入形成一条清晰的流水线。这篇是 LangGraph 系列的第一篇我把这个模式完全讲透并附上一份完整可运行的源代码保证你拿到就能直接跑起来。适合谁来读如果你刚开始了解 LangGraph或者已经用过 LangChain 但一直没搞懂状态是怎么在节点之间流转的这篇能帮你打通关节。如果你已经写过一些 Agent也可以看看我在节点拆分、状态设计和错误排查里的取舍——这些细节通常文档里不会写。1.1 单次生成为什么经常翻车还是用“写一篇产品介绍”来举例。如果你把所有要求一次性塞给模型它会面临一个相当复杂的推理任务要理解产品卖点要设计文章结构要写出吸引人的开头要自然过渡到细节最后还要给出转化引导。这么多目标挤在一次生成里模型只能在各个目标之间“和稀泥”。更关键的是单次生成没有中间检查点。你没法在模型写大纲的时候就看一眼方向对不对也没法在初稿完成之后单独做一次事实核查。就像一个工厂把所有工序都交给一个工人最后再质检一旦出错整条线都作废。很多人在实际项目里反复试 prompt、换模型效果依然不稳定原因就在这里。我们把同样的事情交给提示链情况就完全不一样了。链里的每一步都是独立的节点每个节点只回答一个明确的问题比如“根据这个主题生成三级大纲”“根据大纲写出完整初稿”“检查初稿并润色”。模型每次只需要做好一件单一任务出错率会显著下降而且你可以精准定位到底哪一步出的问题。1.2 提示链不是“多个 prompt 拼在一起”这里要纠正一个常见误解。提示链绝不只是把几个 prompt 按顺序贴在代码里。它真正的价值在于引入了显式的“中间状态转换”。在 LangGraph 里每个节点函数接收当前状态返回一个状态更新然后状态被传递给下一个节点。这意味着每一步生成的内容都会被结构化地保存在状态中。你可以在任意两个节点之间查看、修改、校验甚至可以阻断流程。我举个生活化的类比。你写一篇文章就像做一道菜。单次生成是买一台全能料理机把所有食材扔进去出来的东西能吃但口味不可控提示链则是一个厨房流程洗菜、切菜、焯水、炒制、调味每一步都有明确的操作者哪一步咸了淡了都能及时调整。你不需要一台“完美机器”只需要把流程控制住结果自然稳定。1.3 LangGraph 做提示链的优势在哪有人可能会问我直接写一段 Python按顺序调用三次 LLM不也是提示链吗确实简单的线性流程用普通 Python 也能实现。但 LangGraph 的价值不在于“能串联”而在于它把这个串联过程变成了一个可维护、可扩展、可观测的图结构。第一每个节点是独立函数能单独测试、单独替换。你可以在某个节点换一个更好的模型其他节点不动。第二图的边可以不是单纯的线性流转。你可以在中间加入条件判断比如初稿质量分不够就走“重写”节点够了才继续往下。这就是 LangGraph 的“条件边”后面我会提到。第三LangGraph 自带状态管理和可视化调试能力。跑完一条链之后你可以直接输出状态字典看到每一步到底生成了什么而不是靠日志打点一点点猜。2. 环境安装和模型初始化这部分我不打算铺开太多但第一次跑 LangGraph 的人往往会在环境这里卡住所以值得花一节说清楚。2.1 安装 LangGraph 依赖建议用 Python 3.10 或更高版本。直接在虚拟环境里安装pip install langgraph langchain-openai如果你用的是其他模型服务比如本地模型或者国产模型也可以只安装对应的 LangChain 集成包LangGraph 本身是框架无关的。我只在示例里用 OpenAI 兼容接口这是目前最常见、最省事的方式。这里有个小建议不要图省事把 langchain 全家桶装一大堆。LangGraph 核心其实不依赖 LangChain 的完整生态缺什么装什么依赖越少环境越不容易冲突。2.2 模型密钥与基础调用代码里我直接使用环境变量读取密钥import os os.environ[OPENAI_API_KEY] 你的密钥更推荐的做法是把密钥写到.env文件用dotenv加载或者直接在系统环境变量里配置避免把密钥硬编码进代码。在示例代码里我保留了这个位置但正式项目里不要照抄。初始化一个大模型实例from langchain_openai import ChatOpenAI llm ChatOpenAI( modelgpt-4o-mini, temperature0.3 )temperature这个参数值得多说一句。在提示链里我并不建议所有节点都用同一个温度。比如生成创意大纲的节点可以用 0.7让模型思路更发散一些而润色和格式化的节点建议调低到 0.2 左右减少随机性保证输出风格稳定。后面我还会提到这个细节。3. 完整源代码三步提示链从零到可运行我先带你拆解代码结构然后在 3.4 节给出完整源码。这个链的演示目标是给定一个主题先让模型生成大纲再根据大纲生成初稿最后做润色和排版。这是最典型的 LangGraph 提示链示例官方文档里也把它叫“单一提示链”。3.1 第一步明确状态结构LangGraph 里状态是整个流程的血液。你需要定义一个TypedDict声明哪些字段会在节点之间传递。from typing import TypedDict class ChainState(TypedDict): topic: str # 用户的原始主题 outline: str # 节点1生成的大纲 draft: str # 节点2生成的初稿 final_article: str # 节点3润色后的最终稿这里的设计原则是状态只放需要跨节点传递的数据不要什么都往里塞。比如中间过程里模型返回的token数、响应耗时、日志之类都不适合放进状态它们会污染后续节点的上下文。我见过有人把所有调试信息都塞进状态结果第二轮节点把调试信息也当成了正文输入生成结果惨不忍睹。3.2 第二步编写节点函数节点函数是一个普通 Python 函数接收当前状态返回一个字典字典里的 key 就是要更新的状态字段。先写大纲生成节点def generate_outline(state: ChainState) - dict: prompt f 你是一位资深编辑擅长把宽泛主题拆成层次分明的大纲。 请为主题「{state[topic]}」设计一份文章提纲。 要求 - 包含引言/背景 - 展开 3~5 个核心观点 - 给出总结建议 只输出提纲不要输出正文。 resp llm.invoke(prompt) return {outline: resp.content}第二个节点根据大纲写初稿def generate_draft(state: ChainState) - dict: prompt f 你是一位写作功底扎实的撰稿人。 请根据以下大纲写一篇结构完整、有细节的初稿。 大纲 {state[outline]} 要求 - 语言自然流畅 - 每个核心观点都要展开不能只是列要点 - 直接输出正文 resp llm.invoke(prompt) return {draft: resp.content}第三个节点负责润色def polish_article(state: ChainState) - dict: prompt f 你是一位资深文字编辑。 请把下面的初稿润色为适合正式发布的文章。 要求 1. 段落之间过渡自然 2. 删除重复啰嗦的表达 3. 保留原始信息和结构不要新增无关内容 初稿 {state[draft]} 请输出润色后的最终稿。 resp llm.invoke(prompt) return {final_article: resp.content}每个节点都遵循“角色 目标 输入字段”的结构。为什么要单独指定角色因为在 LangGraph 的链式流程里节点之间是黑盒模型只能通过状态拿到上游的字符串没有额外的“意图”信息。你在 prompt 里明确角色等于告诉模型应该用哪套语言习惯来处理这些输入。3.3 第三步组装状态图并运行组装阶段需要用到StateGraphfrom langgraph.graph import StateGraph, START, END builder StateGraph(ChainState) builder.add_node(generate_outline, generate_outline) builder.add_node(generate_draft, generate_draft) builder.add_node(polish, polish_article) builder.add_edge(START, generate_outline) builder.add_edge(generate_outline, generate_draft) builder.add_edge(generate_draft, polish) builder.add_edge(polish, END) app builder.compile()最后调用result app.invoke({topic: 为什么很多人喜欢在夜间工作}) print( 大纲 ) print(result[outline]) print( 初稿 ) print(result[draft]) print( 最终稿 ) print(result[final_article])app.invoke接收初始状态然后按照图结构依次执行所有节点最终返回一份包含了所有字段的状态字典。你不需要手动维护中间变量所有的传递都是隐式完成的。3.4 完整可运行的源代码下面贴出完整代码复制到 Python 文件填好密钥后直接运行。import os from typing import TypedDict from langgraph.graph import StateGraph, START, END from langchain_openai import ChatOpenAI # 建议改为环境变量加载这里为了演示方便直接占位 os.environ[OPENAI_API_KEY] 你的密钥 llm ChatOpenAI( modelgpt-4o-mini, temperature0.3 ) class ChainState(TypedDict): topic: str outline: str draft: str final_article: str def generate_outline(state: ChainState) - dict: prompt f 你是一位资深编辑擅长把宽泛主题拆成层次分明的大纲。 请为主题「{state[topic]}」设计一份文章提纲。 要求 - 包含引言/背景 - 展开 3~5 个核心观点 - 给出总结建议 只输出提纲不要输出正文。 resp llm.invoke(prompt) return {outline: resp.content} def generate_draft(state: ChainState) - dict: prompt f 你是一位写作功底扎实的撰稿人。 请根据以下大纲写一篇结构完整、有细节的初稿。 大纲 {state[outline]} 要求 - 语言自然流畅 - 每个核心观点都要展开不能只是列要点 - 直接输出正文 resp llm.invoke(prompt) return {draft: resp.content} def polish_article(state: ChainState) - dict: prompt f 你是一位资深文字编辑。 请把下面的初稿润色为适合正式发布的文章。 要求 1. 段落之间过渡自然 2. 删除重复啰嗦的表达 3. 保留原始信息和结构不要新增无关内容 初稿 {state[draft]} 请输出润色后的最终稿。 resp llm.invoke(prompt) return {final_article: resp.content} builder StateGraph(ChainState) builder.add_node(generate_outline, generate_outline) builder.add_node(generate_draft, generate_draft) builder.add_node(polish, polish_article) builder.add_edge(START, generate_outline) builder.add_edge(generate_outline, generate_draft) builder.add_edge(generate_draft, polish) builder.add_edge(polish, END) app builder.compile() result app.invoke({topic: 为什么很多人喜欢在夜间工作}) print( 大纲 ) print(result[outline]) print( 初稿 ) print(result[draft]) print( 最终稿 ) print(result[final_article])这个例子已经是一条非常标准的 LangGraph 提示链了。你后续要做的大部分工作无非是把里面的节点内容替换成你自己的业务逻辑比如把“生成大纲”换成“提取需求”把“写初稿”换成“生成 SQL”把“润色”换成“格式化成 Markdown”整个骨架是一样的。4. 核心参数拆解与实操细节代码能跑只是第一步真正让提示链在业务里稳定工作的是那些看起来不起眼的设计细节。我挑几个最容易踩坑的点详细说。4.1 节点拆分与 prompt 设计的拿捏提示链的关键在于拆分粒度。拆得太细节点数量会爆炸每个节点都要调用一次模型成本和延迟都成倍上涨拆得太粗又回到单次生成的老路起不到隔离职责的作用。我自己的经验是当一个 prompt 里出现“先做 A然后做 B最后做 C”这种连词时就该考虑拆节点了。比如“请分析用户评论的情感并提取三个改进建议然后按优先级排序”这一个 prompt 实际包含三个任务模型的注意力会被分散。拆成三个节点每一步都能被单独验证效果会稳定很多。但反过来也没有必要把“生成标题”和“生成摘要”拆成两个节点因为这两个任务足够轻量而且共享同一批上下文合在一起反而能降低延迟。拆分的原则是看这个节点是否具备独立的校验标准。如果某一环输出好坏你无法单独判断那它就不适合被拆出来。4.2 状态更新的常见陷阱很多新手在写节点时喜欢把返回的 key 写错比如把final_article拼成final_article_new。这在 LangGraph 里不会报错它只会默默新增一个状态字段然后下游节点引用不到最终结果缺失。这种 bug 非常隐蔽所以我建议在关键节点之间加一次打印检查def generate_draft(state: ChainState) - dict: print(大纲字段, state[outline][:100]) ...另一个容易出问题的地方是有些节点会试图在返回值里带上大量历史字段。比如接收了state[outline]之后返回时又把{outline: state[outline], draft: ...}原样带回去。这不是错误但完全没有必要LangGraph 的状态更新是增量的后一个节点返回的 key 会覆盖前一个同名 key不返回的 key 会保留原值。熟悉这个机制之后你可以有意利用“保留”特性让某个节点只修改它应该修改的字段。4.3 模型选型与温度参数提示链的另一个隐形好处是“每个节点可以搭配不同模型”。我最近在做一个内容生成项目时大纲节点用的是参数大一点的模型因为创意和结构需要更强的理解能力润色节点则换成了更快更便宜的模型因为润色属于机械性工作小模型完全能胜任。这种混合搭配能大幅降低成本。关于temperature我的建议是节点类型推荐温度理由创意生成、头脑风暴0.7~0.9需要多样性和发散性内容整理、信息提取0.2~0.4需要稳定输出降低幻觉格式转换、润色0.1~0.3不希望模型自由发挥保持原文语义如果你跑了多次发现结果总不一致先检查是不是温度设太高了再检查是不是 prompt 里允许模型自由发挥的空间太大。5. 常见报错与排查速查表LangGraph 本身不太会报错报错往往来自外部模型调用或是你写的状态逻辑。下面整理一份我在实际使用中遇到的典型问题。5.1 高频问题排查表现象可能原因解决办法报RecursionError状态循环边配置错误节点无限循环检查所有add_edge确保有条件边能够跳出循环返回结果里没有outline字段状态字典 key 拼写不一致检查节点的返回字典和ChainState里的字段名是否完全一致模型返回空字符串某个 prompt 没有传参或模型被系统提示误导打印进入节点时的状态确认字段内容非空调用超时模型响应时间太长或网络不稳定给ChatOpenAI设置timeout参数或重试langgraph.error缺少状态键invoke初始状态里没传必要字段初始状态必须包含所有上游节点会用到的键比如topic输出格式不稳定时好时坏温度过高或者 prompt 没有明确输出格式降低温度并在 prompt 末尾强调“只输出 XXX 格式”5.2 一个亲测有效的调试方法我调试 LangGraph 流程最喜欢用两个手段状态打印和节点内临时降低温度。状态打印是在每个节点函数开头加一行print(state)。这看起来简单但非常直观能立刻看出上一个节点到底传了什么过来。特别是当模型输出被截断时打印能帮你确认是不是字段内容太大导致上下文被撑爆。临时降低温度适合排查“结果不稳定”的问题。把全局温度调到 0.2重新跑两三遍如果结果变得稳定说明问题出在温度上而不是 prompt 逻辑。如果温度很低时结果依然不稳定那就需要检查输入里的信息是否存在歧义。还有一个技巧用app.get_graph().draw_ascii()查看当前图的拓扑结构。LangGraph 提供了 ASCII 可视化比读代码更直观。一旦节点多了这个命令能帮你发现是否有多余的边或者遗漏的连接。5.3 不要忽略模型的“输出超长”问题在提示链里前一个节点的输出会原封不动进入下一个节点的输入。如果第一个节点生成了很长的文本第二个节点的上下文窗口会被占掉很大一部分。遇到这种情况我通常会给节点 prompt 加一句“控制在 500 字以内”或者用代码截断return {outline: resp.content[:800]}不要小看这一步。在长文本任务里状态字段不断累积很容易就把上下文窗口撑爆。保持链里每个中间产物体积可控是整个流程稳定运行的关键。6. 从提示链走向更复杂的 Agent 工作流提示链是 LangGraph 的第一个模式但它不是终点。一旦你掌握了状态和边的概念就可以在链上叠加更多机制。6.1 条件分支让流程自己决定走向LangGraph 里有一种边叫条件边可以根据某个节点的输出来决定下一步。比如你在初稿节点之后加一个“质量检查”节点如果发现初稿质量不合格就跳回“生成初稿”节点重写如果合格才进入润色节点。这种分支逻辑是纯 Python 函数实现的def should_retry(state: ChainState) - str: if state[quality_score] 0.7: return generate_draft return polish有了条件边你的链就不再是一条固定的线而是一个可以自我纠错的工作流。这是普通 Python 顺序调用很难优雅实现的事情。6.2 人工介入把人的判断放在流程中间LangGraph 的一大特色是支持人在回路的交互。你可以在某个节点结束后停住让用户检查中间结果确认之后才继续执行后续节点。在提示链里意味着你可以先让模型生成大纲停下来给用户确认用户点头后再继续写正文。在很多内容生产场景里这个能力比任何自动评估都实用。因为绝大多数情况下你自己才是最懂业务方向的人模型生成的大纲可能看起来很完整但方向完全不对。如果流程一次性跑到底你会浪费大量 token如果中途停一下成本可控质量也可控。6.3 多链并行把不同任务分配到不同节点LangGraph 允许你同时运行多个互不依赖的链。比如你想生成同一主题的多个开场版本可以初始化多个状态并行跑。这个特性在 A/B 测试、多方案生成时非常有用。后面我会在这个系列里详细展开这些高级模式。现在你只需要记住提示链是所有复杂结构的地基。条件边、人工介入、多链并行都是在“状态 节点 边”这个底座上生长出来的。7. 写在后面我对提示链的一点体会从我自己的项目经验来看提示链最大的价值不是“让结果变得完美”而是“让问题可以被定位”。单次生成就像一口黑锅出了问题你只能不停换 prompt 碰运气提示链则把整个过程变成了透明的流水线哪一步出问题一眼就能看出来。我建议你拿到前面这份源码之后不要只跑一遍就完事。试着改改节点里的 prompt看看同一个 input 在不同温度下的差异试着在generate_draft后面插一个新的“检查”节点让它找出初稿里的逻辑问题甚至试着让三个节点用不同的模型对比一下成本和质量。只有亲手改过这些结构你才会真正理解状态流转的含义也才能在下一次面对更复杂的 Agent 需求时心里有底。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑