动手实践!本节课将带你配置开发环境,使用通义千问API,编写第一个LangGraph程序。通过一个简单的Hello World示例,理解LangGraph代码的基本结构和运行流程。本课结束后,你将能够独立运行和调试LangGraph应用。
🎯 学习目标 学完本节课,你将能够:
配置LangGraph开发环境 获取和配置通义千问API密钥 编写和运行第一个LangGraph程序 理解LangGraph代码的基本结构 掌握基本的调试技巧 1. 环境准备 1.1 检查Python版本 LangGraph需要Python 3.10或更高版本:
1.2 安装依赖包 在项目根目录创建 .venv 虚拟环境(如果还没有):
1 2 3 4 5 6 7 cd /Users/t-yuhaiyang/PycharmProjects/demopython -m venv .venv source .venv/bin/activate
使用uv安装依赖(推荐,更快):
1 2 3 4 5 6 7 8 uv pip install langgraph langchain langchain-community uv pip install dashscope uv pip install python-dotenv pydantic
或者使用普通pip:
1 pip install langgraph langchain langchain-community dashscope python-dotenv pydantic
1.3 获取通义千问API密钥 步骤1:注册阿里云账号 访问:https://dashscope.aliyun.com/
步骤2:开通DashScope服务 登录后,点击”开通DashScope” 选择”按量付费”(有免费额度) 步骤3:创建API Key 进入控制台 点击”API Key管理” 创建新的API Key 复制并保存好这个Key! 步骤4:配置环境变量 在 demo_16 目录下创建 .env 文件:
1 2 3 4 5 6 7 DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
安全提示:
⚠️ 不要把API Key提交到git! ⚠️ 确保 .env 已添加到 .gitignore 2. Hello World示例 2.1 最简单的例子 创建 01_hello_world.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 """ 最简单的LangGraph示例:单节点问候程序 学习目标:理解图、节点、状态的基本概念 """ from typing import TypedDictfrom langgraph.graph import StateGraph, ENDclass State (TypedDict ): """状态定义:在节点间传递的数据""" name: str message: str def greet_node (state: State ) -> State: """ 问候节点:生成问候消息 参数: state: 当前状态,包含name字段 返回: 更新后的状态,包含message字段 """ name = state["name" ] state["message" ] = f"你好,{name} !欢迎学习LangGraph!" return state def create_graph () -> StateGraph: """创建并配置图""" graph = StateGraph(State) graph.add_node("greet" , greet_node) graph.set_entry_point("greet" ) graph.add_edge("greet" , END) return graph def main () -> None : """主函数""" app = create_graph().compile () result = app.invoke({"name" : "小明" }) print (result["message" ]) if __name__ == "__main__" : main()
2.2 运行程序 1 2 cd demo_16/lesson_02python 01_hello_world.py
输出:
2.3 代码解析 1️⃣ 定义状态(State) 1 2 3 class State (TypedDict ): name: str message: str
理解:
TypedDict 提供类型提示,让代码更安全定义了在节点间传递的数据结构 就像定义了一个快递包裹的格式 2️⃣ 定义节点(Node) 1 2 3 4 def greet_node (state: State ) -> State: name = state["name" ] state["message" ] = "你好..." return state
理解:
节点就是一个函数 输入:当前状态 输出:更新后的状态 节点的职责:完成一件具体的事 3️⃣ 构建图(Graph) 1 2 3 4 graph = StateGraph(State) graph.add_node("greet" , greet_node) graph.set_entry_point("greet" ) graph.add_edge("greet" , END)
理解:
StateGraph(State) - 创建图,指定状态类型add_node("名称", 函数) - 给节点起名字set_entry_point - 从哪个节点开始add_edge("起点", "终点") - 连接节点END - 特殊标记,表示结束4️⃣ 运行图 1 2 3 app = create_graph().compile () result = app.invoke({"name" : "小明" }) print (result["message" ])
理解:
compile() - 把图编译成可执行的应用invoke(初始状态) - 执行图,传入初始数据返回最终状态 3. 进阶示例:多节点链式调用 3.1 示例:问候+询问 创建 02_chain_nodes.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 """ 多节点示例:链式调用 学习目标:理解节点间的数据传递 """ from typing import TypedDictfrom langgraph.graph import StateGraph, ENDclass State (TypedDict ): name: str greeting: str question: str def greet_node (state: State ) -> State: """生成问候语""" state["greeting" ] = f"你好,{state['name' ]} !" print (f"[节点1] 执行问候: {state['greeting' ]} " ) return state def ask_node (state: State ) -> State: """生成询问""" state["question" ] = "今天过得怎么样?" print (f"[节点2] 执行询问: {state['question' ]} " ) return state def create_graph () -> StateGraph: graph = StateGraph(State) graph.add_node("greet" , greet_node) graph.add_node("ask" , ask_node) graph.set_entry_point("greet" ) graph.add_edge("greet" , "ask" ) graph.add_edge("ask" , END) return graph def main () -> None : app = create_graph().compile () result = app.invoke({"name" : "小红" }) print ("\n[最终结果]" ) print (f"问候: {result['greeting' ]} " ) print (f"询问: {result['question' ]} " ) if __name__ == "__main__" : main()
输出:
1 2 3 4 5 6 [节点1] 执行问候: 你好,小红! [节点2] 执行询问: 今天过得怎么样? [最终结果] 问候: 你好,小红! 询问: 今天过得怎么样?
理解:
节点按顺序执行:greet → ask state 在节点间自动传递每个节点都能访问和修改 state 4. 集成AI:使用通义千问 4.1 示例:AI问候助手 创建 03_with_llm.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 """ 集成通义千问:AI问候助手 学习目标:理解如何在节点中使用LLM """ import osfrom typing import TypedDictfrom langgraph.graph import StateGraph, ENDfrom langchain_community.llms import Tongyifrom dotenv import load_dotenvload_dotenv() class State (TypedDict ): user_name: str user_mood: str ai_response: str def get_llm () -> Tongyi: """ 创建通义千问LLM实例 注意:需要在.env中配置DASHSCOPE_API_KEY """ api_key = os.getenv("DASHSCOPE_API_KEY" ) if not api_key: raise ValueError("请在.env文件中设置DASHSCOPE_API_KEY" ) return Tongyi( model="qwen-turbo" , api_key=api_key, temperature=0.7 , ) def ai_greet_node (state: State ) -> State: """ 使用AI生成个性化问候 根据用户名字和心情,生成贴心的问候语 """ llm = get_llm() prompt = f""" 你是一个热情友好的助手。 用户信息: - 名字:{state['user_name' ]} - 心情:{state['user_mood' ]} 请生成一句贴心的问候语,要体现出对用户心情的关注。 直接输出问候语,不要有其他多余内容。 """ print ("[AI思考中...]" ) response = llm.invoke(prompt) state["ai_response" ] = response return state def create_graph () -> StateGraph: graph = StateGraph(State) graph.add_node("ai_greet" , ai_greet_node) graph.set_entry_point("ai_greet" ) graph.add_edge("ai_greet" , END) return graph def main () -> None : """运行示例""" app = create_graph().compile () test_cases = [ {"user_name" : "小明" , "user_mood" : "开心" }, {"user_name" : "小红" , "user_mood" : "有点累" }, {"user_name" : "小刚" , "user_mood" : "兴奋" }, ] for case in test_cases: print (f"\n{'=' *50 } " ) print (f"输入: {case } " ) print (f"{'=' *50 } " ) result = app.invoke(case ) print (f"\nAI回复: {result['ai_response' ]} " ) if __name__ == "__main__" : main()
4.2 运行AI示例 可能的输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 ================================================== 输入: {'user_name': '小明', 'user_mood': '开心'} ================================================== [AI思考中...] AI回复: 小明,你好呀!看到你这么开心,我也跟着高兴起来了!今天有什么开心的事要分享吗? ================================================== 输入: {'user_name': '小红', 'user_mood': '有点累'} ================================================== [AI思考中...] AI回复: 小红,辛苦了!感觉有点累的话,要记得好好休息哦。要不要喝杯热茶,放松一下?
4.3 代码要点 配置LLM 1 2 3 4 5 6 7 from langchain_community.llms import Tongyillm = Tongyi( model="qwen-turbo" , api_key=api_key, temperature=0.7 , )
模型选择:
qwen-turbo - 便宜快速,适合测试qwen-plus - 更强能力,适合生产qwen-max - 最强能力,适合复杂任务temperature参数:
0.0-0.3 - 稳定、一致(适合事实性任务)0.4-0.7 - 平衡(适合对话)0.8-1.0 - 创意、多样(适合创作)调用LLM 1 2 3 response = llm.invoke(prompt) response = await llm.ainvoke(prompt)
5. 调试技巧 5.1 打印状态变化 在每个节点中打印状态:
1 2 3 4 5 6 7 8 def my_node (state: State ) -> State: print (f"[进入节点] 当前状态: {state} " ) state["result" ] = "处理完成" print (f"[离开节点] 更新后状态: {state} " ) return state
5.2 使用stream查看执行过程 1 2 3 4 5 app = create_graph().compile () for step in app.stream({"name" : "小明" }): print (f"当前步骤: {step} " )
5.3 可视化图结构 1 2 3 4 5 from IPython.display import Image, displayapp = create_graph().compile () display(Image(app.get_graph().draw_mermaid_png()))
5.4 异常处理 1 2 3 4 5 6 7 8 9 10 11 def safe_node (state: State ) -> State: """带异常处理的节点""" try : result = risky_operation(state) state["result" ] = result except Exception as e: print (f"[错误] {e} " ) state["error" ] = str (e) return state
6. 常见问题 Q1: 为什么要用TypedDict? 答: 提供类型提示,让IDE能够自动补全,减少错误:
1 2 3 4 5 6 7 8 class State (TypedDict ): name: str def my_node (state: State ) -> State: x = state["name" ] ✅ y = state["age" ] ❌ IDE会提示错误
Q2: 节点一定要返回state吗? 答: 是的!节点必须返回state,否则数据无法传递到下一个节点:
1 2 3 4 5 6 7 8 9 def bad_node (state: State ): state["result" ] = "done" def good_node (state: State ) -> State: state["result" ] = "done" return state
Q3: 可以修改state中不存在的字段吗? 答: 可以!TypedDict只是类型提示,运行时可以添加新字段:
1 2 3 4 5 6 7 class State (TypedDict ): name: str def my_node (state: State ) -> State: state["age" ] = 18 return state
建议: 提前在State中定义所有可能用到的字段。
Q4: 通义千问调用失败怎么办? 排查步骤:
检查API Key是否正确
检查网络连接
1 ping dashscope.aliyuncs.com
检查余额 登录DashScope控制台查看账户余额
查看错误信息
1 2 3 4 5 try : response = llm.invoke(prompt) except Exception as e: print (f"错误类型: {type (e)} " ) print (f"错误信息: {e} " )
Q5: 节点执行顺序是怎样的? 答: 按照边的定义执行:
1 2 3 4 5 graph.set_entry_point("A" ) graph.add_edge("A" , "B" ) graph.add_edge("B" , "C" ) graph.add_edge("C" , END)
7. 练习题 练习1:个性化问候(基础) 修改 01_hello_world.py,增加时间判断:
如果是早上(6-12点),说”早上好” 如果是下午(12-18点),说”下午好” 如果是晚上(18-24点),说”晚上好” 其他时间说”深夜好” 提示:使用 datetime.now().hour
练习2:多轮对话(进阶) 创建一个三节点的对话链:
节点1:问”你叫什么名字?” 节点2:问”你来自哪里?” 节点3:根据前面的信息生成总结 练习3:AI翻译助手(AI集成) 创建一个翻译助手:
输入:中文句子 节点1:翻译成英文 节点2:翻译成日文 输出:中英日三种语言 提示:使用通义千问,prompt是关键!
练习4:错误处理(健壮性) 修改 03_with_llm.py,增加错误处理:
如果API调用失败,重试3次 如果3次都失败,返回默认问候语 记录错误信息到state 8. 小结 你学到了什么 ✅ 如何配置LangGraph开发环境 ✅ 如何获取和使用通义千问API ✅ LangGraph代码的基本结构:
定义State(状态) 定义Node(节点函数) 构建Graph(添加节点和边) 运行Application(invoke调用) ✅ 如何在节点中集成LLM ✅ 基本的调试技巧
核心代码模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class State (TypedDict ): input : str output: str def my_node (state: State ) -> State: return state graph = StateGraph(State) graph.add_node("my_node" , my_node) graph.set_entry_point("my_node" ) graph.add_edge("my_node" , END) app = graph.compile () result = app.invoke({"input" : "test" })
📚 下一步 现在你已经会写基本的LangGraph程序了!但目前的例子都是简单的线性流程,实际应用中往往需要更复杂的状态管理。
下一课: Lesson 03: 状态管理和节点
我们将深入学习:
State的高级用法(列表、嵌套字典) 节点间如何安全地共享数据 状态更新的最佳实践 构建一个多轮对话机器人 📖 参考资料 LangGraph快速开始 通义千问API文档 Python TypedDict文档