资讯动态

从Vibe Coding到LangGraph:AI编程范式演进与状态图实战

发布时间:2026/9/23 6:57:14 来源:尧图企业网站定制
1. 从补全代码到描述意图编程范式正在发生什么变化过去两年我身边做开发的朋友分成了很明显的两拨。一拨还在纠结“Copilot 补全的代码到底能不能信”另一拨已经开始用自然语言把整个功能模块“说”出来了。这个分水岭本质上就是Vibe Coding带来的冲击——你不再逐行敲代码而是描述你想要什么让模型去生成你负责感受“对不对味”然后迭代调整。Vibe Coding 这个词最早由 Andrej Karpathy 提出核心意思很直白完全沉浸在和 AI 的对话流里凭直觉和感觉去编程忘记代码本身的存在。你描述一个需求AI 给你一版实现你跑一下觉得不对再描述再生成。整个过程像调音师在调音而不是木匠在刨木头。但问题很快就来了。当你用 Vibe Coding 的方式做出一个能跑的原型之后想把它变成一个真正可维护、可扩展、能处理复杂业务逻辑的系统时你会发现对话历史越来越长模型开始遗忘前面的约束状态管理一塌糊涂多个步骤之间的依赖关系根本没法可靠地串联。这时候LangGraph就登场了。LangGraph 是 LangChain 团队推出的一个用于构建有状态、多步骤 AI 应用的框架。它把 AI 工作流建模成一张图节点是计算步骤边是控制流状态在节点之间显式传递。这听起来有点抽象但你可以把它理解成Vibe Coding 是即兴爵士乐LangGraph 是写好的交响乐总谱。前者适合探索和原型后者适合把探索出来的东西固化成可靠的生产系统。这篇文章适合谁看如果你是一个正在用 AI 辅助编程的开发者或者是一个想从“调 API 玩一玩”进阶到“构建真正 AI 应用”的工程师又或者你只是好奇“LangChain 和 LangGraph 到底有什么区别”那接下来的内容应该能帮你省下不少自己踩坑的时间。我会从范式演进的逻辑讲起然后深入到 LangGraph 的核心概念、实操步骤、常见坑最后聊聊这套东西在实际项目里怎么用。2. Vibe Coding 的边界在哪里为什么“感觉对了”不够用2.1 Vibe Coding 到底在做什么先把这个概念说透。Vibe Coding 不是某种具体工具而是一种交互模式。你打开一个 AI 编程助手可能是 IDE 插件也可能是网页对话然后用自然语言描述你的意图。比如你说“帮我写一个函数接收一个用户 ID 列表批量查询数据库返回按注册时间排序的结果要处理空列表的情况”。AI 给你一版代码你看一眼觉得逻辑没问题复制粘贴跑测试通过。这个过程里你的注意力从“怎么写”转移到了“要什么”。代码本身变成了一个黑盒你通过输入和输出来判断它是否满足需求。这种模式在探索性编程和原型搭建阶段效率极高。我自己的体验是用这种方式写一个数据清洗脚本或者一个简单的 CRUD 接口速度大概是手写的三到五倍。但 Vibe Coding 有一个隐含假设你能通过一次或几次对话把需求描述清楚并且 AI 能在一段连续的上下文里保持所有约束。这个假设在简单场景下成立在复杂场景下几乎必然崩塌。2.2 三个让 Vibe Coding 失效的典型场景第一个场景是多步骤依赖。假设你要做一个“用户上传文档 - 解析内容 - 提取关键信息 - 调用外部 API 验证 - 生成报告 - 发送邮件”的流程。你用 Vibe Coding 的方式可能会先让 AI 写解析函数再写提取函数再写调用函数。但当你把它们串起来的时候你会发现解析函数返回的数据结构提取函数并不完全兼容调用外部 API 失败时的重试逻辑在对话里根本没提发送邮件之前需要确认报告生成成功这个条件判断在生成的代码里是缺失的。第二个场景是状态管理。在对话式编程里状态是隐式的它存在于对话历史中。但对话历史是有长度限制的而且模型对早期内容的注意力会衰减。当你聊了二十轮之后模型可能已经“忘记”了你最初说的“所有金额字段必须用 Decimal 类型不能用 float”。这种遗忘不是 bug是当前大模型架构的固有特性。第三个场景是可观测性与调试。Vibe Coding 生成的代码如果出了问题你很难定位是哪一步的“感觉”出了偏差。因为没有显式的中间状态记录你只能重新描述需求重新生成然后祈祷这次能对。这在生产环境里是不可接受的。注意Vibe Coding 不是“低级”的编程方式它只是适用于特定阶段。把它用在原型验证上它是利器把它用在生产系统上它是灾难。2.3 从“对话流”到“状态图”的思维转变LangGraph 的核心贡献就是把这套隐式的对话流显式地建模成一张状态图。在这张图里每个节点是一个明确的处理步骤每条边是一个明确的控制转移条件整个图共享一个状态对象这个状态对象在所有节点之间传递和更新。这个转变的意义在于你把“感觉”变成了“结构”。你不再依赖模型记住所有约束而是把约束写进状态的定义里、写进边的条件里、写进节点的逻辑里。模型仍然可以在每个节点内部发挥作用比如做文本分类、信息提取、决策判断但节点之间的流转是确定性的、可测试的、可观测的。打个比方Vibe Coding 像是你坐在副驾驶上凭感觉告诉司机“往左一点、往右一点”LangGraph 像是你画了一张地图标好了每个路口怎么走司机只需要在每个路口按照地图执行。地图可能画得不够好但至少你可以指着地图说“这个路口应该右转不是左转”而不是只能喊“感觉不对”。3. LangGraph 核心概念拆解节点、边、状态到底怎么理解3.1 状态整个图的共享内存LangGraph 里最重要的概念是State。你可以把它理解成整个图的共享内存所有节点都能读取它也能写入它。状态通常是一个字典或者一个 TypedDict里面定义了所有需要在节点之间传递的字段。举个例子假设你要做一个客服工单处理流程状态可能长这样from typing import TypedDict, List class TicketState(TypedDict): ticket_id: str customer_message: str category: str priority: str assigned_to: str resolution: str history: List[str]这个状态对象会在图的每个节点之间传递。比如一个“分类节点”读取customer_message写入category一个“分配节点”读取category和priority写入assigned_to一个“解决节点”读取所有信息写入resolution。这里的关键设计决策是状态字段的定义决定了图的表达能力。如果你发现某个信息需要在多个节点之间共享但它不在状态里那你就得把它加进去。这听起来很琐碎但正是这种显式定义让整个流程变得可追踪、可调试。实操心得状态字段不要一开始就设计得太细。我通常的做法是先用一个宽泛的字典跑通流程然后在迭代过程中逐步收紧字段类型和约束。过早追求完美的状态定义反而会拖慢原型速度。3.2 节点一个纯粹的計算单元节点是 LangGraph 里的基本执行单元。每个节点是一个函数接收当前状态返回一个更新后的状态通常是部分更新。节点的逻辑可以是任何东西调用大模型、查询数据库、调用外部 API、做条件判断、甚至只是打印日志。节点的设计原则是单一职责。一个节点只做一件事做好一件事。比如“调用大模型做意图识别”是一个节点“根据意图更新状态”是另一个节点。不要把多个逻辑塞进一个节点里否则你就失去了图结构带来的可观测性优势。def classify_ticket(state: TicketState) - dict: # 调用大模型做分类 category llm_classify(state[customer_message]) return {category: category}这个节点只负责分类不负责分配不负责解决。它的输入是状态输出是状态的局部更新。LangGraph 会自动把返回的字典合并到全局状态里。3.3 边控制流的显式表达边定义了节点之间的流转关系。LangGraph 支持几种类型的边普通边从节点 A 直接到节点 B无条件。条件边从节点 A 出发根据一个函数的结果决定去 B 还是去 C。入口点图的起始节点。结束点图的终止节点。条件边是 LangGraph 最强大的特性之一。它让你可以把“如果分类结果是投诉就去投诉处理节点如果是咨询就去咨询回复节点”这种逻辑用显式的代码表达出来而不是藏在提示词里让模型自己判断。def route_by_category(state: TicketState) - str: if state[category] complaint: return complaint_handler elif state[category] inquiry: return inquiry_handler else: return general_handler graph.add_conditional_edges( classify, route_by_category, { complaint_handler: complaint_handler, inquiry_handler: inquiry_handler, general_handler: general_handler, } )这段代码的意思是从classify节点出发调用route_by_category函数根据返回值决定下一个节点。返回值必须是映射字典里的键之一。3.4 检查点让状态可以持久化和恢复LangGraph 还有一个很实用的特性叫Checkpointing。它可以在每一步之后自动保存状态快照。这意味着如果你的流程跑到一半失败了你可以从最后一个成功的检查点恢复而不是从头再来。对于长流程、多步骤的 AI 应用来说这个特性几乎是必需的。检查点还可以用来实现Human-in-the-loop。比如在某个关键决策节点之前暂停执行等待人工确认确认后再继续。这在需要人工审核的场景里非常有用。4. 从零搭建一个 LangGraph 工作流完整实操记录4.1 环境准备与依赖安装先说环境。LangGraph 是一个 Python 库需要 Python 3.9 以上。我建议用 conda 或者 venv 创建一个独立环境避免和系统里的其他包冲突。conda create -n langgraph-demo python3.11 conda activate langgraph-demo pip install langgraph langchain langchain-openai如果你用的是其他模型提供商把langchain-openai换成对应的包就行。LangGraph 本身不绑定任何特定的模型它只负责编排模型调用是节点内部的事情。注意LangChain 和 LangGraph 的版本更新比较快建议在项目里锁定版本号。我遇到过因为版本不兼容导致StateGraph导入失败的情况排查了半天才发现是版本问题。4.2 定义状态与节点函数我们用一个具体的例子来走一遍完整流程一个简单的“文章摘要生成与审核”工作流。流程是这样的输入一篇文章 - 生成摘要 - 审核摘要质量 - 如果质量不合格重新生成如果合格输出最终摘要。先定义状态from typing import TypedDict, Literal class ArticleState(TypedDict): article: str summary: str quality_score: float retry_count: int final_output: str然后定义节点函数。第一个节点是生成摘要from langchain_openai import ChatOpenAI llm ChatOpenAI(modelgpt-4o-mini, temperature0.3) def generate_summary(state: ArticleState) - dict: prompt f请为以下文章生成一段不超过200字的摘要\n\n{state[article]} response llm.invoke(prompt) return {summary: response.content, retry_count: state.get(retry_count, 0) 1}第二个节点是审核摘要质量def review_summary(state: ArticleState) - dict: prompt f请评估以下摘要的质量从1到10打分只返回数字 原文{state[article][:500]} 摘要{state[summary]} response llm.invoke(prompt) try: score float(response.content.strip()) except ValueError: score 5.0 return {quality_score: score}第三个节点是输出最终结果def finalize(state: ArticleState) - dict: return {final_output: state[summary]}4.3 构建图与条件路由现在把这些节点组装成图from langgraph.graph import StateGraph, END workflow StateGraph(ArticleState) workflow.add_node(generate, generate_summary) workflow.add_node(review, review_summary) workflow.add_node(finalize, finalize) workflow.set_entry_point(generate) workflow.add_edge(generate, review) def should_retry(state: ArticleState) - Literal[generate, finalize]: if state[quality_score] 7.0: return finalize if state[retry_count] 3: return finalize return generate workflow.add_conditional_edges( review, should_retry, { generate: generate, finalize: finalize, } ) workflow.add_edge(finalize, END) app workflow.compile()这段代码的逻辑很清晰从generate开始到review然后根据质量分数决定是回到generate重试还是去finalize结束。重试次数上限是3次防止无限循环。4.4 运行与调试运行这个图result app.invoke({ article: 你的文章内容..., retry_count: 0, }) print(result[final_output])如果你想要看到每一步的中间状态可以在编译时开启调试模式app workflow.compile(debugTrue)这样每一步的状态变化都会打印出来方便你定位问题。实操心得在开发阶段我习惯把每个节点的输入和输出都打印出来尤其是状态里关键字段的变化。LangGraph 的调试模式虽然有用但输出比较多自己加日志更灵活。5. LangChain 与 LangGraph 的关系不是替代是分工5.1 两者到底有什么区别这是被问得最多的问题之一。简单说LangChain 是工具箱LangGraph 是编排器。LangChain 提供了大量的组件模型封装、提示词模板、输出解析器、文档加载器、向量存储、检索器、工具调用等等。这些东西你都可以单独使用也可以组合使用。但 LangChain 本身对“多步骤、有状态、带条件分支”的流程支持比较弱。它的 Chain 和 Agent 抽象在处理简单线性流程时够用一旦流程复杂起来就会变得难以维护。LangGraph 则是专门为解决这个问题而生的。它不重复造轮子而是复用 LangChain 的组件把它们组织成图结构。你可以在 LangGraph 的节点里调用 LangChain 的模型、检索器、工具但节点之间的流转由 LangGraph 管理。维度LangChainLangGraph核心抽象Chain, Agent, ToolStateGraph, Node, Edge状态管理隐式依赖上下文显式状态对象传递控制流线性为主条件分支弱图结构条件边强大适用场景简单链式调用、单轮问答多步骤流程、复杂决策、Human-in-the-loop可观测性一般强支持检查点和状态快照学习曲线较平缓需要理解图的概念5.2 什么时候该用 LangGraph我的判断标准很简单如果你的流程里出现了“如果...就...否则...”的分支或者需要多个步骤共享状态或者需要人工介入那就该考虑 LangGraph 了。反过来如果你只是做一个“用户提问 - 检索文档 - 生成回答”的简单 RAG 流程LangChain 的 RetrievalQA 链就足够了没必要上 LangGraph。工具的选择要看场景不要为了用而用。5.3 一个常见的误解LangGraph 是不是 LangChain 的替代品不是。LangGraph 是 LangChain 生态的一部分它建立在 LangChain 的基础之上。你可以只用 LangGraph 不用 LangChain 的其他部分但大多数情况下你会同时用到两者。比如用 LangChain 的ChatOpenAI来调用模型用 LangChain 的RecursiveCharacterTextSplitter来切分文档然后用 LangGraph 来编排整个流程。注意LangChain 和 LangGraph 的版本兼容性需要留意。LangGraph 对 LangChain 的某些版本有依赖要求升级其中一个的时候最好同时检查另一个的版本。6. 常见问题与排查技巧实录6.1 状态更新不生效这是新手最容易遇到的问题。你在节点里返回了一个字典但下一个节点读到的状态还是旧的。原因通常是你直接修改了状态对象而不是返回一个新的字典。LangGraph 的状态更新机制是节点函数返回一个字典LangGraph 把这个字典合并到全局状态里。如果你在节点里写了state[summary] xxx然后返回state这可能会出问题因为状态对象可能是不可变的或者合并逻辑不符合你的预期。正确的做法是返回一个只包含更新字段的字典def my_node(state: MyState) - dict: return {summary: new summary}6.2 条件边返回值不匹配条件边函数的返回值必须是映射字典里的键之一。如果你返回了一个不在字典里的值LangGraph 会报错。排查的时候先检查条件边函数的返回值再检查映射字典的键。# 错误示例 def route(state): return unknown_node # 这个键不在映射字典里 # 正确示例 def route(state): if condition: return node_a return node_b6.3 无限循环如果你的图里有环而且没有正确的终止条件就会无限循环。解决办法有两个一是在条件边函数里加计数器超过阈值就强制走向结束节点二是设置递归限制。app workflow.compile() result app.invoke(input_state, config{recursion_limit: 10})recursion_limit是 LangGraph 的一个保护机制默认是25。如果你的流程正常需要超过25步可以调高这个值但更推荐的做法是优化流程减少不必要的步骤。6.4 模型调用超时或失败节点里调用大模型时网络问题或模型服务问题都可能导致失败。建议在节点里加异常处理和重试逻辑from tenacity import retry, stop_after_attempt, wait_exponential retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min2, max10)) def call_llm_with_retry(prompt): return llm.invoke(prompt)这样即使某次调用失败也会自动重试而不是让整个图崩溃。6.5 常见问题速查表问题现象可能原因排查方向状态字段读不到字段未在状态定义中声明检查 TypedDict 定义节点执行顺序不对边连接错误检查 add_edge 和 add_conditional_edges条件边报错返回值不在映射中检查条件函数返回值流程提前结束入口点或结束点设置错误检查 set_entry_point 和 END无限循环缺少终止条件加计数器或 recursion_limit模型调用失败网络或配额问题加异常处理和重试7. 从原型到生产LangGraph 在实际项目中的落地经验7.1 一个真实的工业智能体案例我之前参与过一个工业设备故障诊断的 AI 助手项目。需求是用户描述设备异常现象系统需要判断故障类型、查询维修手册、给出维修建议、并在必要时转人工。用 Vibe Coding 的方式我们很快做出了一个原型用户输入 - 模型判断 - 模型生成建议。但问题很快暴露模型有时候会跳过查询手册的步骤直接给建议有时候会把不同设备的故障类型搞混有时候生成的建议不符合安全规范。后来我们用 LangGraph 重构了整个流程。状态里定义了设备型号、故障现象、故障类型、手册查询结果、建议内容、是否需要人工审核等字段。节点包括现象解析、故障分类、手册检索、建议生成、安全审核、人工转接。条件边根据故障分类结果决定检索哪本手册根据安全审核结果决定是否转人工。重构之后整个流程的可控性大幅提升。每个节点的输入输出都有记录出了问题可以精确定位是哪个环节的偏差。安全审核节点可以强制拦截不合规的建议而不是依赖模型自己“记得”安全规范。7.2 性能优化的几个方向LangGraph 本身的开销很小性能瓶颈通常在节点内部的模型调用上。几个优化方向并行化如果多个节点之间没有依赖关系可以用add_node的并行执行能力或者用SendAPI 做动态并行。缓存对于重复的模型调用可以用 LangChain 的缓存机制避免重复计算。模型分级不是所有节点都需要用大模型。简单的分类、格式化、校验可以用小模型或者规则引擎。状态精简状态对象越大序列化和传递的开销越大。只保留必要的字段大文本可以存到外部存储状态里只存引用。7.3 可观测性建设生产环境里可观测性是刚需。LangGraph 支持与 LangSmith 集成可以追踪每一步的执行时间、输入输出、状态变化。如果你不想用 LangSmith也可以自己埋点把关键信息写到日志或监控系统里。我自己的做法是在每个节点的入口和出口加日志记录节点名称、执行时间、状态关键字段的变化。这样即使没有专门的追踪工具也能通过日志还原整个执行链路。实操心得不要等到出问题才加日志。在开发阶段就把日志埋好后面排查问题会轻松很多。尤其是状态字段的变化一定要记录。8. 编程范式演进的个人观察从 Vibe Coding 到 LangGraph我看到的是一条清晰的演进路径从依赖模型的隐式记忆到依赖结构的显式编排。这不是说 Vibe Coding 不好而是说不同的阶段需要不同的工具。探索阶段用 Vibe Coding 快速试错固化阶段用 LangGraph 保证可靠性两者是互补的。LangChain 和 LangGraph 的关系也是类似的。LangChain 提供了丰富的组件让你快速搭建原型LangGraph 提供了强大的编排能力让你把原型变成生产系统。你不需要二选一而是根据场景选择合适的组合。我个人的体会是学习 LangGraph 最大的门槛不是 API 本身而是思维方式的转变。你需要从“写一个函数”转变为“设计一张图”从“让模型记住”转变为“让状态传递”。这个转变需要练习但一旦习惯了你会发现很多之前觉得棘手的问题用图的方式表达出来就变得清晰了。最后分享一个小技巧如果你刚开始学 LangGraph不要一上来就做复杂的项目。从一个只有两三个节点的简单流程开始跑通然后逐步增加节点和条件分支。每加一个节点都确保你能清楚地解释它的输入是什么、输出是什么、在什么条件下会被执行。这样积累下来你对图的理解会越来越深后面做复杂流程时也会更有把握。

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

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

免费获取报价