[{"data":1,"prerenderedAt":1539},["ShallowReactive",2],{"article-agent\u002Flanggraph-hello":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"tags":11,"body":14,"_type":1533,"_id":1534,"_source":1535,"_file":1536,"_stem":1537,"_extension":1538},"\u002Farticles\u002Fagent\u002Flanggraph-hello","agent",false,"","LangGraph 入门：用 StateGraph 构建 Agent 的五步流程","从定义状态、节点、连边到条件路由和编译运行，用 LangGraph 把 Stage 3 手写的 ReAct 循环变成一张可执行的图。核心记忆口诀：State 是数据，Node 是动作，Edge 是流程，条件边是 agent 的自主性所在。","2026-06-17",[12,13],"Agent开发","人工智能",{"type":15,"children":16,"toc":1524},"root",[17,26,56,62,68,73,622,628,633,750,756,761,942,948,953,1093,1099,1104,1258,1264,1269,1518],{"type":18,"tag":19,"props":20,"children":22},"element","h2",{"id":21},"用-langgraph-写agent的流程思路",[23],{"type":24,"value":25},"text","用 LangGraph 写Agent的流程思路：",{"type":18,"tag":27,"props":28,"children":29},"ol",{},[30,36,41,46,51],{"type":18,"tag":31,"props":32,"children":33},"li",{},[34],{"type":24,"value":35},"定义状态 State（TypedDict）——图的共享数据\u002F记忆，用 reducer（operator.add）让消息历史自动累积。",{"type":18,"tag":31,"props":37,"children":38},{},[39],{"type":24,"value":40},"定义节点 Node（普通函数）——模型节点 llm_call（大脑，负责判断调不调工具）、工具节点 tool_node（手，负责执行工具）。",{"type":18,"tag":31,"props":42,"children":43},{},[44],{"type":24,"value":45},"创建并配置图：StateGraph(State) 建画布 → add_node 把节点挂上去。",{"type":18,"tag":31,"props":47,"children":48},{},[49],{"type":24,"value":50},"连边定义流转：普通边 add_edge 定固定走向（START→llm_call、tool_node→llm_call）；条件边 add_conditional_edges + 路由函数定动态分支（继续调工具 or 结束）——这是循环和自停止的核心。",{"type":18,"tag":31,"props":52,"children":53},{},[54],{"type":24,"value":55},"编译运行：compile() 定稿成可执行 agent，invoke() 跑起来。",{"type":18,"tag":57,"props":58,"children":59},"p",{},[60],{"type":24,"value":61},"一句话记忆：State 是数据，Node 是动作，Edge 是流程，条件边是 agent 的\"自主性\"所在。",{"type":18,"tag":19,"props":63,"children":65},{"id":64},"_1定义模型和工具",[66],{"type":24,"value":67},"1、定义模型和工具",{"type":18,"tag":57,"props":69,"children":70},{},[71],{"type":24,"value":72},"用的deepseek-v4-pro模型，并定义工具",{"type":18,"tag":74,"props":75,"children":79},"pre",{"className":76,"code":77,"language":78,"meta":7,"style":7},"language-python shiki shiki-themes github-dark","import os\n\nfrom langchain.tools import tool\nfrom langchain.chat_models import init_chat_model\nfrom dotenv import load_dotenv\n\n# 从根目录 .env 读取 DeepSeek 配置（key \u002F base_url \u002F 模型名）\nload_dotenv()\n\n# init_chat_model：LangChain 的通用模型初始化器。\n# 这里接 DeepSeek（OpenAI 兼容），temperature=0 让输出更稳定、可复现。\nmodel = init_chat_model(\n    api_key=os.environ[\"DEEPSEEK_API_KEY\"],\n    base_url=os.environ[\"DEEPSEEK_BASE_URL\"],\n    model=os.environ[\"MODEL\"],\n    temperature=0\n)\n\n\n# 定义工具：@tool 装饰器把一个普通 Python 函数变成 agent 可调用的工具。\n# 关键：函数的 docstring 会成为工具给模型看的“说明书”——模型靠它判断何时调用、怎么传参。\n# 这正是 Stage 3 练过的“工具 schema”，只是 LangChain 帮你从函数签名+docstring 自动生成了。\n@tool\ndef multiply(a: int, b: int) -> int:\n    \"\"\"Multiply `a` and `b`.\n\n    Args:\n        a: First int\n        b: Second int\n    \"\"\"\n    return a * b\n\n\n@tool\ndef add(a: int, b: int) -> int:\n    \"\"\"Adds `a` and `b`.\n\n    Args:\n        a: First int\n        b: Second int\n    \"\"\"\n    return a + b\n\n\n@tool\ndef divide(a: int, b: int) -> float:\n    \"\"\"Divide `a` and `b`.\n\n    Args:\n        a: First int\n        b: Second int\n    \"\"\"\n    return a \u002F b\n\n\n# 把三个工具登记成列表\ntools = [add, multiply, divide]\n# 名字 -> 工具对象 的映射，后面 tool_node 执行时按名字查找用\ntools_by_name = {tool.name: tool for tool in tools}\n# bind_tools：把工具“绑”到模型上。绑定后模型在回答时就能输出 tool_calls（要调哪个工具+参数）\n# 注意：绑定不等于执行——模型只负责“决定调用”，真正执行仍由我们的代码（tool_node）来做。\nmodel_with_tools = model.bind_tools(tools)\n","python",[80],{"type":18,"tag":81,"props":82,"children":83},"code",{"__ignoreMap":7},[84,95,105,114,123,132,140,149,158,166,175,184,193,202,211,220,229,238,246,254,263,272,281,290,299,308,316,325,334,343,352,361,369,377,385,394,403,411,419,427,435,443,452,460,468,476,485,494,502,510,518,526,534,543,551,559,568,577,586,595,604,613],{"type":18,"tag":85,"props":86,"children":89},"span",{"class":87,"line":88},"line",1,[90],{"type":18,"tag":85,"props":91,"children":92},{},[93],{"type":24,"value":94},"import os\n",{"type":18,"tag":85,"props":96,"children":98},{"class":87,"line":97},2,[99],{"type":18,"tag":85,"props":100,"children":102},{"emptyLinePlaceholder":101},true,[103],{"type":24,"value":104},"\n",{"type":18,"tag":85,"props":106,"children":108},{"class":87,"line":107},3,[109],{"type":18,"tag":85,"props":110,"children":111},{},[112],{"type":24,"value":113},"from langchain.tools import tool\n",{"type":18,"tag":85,"props":115,"children":117},{"class":87,"line":116},4,[118],{"type":18,"tag":85,"props":119,"children":120},{},[121],{"type":24,"value":122},"from langchain.chat_models import init_chat_model\n",{"type":18,"tag":85,"props":124,"children":126},{"class":87,"line":125},5,[127],{"type":18,"tag":85,"props":128,"children":129},{},[130],{"type":24,"value":131},"from dotenv import load_dotenv\n",{"type":18,"tag":85,"props":133,"children":135},{"class":87,"line":134},6,[136],{"type":18,"tag":85,"props":137,"children":138},{"emptyLinePlaceholder":101},[139],{"type":24,"value":104},{"type":18,"tag":85,"props":141,"children":143},{"class":87,"line":142},7,[144],{"type":18,"tag":85,"props":145,"children":146},{},[147],{"type":24,"value":148},"# 从根目录 .env 读取 DeepSeek 配置（key \u002F base_url \u002F 模型名）\n",{"type":18,"tag":85,"props":150,"children":152},{"class":87,"line":151},8,[153],{"type":18,"tag":85,"props":154,"children":155},{},[156],{"type":24,"value":157},"load_dotenv()\n",{"type":18,"tag":85,"props":159,"children":161},{"class":87,"line":160},9,[162],{"type":18,"tag":85,"props":163,"children":164},{"emptyLinePlaceholder":101},[165],{"type":24,"value":104},{"type":18,"tag":85,"props":167,"children":169},{"class":87,"line":168},10,[170],{"type":18,"tag":85,"props":171,"children":172},{},[173],{"type":24,"value":174},"# init_chat_model：LangChain 的通用模型初始化器。\n",{"type":18,"tag":85,"props":176,"children":178},{"class":87,"line":177},11,[179],{"type":18,"tag":85,"props":180,"children":181},{},[182],{"type":24,"value":183},"# 这里接 DeepSeek（OpenAI 兼容），temperature=0 让输出更稳定、可复现。\n",{"type":18,"tag":85,"props":185,"children":187},{"class":87,"line":186},12,[188],{"type":18,"tag":85,"props":189,"children":190},{},[191],{"type":24,"value":192},"model = init_chat_model(\n",{"type":18,"tag":85,"props":194,"children":196},{"class":87,"line":195},13,[197],{"type":18,"tag":85,"props":198,"children":199},{},[200],{"type":24,"value":201},"    api_key=os.environ[\"DEEPSEEK_API_KEY\"],\n",{"type":18,"tag":85,"props":203,"children":205},{"class":87,"line":204},14,[206],{"type":18,"tag":85,"props":207,"children":208},{},[209],{"type":24,"value":210},"    base_url=os.environ[\"DEEPSEEK_BASE_URL\"],\n",{"type":18,"tag":85,"props":212,"children":214},{"class":87,"line":213},15,[215],{"type":18,"tag":85,"props":216,"children":217},{},[218],{"type":24,"value":219},"    model=os.environ[\"MODEL\"],\n",{"type":18,"tag":85,"props":221,"children":223},{"class":87,"line":222},16,[224],{"type":18,"tag":85,"props":225,"children":226},{},[227],{"type":24,"value":228},"    temperature=0\n",{"type":18,"tag":85,"props":230,"children":232},{"class":87,"line":231},17,[233],{"type":18,"tag":85,"props":234,"children":235},{},[236],{"type":24,"value":237},")\n",{"type":18,"tag":85,"props":239,"children":241},{"class":87,"line":240},18,[242],{"type":18,"tag":85,"props":243,"children":244},{"emptyLinePlaceholder":101},[245],{"type":24,"value":104},{"type":18,"tag":85,"props":247,"children":249},{"class":87,"line":248},19,[250],{"type":18,"tag":85,"props":251,"children":252},{"emptyLinePlaceholder":101},[253],{"type":24,"value":104},{"type":18,"tag":85,"props":255,"children":257},{"class":87,"line":256},20,[258],{"type":18,"tag":85,"props":259,"children":260},{},[261],{"type":24,"value":262},"# 定义工具：@tool 装饰器把一个普通 Python 函数变成 agent 可调用的工具。\n",{"type":18,"tag":85,"props":264,"children":266},{"class":87,"line":265},21,[267],{"type":18,"tag":85,"props":268,"children":269},{},[270],{"type":24,"value":271},"# 关键：函数的 docstring 会成为工具给模型看的“说明书”——模型靠它判断何时调用、怎么传参。\n",{"type":18,"tag":85,"props":273,"children":275},{"class":87,"line":274},22,[276],{"type":18,"tag":85,"props":277,"children":278},{},[279],{"type":24,"value":280},"# 这正是 Stage 3 练过的“工具 schema”，只是 LangChain 帮你从函数签名+docstring 自动生成了。\n",{"type":18,"tag":85,"props":282,"children":284},{"class":87,"line":283},23,[285],{"type":18,"tag":85,"props":286,"children":287},{},[288],{"type":24,"value":289},"@tool\n",{"type":18,"tag":85,"props":291,"children":293},{"class":87,"line":292},24,[294],{"type":18,"tag":85,"props":295,"children":296},{},[297],{"type":24,"value":298},"def multiply(a: int, b: int) -> int:\n",{"type":18,"tag":85,"props":300,"children":302},{"class":87,"line":301},25,[303],{"type":18,"tag":85,"props":304,"children":305},{},[306],{"type":24,"value":307},"    \"\"\"Multiply `a` and `b`.\n",{"type":18,"tag":85,"props":309,"children":311},{"class":87,"line":310},26,[312],{"type":18,"tag":85,"props":313,"children":314},{"emptyLinePlaceholder":101},[315],{"type":24,"value":104},{"type":18,"tag":85,"props":317,"children":319},{"class":87,"line":318},27,[320],{"type":18,"tag":85,"props":321,"children":322},{},[323],{"type":24,"value":324},"    Args:\n",{"type":18,"tag":85,"props":326,"children":328},{"class":87,"line":327},28,[329],{"type":18,"tag":85,"props":330,"children":331},{},[332],{"type":24,"value":333},"        a: First int\n",{"type":18,"tag":85,"props":335,"children":337},{"class":87,"line":336},29,[338],{"type":18,"tag":85,"props":339,"children":340},{},[341],{"type":24,"value":342},"        b: Second int\n",{"type":18,"tag":85,"props":344,"children":346},{"class":87,"line":345},30,[347],{"type":18,"tag":85,"props":348,"children":349},{},[350],{"type":24,"value":351},"    \"\"\"\n",{"type":18,"tag":85,"props":353,"children":355},{"class":87,"line":354},31,[356],{"type":18,"tag":85,"props":357,"children":358},{},[359],{"type":24,"value":360},"    return a * b\n",{"type":18,"tag":85,"props":362,"children":364},{"class":87,"line":363},32,[365],{"type":18,"tag":85,"props":366,"children":367},{"emptyLinePlaceholder":101},[368],{"type":24,"value":104},{"type":18,"tag":85,"props":370,"children":372},{"class":87,"line":371},33,[373],{"type":18,"tag":85,"props":374,"children":375},{"emptyLinePlaceholder":101},[376],{"type":24,"value":104},{"type":18,"tag":85,"props":378,"children":380},{"class":87,"line":379},34,[381],{"type":18,"tag":85,"props":382,"children":383},{},[384],{"type":24,"value":289},{"type":18,"tag":85,"props":386,"children":388},{"class":87,"line":387},35,[389],{"type":18,"tag":85,"props":390,"children":391},{},[392],{"type":24,"value":393},"def add(a: int, b: int) -> int:\n",{"type":18,"tag":85,"props":395,"children":397},{"class":87,"line":396},36,[398],{"type":18,"tag":85,"props":399,"children":400},{},[401],{"type":24,"value":402},"    \"\"\"Adds `a` and `b`.\n",{"type":18,"tag":85,"props":404,"children":406},{"class":87,"line":405},37,[407],{"type":18,"tag":85,"props":408,"children":409},{"emptyLinePlaceholder":101},[410],{"type":24,"value":104},{"type":18,"tag":85,"props":412,"children":414},{"class":87,"line":413},38,[415],{"type":18,"tag":85,"props":416,"children":417},{},[418],{"type":24,"value":324},{"type":18,"tag":85,"props":420,"children":422},{"class":87,"line":421},39,[423],{"type":18,"tag":85,"props":424,"children":425},{},[426],{"type":24,"value":333},{"type":18,"tag":85,"props":428,"children":430},{"class":87,"line":429},40,[431],{"type":18,"tag":85,"props":432,"children":433},{},[434],{"type":24,"value":342},{"type":18,"tag":85,"props":436,"children":438},{"class":87,"line":437},41,[439],{"type":18,"tag":85,"props":440,"children":441},{},[442],{"type":24,"value":351},{"type":18,"tag":85,"props":444,"children":446},{"class":87,"line":445},42,[447],{"type":18,"tag":85,"props":448,"children":449},{},[450],{"type":24,"value":451},"    return a + b\n",{"type":18,"tag":85,"props":453,"children":455},{"class":87,"line":454},43,[456],{"type":18,"tag":85,"props":457,"children":458},{"emptyLinePlaceholder":101},[459],{"type":24,"value":104},{"type":18,"tag":85,"props":461,"children":463},{"class":87,"line":462},44,[464],{"type":18,"tag":85,"props":465,"children":466},{"emptyLinePlaceholder":101},[467],{"type":24,"value":104},{"type":18,"tag":85,"props":469,"children":471},{"class":87,"line":470},45,[472],{"type":18,"tag":85,"props":473,"children":474},{},[475],{"type":24,"value":289},{"type":18,"tag":85,"props":477,"children":479},{"class":87,"line":478},46,[480],{"type":18,"tag":85,"props":481,"children":482},{},[483],{"type":24,"value":484},"def divide(a: int, b: int) -> float:\n",{"type":18,"tag":85,"props":486,"children":488},{"class":87,"line":487},47,[489],{"type":18,"tag":85,"props":490,"children":491},{},[492],{"type":24,"value":493},"    \"\"\"Divide `a` and `b`.\n",{"type":18,"tag":85,"props":495,"children":497},{"class":87,"line":496},48,[498],{"type":18,"tag":85,"props":499,"children":500},{"emptyLinePlaceholder":101},[501],{"type":24,"value":104},{"type":18,"tag":85,"props":503,"children":505},{"class":87,"line":504},49,[506],{"type":18,"tag":85,"props":507,"children":508},{},[509],{"type":24,"value":324},{"type":18,"tag":85,"props":511,"children":513},{"class":87,"line":512},50,[514],{"type":18,"tag":85,"props":515,"children":516},{},[517],{"type":24,"value":333},{"type":18,"tag":85,"props":519,"children":521},{"class":87,"line":520},51,[522],{"type":18,"tag":85,"props":523,"children":524},{},[525],{"type":24,"value":342},{"type":18,"tag":85,"props":527,"children":529},{"class":87,"line":528},52,[530],{"type":18,"tag":85,"props":531,"children":532},{},[533],{"type":24,"value":351},{"type":18,"tag":85,"props":535,"children":537},{"class":87,"line":536},53,[538],{"type":18,"tag":85,"props":539,"children":540},{},[541],{"type":24,"value":542},"    return a \u002F b\n",{"type":18,"tag":85,"props":544,"children":546},{"class":87,"line":545},54,[547],{"type":18,"tag":85,"props":548,"children":549},{"emptyLinePlaceholder":101},[550],{"type":24,"value":104},{"type":18,"tag":85,"props":552,"children":554},{"class":87,"line":553},55,[555],{"type":18,"tag":85,"props":556,"children":557},{"emptyLinePlaceholder":101},[558],{"type":24,"value":104},{"type":18,"tag":85,"props":560,"children":562},{"class":87,"line":561},56,[563],{"type":18,"tag":85,"props":564,"children":565},{},[566],{"type":24,"value":567},"# 把三个工具登记成列表\n",{"type":18,"tag":85,"props":569,"children":571},{"class":87,"line":570},57,[572],{"type":18,"tag":85,"props":573,"children":574},{},[575],{"type":24,"value":576},"tools = [add, multiply, divide]\n",{"type":18,"tag":85,"props":578,"children":580},{"class":87,"line":579},58,[581],{"type":18,"tag":85,"props":582,"children":583},{},[584],{"type":24,"value":585},"# 名字 -> 工具对象 的映射，后面 tool_node 执行时按名字查找用\n",{"type":18,"tag":85,"props":587,"children":589},{"class":87,"line":588},59,[590],{"type":18,"tag":85,"props":591,"children":592},{},[593],{"type":24,"value":594},"tools_by_name = {tool.name: tool for tool in tools}\n",{"type":18,"tag":85,"props":596,"children":598},{"class":87,"line":597},60,[599],{"type":18,"tag":85,"props":600,"children":601},{},[602],{"type":24,"value":603},"# bind_tools：把工具“绑”到模型上。绑定后模型在回答时就能输出 tool_calls（要调哪个工具+参数）\n",{"type":18,"tag":85,"props":605,"children":607},{"class":87,"line":606},61,[608],{"type":18,"tag":85,"props":609,"children":610},{},[611],{"type":24,"value":612},"# 注意：绑定不等于执行——模型只负责“决定调用”，真正执行仍由我们的代码（tool_node）来做。\n",{"type":18,"tag":85,"props":614,"children":616},{"class":87,"line":615},62,[617],{"type":18,"tag":85,"props":618,"children":619},{},[620],{"type":24,"value":621},"model_with_tools = model.bind_tools(tools)\n",{"type":18,"tag":19,"props":623,"children":625},{"id":624},"_2定义状态",[626],{"type":24,"value":627},"2、定义状态",{"type":18,"tag":57,"props":629,"children":630},{},[631],{"type":24,"value":632},"图的状态用于存储消息和 LLM 调用次数。",{"type":18,"tag":74,"props":634,"children":636},{"className":76,"code":635,"language":78,"meta":7,"style":7},"from langchain.messages import AnyMessage\nfrom typing_extensions import TypedDict, Annotated\nimport operator\n\n\n# 图的“状态”（State）：贯穿整张图、在节点之间传递的共享数据。\n# 每个节点读它、改它，再把更新返回——这就是 LangGraph 的数据流核心。\nclass MessagesState(TypedDict):\n    # messages：对话历史。Annotated[..., operator.add] 是关键——\n    # 它告诉 LangGraph：节点返回的新消息要【追加】到列表，而不是覆盖。\n    # （这就是 Stage 3 手写循环里“messages 越滚越长”那件事，框架用 reducer 替你做了。）\n    messages: Annotated[list[AnyMessage], operator.add]\n    # llm_calls：记录调用了几次 LLM，方便观察循环转了几圈 \u002F 控制成本。\n    llm_calls: int\n",[637],{"type":18,"tag":81,"props":638,"children":639},{"__ignoreMap":7},[640,648,656,664,671,678,686,694,702,710,718,726,734,742],{"type":18,"tag":85,"props":641,"children":642},{"class":87,"line":88},[643],{"type":18,"tag":85,"props":644,"children":645},{},[646],{"type":24,"value":647},"from langchain.messages import AnyMessage\n",{"type":18,"tag":85,"props":649,"children":650},{"class":87,"line":97},[651],{"type":18,"tag":85,"props":652,"children":653},{},[654],{"type":24,"value":655},"from typing_extensions import TypedDict, Annotated\n",{"type":18,"tag":85,"props":657,"children":658},{"class":87,"line":107},[659],{"type":18,"tag":85,"props":660,"children":661},{},[662],{"type":24,"value":663},"import operator\n",{"type":18,"tag":85,"props":665,"children":666},{"class":87,"line":116},[667],{"type":18,"tag":85,"props":668,"children":669},{"emptyLinePlaceholder":101},[670],{"type":24,"value":104},{"type":18,"tag":85,"props":672,"children":673},{"class":87,"line":125},[674],{"type":18,"tag":85,"props":675,"children":676},{"emptyLinePlaceholder":101},[677],{"type":24,"value":104},{"type":18,"tag":85,"props":679,"children":680},{"class":87,"line":134},[681],{"type":18,"tag":85,"props":682,"children":683},{},[684],{"type":24,"value":685},"# 图的“状态”（State）：贯穿整张图、在节点之间传递的共享数据。\n",{"type":18,"tag":85,"props":687,"children":688},{"class":87,"line":142},[689],{"type":18,"tag":85,"props":690,"children":691},{},[692],{"type":24,"value":693},"# 每个节点读它、改它，再把更新返回——这就是 LangGraph 的数据流核心。\n",{"type":18,"tag":85,"props":695,"children":696},{"class":87,"line":151},[697],{"type":18,"tag":85,"props":698,"children":699},{},[700],{"type":24,"value":701},"class MessagesState(TypedDict):\n",{"type":18,"tag":85,"props":703,"children":704},{"class":87,"line":160},[705],{"type":18,"tag":85,"props":706,"children":707},{},[708],{"type":24,"value":709},"    # messages：对话历史。Annotated[..., operator.add] 是关键——\n",{"type":18,"tag":85,"props":711,"children":712},{"class":87,"line":168},[713],{"type":18,"tag":85,"props":714,"children":715},{},[716],{"type":24,"value":717},"    # 它告诉 LangGraph：节点返回的新消息要【追加】到列表，而不是覆盖。\n",{"type":18,"tag":85,"props":719,"children":720},{"class":87,"line":177},[721],{"type":18,"tag":85,"props":722,"children":723},{},[724],{"type":24,"value":725},"    # （这就是 Stage 3 手写循环里“messages 越滚越长”那件事，框架用 reducer 替你做了。）\n",{"type":18,"tag":85,"props":727,"children":728},{"class":87,"line":186},[729],{"type":18,"tag":85,"props":730,"children":731},{},[732],{"type":24,"value":733},"    messages: Annotated[list[AnyMessage], operator.add]\n",{"type":18,"tag":85,"props":735,"children":736},{"class":87,"line":195},[737],{"type":18,"tag":85,"props":738,"children":739},{},[740],{"type":24,"value":741},"    # llm_calls：记录调用了几次 LLM，方便观察循环转了几圈 \u002F 控制成本。\n",{"type":18,"tag":85,"props":743,"children":744},{"class":87,"line":204},[745],{"type":18,"tag":85,"props":746,"children":747},{},[748],{"type":24,"value":749},"    llm_calls: int\n",{"type":18,"tag":19,"props":751,"children":753},{"id":752},"_3定义模型节点",[754],{"type":24,"value":755},"3、定义模型节点",{"type":18,"tag":57,"props":757,"children":758},{},[759],{"type":24,"value":760},"模型节点用于调用 LLM 并决定是否调用工具。",{"type":18,"tag":74,"props":762,"children":764},{"className":76,"code":763,"language":78,"meta":7,"style":7},"from langchain.messages import SystemMessage\n\n# 模型节点（agent 的“大脑”）：让 LLM 看完当前对话后，决定“调工具”还是“直接回答”。\ndef llm_call(state: dict):\n    \"\"\"LLM decides whether to call a tool or not\"\"\"\n\n    return {\n        # 调用绑了工具的模型。输入 = 系统提示 + 到目前为止的全部对话历史。\n        # 返回的 AIMessage 里可能带 tool_calls（要调工具），也可能是纯文本（直接回答）。\n        \"messages\": [\n            model_with_tools.invoke(\n                [\n                    SystemMessage(\n                        content=\"You are a helpful assistant tasked with performing arithmetic on a set of inputs.\"\n                    )\n                ]\n                + state[\"messages\"]  # 拼上历史消息，模型才有上下文\n            )\n        ],\n        # 每经过一次本节点就 +1，等于给“心跳”计数\n        \"llm_calls\": state.get('llm_calls', 0) + 1\n    }\n",[765],{"type":18,"tag":81,"props":766,"children":767},{"__ignoreMap":7},[768,776,783,791,799,807,814,822,830,838,846,854,862,870,878,886,894,902,910,918,926,934],{"type":18,"tag":85,"props":769,"children":770},{"class":87,"line":88},[771],{"type":18,"tag":85,"props":772,"children":773},{},[774],{"type":24,"value":775},"from langchain.messages import SystemMessage\n",{"type":18,"tag":85,"props":777,"children":778},{"class":87,"line":97},[779],{"type":18,"tag":85,"props":780,"children":781},{"emptyLinePlaceholder":101},[782],{"type":24,"value":104},{"type":18,"tag":85,"props":784,"children":785},{"class":87,"line":107},[786],{"type":18,"tag":85,"props":787,"children":788},{},[789],{"type":24,"value":790},"# 模型节点（agent 的“大脑”）：让 LLM 看完当前对话后，决定“调工具”还是“直接回答”。\n",{"type":18,"tag":85,"props":792,"children":793},{"class":87,"line":116},[794],{"type":18,"tag":85,"props":795,"children":796},{},[797],{"type":24,"value":798},"def llm_call(state: dict):\n",{"type":18,"tag":85,"props":800,"children":801},{"class":87,"line":125},[802],{"type":18,"tag":85,"props":803,"children":804},{},[805],{"type":24,"value":806},"    \"\"\"LLM decides whether to call a tool or not\"\"\"\n",{"type":18,"tag":85,"props":808,"children":809},{"class":87,"line":134},[810],{"type":18,"tag":85,"props":811,"children":812},{"emptyLinePlaceholder":101},[813],{"type":24,"value":104},{"type":18,"tag":85,"props":815,"children":816},{"class":87,"line":142},[817],{"type":18,"tag":85,"props":818,"children":819},{},[820],{"type":24,"value":821},"    return {\n",{"type":18,"tag":85,"props":823,"children":824},{"class":87,"line":151},[825],{"type":18,"tag":85,"props":826,"children":827},{},[828],{"type":24,"value":829},"        # 调用绑了工具的模型。输入 = 系统提示 + 到目前为止的全部对话历史。\n",{"type":18,"tag":85,"props":831,"children":832},{"class":87,"line":160},[833],{"type":18,"tag":85,"props":834,"children":835},{},[836],{"type":24,"value":837},"        # 返回的 AIMessage 里可能带 tool_calls（要调工具），也可能是纯文本（直接回答）。\n",{"type":18,"tag":85,"props":839,"children":840},{"class":87,"line":168},[841],{"type":18,"tag":85,"props":842,"children":843},{},[844],{"type":24,"value":845},"        \"messages\": [\n",{"type":18,"tag":85,"props":847,"children":848},{"class":87,"line":177},[849],{"type":18,"tag":85,"props":850,"children":851},{},[852],{"type":24,"value":853},"            model_with_tools.invoke(\n",{"type":18,"tag":85,"props":855,"children":856},{"class":87,"line":186},[857],{"type":18,"tag":85,"props":858,"children":859},{},[860],{"type":24,"value":861},"                [\n",{"type":18,"tag":85,"props":863,"children":864},{"class":87,"line":195},[865],{"type":18,"tag":85,"props":866,"children":867},{},[868],{"type":24,"value":869},"                    SystemMessage(\n",{"type":18,"tag":85,"props":871,"children":872},{"class":87,"line":204},[873],{"type":18,"tag":85,"props":874,"children":875},{},[876],{"type":24,"value":877},"                        content=\"You are a helpful assistant tasked with performing arithmetic on a set of inputs.\"\n",{"type":18,"tag":85,"props":879,"children":880},{"class":87,"line":213},[881],{"type":18,"tag":85,"props":882,"children":883},{},[884],{"type":24,"value":885},"                    )\n",{"type":18,"tag":85,"props":887,"children":888},{"class":87,"line":222},[889],{"type":18,"tag":85,"props":890,"children":891},{},[892],{"type":24,"value":893},"                ]\n",{"type":18,"tag":85,"props":895,"children":896},{"class":87,"line":231},[897],{"type":18,"tag":85,"props":898,"children":899},{},[900],{"type":24,"value":901},"                + state[\"messages\"]  # 拼上历史消息，模型才有上下文\n",{"type":18,"tag":85,"props":903,"children":904},{"class":87,"line":240},[905],{"type":18,"tag":85,"props":906,"children":907},{},[908],{"type":24,"value":909},"            )\n",{"type":18,"tag":85,"props":911,"children":912},{"class":87,"line":248},[913],{"type":18,"tag":85,"props":914,"children":915},{},[916],{"type":24,"value":917},"        ],\n",{"type":18,"tag":85,"props":919,"children":920},{"class":87,"line":256},[921],{"type":18,"tag":85,"props":922,"children":923},{},[924],{"type":24,"value":925},"        # 每经过一次本节点就 +1，等于给“心跳”计数\n",{"type":18,"tag":85,"props":927,"children":928},{"class":87,"line":265},[929],{"type":18,"tag":85,"props":930,"children":931},{},[932],{"type":24,"value":933},"        \"llm_calls\": state.get('llm_calls', 0) + 1\n",{"type":18,"tag":85,"props":935,"children":936},{"class":87,"line":274},[937],{"type":18,"tag":85,"props":938,"children":939},{},[940],{"type":24,"value":941},"    }\n",{"type":18,"tag":19,"props":943,"children":945},{"id":944},"_4定义工具节点",[946],{"type":24,"value":947},"4、定义工具节点",{"type":18,"tag":57,"props":949,"children":950},{},[951],{"type":24,"value":952},"工具节点用于调用工具并返回结果。",{"type":18,"tag":74,"props":954,"children":956},{"className":76,"code":955,"language":78,"meta":7,"style":7},"from langchain.messages import ToolMessage\n\n\n# 工具节点（agent 的“手”）：真正去执行上一步模型决定调用的工具。\ndef tool_node(state: dict):\n    \"\"\"Performs the tool call\"\"\"\n\n    result = []\n    # 取最后一条消息（来自 llm_call 的 AIMessage），遍历它要求的每个工具调用。\n    # 一条消息里可能有多个 tool_calls —— 模型可以一次要求并行调多个工具。\n    for tool_call in state[\"messages\"][-1].tool_calls:\n        tool = tools_by_name[tool_call[\"name\"]]          # 按名字找到对应函数\n        observation = tool.invoke(tool_call[\"args\"])      # 用模型给的参数真正执行\n        # 把结果包成 ToolMessage 返回。tool_call_id 必须和发起调用的 id 配对，\n        # 模型才知道这条结果是哪次调用的回执（Stage 3 手写时要自己管，这里框架替你对齐）。\n        result.append(ToolMessage(content=observation, tool_call_id=tool_call[\"id\"]))\n    return {\"messages\": result}\n",[957],{"type":18,"tag":81,"props":958,"children":959},{"__ignoreMap":7},[960,968,975,982,990,998,1006,1013,1021,1029,1037,1045,1053,1061,1069,1077,1085],{"type":18,"tag":85,"props":961,"children":962},{"class":87,"line":88},[963],{"type":18,"tag":85,"props":964,"children":965},{},[966],{"type":24,"value":967},"from langchain.messages import ToolMessage\n",{"type":18,"tag":85,"props":969,"children":970},{"class":87,"line":97},[971],{"type":18,"tag":85,"props":972,"children":973},{"emptyLinePlaceholder":101},[974],{"type":24,"value":104},{"type":18,"tag":85,"props":976,"children":977},{"class":87,"line":107},[978],{"type":18,"tag":85,"props":979,"children":980},{"emptyLinePlaceholder":101},[981],{"type":24,"value":104},{"type":18,"tag":85,"props":983,"children":984},{"class":87,"line":116},[985],{"type":18,"tag":85,"props":986,"children":987},{},[988],{"type":24,"value":989},"# 工具节点（agent 的“手”）：真正去执行上一步模型决定调用的工具。\n",{"type":18,"tag":85,"props":991,"children":992},{"class":87,"line":125},[993],{"type":18,"tag":85,"props":994,"children":995},{},[996],{"type":24,"value":997},"def tool_node(state: dict):\n",{"type":18,"tag":85,"props":999,"children":1000},{"class":87,"line":134},[1001],{"type":18,"tag":85,"props":1002,"children":1003},{},[1004],{"type":24,"value":1005},"    \"\"\"Performs the tool call\"\"\"\n",{"type":18,"tag":85,"props":1007,"children":1008},{"class":87,"line":142},[1009],{"type":18,"tag":85,"props":1010,"children":1011},{"emptyLinePlaceholder":101},[1012],{"type":24,"value":104},{"type":18,"tag":85,"props":1014,"children":1015},{"class":87,"line":151},[1016],{"type":18,"tag":85,"props":1017,"children":1018},{},[1019],{"type":24,"value":1020},"    result = []\n",{"type":18,"tag":85,"props":1022,"children":1023},{"class":87,"line":160},[1024],{"type":18,"tag":85,"props":1025,"children":1026},{},[1027],{"type":24,"value":1028},"    # 取最后一条消息（来自 llm_call 的 AIMessage），遍历它要求的每个工具调用。\n",{"type":18,"tag":85,"props":1030,"children":1031},{"class":87,"line":168},[1032],{"type":18,"tag":85,"props":1033,"children":1034},{},[1035],{"type":24,"value":1036},"    # 一条消息里可能有多个 tool_calls —— 模型可以一次要求并行调多个工具。\n",{"type":18,"tag":85,"props":1038,"children":1039},{"class":87,"line":177},[1040],{"type":18,"tag":85,"props":1041,"children":1042},{},[1043],{"type":24,"value":1044},"    for tool_call in state[\"messages\"][-1].tool_calls:\n",{"type":18,"tag":85,"props":1046,"children":1047},{"class":87,"line":186},[1048],{"type":18,"tag":85,"props":1049,"children":1050},{},[1051],{"type":24,"value":1052},"        tool = tools_by_name[tool_call[\"name\"]]          # 按名字找到对应函数\n",{"type":18,"tag":85,"props":1054,"children":1055},{"class":87,"line":195},[1056],{"type":18,"tag":85,"props":1057,"children":1058},{},[1059],{"type":24,"value":1060},"        observation = tool.invoke(tool_call[\"args\"])      # 用模型给的参数真正执行\n",{"type":18,"tag":85,"props":1062,"children":1063},{"class":87,"line":204},[1064],{"type":18,"tag":85,"props":1065,"children":1066},{},[1067],{"type":24,"value":1068},"        # 把结果包成 ToolMessage 返回。tool_call_id 必须和发起调用的 id 配对，\n",{"type":18,"tag":85,"props":1070,"children":1071},{"class":87,"line":213},[1072],{"type":18,"tag":85,"props":1073,"children":1074},{},[1075],{"type":24,"value":1076},"        # 模型才知道这条结果是哪次调用的回执（Stage 3 手写时要自己管，这里框架替你对齐）。\n",{"type":18,"tag":85,"props":1078,"children":1079},{"class":87,"line":222},[1080],{"type":18,"tag":85,"props":1081,"children":1082},{},[1083],{"type":24,"value":1084},"        result.append(ToolMessage(content=observation, tool_call_id=tool_call[\"id\"]))\n",{"type":18,"tag":85,"props":1086,"children":1087},{"class":87,"line":231},[1088],{"type":18,"tag":85,"props":1089,"children":1090},{},[1091],{"type":24,"value":1092},"    return {\"messages\": result}\n",{"type":18,"tag":19,"props":1094,"children":1096},{"id":1095},"_5-定义结束逻辑",[1097],{"type":24,"value":1098},"5、 定义结束逻辑",{"type":18,"tag":57,"props":1100,"children":1101},{},[1102],{"type":24,"value":1103},"条件边函数用于根据 LLM 是否发出工具调用来路由到工具节点或终点。",{"type":18,"tag":74,"props":1105,"children":1107},{"className":76,"code":1106,"language":78,"meta":7,"style":7},"from typing import Literal\nfrom langgraph.graph import StateGraph, START, END\n\n\n# 条件边函数：决定循环“继续”还是“结束”——这就是 agent 的【自停止条件】。\n# 返回值是下一个要去的节点名（字符串），LangGraph 据此决定走向。\ndef should_continue(state: MessagesState) -> Literal[\"tool_node\", END]:\n    \"\"\"Decide if we should continue the loop or stop based upon whether the LLM made a tool call\"\"\"\n\n    messages = state[\"messages\"]\n    last_message = messages[-1]\n\n    # 模型还要调工具 → 去 tool_node 执行，然后还会绕回来继续想（循环继续）\n    if last_message.tool_calls:\n        return \"tool_node\"\n\n    # 模型不再调工具（只给文字）→ 结束，把答案返回给用户\n    # 这等价于 Stage 3 手写循环里的 `if finish_reason == \"stop\": break`\n    return END\n",[1108],{"type":18,"tag":81,"props":1109,"children":1110},{"__ignoreMap":7},[1111,1119,1127,1134,1141,1149,1157,1165,1173,1180,1188,1196,1203,1211,1219,1227,1234,1242,1250],{"type":18,"tag":85,"props":1112,"children":1113},{"class":87,"line":88},[1114],{"type":18,"tag":85,"props":1115,"children":1116},{},[1117],{"type":24,"value":1118},"from typing import Literal\n",{"type":18,"tag":85,"props":1120,"children":1121},{"class":87,"line":97},[1122],{"type":18,"tag":85,"props":1123,"children":1124},{},[1125],{"type":24,"value":1126},"from langgraph.graph import StateGraph, START, END\n",{"type":18,"tag":85,"props":1128,"children":1129},{"class":87,"line":107},[1130],{"type":18,"tag":85,"props":1131,"children":1132},{"emptyLinePlaceholder":101},[1133],{"type":24,"value":104},{"type":18,"tag":85,"props":1135,"children":1136},{"class":87,"line":116},[1137],{"type":18,"tag":85,"props":1138,"children":1139},{"emptyLinePlaceholder":101},[1140],{"type":24,"value":104},{"type":18,"tag":85,"props":1142,"children":1143},{"class":87,"line":125},[1144],{"type":18,"tag":85,"props":1145,"children":1146},{},[1147],{"type":24,"value":1148},"# 条件边函数：决定循环“继续”还是“结束”——这就是 agent 的【自停止条件】。\n",{"type":18,"tag":85,"props":1150,"children":1151},{"class":87,"line":134},[1152],{"type":18,"tag":85,"props":1153,"children":1154},{},[1155],{"type":24,"value":1156},"# 返回值是下一个要去的节点名（字符串），LangGraph 据此决定走向。\n",{"type":18,"tag":85,"props":1158,"children":1159},{"class":87,"line":142},[1160],{"type":18,"tag":85,"props":1161,"children":1162},{},[1163],{"type":24,"value":1164},"def should_continue(state: MessagesState) -> Literal[\"tool_node\", END]:\n",{"type":18,"tag":85,"props":1166,"children":1167},{"class":87,"line":151},[1168],{"type":18,"tag":85,"props":1169,"children":1170},{},[1171],{"type":24,"value":1172},"    \"\"\"Decide if we should continue the loop or stop based upon whether the LLM made a tool call\"\"\"\n",{"type":18,"tag":85,"props":1174,"children":1175},{"class":87,"line":160},[1176],{"type":18,"tag":85,"props":1177,"children":1178},{"emptyLinePlaceholder":101},[1179],{"type":24,"value":104},{"type":18,"tag":85,"props":1181,"children":1182},{"class":87,"line":168},[1183],{"type":18,"tag":85,"props":1184,"children":1185},{},[1186],{"type":24,"value":1187},"    messages = state[\"messages\"]\n",{"type":18,"tag":85,"props":1189,"children":1190},{"class":87,"line":177},[1191],{"type":18,"tag":85,"props":1192,"children":1193},{},[1194],{"type":24,"value":1195},"    last_message = messages[-1]\n",{"type":18,"tag":85,"props":1197,"children":1198},{"class":87,"line":186},[1199],{"type":18,"tag":85,"props":1200,"children":1201},{"emptyLinePlaceholder":101},[1202],{"type":24,"value":104},{"type":18,"tag":85,"props":1204,"children":1205},{"class":87,"line":195},[1206],{"type":18,"tag":85,"props":1207,"children":1208},{},[1209],{"type":24,"value":1210},"    # 模型还要调工具 → 去 tool_node 执行，然后还会绕回来继续想（循环继续）\n",{"type":18,"tag":85,"props":1212,"children":1213},{"class":87,"line":204},[1214],{"type":18,"tag":85,"props":1215,"children":1216},{},[1217],{"type":24,"value":1218},"    if last_message.tool_calls:\n",{"type":18,"tag":85,"props":1220,"children":1221},{"class":87,"line":213},[1222],{"type":18,"tag":85,"props":1223,"children":1224},{},[1225],{"type":24,"value":1226},"        return \"tool_node\"\n",{"type":18,"tag":85,"props":1228,"children":1229},{"class":87,"line":222},[1230],{"type":18,"tag":85,"props":1231,"children":1232},{"emptyLinePlaceholder":101},[1233],{"type":24,"value":104},{"type":18,"tag":85,"props":1235,"children":1236},{"class":87,"line":231},[1237],{"type":18,"tag":85,"props":1238,"children":1239},{},[1240],{"type":24,"value":1241},"    # 模型不再调工具（只给文字）→ 结束，把答案返回给用户\n",{"type":18,"tag":85,"props":1243,"children":1244},{"class":87,"line":240},[1245],{"type":18,"tag":85,"props":1246,"children":1247},{},[1248],{"type":24,"value":1249},"    # 这等价于 Stage 3 手写循环里的 `if finish_reason == \"stop\": break`\n",{"type":18,"tag":85,"props":1251,"children":1252},{"class":87,"line":248},[1253],{"type":18,"tag":85,"props":1254,"children":1255},{},[1256],{"type":24,"value":1257},"    return END\n",{"type":18,"tag":19,"props":1259,"children":1261},{"id":1260},"_6构建并编译agent程序",[1262],{"type":24,"value":1263},"6、构建并编译Agent程序",{"type":18,"tag":57,"props":1265,"children":1266},{},[1267],{"type":24,"value":1268},"该Agent是使用该类构建的StateGraph，并使用该compile方法进行编译的。",{"type":18,"tag":74,"props":1270,"children":1272},{"className":76,"code":1271,"language":78,"meta":7,"style":7},"# 构建工作流：StateGraph 是“图”的画布，传入状态类型让节点共享同一份 State\nagent_builder = StateGraph(MessagesState) # 记忆\n\n# 添加节点：给图登记两个“工序”——大脑(llm_call) 和 手(tool_node)\nagent_builder.add_node(\"llm_call\", llm_call)\nagent_builder.add_node(\"tool_node\", tool_node)\n\n# 连边：定义节点之间怎么走（这就是把 Stage 3 的循环“画”出来）\nagent_builder.add_edge(START, \"llm_call\")           # 入口 → 先进大脑\n# 条件边：从 llm_call 出来后，由 should_continue 决定去 tool_node 还是 END\nagent_builder.add_conditional_edges(\n    \"llm_call\",\n    should_continue,\n    [\"tool_node\", END]                              # 可能的去向(工具或者结束)\n)\nagent_builder.add_edge(\"tool_node\", \"llm_call\")     # 工具执行完 → 回到大脑（这条边形成“循环”）\n\n# 编译：把图“定稿”成可执行的 agent\nagent = agent_builder.compile()\n\n# 可视化：画出这张图的结构，直观看到 llm_call ⇄ tool_node 的循环\nfrom IPython.display import Image, display\ndisplay(Image(agent.get_graph(xray=True).draw_mermaid_png()))\n\n# 运行：给一个任务，invoke 会自动按图把循环跑完，直到 should_continue 返回 END\nfrom langchain.messages import HumanMessage\nmessages = [HumanMessage(content=\"Add 3 and 4.\")]\nmessages = agent.invoke({\"messages\": messages})\n# pretty_print 逐条打印消息，能清楚看到：用户提问 → 模型决定调 add → 工具结果 → 模型给出答案\nfor m in messages[\"messages\"]:\n    m.pretty_print()\n",[1273],{"type":18,"tag":81,"props":1274,"children":1275},{"__ignoreMap":7},[1276,1284,1292,1299,1307,1315,1323,1330,1338,1346,1354,1362,1370,1378,1386,1393,1401,1408,1416,1424,1431,1439,1447,1455,1462,1470,1478,1486,1494,1502,1510],{"type":18,"tag":85,"props":1277,"children":1278},{"class":87,"line":88},[1279],{"type":18,"tag":85,"props":1280,"children":1281},{},[1282],{"type":24,"value":1283},"# 构建工作流：StateGraph 是“图”的画布，传入状态类型让节点共享同一份 State\n",{"type":18,"tag":85,"props":1285,"children":1286},{"class":87,"line":97},[1287],{"type":18,"tag":85,"props":1288,"children":1289},{},[1290],{"type":24,"value":1291},"agent_builder = StateGraph(MessagesState) # 记忆\n",{"type":18,"tag":85,"props":1293,"children":1294},{"class":87,"line":107},[1295],{"type":18,"tag":85,"props":1296,"children":1297},{"emptyLinePlaceholder":101},[1298],{"type":24,"value":104},{"type":18,"tag":85,"props":1300,"children":1301},{"class":87,"line":116},[1302],{"type":18,"tag":85,"props":1303,"children":1304},{},[1305],{"type":24,"value":1306},"# 添加节点：给图登记两个“工序”——大脑(llm_call) 和 手(tool_node)\n",{"type":18,"tag":85,"props":1308,"children":1309},{"class":87,"line":125},[1310],{"type":18,"tag":85,"props":1311,"children":1312},{},[1313],{"type":24,"value":1314},"agent_builder.add_node(\"llm_call\", llm_call)\n",{"type":18,"tag":85,"props":1316,"children":1317},{"class":87,"line":134},[1318],{"type":18,"tag":85,"props":1319,"children":1320},{},[1321],{"type":24,"value":1322},"agent_builder.add_node(\"tool_node\", tool_node)\n",{"type":18,"tag":85,"props":1324,"children":1325},{"class":87,"line":142},[1326],{"type":18,"tag":85,"props":1327,"children":1328},{"emptyLinePlaceholder":101},[1329],{"type":24,"value":104},{"type":18,"tag":85,"props":1331,"children":1332},{"class":87,"line":151},[1333],{"type":18,"tag":85,"props":1334,"children":1335},{},[1336],{"type":24,"value":1337},"# 连边：定义节点之间怎么走（这就是把 Stage 3 的循环“画”出来）\n",{"type":18,"tag":85,"props":1339,"children":1340},{"class":87,"line":160},[1341],{"type":18,"tag":85,"props":1342,"children":1343},{},[1344],{"type":24,"value":1345},"agent_builder.add_edge(START, \"llm_call\")           # 入口 → 先进大脑\n",{"type":18,"tag":85,"props":1347,"children":1348},{"class":87,"line":168},[1349],{"type":18,"tag":85,"props":1350,"children":1351},{},[1352],{"type":24,"value":1353},"# 条件边：从 llm_call 出来后，由 should_continue 决定去 tool_node 还是 END\n",{"type":18,"tag":85,"props":1355,"children":1356},{"class":87,"line":177},[1357],{"type":18,"tag":85,"props":1358,"children":1359},{},[1360],{"type":24,"value":1361},"agent_builder.add_conditional_edges(\n",{"type":18,"tag":85,"props":1363,"children":1364},{"class":87,"line":186},[1365],{"type":18,"tag":85,"props":1366,"children":1367},{},[1368],{"type":24,"value":1369},"    \"llm_call\",\n",{"type":18,"tag":85,"props":1371,"children":1372},{"class":87,"line":195},[1373],{"type":18,"tag":85,"props":1374,"children":1375},{},[1376],{"type":24,"value":1377},"    should_continue,\n",{"type":18,"tag":85,"props":1379,"children":1380},{"class":87,"line":204},[1381],{"type":18,"tag":85,"props":1382,"children":1383},{},[1384],{"type":24,"value":1385},"    [\"tool_node\", END]                              # 可能的去向(工具或者结束)\n",{"type":18,"tag":85,"props":1387,"children":1388},{"class":87,"line":213},[1389],{"type":18,"tag":85,"props":1390,"children":1391},{},[1392],{"type":24,"value":237},{"type":18,"tag":85,"props":1394,"children":1395},{"class":87,"line":222},[1396],{"type":18,"tag":85,"props":1397,"children":1398},{},[1399],{"type":24,"value":1400},"agent_builder.add_edge(\"tool_node\", \"llm_call\")     # 工具执行完 → 回到大脑（这条边形成“循环”）\n",{"type":18,"tag":85,"props":1402,"children":1403},{"class":87,"line":231},[1404],{"type":18,"tag":85,"props":1405,"children":1406},{"emptyLinePlaceholder":101},[1407],{"type":24,"value":104},{"type":18,"tag":85,"props":1409,"children":1410},{"class":87,"line":240},[1411],{"type":18,"tag":85,"props":1412,"children":1413},{},[1414],{"type":24,"value":1415},"# 编译：把图“定稿”成可执行的 agent\n",{"type":18,"tag":85,"props":1417,"children":1418},{"class":87,"line":248},[1419],{"type":18,"tag":85,"props":1420,"children":1421},{},[1422],{"type":24,"value":1423},"agent = agent_builder.compile()\n",{"type":18,"tag":85,"props":1425,"children":1426},{"class":87,"line":256},[1427],{"type":18,"tag":85,"props":1428,"children":1429},{"emptyLinePlaceholder":101},[1430],{"type":24,"value":104},{"type":18,"tag":85,"props":1432,"children":1433},{"class":87,"line":265},[1434],{"type":18,"tag":85,"props":1435,"children":1436},{},[1437],{"type":24,"value":1438},"# 可视化：画出这张图的结构，直观看到 llm_call ⇄ tool_node 的循环\n",{"type":18,"tag":85,"props":1440,"children":1441},{"class":87,"line":274},[1442],{"type":18,"tag":85,"props":1443,"children":1444},{},[1445],{"type":24,"value":1446},"from IPython.display import Image, display\n",{"type":18,"tag":85,"props":1448,"children":1449},{"class":87,"line":283},[1450],{"type":18,"tag":85,"props":1451,"children":1452},{},[1453],{"type":24,"value":1454},"display(Image(agent.get_graph(xray=True).draw_mermaid_png()))\n",{"type":18,"tag":85,"props":1456,"children":1457},{"class":87,"line":292},[1458],{"type":18,"tag":85,"props":1459,"children":1460},{"emptyLinePlaceholder":101},[1461],{"type":24,"value":104},{"type":18,"tag":85,"props":1463,"children":1464},{"class":87,"line":301},[1465],{"type":18,"tag":85,"props":1466,"children":1467},{},[1468],{"type":24,"value":1469},"# 运行：给一个任务，invoke 会自动按图把循环跑完，直到 should_continue 返回 END\n",{"type":18,"tag":85,"props":1471,"children":1472},{"class":87,"line":310},[1473],{"type":18,"tag":85,"props":1474,"children":1475},{},[1476],{"type":24,"value":1477},"from langchain.messages import HumanMessage\n",{"type":18,"tag":85,"props":1479,"children":1480},{"class":87,"line":318},[1481],{"type":18,"tag":85,"props":1482,"children":1483},{},[1484],{"type":24,"value":1485},"messages = [HumanMessage(content=\"Add 3 and 4.\")]\n",{"type":18,"tag":85,"props":1487,"children":1488},{"class":87,"line":327},[1489],{"type":18,"tag":85,"props":1490,"children":1491},{},[1492],{"type":24,"value":1493},"messages = agent.invoke({\"messages\": messages})\n",{"type":18,"tag":85,"props":1495,"children":1496},{"class":87,"line":336},[1497],{"type":18,"tag":85,"props":1498,"children":1499},{},[1500],{"type":24,"value":1501},"# pretty_print 逐条打印消息，能清楚看到：用户提问 → 模型决定调 add → 工具结果 → 模型给出答案\n",{"type":18,"tag":85,"props":1503,"children":1504},{"class":87,"line":345},[1505],{"type":18,"tag":85,"props":1506,"children":1507},{},[1508],{"type":24,"value":1509},"for m in messages[\"messages\"]:\n",{"type":18,"tag":85,"props":1511,"children":1512},{"class":87,"line":354},[1513],{"type":18,"tag":85,"props":1514,"children":1515},{},[1516],{"type":24,"value":1517},"    m.pretty_print()\n",{"type":18,"tag":1519,"props":1520,"children":1521},"style",{},[1522],{"type":24,"value":1523},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":7,"searchDepth":97,"depth":97,"links":1525},[1526,1527,1528,1529,1530,1531,1532],{"id":21,"depth":97,"text":25},{"id":64,"depth":97,"text":67},{"id":624,"depth":97,"text":627},{"id":752,"depth":97,"text":755},{"id":944,"depth":97,"text":947},{"id":1095,"depth":97,"text":1098},{"id":1260,"depth":97,"text":1263},"markdown","content:articles:agent:langgraph-hello.md","content","articles\u002Fagent\u002Flanggraph-hello.md","articles\u002Fagent\u002Flanggraph-hello","md",1781678620532]