Files
langgraph-learning/chapter02/01_dynamic_send.ipynb
T

341 lines
31 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"cells": [
{
"cell_type": "code",
"id": "initial_id",
"metadata": {
"collapsed": true,
"ExecuteTime": {
"end_time": "2026-07-30T13:49:53.991892Z",
"start_time": "2026-07-30T13:49:53.953141Z"
}
},
"source": [
"\"\"\"\n",
"动态扇出(fan-out)\n",
"\"\"\"\n",
"\n",
"import os\n",
"from dotenv import load_dotenv\n",
"from langchain.chat_models import init_chat_model\n",
"\n",
"load_dotenv(override=True)\n",
"\n",
"llm = init_chat_model(\n",
" model=\"openai:qwen3.6-flash\",\n",
" base_url=os.getenv(\"BASE_URL\"),\n",
" api_key=os.getenv(\"API_KEY\")\n",
")"
],
"outputs": [],
"execution_count": 23
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-07-30T13:49:54.025529Z",
"start_time": "2026-07-30T13:49:54.012229Z"
}
},
"cell_type": "code",
"source": [
"from typing import TypedDict, Literal, Sequence\n",
"\n",
"\n",
"class OverallState(TypedDict):\n",
" topic: str\n",
" poem: str\n",
" ci_poem: str\n",
" joke: str\n",
"\n",
"\n",
"class WorkerState(TypedDict):\n",
" content_type: Literal[\"poem\", \"joke\", \"ci_poem\"]\n",
" prompt: str\n",
"\n",
"\n",
"class InputState(TypedDict):\n",
" topic: str\n",
"\n",
"\n",
"class OutputState(TypedDict):\n",
" poem: str\n",
" ci_poem: str\n",
" joke: str"
],
"id": "ae2dc1758905a3a9",
"outputs": [],
"execution_count": 24
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-07-30T13:49:54.037302Z",
"start_time": "2026-07-30T13:49:54.025996Z"
}
},
"cell_type": "code",
"source": [
"from langgraph.graph import StateGraph, START, END\n",
"from langchain.messages import HumanMessage\n",
"from langgraph.types import Send\n",
"\n",
"\n",
"def worker_node(state: WorkerState) -> OutputState:\n",
" content_type = state[\"content_type\"]\n",
" prompt = state[\"prompt\"]\n",
" res = llm.invoke([HumanMessage(prompt)])\n",
"\n",
" return {\n",
" content_type: res.content\n",
" }\n",
"\n",
"\n",
"def router(state: InputState) -> Sequence[Send]:\n",
" router_prompt = \"请生成关于 {} 主题的{}\"\n",
" en_zh_map = {\n",
" \"poem\": \"七言绝句\",\n",
" \"joke\": \"笑话\",\n",
" \"ci_poem\": \"中文词\"\n",
" }\n",
"\n",
" topic = state[\"topic\"]\n",
" return [\n",
" Send(\n",
" \"worker_node\",\n",
" arg=WorkerState(\n",
" content_type=content_type,\n",
" prompt=router_prompt\n",
" .format(topic, en_zh_map[content_type])\n",
" )\n",
" )\n",
" for content_type in [\"poem\", \"joke\", \"ci_poem\"]\n",
" ]"
],
"id": "3ac3b9c730c21141",
"outputs": [],
"execution_count": 25
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-07-30T13:49:54.057696Z",
"start_time": "2026-07-30T13:49:54.043335Z"
}
},
"cell_type": "code",
"source": [
"builder = StateGraph(\n",
" state_schema=OverallState,\n",
" input_schema=InputState,\n",
" output_schema=OutputState\n",
")\n",
"\n",
"builder.add_node(\"router\", router)\n",
"builder.add_node(\"worker_node\", worker_node)\n",
"\n",
"builder.add_conditional_edges(\n",
" START,\n",
" router,\n",
" path_map={\"worker_node\": \"worker_node\"}\n",
")\n",
"builder.add_edge(\"worker_node\", END)\n",
"\n",
"graph = builder.compile()"
],
"id": "ac2b29823a3df4ee",
"outputs": [],
"execution_count": 26
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-07-30T13:50:20.554204Z",
"start_time": "2026-07-30T13:49:54.058820Z"
}
},
"cell_type": "code",
"source": "res=graph.invoke(InputState(topic=\"汤姆猫\"))",
"id": "235958e92cc997aa",
"outputs": [],
"execution_count": 27
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-07-30T13:50:20.742345Z",
"start_time": "2026-07-30T13:50:20.575371Z"
}
},
"cell_type": "code",
"source": [
"from rich import print as rp\n",
"rp(res)"
],
"id": "354d39c58926f899",
"outputs": [
{
"data": {
"text/plain": [
"\u001B[1m{\u001B[0m\n",
" \u001B[32m'poem'\u001B[0m: \u001B[32m'《咏汤姆猫》\\n蓝灰掠地逐轻风,\\n百计千谋总落空。\\n莫叹连环招屡败,\\n犹凭百态逗群童。\\n\\n【简析】\\n- \u001B[0m\n",
"\u001B[32m**起承转合**:首句以“蓝灰掠地”勾勒经典动画形象,“逐轻风”暗写其与杰瑞的追逐场景;次句“百计千谋总落空”浓缩汤姆屡次布\u001B[0m\n",
"\u001B[32m陷阱却反受挫的喜剧内核;第三句笔锋一转,化贬为褒,赞其屡败屡战的韧劲;末句“百态”既指动画中丰富的肢体与表情夸张,亦\u001B[0m\n",
"\u001B[32m点出其跨越时代的童趣价值。\\n- \u001B[0m\n",
"\u001B[32m**格律**:本诗依《平水韵》上平一东韵,采用七绝平起首句入韵正格,平仄严谨,符合传统近体诗法度。\\n- \u001B[0m\n",
"\u001B[32m**文化呼应**:以古典诗语书写现代卡通形象,不涉直白名词,而通过“逐、空、败、童”等字眼,精准捕捉该角色“滑稽中带温情、\u001B[0m\n",
"\u001B[32m狼狈中见执着”的精神气质,使传统诗体与当代流行文化自然交融。'\u001B[0m,\n",
" \u001B[32m'ci_poem'\u001B[0m: \u001B[32m'以下为您整理围绕 **“汤姆猫(Talking Tom)”** \u001B[0m\n",
"\u001B[32m主题的中文词汇库,已按使用场景分类,方便您在内容创作、市场分析、产品策划或SEO优化中直接调用:\\n\\n### 🐾 \u001B[0m\n",
"\u001B[32m核心IP与角色\\n`会说话的汤姆猫`|`安吉拉(Talking \u001B[0m\n",
"\u001B[32mAngela)`|`本熊`|`汉克狗`|`金杰猫`|`数字宠物`|`虚拟陪伴角色`|`Outfit7`(出品方)\\n\\n### 🎮 \u001B[0m\n",
"\u001B[32m核心玩法与功能\\n`语音模仿`|`回声重播`|`趣味变声`|`指尖互动`|`换装打扮`|`迷你游戏合集`|`家园养成`|`触控反馈`\u001B[0m\n",
"\u001B[32m|`表情捕捉`|`节奏拍手`\\n\\n### ⚙️ \u001B[0m\n",
"\u001B[32m技术与研发方向\\n`实时语音识别`|`声音特征提取`|`口型同步驱动`|`3D动画渲染`|`多语言适配`|`低延迟交互`|`版本热更\u001B[0m\n",
"\u001B[32m新`|`轻量化客户端`\\n\\n### 📦 \u001B[0m\n",
"\u001B[32m衍生内容与生态\\n`同名动画剧集`|`实体毛绒公仔`|`主题盲盒/积木`|`儿童早教延伸应用`|`IP联名文具`|`粉丝同人二创`|\u001B[0m\n",
"\u001B[32m`线上音乐频道`|`AR互动滤镜`\\n\\n### 🌍 \u001B[0m\n",
"\u001B[32m市场与文化属性\\n`亲子互动娱乐`|`解压轻量游戏`|`网络模因(Meme)`|`Z世代童年数字记忆`|`全球化爆款应用`|`寓教于\u001B[0m\n",
"\u001B[32m乐定位`|`跨代际传播`|`安全合规设计`\\n\\n### 💼 \u001B[0m\n",
"\u001B[32m商业与运营术语\\n`免费下载+内购模式`|`激励视频广告`|`应用商店推荐位`|`用户次日留存`|`跨媒介IP发行`|`分级授权体\u001B[0m\n",
"\u001B[32m系`|`区域化本地运营`|`生命周期管理`\\n\\n---\\n📌 **使用建议**:\\n- **内容创作/短视频脚本**:优先组合 `语音模仿 + \u001B[0m\n",
"\u001B[32m指尖互动 + 解压轻量游戏 + 网络模因`\\n- **竞品分析/行业报告**:侧重 `免费+内购模式 + 用户留存 + 跨媒介IP发行 + \u001B[0m\n",
"\u001B[32m生命周期管理`\\n- **儿童产品策划**:可搭配 `安全合规设计 + 亲子互动娱乐 + 家园养成 + 儿童早教延伸`\\n- \u001B[0m\n",
"\u001B[32m**SEO/标签优化**:建议采用 `会说话的汤姆猫系列 / Talking Tom中文版 / 虚拟宠物手游 / \u001B[0m\n",
"\u001B[32m儿童互动APP`\\n\\n如需特定场景的短语搭配、长尾关键词清单或英文对照表,可告知具体用途(如:电商上架、公众号排版、投融\u001B[0m\n",
"\u001B[32m资BP等),我将为您进一步定制。'\u001B[0m,\n",
" \u001B[32m'joke'\u001B[0m: \n",
"\u001B[32m'这里为你原创创作了5个以《猫和老鼠》中“汤姆猫”为主题的笑话,保留了他屡败屡战、表情丰富、总被杰瑞机智反杀的经典人设\u001B[0m\n",
"\u001B[32m,风格轻松幽默,适合全年龄阅读:\\n\\n1. **【职场篇】** \\n汤姆去面试“高级防鼠专员”。 \u001B[0m\n",
"\u001B[32m\\nHR问:“你的核心竞争力是什么?” \u001B[0m\n",
"\u001B[32m\\n汤姆挺起胸膛:“二十年实战经验!精通高空坠落缓冲、电击后快速重启、以及被锅砸扁后的三维复原技术。” \u001B[0m\n",
"\u001B[32m\\nHR点头:“履历很扎实,但我们更看重结果。” \u001B[0m\n",
"\u001B[32m\\n汤姆叹气:“那我补充一条:我擅长在失败中积累素材,为下一代动画师提供反面PPT。” \\n\\n2. **【健身篇】** \u001B[0m\n",
"\u001B[32m\\n汤姆痛定思痛办卡健身,三个月练出八块腹肌,连杰瑞都在评论区留言:“哥,你这体格不当举重冠军可惜了!” \u001B[0m\n",
"\u001B[32m\\n第二天抓鼠,杰瑞只是把奶酪往前挪了半寸。 \\n汤姆全力冲刺,“砰!”直接撞穿三道墙。 \u001B[0m\n",
"\u001B[32m\\n邻居围观问他咋回事,汤姆揉着额头顶着大包说:“力量够了,就是惯性没收住。” \\n\\n3. **【哲学篇】** \u001B[0m\n",
"\u001B[32m\\n小朋友问汤姆:“你明明每次都被打败,为什么从不放弃?” \u001B[0m\n",
"\u001B[32m\\n汤姆推了推不存在的眼镜:“首先,这是工作;其次,如果我赢了,导演就得给我加薪;最后……你想想,谁舍得让一部播了八十多\u001B[0m\n",
"\u001B[32m年的动画片断更?” \\n小朋友恍然大悟:“原来你不是在抓老鼠,是在守护童年呀!” \\n\\n4. **【科技篇】** \u001B[0m\n",
"\u001B[32m\\n汤姆斥巨资下载了“AI智能捕鼠系统”,支持卫星定位、行为预测、自动诱捕。 \u001B[0m\n",
"\u001B[32m\\n首日上阵,屏幕显示:“目标已锁定,正在计算逃逸路线……” \\n杰瑞掏出小圆镜反射阳光晃了汤姆一下。 \u001B[0m\n",
"\u001B[32m\\n手机秒弹:“识别异常,疑似光学干扰。已为您切换至传统模式:跑、跳、躲、挨打。” \u001B[0m\n",
"\u001B[32m\\n汤姆默默关机:“算法再强,也算不出鼠标垫的反光角。” \\n\\n5. **【考试篇】** \\n语文题:请用“汤姆猫”造句。 \u001B[0m\n",
"\u001B[32m\\n学生甲写:“汤姆猫很有毅力。”(老师批:✅ 但缺乏细节) \u001B[0m\n",
"\u001B[32m\\n学生乙写:“汤姆猫每天都在努力,虽然每次都失败,但他从未停止。”(老师批:✅✅ 加分项) \u001B[0m\n",
"\u001B[32m\\n学生丙写:“汤姆猫的创可贴库存比我的作业本还多。”(老师批:✅✅✅ 现实主义杰作,附赠一张漫画配图) \\n\\n💡 \u001B[0m\n",
"\u001B[32m**小贴士**:这些笑话均基于经典动画的喜剧内核二次创作,未涉及任何版权侵权内容。如果你喜欢某种特定风格(比如谐音梗、\u001B[0m\n",
"\u001B[32m冷笑话、或结合现代生活场景),可以告诉我,我再为你定制一批!'\u001B[0m\n",
"\u001B[1m}\u001B[0m\n"
],
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span>\n",
" <span style=\"color: #008000; text-decoration-color: #008000\">'poem'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'《咏汤姆猫》\\n蓝灰掠地逐轻风,\\n百计千谋总落空。\\n莫叹连环招屡败,\\n犹凭百态逗群童。\\n\\n【简析】\\n- </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">**起承转合**:首句以“蓝灰掠地”勾勒经典动画形象,“逐轻风”暗写其与杰瑞的追逐场景;次句“百计千谋总落空”浓缩汤姆屡次布</span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">陷阱却反受挫的喜剧内核;第三句笔锋一转,化贬为褒,赞其屡败屡战的韧劲;末句“百态”既指动画中丰富的肢体与表情夸张,亦</span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">点出其跨越时代的童趣价值。\\n- </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">**格律**:本诗依《平水韵》上平一东韵,采用七绝平起首句入韵正格,平仄严谨,符合传统近体诗法度。\\n- </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">**文化呼应**:以古典诗语书写现代卡通形象,不涉直白名词,而通过“逐、空、败、童”等字眼,精准捕捉该角色“滑稽中带温情、</span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">狼狈中见执着”的精神气质,使传统诗体与当代流行文化自然交融。'</span>,\n",
" <span style=\"color: #008000; text-decoration-color: #008000\">'ci_poem'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'以下为您整理围绕 **“汤姆猫(Talking Tom)”** </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">主题的中文词汇库,已按使用场景分类,方便您在内容创作、市场分析、产品策划或SEO优化中直接调用:\\n\\n### 🐾 </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">核心IP与角色\\n`会说话的汤姆猫`|`安吉拉(Talking </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">Angela)`|`本熊`|`汉克狗`|`金杰猫`|`数字宠物`|`虚拟陪伴角色`|`Outfit7`(出品方)\\n\\n### 🎮 </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">核心玩法与功能\\n`语音模仿`|`回声重播`|`趣味变声`|`指尖互动`|`换装打扮`|`迷你游戏合集`|`家园养成`|`触控反馈`</span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">|`表情捕捉`|`节奏拍手`\\n\\n### ⚙️ </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">技术与研发方向\\n`实时语音识别`|`声音特征提取`|`口型同步驱动`|`3D动画渲染`|`多语言适配`|`低延迟交互`|`版本热更</span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">新`|`轻量化客户端`\\n\\n### 📦 </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">衍生内容与生态\\n`同名动画剧集`|`实体毛绒公仔`|`主题盲盒/积木`|`儿童早教延伸应用`|`IP联名文具`|`粉丝同人二创`|</span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">`线上音乐频道`|`AR互动滤镜`\\n\\n### 🌍 </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">市场与文化属性\\n`亲子互动娱乐`|`解压轻量游戏`|`网络模因(Meme)`|`Z世代童年数字记忆`|`全球化爆款应用`|`寓教于</span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">乐定位`|`跨代际传播`|`安全合规设计`\\n\\n### 💼 </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">商业与运营术语\\n`免费下载+内购模式`|`激励视频广告`|`应用商店推荐位`|`用户次日留存`|`跨媒介IP发行`|`分级授权体</span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">系`|`区域化本地运营`|`生命周期管理`\\n\\n---\\n📌 **使用建议**:\\n- **内容创作/短视频脚本**:优先组合 `语音模仿 + </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">指尖互动 + 解压轻量游戏 + 网络模因`\\n- **竞品分析/行业报告**:侧重 `免费+内购模式 + 用户留存 + 跨媒介IP发行 + </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">生命周期管理`\\n- **儿童产品策划**:可搭配 `安全合规设计 + 亲子互动娱乐 + 家园养成 + 儿童早教延伸`\\n- </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">**SEO/标签优化**:建议采用 `会说话的汤姆猫系列 / Talking Tom中文版 / 虚拟宠物手游 / </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">儿童互动APP`\\n\\n如需特定场景的短语搭配、长尾关键词清单或英文对照表,可告知具体用途(如:电商上架、公众号排版、投融</span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">资BP等),我将为您进一步定制。'</span>,\n",
" <span style=\"color: #008000; text-decoration-color: #008000\">'joke'</span>: \n",
"<span style=\"color: #008000; text-decoration-color: #008000\">'这里为你原创创作了5个以《猫和老鼠》中“汤姆猫”为主题的笑话,保留了他屡败屡战、表情丰富、总被杰瑞机智反杀的经典人设</span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">,风格轻松幽默,适合全年龄阅读:\\n\\n1. **【职场篇】** \\n汤姆去面试“高级防鼠专员”。 </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">\\nHR问:“你的核心竞争力是什么?” </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">\\n汤姆挺起胸膛:“二十年实战经验!精通高空坠落缓冲、电击后快速重启、以及被锅砸扁后的三维复原技术。” </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">\\nHR点头:“履历很扎实,但我们更看重结果。” </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">\\n汤姆叹气:“那我补充一条:我擅长在失败中积累素材,为下一代动画师提供反面PPT。” \\n\\n2. **【健身篇】** </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">\\n汤姆痛定思痛办卡健身,三个月练出八块腹肌,连杰瑞都在评论区留言:“哥,你这体格不当举重冠军可惜了!” </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">\\n第二天抓鼠,杰瑞只是把奶酪往前挪了半寸。 \\n汤姆全力冲刺,“砰!”直接撞穿三道墙。 </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">\\n邻居围观问他咋回事,汤姆揉着额头顶着大包说:“力量够了,就是惯性没收住。” \\n\\n3. **【哲学篇】** </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">\\n小朋友问汤姆:“你明明每次都被打败,为什么从不放弃?” </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">\\n汤姆推了推不存在的眼镜:“首先,这是工作;其次,如果我赢了,导演就得给我加薪;最后……你想想,谁舍得让一部播了八十多</span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">年的动画片断更?” \\n小朋友恍然大悟:“原来你不是在抓老鼠,是在守护童年呀!” \\n\\n4. **【科技篇】** </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">\\n汤姆斥巨资下载了“AI智能捕鼠系统”,支持卫星定位、行为预测、自动诱捕。 </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">\\n首日上阵,屏幕显示:“目标已锁定,正在计算逃逸路线……” \\n杰瑞掏出小圆镜反射阳光晃了汤姆一下。 </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">\\n手机秒弹:“识别异常,疑似光学干扰。已为您切换至传统模式:跑、跳、躲、挨打。” </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">\\n汤姆默默关机:“算法再强,也算不出鼠标垫的反光角。” \\n\\n5. **【考试篇】** \\n语文题:请用“汤姆猫”造句。 </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">\\n学生甲写:“汤姆猫很有毅力。”(老师批:✅ 但缺乏细节) </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">\\n学生乙写:“汤姆猫每天都在努力,虽然每次都失败,但他从未停止。”(老师批:✅✅ 加分项) </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">\\n学生丙写:“汤姆猫的创可贴库存比我的作业本还多。”(老师批:✅✅✅ 现实主义杰作,附赠一张漫画配图) \\n\\n💡 </span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">**小贴士**:这些笑话均基于经典动画的喜剧内核二次创作,未涉及任何版权侵权内容。如果你喜欢某种特定风格(比如谐音梗、</span>\n",
"<span style=\"color: #008000; text-decoration-color: #008000\">冷笑话、或结合现代生活场景),可以告诉我,我再为你定制一批!'</span>\n",
"<span style=\"font-weight: bold\">}</span>\n",
"</pre>\n"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"execution_count": 28
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-07-30T13:50:27.890134Z",
"start_time": "2026-07-30T13:50:20.745185Z"
}
},
"cell_type": "code",
"source": [
"from IPython.display import display\n",
"display(graph)"
],
"id": "6cb9f09b1ee2d10",
"outputs": [
{
"data": {
"text/plain": [
"<langgraph.graph.state.CompiledStateGraph object at 0x10c8e3890>"
],
"image/png": "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"
},
"metadata": {},
"output_type": "display_data"
}
],
"execution_count": 29
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}