news 2026/6/9 11:41:22

LangGraph学习-(1)跑通一个最小状态图

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LangGraph学习-(1)跑通一个最小状态图

学习目标:

  • 跑通一个最小状态图。
  • 理解State / Node / Edge的关系。
  • 观察条件边如何改变执行路径。
  1. StateGraph
    StateGraph 是 LangGraph 的核心概念,它用"图"的方式来编排任务执行流程。
    StateGraph 的三个核心概念
    — State(状态):数据在节点之间流动的容器,由 TypedDict 定义结构。
    — Node(节点):图中的执行单元,每个节点读取并修改状态。
    — Edge(边):定义节点之间的连接关系。
    — Conditional Edge(条件边):根据当前状态选择走哪条路径。

注意事项:

1. 节点拿到的是“当前完整状态”,LangGraph 的 StateGraph 本质就是:节点读取共享状态,然后返回 Partial<State>,也就是状态的一部分2. 节点不需要返回完整 state,只返回要修改的字段3. 下一个节点看到的是“合并后的状态”

建图流程分为四步:
1. 创建 StateGraph 实例,传入状态结构定义。
2. 用 add_node 注册所有节点(名字→函数的映射)。
3. 用 set_entry_point / add_edge / add_conditional_edges 定义边。
4. 调用 compile() 编译为可执行的图。

"""Day1:StateGraph 入门学习实现。 StateGraph 是 LangGraph 的核心概念,它用"图"的方式来编排任务执行流程。 本 demo 通过一个最小状态图(不接 LLM),帮助你理解三个核心概念: — State(状态):数据在节点之间流动的容器,由 TypedDict 定义结构。 — Node(节点):图中的执行单元,每个节点读取并修改状态。 — Edge(边):定义节点之间的连接关系。 — Conditional Edge(条件边):根据当前状态选择走哪条路径。 运行本 demo 会执行两个测试用例,展示条件边如何让同一张图走出两种不同的路径。""" from typingimportLiteral, TypedDict from langgraph.graphimportEND, StateGraph class GraphState(TypedDict):"""最小状态定义,控制在4个字段以内。 每个字段承担不同的角色: — question:外部输入,由图调用者传入,节点只读取不修改。 — need_tool:节点分析后产出的布尔标记,驱动条件边做路由决策。 — answer:多个节点接力构建的输出字段,展示状态如何逐节点累积。 — steps:路径追踪列表,每个节点将自己的名字追加到末尾, 最终形成完整的执行路径,用于验证图的执行顺序。""" question: str need_tool: bool answer: str steps: list[str]def analyze_question(state: GraphState)->dict:"""第一个节点:分析问题是否包含需要工具检索的关键词。 这是图中的"入口节点"。它读取 question 并判断 need_tool, 后续的条件边将根据这个标记决定走哪条分支。 注意:节点函数接收当前完整状态,返回一个字典(只包含要更新的字段)。 StateGraph 会把返回的字典合并到状态中——不需要返回所有字段。""" need_tool_words=["搜索","查找","计算","翻译"]need_tool=any(wordinstate["question"]forwordinneed_tool_words)print(f'[analyze_question] 问题:"{state["question"]}"')print(f"[analyze_question] 判断结果:need_tool={need_tool}")return{"need_tool":need_tool,"steps":state["steps"]+["analyze_question"],}def retrieve_info(state: GraphState)->dict:"""模拟工具检索(仅当need_tool=True 时才会走到此节点)。 这个节点展示"需要工具"的分支逻辑。在真实场景中,这里会调用 搜索引擎或 API,本 demo 出于学习目的,只是模拟生成一个检索结果。 注意此节点的执行路径可能被跳过:如果 analyze_question 认为 不需要工具,条件边会直接绕开此节点。""" print(f"[retrieve_info] 模拟检索:{state['question']}")result=f'[检索结果] 关于"{state["question"]}"的参考资料'print(f"[retrieve_info] 检索到:{result}")return{"answer":result,"steps":state["steps"]+["retrieve_info"],}def generate_answer(state: GraphState)->dict:"""根据是否有检索结果,以不同策略合成最终回答。 这个节点展示了状态驱动的分支行为——它的执行逻辑依赖上游节点 在状态中留下的数据。如果有检索结果,就做综合回答;否则直接回答。 条件边决定节点是否执行,而节点内部还可以根据状态做进一步的逻辑判断。"""ifstate["answer"]: answer=f"综合检索结果回答:{state['answer']}"else: answer=f"直接回答:{state['question']} 是一个基础问题,直接回答即可。"print(f"[generate_answer] 生成回答:{answer}")return{"answer":answer,"steps":state["steps"]+["generate_answer"],}def finalize(state: GraphState)->dict:"""终结点:输出最终结果快照,然后结束。 这个节点之后不再有处理逻辑,直接连接到 END。 它主要负责输出最终状态供学习者观察完整的执行结果。""" print(f"[finalize] 最终 answer = {state['answer']}")print(f"[finalize] 执行路径 steps = {state['steps']}")return{"steps":state["steps"]+["finalize"],}def route_decision(state: GraphState)->Literal["retrieve","direct"]:"""条件边路由函数:根据 need_tool 选择下一个节点。 条件边是 StateGraph 区别于普通函数链的关键特性: 普通函数链是固定的 A→B→C,而条件边让执行路径在运行时动态决定。 路由函数返回一个字符串键,图会查找对应的边映射表来确定下一个节点。 这种"函数 + 映射表"的模式使得路由逻辑既可读又灵活。"""ifstate.get("need_tool", False): print("[route_decision] 需要工具 → 走 retrieve 分支")return"retrieve"print("[route_decision] 不需要工具 → 走 direct 分支")return"direct"def build_graph()->StateGraph:"""构建并编译状态图。 建图流程分为四步:1. 创建 StateGraph 实例,传入状态结构定义。2. 用 add_node 注册所有节点(名字→函数的映射)。3. 用 set_entry_point / add_edge / add_conditional_edges 定义边。4. 调用 compile()编译为可执行的图。 注意编译前的图是"定义态",compile()之后才是"运行态"。 编译过程会做拓扑检查,确保所有节点可达、没有死环。""" builder=StateGraph(GraphState)# 注册 4 个节点builder.add_node("analyze_question", analyze_question)builder.add_node("retrieve_info", retrieve_info)builder.add_node("generate_answer", generate_answer)builder.add_node("finalize", finalize)# 设置图的入口点builder.set_entry_point("analyze_question")# 条件边:从 analyze_question 出发,根据路由函数决定走向builder.add_conditional_edges("analyze_question", route_decision,{"retrieve":"retrieve_info","direct":"generate_answer"},)# 普通边:确定性的节点间连接builder.add_edge("retrieve_info","generate_answer")builder.add_edge("generate_answer","finalize")# builder.set_finish_point("finalize")returnbuilder.compile()def run_case(graph: StateGraph, question: str)->None:"""运行单个测试用例,展示节点执行顺序和状态变化。 graph.stream()是 LangGraph 的流式执行方法,逐节点产出事件。 每个事件是一个 dict:{节点名: 该节点产出的状态更新}。 这样可以在每个节点执行后立即看到它对状态做了哪些修改。 相比 graph.invoke()只返回最终状态,stream()更适合学习目的, 因为它暴露了图执行的中间过程。""" print(f">>> 输入:{question}")initial_state: GraphState={"question":question,"need_tool":False,"answer":"","steps":[],}all_steps: list[str]=[]foreventingraph.stream(initial_state):fornode_name, state_updateinevent.items(): print(f"--- 节点 {node_name} 产出的状态更新 ---")forkey, valueinstate_update.items(): print(f" {key} = {value}")if"steps"instate_update: all_steps=state_update["steps"]print()print(f"执行路径:{' → '.join(all_steps)}")print("="*60)def main()->None:"""演示条件边在不同输入下的两种执行路径。 测试用例1不包含关键词走 direct 分支:3 个节点执行。 测试用例2包含"搜索"关键词走 retrieve 分支:4 个节点执行。 同一张图,不同的输入,走出了不同的路径——这就是条件边的价值。""" print("="*60)print(" Day 1:StateGraph 入门")print(" 条件边演示:同一图结构,两种执行路径")print("="*60)print()graph=build_graph()# 测试用例 1:不包含关键词 → need_tool=False → direct 分支run_case(graph,"Python 的列表推导式怎么用?")# 测试用例 2:包含关键词 → need_tool=True → retrieve 分支# run_case(graph, "搜索一下 Python 语法教程")if__name__=="__main__":main()

