凌晨五点的城市地铁控制中心,调度员小李盯着大屏上的线路图:早高峰还没到,但列车已经开始按时刻表有序出动。每一列列车从始发站出发,经过固定的轨道、在道岔处根据客流选择方向、最终抵达终点站汇入主线。整张地铁线路图,就是一张“有向无环图”——站台是节点,轨道是边,车上载的乘客就是流动的数据。而LangGraph,正是让Agent像地铁系统一样可靠运转的“调度图”。它用节点表示AI要执行的动作,用边定义数据流动的方向,用状态对象承载上下文信息,最终把一段松散的提示词调用,变成一条设计精密的“智能流水线”。本文就用“地铁系统”这个比喻,带你走一遍LangGraph DAG状态机建模的全过程。
对AI从业者而言,这是从“会调接口”进阶到“能架构Agent系统”的关键一步:掌握了DAG状态机,你就能设计出可预测、可回放、可运维的多智能体流程,这是生产级Agent工程的必备能力。
可视化演示
展示K-Means算法的分配与更新两阶段迭代过程,观察质心移动与数据点归属变化,支持步进与自动播放。
本节配有交互动效(进入视口后自动加载,约 3KB)。
K-Means分配阶段 将每个数据点分配到最近的质心,形成簇 距离度量使用欧氏距离的平方 d(p,c) = ||p-c||² 分配后进入质心更新阶段
DAG基础概念
想象你要为一座新建的城市设计地铁线路图。规划师的首要原则是:明确有哪些站台,哪些站台之间有轨道相连,以及轨道的方向。你绝不会画一条“环形迷魂阵”让列车永远找不到出口——每一条线路都朝一个方向延伸,最终抵达终点。这就是“有向无环图”(DAG)的精髓:方向明确、没有环路。
在LangGraph中,“站台”就是节点(Node),“轨道”就是有向边(Edge)。节点是Agent执行的一个动作单元,比如“调用大模型生成回复”、“调用搜索工具获取资料”;有向边定义了动作之间的依赖关系——只有前一个节点执行完毕,后一个节点才能开始。因为图是有向的,所以数据的流动方向是确定的;因为是无环的,所以流程一定有终点,不会陷入无限循环。
LangGraph在编译图(Compile)时会做一次“拓扑排序”,就像地铁控制中心在发车前列出每列车的运行顺序。拓扑序保证了:无论图多复杂,总能找到一个合法的执行顺序,让每个节点都在其所有前置节点完成后才启动。这为后续的稳定性、可调试性打下了地基。地铁图是物理世界的DAG,LangGraph是逻辑世界的DAG——二者都是“先画图,再运行”。
当你把Agent流程画成DAG,等于给系统装上了一个“内在时钟”:每一步该做什么、做完之后去哪里,全部由线路图决定。这正是生产中“可控Agent”的第一性原理——别让模型自由发挥,让模型在轨道上奔跑。
- TopoOrder(G)表示对图 G 做拓扑排序后得到的节点执行序列。
- s.t.(使得)表示这个序列必须满足下面的约束条件。
- ∀(u,v) ∈ E对于图中的每一条有向边。
- u 排在 v 之前拓扑序保证了依赖关系不被破坏,前驱节点一定先执行。
状态管理机制
地铁列车上总有一个“行车信息屏”,显示当前到站、下一站、车上乘客数量等信息。不管列车跑到哪里,这个信息屏始终跟随。在LangGraph中,这个“信息屏”就是状态对象(State)——一个贯穿整个流程的字典结构,记录着所有节点共享的数据。
每个节点在执行时,会从State中读取它需要的字段(比如“用户问题”、“历史记录”),经过处理后,将结果写回State中新增或覆盖的字段(比如“检索结果”、“模型回复”)。LangGraph的状态更新机制像地铁调度规则一样严谨:每个节点返回一个字典,LangGraph会将这个字典与当前State做“合并更新”——新增字段直接加入,已有字段按规则覆盖。
值得强调的是,State是“字段级共享”的:不同节点可以只读写自己关心的字段。比如“检索节点”只读“用户问题”、写“检索结果”,而“生成节点”读“检索结果”和“历史记录”、写“最终回复”。这种设计让节点之间的耦合降到最低——每个节点像一列独立列车,只在特定的“站点”(字段)上下客。这比把全部信息塞进一个全局对话历史要清晰得多,也更容易排查问题。
用“信息屏”视角看LangGraph,你会发现它天然适合多人协作:每个Agent(或每个模块)只管好自己的那几行“显示屏字段”,写完就交给下一站。状态字典是唯一的真相来源,所有节点都在为它添砖加瓦——这就是Agent流程不失控的底层保障。
- S_t表示第 t 步执行前的状态字典。
- Node_i(S_t)节点 i 读取当前状态并执行计算,返回一个增量字典。
- ⊕表示“合并更新”操作,新字段加入、同名字段覆盖。
- S_{t+1}更新后的状态,作为下一节点的输入。
条件分支路由
地铁线路中常有“道岔”:列车驶近时,调度系统根据前方客流情况决定扳向左边还是右边。LangGraph中的条件边(Conditional Edge)就是这样的道岔——它不是一个静态的固定轨道,而是一个“路由函数”:读当前状态,判断结果,返回下一步要走的边的名字。
写一个路由函数就像给调度员写一条决策规则:“如果站台上等待人数超过阈值,就开往备用线路;否则正常行驶。”在LangGraph里,你写一个Python函数,入参是State,返回值是字符串(边名)。LangGraph根据这个返回值走相应的边。例如,在“内容质检”节点之后接一个条件边:若State中“合规分数”大于0.8,走“通过”边进入“发布节点”;否则走“驳回”边回到“修改节点”。
条件分支的价值在于把决策权显式地写在图上,而不是藏在模型提示词里。调试时你一眼就能看出“为什么走到这条路”——因为某个状态字段满足/不满足了某个条件。为了可靠性,LangGraph还允许指定一个默认边(兜底),就像道岔故障时列车自动切换到备用轨道,保证流程不会断掉。
有了道岔,Agent就不再是一条直线走到底的“傻列车”,而能根据实际情况动态选择路径。条件分支是Agent从“执行器”变成“决策者”的关键一步——但决策规则是你画的,不是模型自由发挥的,可控性依然握在工程师手里。
- Router(S)路由函数,输入当前状态,输出对应分支的边名。
- score(S)从状态中提取的评分(如合规分数、置信度)。
- e₁ / e₂两条不同方向的边,对应“通过”或“驳回”。
循环与递归
“有向无环”听起来与“循环”矛盾——但Agent流程中经常需要“重试”、“多轮反思”。怎么办?地铁设计者的智慧是:用“折返线”实现循环。列车开到终点站后,通过一条折返轨道回到起点站,从图论角度看,这不是在图里画了一个环,而是从一个终点节点连一条边回到之前的节点,形成逻辑上的“往复运行”。
在LangGraph中,你可以从节点B画一条有向边回到节点A,只要这条边不形成一个严格的“环”结构约束(LangGraph本身支持这种“回到上游”的边设计,只要在编译时能确定终止条件即可,一般通过条件边控制)。更常见的做法是:节点A → 节点B → 条件边 →(若未达标)回到节点A。这样,图仍然是有限的、可终止的,但执行时可以来回多轮,直到满足条件。
递归则是把一个子图当作一个节点来调用。如果你有一套“质量检查子图”要复用到多个Agent流程中,可以把这个子图封装成一个节点,在父图中引用它。子图内部可以有自己的状态管理、自己的循环。但务必设定迭代次数上限(例如最多重试3次),否则一旦模型持续不达标,列车就会在折返线上永远空转——这在生产环境中会烧掉大量token。
循环就像地铁的“高峰限流”:进站人数太多时,让乘客在站外排队绕圈,但调度员一定设置一个“最大等待时间”避免队伍无限增长。在Agent设计里,有循环不可怕,没上限才可怕。
- pass(S)判断当前状态是否满足退出条件。
- k ≥ K_max重试次数达到上限时强制退出。
- Exit / Loop返回“退出”或“继续循环”两个方向。
工作流编排
真实的地铁系统不是一条孤零零的线路,而是多条线交织成网:有环线、有支线、有快慢车。LangGraph的“工作流编排”能力,就是让开发者像规划地铁网一样组合复杂流程。核心手段之一是子图嵌套:一个大的Agent工作流,可以拆成多个子图(子流程),先分别定义,再在父图中用节点引用它们。
为什么需要“拆了再拼”?因为可维护性。一个10个节点的Agent流程,你可以画成“理解用户需求 → 检索知识库 → 规划方案 → 调用工具 → 组织回复”5个子流程,每个子流程内部还有若干节点。这样做的好处是:每个子图可以单独调试、单独测试,出了问题只需定位到对应的子图。
此外,工作流编排还包含超时与重试机制:每个节点可以设置超时时间,超时则按策略重试或走兜底分支。检查点(Checkpoint)则像地铁的“行车记录仪”:每执行到一个节点,就把状态持久化。如果列车中途故障,重新启动后可以从最近一个检查点继续跑,而不必从头再来。异常兜底则是在图的入口处加一个“全局异常处理”节点,像地铁的紧急救援系统,保证任何故障都有响应路径。
“DAG之上还有DAG”——子图嵌套就像俄罗斯套娃:每一层都有完整的地图,打开一个节点,里面又是一张精致的地图。有了这种能力,Agent系统从“小作坊”升级为“工业流水线”。
- SubGraph_i第 i 个子图,封装一段独立流程。
- ∘复合运算,表示先执行前一个子图、再执行后一个子图。
多Agent协作
一座大城市往往有多条地铁线,每条线有自己的列车和调度员,但它们在换乘站无缝衔接,乘客可以跨线到达目的地。多Agent协作正是这个逻辑:每个Agent是一条地铁线,LangGraph是决定换乘关系的总网图。
在LangGraph中,每个Agent被建模为子图或一组节点(一套“认知线路”),Agent之间通过共享状态和消息传递协作。比如你搭建一个“研究写作双Agent系统”:研究员Agent负责检索资料、输出信息摘要到State的"research_notes"字段;作家Agent读取该字段,结合用户需求写出文章。两者之间通过一条有向边连接,共享同一个状态字典——就像两个地铁班次在换乘站交接乘客。
分工编排的关键是明确每个Agent的职责边界。地铁图上有清晰的站点归属,Agent也应该有清晰的输入输出约定。你可以在State中为每个Agent划定专属字段(如“搜索Agent”只写"search_results",“写作Agent”只读它),避免字段冲突。当协作流程复杂时,还可以引入“协调者Agent”作为总调度,像地铁控制中心一样观察所有Agent的运行状态,决定下一步让谁出场。
“独立运行,共享枢纽”——多Agent系统的优雅之处在于:每个Agent不需要知道其他Agent的内部细节,只需要了解“哪个字段去取、哪个字段去存”。图就是你团队的通讯录,一切协作都在节点边上有迹可循。
- S_shared全局共享的状态区,所有Agent可读写。
- S_agent_iAgent i 的私有状态区,其他Agent不直接写。
并行与汇合
早高峰的地铁发车不是一列接一列傻跑,而是多条支线同时发车,在换乘大站汇合。LangGraph的并行能力让Agent流程“多条腿同时跑”,大幅缩短总耗时。图示中就是典型的扇形散开-汇合模式:一个节点(分发器)同时连出多条边到多个并行节点(fan-out),这些节点各自独立执行完毕后,汇入同一个节点(汇合点,barrier)。
实现上LangGraph天然支持并行:只要一个节点有多条出边,下游节点就可以并发执行,前提是它们之间没有依赖关系(这正是DAG的拓扑特性)。例如“分析用户问题”之后,可以同时触发“知识库检索”和“网络搜索”,两个节点互不依赖,并行执行后将结果一起送到“综合答案生成”节点。
但“汇合点”需要理解一个核心语义:必须等所有入边到达,才触发执行。就像地铁换乘站的“聚集等待区”,必须等到所有支线的乘客都到达,才统一出站。LangGraph的汇合节点默认就是这种"AND-barrier"语义。设计时要注意:并行节点的状态写入应互不冲突(分别写不同字段),否则汇合时会发生覆盖竞争——这就是为什么要约定字段分区。
并行不是灵丹妙药:只有在分支之间确实无依赖时才值得用。如果节点A要等节点B的结果,强行并行反而制造混乱。并行是DAG给工程师的礼物,但拆解分支需要眼光——如同地铁规划者决定哪两条线可以共享一段路,哪两条必须各走各的。
- max(T_i)所有并行分支耗时的最大值。
- T_merge汇合点做结果合并的耗时。
工程实践要点
地铁系统安全运营几十年,靠的不是图纸漂亮,而是维护纪律。LangGraph DAG建模落到工程上,同样有几条“纪律”值得遵守。
第一,调试可视化。LangGraph编译后的图可以直接渲染出来,节点间连线一目了然。生产环境里,每个节点的输入输出状态都应记录日志,这样当Agent答错问题时,你能像翻行车记录一样,定位到是“检索节点给错了上下文”还是“生成节点理解偏了方向”。
第二,幂等设计。同一节点因重试被重复执行时,不应对状态产生重复副作用(比如重复扣费、重复插入数据库)。设计状态更新规则时,保证节点是“幂等”的——执行一次和执行多次结果相同。
第三,演进式建模。不要一开始就画一张20个节点的巨图。先画3个节点的MVP流程跑通,再加分支、加子图、加并行。地铁是一段一段建成的,Agent流程也应当如此。每一次改动后运行测试集,确保旧功能不回归。
第四,性能优化。在并行节点较多的图中,关注最慢节点(瓶颈),考虑给它加缓存或拆分;对于高频调用的子图,设置合理的超时上限,避免某个外部API卡死拖垮全流程。
画图容易,养图难——LangGraph把Agent流程变成了一张可维护的地铁图,但只有配上日志、幂等与演进式开发,这张图才能真正跑在生产环境里几十年不“瘫痪”。
- Recoverable Failures可以通过重试或回退恢复的故障数。
- Total Failures总故障数。
AI 应用场景
客服工单自动分类与流转
阶段:工作流编排(DAG建模 + 条件分支)
解决的问题:传统客服系统靠人工读工单、判断类别、分派给对应部门,高峰期积压严重。用LangGraph建模后,工单进入DAG:先由“分类节点”调用大模型判断工单类型(退换货/技术咨询/投诉),再经条件分支路由到不同处理子图,每个子图内部再并行执行不同动作。
具体案例:某电商平台将用户退货申请接入LangGraph流程:节点A提取订单信息,节点B并行调用“库存系统查询”与“用户信用分查询”,汇合后由条件边判定“是否符合无理由退货”,符合则调用退款API,否则转人工。整个流程从原来的15分钟缩短到40秒。
多Agent研究报告生成系统
阶段:多Agent协作 + 并行与汇合
解决的问题:单一大模型生成研究报告时,常常“资料查不全、结论偏一面”。基于LangGraph搭建“研究员+分析师+写作者”三Agent协作流程:研究员Agent并行检索多路信息源,分析师Agent对检索结果做交叉验证与提炼,写作者Agent最终成稿。
具体案例:某投研团队部署了一个“行业研究报告生成器”:3个研究员子图分别负责“政策面”“基本面”“市场情绪”三路并行检索,汇合后交给分析师子图做矛盾检测,写作子图再行文。每份报告的执行过程都可视化呈现,分析师可以回溯任何结论的生成路径。
代码仓库智能审查流水线
阶段:循环与递归 + 条件分支路由
解决的问题:代码审查中,AI需要基于审查意见多次修改代码,形成“提交→审查→修改→再审查”的循环。LangGraph把这个循环显式建模出来:审查节点输出问题列表,条件边判断“是否有严重问题”,是则回到修改节点重试,达到最大迭代次数后强制结束并通知人工介入。
具体案例:某中型技术团队用LangGraph搭建PR审查助手:每次代码提交自动触发审查子图,LLM逐文件检查并将问题写入State中的"review_issues"字段。若存在问题,则代码修改子图根据建议自动生成补丁并重新提交,最多迭代3轮。全程运行记录在案,开发者可随时查看每一轮的问题与修改。
Python 演示
"""简易LangGraph风格DAG状态机:内容合规审查工作流"""from typing import Dict, Any, List class DAGStateMachine: """一个极简DAG状态机,仿LangGraph核心语义:节点+条件边+状态字典""" def __init__(self) -> None: self.nodes: Dict[str, Any] = {} self.edges: Dict[str, List[str]] = {} self.cond_edges: Dict[str, Any] = {} self.start_node: str = "" self.gc_count = 0 # 流程执行计数器 def add_node(self, name: str, fn: Any) -> None: """注册节点:fn为可调用对象,入参state,返回更新字典""" self.nodes[name] = fn self.edges[name] = [] self.cond_edges[name] = [] def add_edge(self, src: str, dst: str) -> None: self.edges[src].append(dst) def add_conditional_edge(self, src: str, router: Any, mapping: Dict[str, str]) -> None: self.cond_edges[src] = (router, mapping) def set_start(self, name: str) -> None: self.start_node = name def run(self, init: Dict[str, Any], max_steps: int = 10) -> Dict[str, Any]: """从起始节点开始执行,直到没有出边或达到步数上限""" state: Dict[str, Any] = {} if init is None else dict(init) current = self.start_node steps = 0 while current and steps < max_steps: self.gc_count += 1 steps += 1 node_fn = self.nodes[current] delta = node_fn(state) if delta is not None: state.update(delta) if self.cond_edges.get(current): router, mapping = self.cond_edges[current] branch = router(state) target = mapping.get(branch) if target is None: target = mapping.get("__default__") current = target else: if not self.edges.get(current): break current = self.edges[current][0] state["__steps__"] = steps return state def extract_questions(state: Dict[str, Any]) -> Dict[str, Any]: """节点1:从用户输入中提取待检查要点""" text = state.get("user_input", "") questions = [s.strip() for s in text.split("。") if s.strip()] return {"questions": questions, "status": "extracted"} def check_compliance(state: Dict[str, Any]) -> Dict[str, Any]: """节点2:模拟合规检查(此处为规则示例,可替换为LLM调用)""" questions = state.get("questions", []) bad_words = ["暴力", "违法", "刷单"] issues = [] for i, q in enumerate(questions): for w in bad_words: if w in q: issues.append({"index": i, "word": w}) break return {"issues": issues} def router_quality(state: Dict[str, Any]) -> str: """条件边:根据是否有违规词决定走'通过'还是'驳回'""" if len(state.get("issues", [])) == 0: return "pass" return "reject" def finalize(state: Dict[str, Any]) -> Dict[str, Any]: state["review_result"] = "通过" if not state.get("issues") else "需人工复核" state["status"] = "done" return state def fix_content(state: Dict[str, Any]) -> Dict[str, Any]: """驳回后回到修复节点,重试计数+1""" state["retry_count"] = state.get("retry_count", 0) + 1 return {"fixed": True} def main() -> None: sm = DAGStateMachine() sm.add_node("extract", extract_questions) sm.add_node("check", check_compliance) sm.add_node("fix", fix_content) sm.add_node("final", finalize) sm.set_start("extract") sm.add_edge("extract", "check") sm.add_edge("final", "final") sm.add_conditional_edge( "check", router_quality, {"pass": "final", "reject": "fix", "__default__": "final"}, ) sm.add_conditional_edge( "fix", lambda s: "recheck", {"recheck": "check"}, ) # fix -> check 的隐式条件边:只要在fix中retry_count不超过3就继续 result = sm.run( {"user_input": "如何提升商品销量?请讲解合规运营方法,不要提刷单等违法手段。"}, max_steps=10, ) print("执行步数:", result["__steps__"]) print("违规问题:", len(result.get("issues", []))) print("审查结果:", result.get("review_result")) if __name__ == "__main__": main()
class DAGStateMachine状态机核心类,self.nodes存节点函数,self.edges存静态边,self.cond_edges存条件边。注意self.gc_count用_count后缀避免与方法重名。def add_node(self, name, fn)注册节点,fn必须是入参state、返回字典的可调用对象。add_conditional_edge(self, src, router, mapping)注册条件边:router(state)返回分支名,mapping决定分支名→目标节点。def run(self, init, max_steps)核心执行循环:按DAG拓扑执行节点,每次更新状态,遇到条件边就路由,直到终点或步数上限。state.update(delta)状态合并更新:节点返回的字典覆盖到全局状态,这正是LangGraph的状态管理语义。def router_quality(state)路由函数:从状态中读issues列表判断返回pass或reject。sm.add_conditional_edge("fix", lambda s: "recheck", {...})演示循环:修复节点无条件回到检查节点,形成“检查→修复→再检查”循环。
常见误区
误区一听到“有向无环图”,初学者就认为LangGraph做不了多轮对话、做不了“反思-修正-再反思”这类循环流程,因为“无环”就是禁止回头。
正解DAG约束的是“图结构本身不能有闭合环路”,但完全可以通过“条件边+上游节点引用”的方式模拟循环。就像地铁用折返线让列车在两条轨道间来回跑。LangGraph中常见的做法是:节点A→节点B→条件边→(未达标则回到A),但必须用计数器或退出条件控制循环次数上限,否则会死循环烧token。
为何常见:因为“有向无环图”这个词太有迷惑性,让初学者把“图结构约束”与“执行流程能力”混为一谈。实际上图的边是静态定义,而执行时的动态跳转是由条件边控制的。
误区总觉得LangGraph里的State本质上就是“聊天记录”,把用户消息和AI回复存成一个列表就完事了,其他字段都是多余的。
正解对话历史只是State中一个普通字段。LangGraph的State是一个结构化字典,可以包含用户画像、检索结果、中间评分、重试次数、业务标识等任意字段。节点只读写自己关心的字段,这才是它支持复杂编排的根基。
为何常见:因为大多数初学者的Agent启蒙都是从“ChatCompletion对话”来的,天然把“上下文”等同于“消息列表”。而工程化Agent需要的是“全流程上下文”,消息只是其中一部分。
误区看到LangGraph支持并行fan-out,就恨不得把每个节点都拆成并行的,以为这样总时间一定会大幅缩短。
正解并行只在分支之间真正无依赖时才有收益。如果分支B需要分支A的结果,强行并行只会导致B空等或出错。另外并行节点共享状态时若写同一字段,会互相覆盖。设计时要先画出依赖关系,只在“多路独立信息获取”这种场景下用并行。
为何常见:因为直觉上“同时做多件事肯定更快”,忽略了DAG中依赖边的存在以及汇合点的等待语义。
误区写条件路由函数时,凭直觉返回`True`/`False`或返回“下一个节点对象”,然后在LangGraph里配置时报错。
正解LangGraph条件边返回的是字符串类型的边名(或“下一个节点名”),由路由函数根据状态内容动态决定。映射关系在注册条件边时指定。这相当于道岔扳向哪条轨道,轨道是有名字的。
为何常见:编程直觉里“条件判断就应该返回bool”,忽略了图模型中边本身就是一等公民。
面试题
Q1. LangGraph中的DAG指的是什么?
- A.一种深度学习网络结构
- B.有向无环图,节点表示动作、边表示数据流向← 正确答案
- C.数据增强生成技术
- D.分布式应用网关
答案:B。DAG即Directed Acyclic Graph(有向无环图),在LangGraph中节点代表Agent动作,有向边代表执行依赖与数据流动方向。选项A是神经网络概念,C是RAG(检索增强生成)的变体,D是后端架构术语,均与LangGraph的编排建模无关。
Q2. 关于LangGraph中的State,下列说法正确的是?
- A.State只能保存字符串类型的数据
- B.State是一个贯穿流程的结构化字典,各节点可读写不同字段← 正确答案
- C.State与对话历史完全等价,不需要其他字段
- D.每个节点拥有完全独立的State
答案:B。State是全局共享的结构化字典,节点从其中读取输入并写入输出,字段可任意扩展。对话历史只是其中的一个字段。选项A错误(State支持任意Python类型),C过于狭隘,D与“全局共享”的核心设计矛盾。
Q3. LangGraph条件边中,路由函数的返回值是?
- A.布尔值(True/False)
- B.下一个节点的Python对象
- C.字符串(分支边名或目标节点名)← 正确答案
- D.一段提示词文本
答案:C。路由函数读取State后返回一个字符串,LangGraph用该字符串与注册条件边时的映射关系匹配,决定下一步走向。返回布尔值或节点对象都不符合LangGraph的API契约。这就像道岔扳向岔道的“名字”而非“道岔本身”。
Q4. 下列哪种方式可以在LangGraph中实现“循环”效果?
- A.在图中画一条从节点B回到节点A的无条件边
- B.使用条件边,当条件不满足时回到上游节点,并设置最大迭代次数← 正确答案
- C.使用深度优先搜索遍历整个图
- D.在节点内部递归调用自身且不设退出条件
答案:B。LangGraph中循环的常规做法是“条件边+回到上游”,并必须设置终止条件(如重试次数上限)。选项A无条件边会导致死循环,C是图遍历算法并非Agent流程,D的无限递归会导致栈溢出和token浪费。
Q5. 关于LangGraph并行(fan-out/barrier),以下说法正确的有?
- A.一个节点可以有多条出边指向多个并行节点← 正确答案
- B.汇合节点在全部入边完成后才会执行← 正确答案
- C.并行节点可以同时写入同一个状态字段,互不影响
- D.并行只适合分支之间无依赖的场景← 正确答案
答案:ABD。A正确,多出边即fan-out;B正确,汇合点是AND-barrier语义;D正确,无依赖才适合并行;C错误,多个并行节点同时写同一字段会发生覆盖竞争,应在状态设计中为各分支分配独立字段。这就好比各支线列车都在“同一站台上写字”,必须划分区域。
Q6. LangGraph中State字段更新合并的语义包括?
- A.新字段直接加入State← 正确答案
- B.已有字段默认被新值覆盖← 正确答案
- C.字段支持自定义Reducer函数来合并← 正确答案
- D.所有字段一旦写入就不可修改
答案:ABC。State更新默认是“新字段加入、同名字段覆盖”,也支持通过Reducer自定义合并逻辑(如列表追加、计数器累加)。D错误,State在流程中就是不断被更新的。整个流程就像列车沿途不断装卸不同货物。
Q7. 在LangGraph中,一个子图(SubGraph)被封装为一个节点嵌套进父图,这属于什么能力?
- A.并行计算
- B.递归调用
- C.工作流编排与模块复用← 正确答案
- D.状态持久化
答案:C。子图嵌套是LangGraph工作流编排的核心能力,它让开发者把复杂流程拆分为可独立调试的模块,再组合成更大流程。它不等同于并行(A),也不仅是递归(B,子图内部可以没有递归),检查点(D)是另一套机制。就像城市地铁网中一条线路内部还分多个区段。
Q8. 关于LangGraph多Agent协作的状态设计,以下做法合理的有?
- A.为每个Agent划分独立的私有字段区← 正确答案
- B.设定全局共享字段区给Agent间传递消息← 正确答案
- C.所有Agent共同读写同一个字段以避免同步成本
- D.在条件边中读取特定Agent的写入结果来决定下一步← 正确答案
答案:ABD。合理设计是“私有字段+共享字段”结合,私有不被他人乱写,共享用于协调。C错误:所有Agent共同读写同一字段极易产生覆盖竞争,且难以追踪数据来源。D正确:通过条件边读取某Agent产出做路由判断,是多Agent编排的常见模式。这如同每列地铁有自己的车厢(私有),但换乘大厅(共享)供乘客通行。
Q9. 在LangGraph中设置节点超时与检查点(Checkpoint)的主要目的是?
- A.提升模型推理精度
- B.保证流程在故障后可从最近状态恢复← 正确答案
- C.减少模型调用的token数
- D.让节点自动生成测试用例
答案:B。检查点将每步状态持久化,超时则触发重试或降级,二者配合实现了“可恢复性”。它们不直接提升模型精度(A错)、不减少token(C错)、与测试生成无关(D错)。类似地铁的行车记录黑匣子和应急预案。
Q10. 一个LangGraph工作流包含节点A(起点)、B、C(可由A并行到达)、D(汇合点,接收B和C),其中B和C分别向State的b_result和c_result字段写入数据,D节点需要同时使用两者。问:D触发时默认能拿到完整数据吗?
- A.不能,因为并行执行时数据会被覆盖
- B.能,汇合点在所有入边完成后触发,且两个字段互不冲突← 正确答案
- C.能,因为LangGraph会缓存所有历史状态
- D.不能,因为B和C只能同时写一个字段
答案:B。汇合节点默认AND语义,即B和C都执行完才轮到D;同时B、C分别写b_result和c_result两个不同字段,状态合并不会冲突。A的“覆盖”只在同名字段写入时发生。这就像两条不同编号的地铁线(3号线、5号线)各自开到同一换乘站,乘客下车后在站厅汇合,互不挤占站台。