其中

foreventingraph.stream(initial_state):fornode_name, state_updateinevent.items(): print(f"--- 节点 {node_name} 产出的状态更新 ---")forkey, valueinstate_update.items(): print(f" {key} = {value}")if"steps"instate_update: all_steps=state_update["steps"]print()

重点讲解

1. graph.stream(initial_state)是什么 它会启动整张图的执行,并且不是等整张图跑完才一次性返回结果,而是: 跑完一个节点 立刻产出一个事件 event 再继续下一个节点 所以它是“流式”的。2. 和它对应的是 graph.invoke(initial_state): invoke():只给你最后结果 stream():把中间每一步也给你看 注意事项: event 不是完整状态。 state_update 只是这个节点“这一次返回的更新内容”。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 11:40:31

08-MCP 下篇:开发自己的 MCP Server —— 当现成的工具不够用

MCP 下篇&#xff1a;开发自己的 MCP Server —— 当现成的工具不够用 上篇你学会了装别人的 MCP Server。但每个公司的 API、每个团队的内部工具、每个项目的数据源都不一样。总有一个时刻&#xff0c;现成的 MCP Server 都满足不了你。这篇就是为那个时刻准备的。 什么时候需…

作者头像 李华
网站建设 2026/6/9 11:38:21

终极免费指南:如何用Wand-Enhancer解锁WeMod完整专业功能

终极免费指南&#xff1a;如何用Wand-Enhancer解锁WeMod完整专业功能 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/gh_mirrors/we/Wand-Enhancer 还在为WeMod专业版的高昂订阅费发愁…

作者头像 李华
网站建设 2026/6/9 11:37:37

潜在扩散模型与Kandinsky 2.1架构深度解析

1. 潜在扩散模型技术解析潜在扩散模型&#xff08;Latent Diffusion Models, LDMs&#xff09;是当前生成式AI领域最具突破性的技术架构之一。与直接在像素空间操作的扩散模型不同&#xff0c;LDMs将扩散过程转移到经过压缩的潜在空间&#xff0c;这一设计带来了显著的效率提升…

作者头像 李华
网站建设 2026/6/9 11:35:32

免费解密网易云NCM音频:ncmdumpGUI终极转换指南

免费解密网易云NCM音频&#xff1a;ncmdumpGUI终极转换指南 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换&#xff0c;Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 你是否遇到过这样的情况&#xff1a;在网易云音乐下…

作者头像 李华
网站建设 2026/6/9 11:28:47

从Aspose.Words的License验证机制聊起:如何安全合规地评估文档转换组件?

深度解析Aspose.Words许可验证机制与商业组件合规评估指南技术选型中的商业组件评估挑战在企业级文档处理系统的技术选型过程中&#xff0c;像Aspose.Words这样的商业组件常常成为架构师的首选方案。这类组件提供了强大的文档转换能力&#xff0c;支持从DOCX到PDF等数十种格式的…

作者头像 李华