{ "cells": [ { "cell_type": "markdown", "id": "30233352-e612-44ad-b42e-8f0df455428f", "metadata": {}, "source": [ "#
大模型 AI Agent 开发实战" ] }, { "cell_type": "markdown", "id": "7644ad5b-c3fd-4a00-8c80-c225b4735246", "metadata": { "tags": [] }, "source": [ "##
Ch.10 LangGraph 实现自治循环代理(ReAct)及事件流的使用" ] }, { "cell_type": "markdown", "id": "083e91c5-1b61-451b-90f9-ac71bf62da21", "metadata": {}, "source": [ "  上节课介绍的 `Router Agent` 和 `Tool Calling Agent`,我们通过两个实际的案例证明了**随着任务需求的复杂性增加,代理架构中对中间流程的控制自由度也必须相应提高。**之所以要从 `Router Agent` 调整到 `Tool Calling Agent` 架构的原因在于,**当路由分支中的节点涉及多个工具时,需要`Agent`可以根据用户的实际输入灵活选择工具,这一需求是 `Router Agent` 无法满足的。然而,`Tool Calling Agent` 的局限性又在于:虽然它可以自主选择工具,但在其架构中,每次仅能执行一次函数调用(无论是单个外部函数还是多个外部函数)。**因此,当任务需要依次执行 A 工具、B 工具和 C 工具时,它无法支持这种自主控制的过程。因此,面对这种更复杂的需求,就需要引入了 `Full Autonomous`(自治循环代理)架构,即如下图所示:" ] }, { "cell_type": "markdown", "id": "6e3f8aad-e069-481d-8236-f894546cec53", "metadata": {}, "source": [ "
" ] }, { "cell_type": "markdown", "id": "429c955a-46fa-4cad-a62c-5c60c034d91f", "metadata": {}, "source": [ "   `Full Autonmonous` 以两种主要的方式去扩展了`Agent`对工作流的控制,分别是:\n", "\n", "- 多步骤决策: `Agent`可以控制一系列决策,而不仅仅是一个决策。\n", "- 工具访问: `Agent`可以选择并使用多种工具来完成任务。" ] }, { "cell_type": "markdown", "id": "6fb88227-87e0-48d3-8ce1-ca516cd2bd7e", "metadata": {}, "source": [ "  **满足上述两个条件,典型且通用的代理架构,就是基于`ReAct`思想而形成的代理模式**。在《Ch.3 ReAct Agent 基本理论与项目实战》课程中,我们重点讲解了`ReAct`的核心理念,并手动实现了基于该思想构建的自治循环代理流程。整体来看,其核心还是在于**为大模型配备足够丰富的外部工具,使用合适的提示词,引导大模型在接收到用户输入后,进入自主思考和循环执行的状态,以实现最终目标。** 当然,这是我们从非技术角度通俗的解释和理解自治循环代理的这个过程。" ] }, { "cell_type": "markdown", "id": "f303749a-6083-4624-9d75-f3cbf4b31518", "metadata": {}, "source": [ "# 1. LangGraph中ReAct的构建原理" ] }, { "cell_type": "markdown", "id": "a55cdd26-cf95-4237-995f-81b9eeddfb33", "metadata": {}, "source": [ "  在 `LangGraph` 开发框架中有一些预构建的组件。上节课介绍的 `ToolNode` 是其中一个,它用于处理外部函数调用,其内部结合了 `LangGraph` 底层的图结构,能够接收 `JSON Schema` 形式的数据,执行工具函数并返回结果。除此之外,`LangGraph`的预构建组件中还包含了 `ReAct` 代理架构,该架构与我们在《Ch.3 ReAct Agent 基本理论与项目实战》中手动实现的思路和流程基本一致。不同之处在于,**在 `LangGraph` 框架中,`ReAct` 组件被改造成适配图结构的循环代理,其具体过程是:大模型可以在一个 `while` 循环中被重复调用。每一步,代理来自主决定调用哪些工具及其输入,然后执行这些工具,并将输出作为观察结果反馈给大模型。当代理判断不再需要调用更多工具时,`while` 循环便会终止,输出最终的结果。**" ] }, { "cell_type": "markdown", "id": "8171c380-e5d7-4ba3-91c9-2edd5037bfa4", "metadata": {}, "source": [ "  **因此,我们需要理解的关键概念是:`LangGraph`预构建的`ReAct`组件,其实就是通过接入大模型,搭配着`Tool Calling Agent`,再结合`Router Agent` 共同构建起来的图,这个图以自治循环代理的架构形式提供服务。**其图结构如下图所示:" ] }, { "cell_type": "markdown", "id": "3d317441-45d1-4658-b45c-d93fb4b944d4", "metadata": {}, "source": [ "
" ] }, { "cell_type": "markdown", "id": "8d57b011-2029-4ebf-a158-4ebc15c65ce5", "metadata": {}, "source": [ "  如上图所示的代理架构在 `LangGraph` 中的实现机制类似于 `LangChain` 中的 `ReAct Agent`,毕竟 `LangGraph` 的底层语言是 `LangChain` 的 `LCEL` 表达式语言。因此,该 `ReAct Agent` 架构是从 `LangChain` 已实现的 `ReAct Agent` 迁移而来,不同的是在`LangGraph`框架中适配的是图结构,而非`AgentExecuter`。其本质依然基于一种规划(Planning)的思想:" ] }, { "cell_type": "markdown", "id": "e569d5d3-5db5-4553-8481-a5820ab6c12f", "metadata": {}, "source": [ "> LangChain Agents Type: https://python.langchain.com/v0.1/docs/modules/agents/agent_types/\n", ">\n", "> Agent Planning:https://smith.langchain.com/hub/hwchase17/react?organizationId=33612d73-91c5-5140-b8a0-f3155ff5dc45" ] }, { "cell_type": "markdown", "id": "88f5c294-d1e7-4d04-8c9f-3387c1d8bdda", "metadata": {}, "source": [ "```json\n", "Answer the following questions as best you can. You have access to the following tools:\n", "\n", "{tools}\n", "\n", "Use the following format:\n", "\n", "Question: the input question you must answer\n", "\n", "Thought: you should always think about what to do\n", "\n", "Action: the action to take, should be one of [{tool_names}]\n", "\n", "Action Input: the input to the action\n", "\n", "Observation: the result of the action\n", "\n", "... (this Thought/Action/Action Input/Observation can repeat N times)\n", "\n", "Thought: I now know the final answer\n", "\n", "Final Answer: the final answer to the original input question\n", "\n", "Begin!\n", "\n", "Question: {input}\n", "\n", "Thought:{agent_scratchpad}\n", "```" ] }, { "cell_type": "markdown", "id": "73386feb-ff05-4093-b2fc-33981010ac34", "metadata": {}, "source": [ "  这种代理实现的机制表明了,在`LangGraph`中实现的预构建`ReAct`代理结构,它支持:\n", "\n", "- **Tool calling :允许大模型根据需要选择和使用各种工具。**\n", "- **Memory:使代理能够保留和使用先前步骤中的信息。**\n", "- **Planning :授权大模型制定并遵循多步骤计划以实现目标。**\n", "\n", "\n", "  而其在图结构中的具体构建的工作流如下图所示:" ] }, { "cell_type": "markdown", "id": "88178062-598d-4007-9468-5d732875e44d", "metadata": {}, "source": [ "
" ] }, { "cell_type": "markdown", "id": "780a7fcf-1dd4-4dca-8180-be1a03e06fcd", "metadata": {}, "source": [ "  如图所示,`Agent`节点使用消息列表的形式来调用大语言模型,`Messages Modifier`指的是在传递到大模型之前,修饰用户的原始输入内容,可以是`SystemMessage`(作为背景信息添加的消息列表的开头)、`Runnable`(可运行)等不同状态,如果生成的 `AIMessage` 包含`tool_calls`,则该图将调用 `tools` 。 `tools` 节点执行工具(每个 `tool_call` 1 个工具)并且将响应作为`ToolMessage`对象添加到消息列表。然后`Agent`节点再次调用大语言模型。重复该过程,直到响应中不再存在`tool_calls` ,最终由`Agent`节点将完整的消息列表作为包含键 `messages`的字典返回。" ] }, { "cell_type": "markdown", "id": "619e2891-0879-43fa-8107-ba9d08a905f1", "metadata": {}, "source": [ "  那么如何实现上述这个非常复杂的过程呢?非常简单,既然我们一直提到的是预构建组件,则说明整个过程已经由`LangGraph`内部封装好了,其提供给开发者使用的接口就是:`create_react_agent`方法。" ] }, { "cell_type": "markdown", "id": "a226434d-6e8d-4b4c-9fd4-f26a15f9ed93", "metadata": {}, "source": [ "> LangGraph create_react_agent:[点击查看源码参数介绍](https://langchain-ai.github.io/langgraph/reference/prebuilt/?h=create+react+agent#langgraph.prebuilt.chat_agent_executor.create_react_agent)" ] }, { "cell_type": "markdown", "id": "0e4b20e5-8c38-4297-a4e3-8720e6b3b282", "metadata": {}, "source": [ "# 2. 案例实操:构建复杂工具应用的ReAct自治代理" ] }, { "cell_type": "markdown", "id": "f8150470-93bc-4801-af47-0379fd4fa3d8", "metadata": {}, "source": [ "  在这个案例中,我们将通过一个多工具场景需求来测试 `LangGraph` 中 `ReAct` 代理的构建方法和效果。我们设计了几个工具,以实现实时数据的查询和管理。首先,用户可以通过一个工具根据城市名称实时获取当前天气信息。接着,如果用户希望将查询到的天气数据保存到本地数据库中,可以使用另一个工具完成数据的插入操作。此外,我们还提供了一个工具,允许用户基于本地数据库中的天气数据进行提问数据进行提问。通过这些工具的组合,我们能够快速验证如何在复杂的应用场景中有效地整合不同功能,并实际的感知`LangGraph`框架下`ReAct`代理模式带来的开发便捷性和可扩展性。" ] }, { "cell_type": "markdown", "id": "b4260e55-769d-42fc-bd5a-a850db6a7b9c", "metadata": {}, "source": [ "  首先,我们设置一下`LangSmith`的配置,用于追踪和可视化`ReAct`的中间过程。这里新建了一个项目,命名为`langGraph_ReAct`,导入基础配置的代码如下图所示:" ] }, { "cell_type": "markdown", "id": "c7b1fefa-62a7-484c-ae84-905165e8e1cd", "metadata": {}, "source": [ "> LangSmith:https://www.langchain.com/langsmith" ] }, { "cell_type": "code", "execution_count": 46, "id": "e286aaec-10b8-411d-9f13-19137df2f06c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "true\n", "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\n", "langGraph_ReAct\n" ] } ], "source": [ "import os\n", "\n", "# 设置环境变量\n", "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", "os.environ[\"LANGCHAIN_API_KEY\"] = \"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\" # 需要更改为自己的 langsmith API_KEY\n", "os.environ[\"LANGCHAIN_PROJECT\"]=\"langGraph_ReAct\" # 需要更改为自己实际创建的项目名称\n", "\n", "# 验证环境变量是否设置成功\n", "print(os.getenv(\"LANGCHAIN_TRACING_V2\"))\n", "print(os.getenv(\"LANGCHAIN_API_KEY\"))\n", "print(os.getenv(\"LANGCHAIN_PROJECT\"))" ] }, { "cell_type": "markdown", "id": "b4ade83b-0251-4f90-8216-a95559add591", "metadata": {}, "source": [ "
" ] }, { "cell_type": "markdown", "id": "a6133063-ecfd-479a-915f-27cde63c6f27", "metadata": {}, "source": [ "  首先,我们接入实时天气数据查询的在线API,代码定义如下:" ] }, { "cell_type": "markdown", "id": "4b07e8cb-c9b5-447d-b4b9-52b1776f8262", "metadata": {}, "source": [ "> OpenWeather API的注册与使用,请参考赠送课程的大模型技术实战部分 - 《Ch.10 借助Function calling调用外部工具API方法》" ] }, { "cell_type": "code", "execution_count": 54, "id": "9a3aad1b-55ed-4343-913b-0c5ebd610272", "metadata": {}, "outputs": [], "source": [ "import requests\n", "\n", "def get_weather(loc):\n", " \"\"\"\n", " Function to query current weather.\n", " :param loc: Required parameter, of type string, representing the specific city name for the weather query. \\\n", " Note that for cities in China, the corresponding English city name should be used. For example, to query the weather for Beijing, \\\n", " the loc parameter should be input as 'Beijing'.\n", " :return: The result of the OpenWeather API query for current weather, with the specific URL request address being: https://api.openweathermap.org/data/2.5/weather. \\\n", " The return type is a JSON-formatted object after parsing, represented as a string, containing all important weather information.\n", " \"\"\"\n", " # Step 1.构建请求\n", " url = \"https://api.openweathermap.org/data/2.5/weather\"\n", "\n", " # Step 2.设置查询参数\n", " params = {\n", " \"q\": loc, \n", " \"appid\": \"7b34ea15a881668d4255910e5899920c\", # 输入API key\n", " \"units\": \"metric\", # 使用摄氏度而不是华氏度\n", " \"lang\":\"zh_cn\" # 输出语言为简体中文\n", " }\n", "\n", " # Step 3.发送GET请求\n", " response = requests.get(url, params=params)\n", " \n", " # Step 4.解析响应\n", " data = response.json()\n", " return json.dumps(data)" ] }, { "cell_type": "markdown", "id": "fc560293-1fdc-4b5d-a8ea-698e6ca7676c", "metadata": {}, "source": [ "  测试一下`get_weather`函数的有效性,正常情况下可以得到输入城市名的实时天气信息。测试代码如下所示:" ] }, { "cell_type": "code", "execution_count": 61, "id": "40c98bf7-0ad7-4a47-a95a-6ab2d31332e3", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'{\"coord\": {\"lon\": 116.3972, \"lat\": 39.9075}, \"weather\": [{\"id\": 800, \"main\": \"Clear\", \"description\": \"\\\\u6674\", \"icon\": \"01d\"}], \"base\": \"stations\", \"main\": {\"temp\": 20.5, \"feels_like\": 19.28, \"temp_min\": 19.94, \"temp_max\": 20.5, \"pressure\": 1016, \"humidity\": 26, \"sea_level\": 1016, \"grnd_level\": 1010}, \"visibility\": 10000, \"wind\": {\"speed\": 1.95, \"deg\": 270, \"gust\": 5.15}, \"clouds\": {\"all\": 0}, \"dt\": 1730190567, \"sys\": {\"type\": 2, \"id\": 2000403, \"country\": \"CN\", \"sunrise\": 1730155186, \"sunset\": 1730193377}, \"timezone\": 28800, \"id\": 1816670, \"name\": \"Beijing\", \"cod\": 200}'" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "get_weather('beijing')" ] }, { "cell_type": "markdown", "id": "aa0c16fd-1b76-4438-bb3d-538d5f0badbc", "metadata": {}, "source": [ "  从返回的结果是`Json`数据类型,包含了非常丰富的实时天气数据,如天气条件、温度、湿度、风速、天气描述等信息,这里我们选择一些重要的数据参数进行存储操作(存储至Mysql数据库中)。提取的参数如下:" ] }, { "cell_type": "markdown", "id": "37014aeb-9378-4b31-a0b3-2bef9cca9a9d", "metadata": {}, "source": [ "| 字段名称 | 描述 |\r\n", "|---------------|----------------------------|\r\n", "| city_id | 城市的唯一标识符 |\r\n", "| city_name | 城市名称 |\r\n", "| main_weather | 主要天气状况 |\r\n", "| description | 天气的详细描述 |\r\n", "| temperature | 当前温度 |\r\n", "| feels_like | 体感温度 |\r\n", "| temp_min | 最低温度 |\r\n", "| temp_max | 最高温度 |\r\n" ] }, { "cell_type": "markdown", "id": "ca3110b1-2373-420e-b13e-e8d500caf497", "metadata": {}, "source": [ "  接下来,设计一个用于存储实时天气信息的表。这里我们定义一个新的模型 `Weather`,并包括上述所提取出来的的字段。连接 `Mysql`数据库及创建表的代码如下所示:" ] }, { "cell_type": "code", "execution_count": 73, "id": "a0c351a6-e10e-44a8-b54e-dfb750b6b105", "metadata": {}, "outputs": [], "source": [ "from sqlalchemy import create_engine, Column, Integer, String, Float\n", "from sqlalchemy.orm import sessionmaker, declarative_base\n", "\n", "# 创建基类\n", "Base = declarative_base()\n", "\n", "# 定义 WeatherInfo 模型\n", "class Weather(Base):\n", " __tablename__ = 'weather'\n", " city_id = Column(Integer, primary_key=True) # 城市ID\n", " city_name = Column(String(50)) # 城市名称\n", " main_weather = Column(String(50)) # 主要天气状况\n", " description = Column(String(100)) # 描述\n", " temperature = Column(Float) # 温度\n", " feels_like = Column(Float) # 体感温度\n", " temp_min = Column(Float) # 最低温度\n", " temp_max = Column(Float) # 最高温度\n", "\n", "\n", "# 数据库连接 URI,这里要替换成自己的Mysql 连接信息,以下是各个字段的对应解释:\n", "# root:MySQL 数据库的用户名。\n", "# snowball950123:MySQL 数据库的密码。\n", "# 192.168.110.131:MySQL 服务器的 IP 地址。\n", "# langgraph_agent:要连接的数据库的名称。\n", "# charset=utf8mb4:设置数据库的字符集为 utf8mb4,支持更广泛的 Unicode 字符\n", "DATABASE_URI = 'mysql+pymysql://root:snowball950123@192.168.110.131/langgraph_agent?charset=utf8mb4' \n", "engine = create_engine(DATABASE_URI)\n", "\n", "# 如果表不存在,则创建表\n", "Base.metadata.create_all(engine)\n", "\n", "# 创建会话\n", "Session = sessionmaker(bind=engine)" ] }, { "cell_type": "markdown", "id": "0625574f-7582-4118-91c8-1184e7dad3e4", "metadata": {}, "source": [ "  接下来,使用`LangChain`的`tool` 装饰器将普通的函数注册为`LangGraph`中支持的工具服务,根据需求的设计,我们要依次创建三个外部函数,分别是:\n", "1. `get_weather`工具:用于根据城市名称实时查询该城市的当前天气数据。\n", "2. `insert_weather_to_db`工具:如果用户想要把查询到的天气数据插入到数据库的表中,则使用此函数完成数据库的插入操作。\n", "3. `query_weather_from_db`工具:如果用户想基于本地数据库的天气数据直接进行提问,则使用此函数完成数据库的查询操作。" ] }, { "cell_type": "markdown", "id": "a4ba315e-c210-412e-ae0f-d832221df5b3", "metadata": {}, "source": [ "  如上节课实践的流程一样,我们依然使用`pydantic`来做工具的参数校验和结构化输出。三个工具函数的定义代码依次如下所示:" ] }, { "cell_type": "code", "execution_count": 82, "id": "f039aea7-7e8d-4f09-9d62-27b36d0b5a9b", "metadata": {}, "outputs": [], "source": [ "from langchain_core.tools import tool\n", "from typing import Union, Optional\n", "from pydantic import BaseModel, Field\n", "import requests\n", "\n", "class WeatherLoc(BaseModel):\n", " location: str = Field(description=\"The location name of the city\")\n", "\n", "\n", "class WeatherInfo(BaseModel):\n", " \"\"\"Extracted weather information for a specific city.\"\"\"\n", " city_id: int = Field(..., description=\"The unique identifier for the city\")\n", " city_name: str = Field(..., description=\"The name of the city\")\n", " main_weather: str = Field(..., description=\"The main weather condition\")\n", " description: str = Field(..., description=\"A detailed description of the weather\")\n", " temperature: float = Field(..., description=\"Current temperature in Celsius\")\n", " feels_like: float = Field(..., description=\"Feels-like temperature in Celsius\")\n", " temp_min: float = Field(..., description=\"Minimum temperature in Celsius\")\n", " temp_max: float = Field(..., description=\"Maximum temperature in Celsius\")\n", "\n", "class QueryWeatherSchema(BaseModel):\n", " \"\"\"Schema for querying weather information by city name.\"\"\"\n", " city_name: str = Field(..., description=\"The name of the city to query weather information\")\n", "\n", "\n", "@tool(args_schema=WeatherInfo)\n", "def insert_weather_to_db(city_id, city_name, main_weather, description, temperature, feels_like, temp_min, temp_max):\n", " \"\"\"Insert weather information into the database.\"\"\"\n", " session = Session() # 确保为每次操作创建新的会话\n", " try:\n", " # 创建天气实例\n", " weather = Weather(\n", " city_id=city_id,\n", " city_name=city_name,\n", " main_weather=main_weather,\n", " description=description,\n", " temperature=temperature,\n", " feels_like=feels_like,\n", " temp_min=temp_min,\n", " temp_max=temp_max\n", " )\n", " # 使用 merge 方法来插入或更新(如果已有记录则更新)\n", " session.merge(weather)\n", " # 提交事务\n", " session.commit()\n", " return {\"messages\": [f\"天气数据已成功存储至Mysql数据库。\"]}\n", " except Exception as e:\n", " session.rollback() # 出错时回滚\n", " return {\"messages\": [f\"数据存储失败,错误原因:{e}\"]}\n", " finally:\n", " session.close() # 关闭会话\n", "\n", "\n", "@tool(args_schema=QueryWeatherSchema)\n", "def query_weather_from_db(city_name: str):\n", " \"\"\"Query weather information from the database by city name.\"\"\"\n", " session = Session()\n", " try:\n", " # 查询天气数据\n", " weather_data = session.query(Weather).filter(Weather.city_name == city_name).first()\n", " if weather_data:\n", " return {\n", " \"city_id\": weather_data.city_id,\n", " \"city_name\": weather_data.city_name,\n", " \"main_weather\": weather_data.main_weather,\n", " \"description\": weather_data.description,\n", " \"temperature\": weather_data.temperature,\n", " \"feels_like\": weather_data.feels_like,\n", " \"temp_min\": weather_data.temp_min,\n", " \"temp_max\": weather_data.temp_max\n", " }\n", " else:\n", " return {\"messages\": [f\"未找到城市 '{city_name}' 的天气信息。\"]}\n", " except Exception as e:\n", " return {\"messages\": [f\"查询失败,错误原因:{e}\"]}\n", " finally:\n", " session.close() # 关闭会话" ] }, { "cell_type": "markdown", "id": "494ef630-205f-4448-aea3-5b7025b34828", "metadata": {}, "source": [ "  然后,定义实时联网检索外部工具,通过该函数获取最新的网络数据信息。" ] }, { "cell_type": "code", "execution_count": 86, "id": "847cd8a7-1a6e-4846-87bb-9d97b240a0f8", "metadata": {}, "outputs": [], "source": [ "class SearchQuery(BaseModel):\n", " query: str = Field(description=\"Questions for networking queries\")\n", "\n", "\n", "@tool(args_schema = SearchQuery)\n", "def fetch_real_time_info(query):\n", " \"\"\"Get real-time Internet information\"\"\"\n", " url = \"https://google.serper.dev/search\"\n", " payload = json.dumps({\n", " \"q\": query,\n", " \"num\": 1,\n", " })\n", " headers = {\n", " 'X-API-KEY': 'cd872fca99047eb9165242365c65b858bc8970c0',\n", " 'Content-Type': 'application/json'\n", " }\n", " \n", " response = requests.post(url, headers=headers, data=payload)\n", " data = json.loads(response.text) # 将返回的JSON字符串转换为字典\n", " if 'organic' in data:\n", " return json.dumps(data['organic'], ensure_ascii=False) # 返回'organic'部分的JSON字符串\n", " else:\n", " return json.dumps({\"error\": \"No organic results found\"}, ensure_ascii=False) # 如果没有'organic'键,返回错误信息" ] }, { "cell_type": "markdown", "id": "1dc9f46e-12c6-4a90-9853-04064eb9226e", "metadata": {}, "source": [ "  然后把所有定义的工具存储在一个列表中,如下代码所示:" ] }, { "cell_type": "code", "execution_count": 92, "id": "67ecaf1b-22ac-42a5-afb3-f1eeb7eca146", "metadata": {}, "outputs": [], "source": [ "tools = [fetch_real_time_info, get_weather, insert_weather_to_db, query_weather_from_db]" ] }, { "cell_type": "code", "execution_count": 94, "id": "c45325e4-c099-41c0-8af5-b2789239a8cd", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[StructuredTool(name='fetch_real_time_info', description='Get real-time Internet information', args_schema=, func=),\n", " ,\n", " StructuredTool(name='insert_weather_to_db', description='Insert weather information into the database.', args_schema=, func=),\n", " StructuredTool(name='query_weather_from_db', description='Query weather information from the database by city name.', args_schema=, func=)]" ] }, "execution_count": 94, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tools" ] }, { "cell_type": "markdown", "id": "baec2cbb-6fae-4b50-86d4-25b43a589180", "metadata": {}, "source": [ "  准备好工具后,接下来定义用于构建`AI Agent`的大模型实例,这里我们使用`OpenAI`的在线`GPT-4`模型。代码如下:" ] }, { "cell_type": "code", "execution_count": 89, "id": "0f7e7fc6-c097-463d-b5bd-fbd44855e29d", "metadata": {}, "outputs": [], "source": [ "import getpass\n", "import os\n", "from langchain_openai import ChatOpenAI\n", "\n", "if not os.environ.get(\"OPENAI_API_KEY\"):\n", " os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"Enter your OpenAI API key: \")\n", "\n", "\n", "llm = ChatOpenAI(model=\"gpt-4o\")" ] }, { "cell_type": "markdown", "id": "2733c866-cbd2-4969-bdee-2a58503b50b7", "metadata": {}, "source": [ "  当有了工具列表和模型后,就可以通过`create_react_agent`这个`LangGraph`框架中预构建的方法来创建自治循环代理(ReAct)的工作流,其必要的参数如下:\n", "\n", "- model: 支持工具调用的LangChain聊天模型。\n", "- tools: 工具列表、ToolExecutor 或 ToolNode 实例。\n", "- state_schema:图的状态模式。必须有`messages`和`is_last_step`键。默认为定义这两个键的`Agent State`。" ] }, { "cell_type": "markdown", "id": "29c369b4-9c06-474d-9c3e-b7d94fdd67f7", "metadata": {}, "source": [ "  上述三点我们均在前面的课程中详细且作为重点介绍过,大家应该是比较容易理解的。所以,创建`ReAct`代理的代码就如下所示:" ] }, { "cell_type": "code", "execution_count": 113, "id": "735fbb39-2872-4da6-80c2-bf9918dcbb2a", "metadata": {}, "outputs": [], "source": [ "from langgraph.prebuilt import create_react_agent\n", "\n", "graph = create_react_agent(llm, tools=tools)" ] }, { "cell_type": "code", "execution_count": 115, "id": "2379bee8-5f61-4946-9900-d846a4f6ae34", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 115, "metadata": {}, "output_type": "execute_result" } ], "source": [ "graph" ] }, { "cell_type": "markdown", "id": "0f477365-9f76-4590-ae53-bc0c8c55eaf6", "metadata": {}, "source": [ "  我们可以逐步的分析和解释一下这一行代码中涉及的图构建过程:" ] }, { "cell_type": "markdown", "id": "7cbf93aa-c0ca-416c-b3a3-d8c0825566c1", "metadata": {}, "source": [ "- **Step 1. 定义图状态模式**" ] }, { "cell_type": "markdown", "id": "4064428b-60f5-4adf-9501-fb6b3ff29d60", "metadata": {}, "source": [ "  `LangGraph`中的主要图类型是`StateGraph`。每个节点通过`State`中的参数获取有效信息,执行完节点的内部逻辑后,更新该`State`状态中的值。不同的状态模式,可以通过注释设置状态的特定属性(例如覆盖现有值)或添加到现有属性。伪代码如下:" ] }, { "cell_type": "markdown", "id": "ac76a896-52f3-4e10-8811-d6228565f18f", "metadata": {}, "source": [ "```python\n", "from typing import Annotated\n", "from typing_extensions import TypedDict\n", "from langgraph.graph.message import add_messages\n", "\n", "\n", "class State(TypedDict):\n", " messages: Annotated[list, add_messages]\n", "```" ] }, { "cell_type": "markdown", "id": "7616662f-553c-42e4-b55c-027a892a1474", "metadata": {}, "source": [ "- **Step 2. 定义`Router Function`**" ] }, { "cell_type": "markdown", "id": "b559a066-d4c1-41ec-93fd-78ae7798f578", "metadata": {}, "source": [ "  设置边缘条件,有条件的原因是,根据节点的输出,可以采用多个路径之一。在该节点运行之前,所采用的路径是未知的(由大模型决定)。\n", "- 条件边缘:调用代理后,如果代理说要采取行动,那么应该调用调用工具的函数。如果代理说已经完成,那么就应该完成。\n", "- 正常边:调用工具后,它应该始终返回给代理来决定下一步做什么。\n", "\n", "  伪代码如下:" ] }, { "cell_type": "markdown", "id": "9156fb50-2421-4b22-980c-91b3485ffd39", "metadata": {}, "source": [ "```python\n", "# 定义决定是否继续执行任务的路由函数\n", "def should_continue(state: State):\n", " messages = state[\"messages\"]\n", " last_message = messages[-1]\n", " # 如果不是工具调用,则结束\n", " if not last_message.tool_calls:\n", " return END\n", " # 如果是的话,则进入工具库中选择函数执行\n", " else:\n", " return \"tools\"\n", "```" ] }, { "cell_type": "markdown", "id": "b3bfd4ed-1807-48ba-a64b-7ccdc9d223f4", "metadata": {}, "source": [ "- **Step 3. 定义大模型的交互函数**" ] }, { "cell_type": "markdown", "id": "126aad2c-2703-447c-99f8-e473942a72e3", "metadata": {}, "source": [ "  接下来需要通过一个节点函数加载我想要使用的大模型。它需要满足两个标准:\n", "- 应该与消息一起使用,因为图的状态主要是消息列表(聊天历史记录)。\n", "- 需要与工具调用一起使用,其内部使用的是预构建的ToolNode。\n", "\n", "  伪代码如下:" ] }, { "cell_type": "markdown", "id": "ba357f1f-b1e2-48ba-951d-87ccb672e104", "metadata": {}, "source": [ "```python\n", "from typing import Literal\n", "\n", "from langchain_core.runnables import RunnableConfig\n", "\n", "# 定义大模型交互的节点函数\n", "async def call_model(state: State, config: RunnableConfig):\n", " messages = state[\"messages\"]\n", " response = await model.ainvoke(messages, config)\n", " # 将调用大模型后得到的响应,追加到消息列表中\n", " return {\"messages\": response}\n", "```" ] }, { "cell_type": "markdown", "id": "94fde33f-986d-445d-8fec-d4129e569c42", "metadata": {}, "source": [ "- **Step 4. 构建图结构**" ] }, { "cell_type": "markdown", "id": "a0370f6a-9b37-48b2-9748-297ec63319c1", "metadata": {}, "source": [ "  最后,把上述所有的组件放在一起构建图结构,这与我们手动构建图的方式基本一致,伪代码如下:" ] }, { "cell_type": "markdown", "id": "932fc1de-e3d2-49e6-a159-74e572025b7b", "metadata": {}, "source": [ "```python\n", "from langgraph.graph import END, START, StateGraph\n", "\n", "# 定义一个新图\n", "workflow = StateGraph(State)\n", "\n", "# 添加两个节点\n", "workflow.add_node(\"agent\", call_model)\n", "workflow.add_node(\"tools\", tool_node)\n", "\n", "# 设置起始节点为 agent\n", "workflow.add_edge(START, \"agent\")\n", "\n", "# 添加条件边 -- > Router Agent\n", "workflow.add_conditional_edges(\n", " \"agent\",\n", " should_continue,\n", " [\"tools\", END],\n", ")\n", "\n", "# 添加回调边\n", "workflow.add_edge(\"tools\", \"agent\")\n", "\n", "# 编译图\n", "app = workflow.compile()\n", "```" ] }, { "cell_type": "markdown", "id": "c79ff959-3be3-46ae-a5f7-19119307a4b4", "metadata": {}, "source": [ "  理解了上面的`create_react_agent`方法内部的构建原理后,其实就能明白:当通过`create_react_agent(llm, tools=tools)`一行代码的执行,现在得到的已经是一个编译后、可执行的图了。我们可以通过`mermaid`方法来可视化经过`create_react_agent`方法构造出来的图结构,代码如下所示:" ] }, { "cell_type": "code", "execution_count": 136, "id": "cb7f6a67-7089-46fe-a0b8-55a97980ec10", "metadata": {}, "outputs": [ { "data": { "image/jpeg": "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", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from IPython.display import Image, display\n", "\n", "display(Image(graph.get_graph().draw_mermaid_png()))" ] }, { "cell_type": "markdown", "id": "dc637164-7a86-4ed3-bf89-b7d28b4dbf3a", "metadata": {}, "source": [ "  返回的是编译好的`LangGraph`可运行程序,可直接用于聊天交互。调用方式则和之前使用的方法一样,我们可以依次针对不同复杂程度的需求依次进行提问。首先是测试是否可以不使用工具,直接调用大模型生成响应。" ] }, { "cell_type": "code", "execution_count": 139, "id": "2b397283-67f3-4bfb-b1cb-f67dcee97ac8", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'messages': [HumanMessage(content='你好,请你介绍一下你自己', additional_kwargs={}, response_metadata={}, id='268158b3-ec6d-44a7-bbd5-bb43d449ff7e'),\n", " AIMessage(content='你好!我是一个由人工智能驱动的助手,旨在帮助解答问题、提供信息并协助完成各种任务。我可以处理广泛的主题,包括但不限于科技、教育、娱乐、天气、新闻等。如果你有任何特定的问题或需要帮助的地方,请随时告诉我!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 67, 'prompt_tokens': 343, 'total_tokens': 410, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'stop', 'logprobs': None}, id='run-968ec2f8-eb4b-45cf-ac55-398deba6e6fe-0', usage_metadata={'input_tokens': 343, 'output_tokens': 67, 'total_tokens': 410, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}})]}" ] }, "execution_count": 139, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# query=\"你好,请你介绍一下你自己\"\n", "# input_message = {\"messages\": [HumanMessage(content=query)]}\n", "\n", "# 可以自动处理成 HumanMessage 的消息格式\n", "finan_response = graph.invoke({\"messages\":[\"你好,请你介绍一下你自己\"]})\n", "finan_response" ] }, { "cell_type": "code", "execution_count": 144, "id": "85500ff9-9656-472d-8eca-77a642e7d82c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'你好!我是一个由人工智能驱动的助手,旨在帮助解答问题、提供信息并协助完成各种任务。我可以处理广泛的主题,包括但不限于科技、教育、娱乐、天气、新闻等。如果你有任何特定的问题或需要帮助的地方,请随时告诉我!'" ] }, "execution_count": 144, "metadata": {}, "output_type": "execute_result" } ], "source": [ "finan_response[\"messages\"][-1].content" ] }, { "cell_type": "markdown", "id": "b71144e7-678a-486b-9649-99f6b2d06940", "metadata": {}, "source": [ "  加大输入问题的复杂度,接下来我们提问的问题希望它能够自动找到正确的工具函数,基于工具的执行结果作为既定的事实,引导生成最终的回复。" ] }, { "cell_type": "code", "execution_count": 147, "id": "729dcc03-f51e-451d-b60c-6ce74a7494f8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "<__main__.Weather object at 0x00000222A5896390>\n" ] }, { "data": { "text/plain": [ "{'messages': [HumanMessage(content='北京今天的天气怎么样?', additional_kwargs={}, response_metadata={}, id='3ea29a69-311d-4003-935f-18ca24bc9e6f'),\n", " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_BMA4Jp782AMVBw4VS1x3nv8h', 'function': {'arguments': '{\"city_name\":\"Beijing\"}', 'name': 'query_weather_from_db'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 18, 'prompt_tokens': 342, 'total_tokens': 360, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-eb43ab41-161c-4acb-b8d7-b5890745ce55-0', tool_calls=[{'name': 'query_weather_from_db', 'args': {'city_name': 'Beijing'}, 'id': 'call_BMA4Jp782AMVBw4VS1x3nv8h', 'type': 'tool_call'}], usage_metadata={'input_tokens': 342, 'output_tokens': 18, 'total_tokens': 360, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}}),\n", " ToolMessage(content='{\"city_id\": 1816670, \"city_name\": \"Beijing\", \"main_weather\": \"Clouds\", \"description\": \"晴,少云\", \"temperature\": 14.94, \"feels_like\": 13.59, \"temp_min\": 14.94, \"temp_max\": 14.94}', name='query_weather_from_db', id='8b21e310-8581-4a4f-b3a0-c3c2b4b22195', tool_call_id='call_BMA4Jp782AMVBw4VS1x3nv8h'),\n", " AIMessage(content='北京今天的天气是多云的。具体情况如下:\\n\\n- 天气描述:晴,少云\\n- 当前温度:14.94°C\\n- 体感温度:13.59°C\\n- 最低温度:14.94°C\\n- 最高温度:14.94°C', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 67, 'prompt_tokens': 440, 'total_tokens': 507, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'stop', 'logprobs': None}, id='run-25dabd3b-195e-49ce-a431-b9f535fdd01c-0', usage_metadata={'input_tokens': 440, 'output_tokens': 67, 'total_tokens': 507, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}})]}" ] }, "execution_count": 147, "metadata": {}, "output_type": "execute_result" } ], "source": [ "finan_response = graph.invoke({\"messages\":[\"北京今天的天气怎么样?\"]})\n", "\n", "finan_response" ] }, { "cell_type": "code", "execution_count": 149, "id": "808cff4b-98d7-46ef-a464-5922d8ee9ca1", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'北京今天的天气是多云的。具体情况如下:\\n\\n- 天气描述:晴,少云\\n- 当前温度:14.94°C\\n- 体感温度:13.59°C\\n- 最低温度:14.94°C\\n- 最高温度:14.94°C'" ] }, "execution_count": 149, "metadata": {}, "output_type": "execute_result" } ], "source": [ "finan_response[\"messages\"][-1].content" ] }, { "cell_type": "code", "execution_count": 147, "id": "b9e816fc-3468-468b-9a71-c2c80c54564d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "<__main__.Weather object at 0x00000222A5896390>\n" ] }, { "data": { "text/plain": [ "{'messages': [HumanMessage(content='北京今天的天气怎么样?', additional_kwargs={}, response_metadata={}, id='3ea29a69-311d-4003-935f-18ca24bc9e6f'),\n", " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_BMA4Jp782AMVBw4VS1x3nv8h', 'function': {'arguments': '{\"city_name\":\"Beijing\"}', 'name': 'query_weather_from_db'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 18, 'prompt_tokens': 342, 'total_tokens': 360, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-eb43ab41-161c-4acb-b8d7-b5890745ce55-0', tool_calls=[{'name': 'query_weather_from_db', 'args': {'city_name': 'Beijing'}, 'id': 'call_BMA4Jp782AMVBw4VS1x3nv8h', 'type': 'tool_call'}], usage_metadata={'input_tokens': 342, 'output_tokens': 18, 'total_tokens': 360, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}}),\n", " ToolMessage(content='{\"city_id\": 1816670, \"city_name\": \"Beijing\", \"main_weather\": \"Clouds\", \"description\": \"晴,少云\", \"temperature\": 14.94, \"feels_like\": 13.59, \"temp_min\": 14.94, \"temp_max\": 14.94}', name='query_weather_from_db', id='8b21e310-8581-4a4f-b3a0-c3c2b4b22195', tool_call_id='call_BMA4Jp782AMVBw4VS1x3nv8h'),\n", " AIMessage(content='北京今天的天气是多云的。具体情况如下:\\n\\n- 天气描述:晴,少云\\n- 当前温度:14.94°C\\n- 体感温度:13.59°C\\n- 最低温度:14.94°C\\n- 最高温度:14.94°C', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 67, 'prompt_tokens': 440, 'total_tokens': 507, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'stop', 'logprobs': None}, id='run-25dabd3b-195e-49ce-a431-b9f535fdd01c-0', usage_metadata={'input_tokens': 440, 'output_tokens': 67, 'total_tokens': 507, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}})]}" ] }, "execution_count": 147, "metadata": {}, "output_type": "execute_result" } ], "source": [ "finan_response = graph.invoke({\"messages\":[\"北京今天的天气怎么样?\"]})\n", "\n", "finan_response" ] }, { "cell_type": "code", "execution_count": 159, "id": "c11e26eb-32b2-44cf-9dec-27d08be1b1ca", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'messages': [HumanMessage(content='你知道 cloud 3.5 发布的 computer use 吗?请用中文回复我', additional_kwargs={}, response_metadata={}, id='c1ba742a-2c74-4aed-b774-b176ab60e8e1'),\n", " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_xt4I3LDtRRzOYKyb01fA4Rv5', 'function': {'arguments': '{\"query\":\"cloud 3.5 发布的 computer use\"}', 'name': 'fetch_real_time_info'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 24, 'prompt_tokens': 355, 'total_tokens': 379, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-cb591e6e-f410-4ab0-a77a-ece56767b728-0', tool_calls=[{'name': 'fetch_real_time_info', 'args': {'query': 'cloud 3.5 发布的 computer use'}, 'id': 'call_xt4I3LDtRRzOYKyb01fA4Rv5', 'type': 'tool_call'}], usage_metadata={'input_tokens': 355, 'output_tokens': 24, 'total_tokens': 379, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}}),\n", " ToolMessage(content='[{\"title\": \"升级了来自Anthropic 的Claude 3.5 Sonnet(现已推出) - AWS\", \"link\": \"https://aws.amazon.com/cn/blogs/china/upgraded-claude-3-5-sonnet-from-anthropic-available-now-computer-use-public-beta-and-claude-3-5-haiku-coming-soon-in-amazon-bedrock/\", \"snippet\": \"计算机使用– Claude 3.5 Sonnet 目前在Amazon Bedrock 中提供计算机使用功能(公测版),这使得Claude 能够感知并与计算机界面进行交互。开发人员可以 ...\", \"date\": \"5 days ago\", \"position\": 1}]', name='fetch_real_time_info', id='dba1ac63-46d2-4b19-926f-3c28da7b29f6', tool_call_id='call_xt4I3LDtRRzOYKyb01fA4Rv5'),\n", " AIMessage(content='关于 Cloud 3.5 发布的 computer use 功能,目前的信息显示,Claude 3.5 Sonnet 现在在 Amazon Bedrock 中提供计算机使用功能(公测版)。这使得 Claude 能够感知并与计算机界面进行交互。更多详情可以查看 [AWS 官方博客](https://aws.amazon.com/cn/blogs/china/upgraded-claude-3-5-sonnet-from-anthropic-available-now-computer-use-public-beta-and-claude-3-5-haiku-coming-soon-in-amazon-bedrock/)。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 122, 'prompt_tokens': 541, 'total_tokens': 663, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_45c6de4934', 'finish_reason': 'stop', 'logprobs': None}, id='run-e45a1550-0c7d-4096-b1a0-238ca7cc8248-0', usage_metadata={'input_tokens': 541, 'output_tokens': 122, 'total_tokens': 663, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}})]}" ] }, "execution_count": 159, "metadata": {}, "output_type": "execute_result" } ], "source": [ "finan_response = graph.invoke({\"messages\":[\"你知道 cloud 3.5 发布的 computer use 吗?请用中文回复我\"]})\n", "\n", "finan_response" ] }, { "cell_type": "code", "execution_count": 161, "id": "740d12ba-768a-4ef3-afc0-680ead5286ee", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'关于 Cloud 3.5 发布的 computer use 功能,目前的信息显示,Claude 3.5 Sonnet 现在在 Amazon Bedrock 中提供计算机使用功能(公测版)。这使得 Claude 能够感知并与计算机界面进行交互。更多详情可以查看 [AWS 官方博客](https://aws.amazon.com/cn/blogs/china/upgraded-claude-3-5-sonnet-from-anthropic-available-now-computer-use-public-beta-and-claude-3-5-haiku-coming-soon-in-amazon-bedrock/)。'" ] }, "execution_count": 161, "metadata": {}, "output_type": "execute_result" } ], "source": [ "finan_response[\"messages\"][-1].content" ] }, { "cell_type": "markdown", "id": "172fe2f1-9880-48ac-882b-bde549d6f220", "metadata": {}, "source": [ "  继续加大问题的难度,我们要在一个问题中涉及多个工具的使用,比如:" ] }, { "cell_type": "code", "execution_count": 164, "id": "7107dae6-9737-4d42-910e-5ea053939d99", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'messages': [HumanMessage(content='帮我查一下北京、上海,哈尔滨三个城市的天气,告诉我哪个城市最适合出游。同时,把查询到的数据存储到数据库中', additional_kwargs={}, response_metadata={}, id='7da34790-3f00-46d0-859e-b6f7cef4ee5d'),\n", " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_ro4igb9igPivvs0HZXKHAhwG', 'function': {'arguments': '{\"loc\": \"Beijing\"}', 'name': 'get_weather'}, 'type': 'function'}, {'id': 'call_IRBeslzB7okZa28rAnrNdNbB', 'function': {'arguments': '{\"loc\": \"Shanghai\"}', 'name': 'get_weather'}, 'type': 'function'}, {'id': 'call_J3yXYOErmnZmooW7A0Wrsh5I', 'function': {'arguments': '{\"loc\": \"Harbin\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 60, 'prompt_tokens': 370, 'total_tokens': 430, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-e630e698-033f-463c-8b48-2107eaa616c2-0', tool_calls=[{'name': 'get_weather', 'args': {'loc': 'Beijing'}, 'id': 'call_ro4igb9igPivvs0HZXKHAhwG', 'type': 'tool_call'}, {'name': 'get_weather', 'args': {'loc': 'Shanghai'}, 'id': 'call_IRBeslzB7okZa28rAnrNdNbB', 'type': 'tool_call'}, {'name': 'get_weather', 'args': {'loc': 'Harbin'}, 'id': 'call_J3yXYOErmnZmooW7A0Wrsh5I', 'type': 'tool_call'}], usage_metadata={'input_tokens': 370, 'output_tokens': 60, 'total_tokens': 430, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}}),\n", " ToolMessage(content='{\"coord\": {\"lon\": 116.3972, \"lat\": 39.9075}, \"weather\": [{\"id\": 800, \"main\": \"Clear\", \"description\": \"\\\\u6674\", \"icon\": \"01n\"}], \"base\": \"stations\", \"main\": {\"temp\": 17.72, \"feels_like\": 16.36, \"temp_min\": 14.94, \"temp_max\": 17.72, \"pressure\": 1017, \"humidity\": 31, \"sea_level\": 1017, \"grnd_level\": 1012}, \"visibility\": 10000, \"wind\": {\"speed\": 1.3, \"deg\": 282, \"gust\": 4.19}, \"clouds\": {\"all\": 0}, \"dt\": 1730195826, \"sys\": {\"type\": 2, \"id\": 2000403, \"country\": \"CN\", \"sunrise\": 1730155186, \"sunset\": 1730193377}, \"timezone\": 28800, \"id\": 1816670, \"name\": \"Beijing\", \"cod\": 200}', name='get_weather', id='969b2663-e3db-4b18-b5ce-433b65adf673', tool_call_id='call_ro4igb9igPivvs0HZXKHAhwG'),\n", " ToolMessage(content='{\"coord\": {\"lon\": 121.4581, \"lat\": 31.2222}, \"weather\": [{\"id\": 800, \"main\": \"Clear\", \"description\": \"\\\\u6674\", \"icon\": \"01n\"}], \"base\": \"stations\", \"main\": {\"temp\": 17.92, \"feels_like\": 17.31, \"temp_min\": 17.92, \"temp_max\": 17.93, \"pressure\": 1019, \"humidity\": 59, \"sea_level\": 1019, \"grnd_level\": 1017}, \"visibility\": 10000, \"wind\": {\"speed\": 5, \"deg\": 20}, \"clouds\": {\"all\": 0}, \"dt\": 1730196000, \"sys\": {\"type\": 1, \"id\": 9659, \"country\": \"CN\", \"sunrise\": 1730153230, \"sunset\": 1730192904}, \"timezone\": 28800, \"id\": 1796236, \"name\": \"Shanghai\", \"cod\": 200}', name='get_weather', id='75725f62-f9d2-4fe2-8865-fea606416b6a', tool_call_id='call_IRBeslzB7okZa28rAnrNdNbB'),\n", " ToolMessage(content='{\"coord\": {\"lon\": 126.65, \"lat\": 45.75}, \"weather\": [{\"id\": 500, \"main\": \"Rain\", \"description\": \"\\\\u5c0f\\\\u96e8\", \"icon\": \"10n\"}], \"base\": \"stations\", \"main\": {\"temp\": 8.62, \"feels_like\": 8.62, \"temp_min\": 8.62, \"temp_max\": 8.62, \"pressure\": 1016, \"humidity\": 70, \"sea_level\": 1016, \"grnd_level\": 999}, \"visibility\": 10000, \"wind\": {\"speed\": 0.91, \"deg\": 129, \"gust\": 1.16}, \"rain\": {\"1h\": 0.36}, \"clouds\": {\"all\": 97}, \"dt\": 1730196009, \"sys\": {\"country\": \"CN\", \"sunrise\": 1730153336, \"sunset\": 1730190306}, \"timezone\": 28800, \"id\": 2037013, \"name\": \"Harbin\", \"cod\": 200}', name='get_weather', id='6b49bbfe-e45d-449b-84b2-314f760467ca', tool_call_id='call_J3yXYOErmnZmooW7A0Wrsh5I'),\n", " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_izOMSYgvfloxuJkXgDB1Voz6', 'function': {'arguments': '{\"city_id\": 1816670, \"city_name\": \"Beijing\", \"main_weather\": \"Clear\", \"description\": \"晴\", \"temperature\": 17.72, \"feels_like\": 16.36, \"temp_min\": 14.94, \"temp_max\": 17.72}', 'name': 'insert_weather_to_db'}, 'type': 'function'}, {'id': 'call_Bc8WJdfNZMzo3hXCyunaCznj', 'function': {'arguments': '{\"city_id\": 1796236, \"city_name\": \"Shanghai\", \"main_weather\": \"Clear\", \"description\": \"晴\", \"temperature\": 17.92, \"feels_like\": 17.31, \"temp_min\": 17.92, \"temp_max\": 17.93}', 'name': 'insert_weather_to_db'}, 'type': 'function'}, {'id': 'call_NanSRD2EDHeOlUktShccuSaW', 'function': {'arguments': '{\"city_id\": 2037013, \"city_name\": \"Harbin\", \"main_weather\": \"Rain\", \"description\": \"小雨\", \"temperature\": 8.62, \"feels_like\": 8.62, \"temp_min\": 8.62, \"temp_max\": 8.62}', 'name': 'insert_weather_to_db'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 202, 'prompt_tokens': 1177, 'total_tokens': 1379, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-8a14762c-6309-4f27-8eae-a422867a293d-0', tool_calls=[{'name': 'insert_weather_to_db', 'args': {'city_id': 1816670, 'city_name': 'Beijing', 'main_weather': 'Clear', 'description': '晴', 'temperature': 17.72, 'feels_like': 16.36, 'temp_min': 14.94, 'temp_max': 17.72}, 'id': 'call_izOMSYgvfloxuJkXgDB1Voz6', 'type': 'tool_call'}, {'name': 'insert_weather_to_db', 'args': {'city_id': 1796236, 'city_name': 'Shanghai', 'main_weather': 'Clear', 'description': '晴', 'temperature': 17.92, 'feels_like': 17.31, 'temp_min': 17.92, 'temp_max': 17.93}, 'id': 'call_Bc8WJdfNZMzo3hXCyunaCznj', 'type': 'tool_call'}, {'name': 'insert_weather_to_db', 'args': {'city_id': 2037013, 'city_name': 'Harbin', 'main_weather': 'Rain', 'description': '小雨', 'temperature': 8.62, 'feels_like': 8.62, 'temp_min': 8.62, 'temp_max': 8.62}, 'id': 'call_NanSRD2EDHeOlUktShccuSaW', 'type': 'tool_call'}], usage_metadata={'input_tokens': 1177, 'output_tokens': 202, 'total_tokens': 1379, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}}),\n", " ToolMessage(content='{\"messages\": [\"天气数据已成功存储至Mysql数据库。\"]}', name='insert_weather_to_db', id='3faca92c-6a82-40ba-9f59-a5f62326400f', tool_call_id='call_izOMSYgvfloxuJkXgDB1Voz6'),\n", " ToolMessage(content='{\"messages\": [\"天气数据已成功存储至Mysql数据库。\"]}', name='insert_weather_to_db', id='fafbdce3-6ea1-4d93-8ec3-8a32606e6ca7', tool_call_id='call_Bc8WJdfNZMzo3hXCyunaCznj'),\n", " ToolMessage(content='{\"messages\": [\"天气数据已成功存储至Mysql数据库。\"]}', name='insert_weather_to_db', id='0c98e550-8562-4125-9e08-2391c7c1b703', tool_call_id='call_NanSRD2EDHeOlUktShccuSaW'),\n", " AIMessage(content='以下是北京、上海和哈尔滨的天气情况:\\n\\n1. **北京**:\\n - 天气:晴\\n - 温度:17.72°C\\n - 体感温度:16.36°C\\n - 最低温度:14.94°C\\n - 最高温度:17.72°C\\n\\n2. **上海**:\\n - 天气:晴\\n - 温度:17.92°C\\n - 体感温度:17.31°C\\n - 最低温度:17.92°C\\n - 最高温度:17.93°C\\n\\n3. **哈尔滨**:\\n - 天气:小雨\\n - 温度:8.62°C\\n - 体感温度:8.62°C\\n - 最低温度:8.62°C\\n - 最高温度:8.62°C\\n\\n根据天气情况,北京和上海的天气都是晴朗的,比较适合出游。哈尔滨有小雨,不太适合出游。如果一定要选择一个最适合出游的城市,我会推荐上海,因为气温稍微高一点,更加舒适。\\n\\n所有查询到的天气数据已经成功存储到数据库中。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 267, 'prompt_tokens': 1558, 'total_tokens': 1825, 'prompt_tokens_details': {'cached_tokens': 1280}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'stop', 'logprobs': None}, id='run-51e04ca7-3475-49fb-a5c0-8493faf347a1-0', usage_metadata={'input_tokens': 1558, 'output_tokens': 267, 'total_tokens': 1825, 'input_token_details': {'cache_read': 1280}, 'output_token_details': {'reasoning': 0}})]}" ] }, "execution_count": 164, "metadata": {}, "output_type": "execute_result" } ], "source": [ "finan_response = graph.invoke({\"messages\":[\"帮我查一下北京、上海,哈尔滨三个城市的天气,告诉我哪个城市最适合出游。同时,把查询到的数据存储到数据库中\"]})\n", "\n", "finan_response" ] }, { "cell_type": "code", "execution_count": 166, "id": "3beb83cd-4bfc-4c93-8772-4c5779630ddf", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'以下是北京、上海和哈尔滨的天气情况:\\n\\n1. **北京**:\\n - 天气:晴\\n - 温度:17.72°C\\n - 体感温度:16.36°C\\n - 最低温度:14.94°C\\n - 最高温度:17.72°C\\n\\n2. **上海**:\\n - 天气:晴\\n - 温度:17.92°C\\n - 体感温度:17.31°C\\n - 最低温度:17.92°C\\n - 最高温度:17.93°C\\n\\n3. **哈尔滨**:\\n - 天气:小雨\\n - 温度:8.62°C\\n - 体感温度:8.62°C\\n - 最低温度:8.62°C\\n - 最高温度:8.62°C\\n\\n根据天气情况,北京和上海的天气都是晴朗的,比较适合出游。哈尔滨有小雨,不太适合出游。如果一定要选择一个最适合出游的城市,我会推荐上海,因为气温稍微高一点,更加舒适。\\n\\n所有查询到的天气数据已经成功存储到数据库中。'" ] }, "execution_count": 166, "metadata": {}, "output_type": "execute_result" } ], "source": [ "finan_response[\"messages\"][-1].content" ] }, { "cell_type": "markdown", "id": "7c5522a5-26af-484e-ab2c-b28b4f2442b3", "metadata": {}, "source": [ "  同时,可以在数据库中查看数据的插入情况:" ] }, { "cell_type": "markdown", "id": "be607416-30bd-4c0a-bf68-eb8430d27b68", "metadata": {}, "source": [ "
" ] }, { "cell_type": "code", "execution_count": 170, "id": "90c00079-83ad-45cb-a81c-d0daf39a78c2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "<__main__.Weather object at 0x00000222A72E6350>\n", "<__main__.Weather object at 0x00000222A6F4AC50>\n" ] }, { "data": { "text/plain": [ "{'messages': [HumanMessage(content='帮我分析一下数据库中北京和哈尔滨城市天气的信息,做一个详细的对比,并生成出行建议', additional_kwargs={}, response_metadata={}, id='de625a6f-c247-41dd-9bf0-e5e2af41459f'),\n", " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_f2h95mzFmIAZ7LwcBaL1QUHP', 'function': {'arguments': '{\"city_name\": \"Beijing\"}', 'name': 'query_weather_from_db'}, 'type': 'function'}, {'id': 'call_8P3qlq6yM9kelaHqRZ9zZ4O0', 'function': {'arguments': '{\"city_name\": \"Harbin\"}', 'name': 'query_weather_from_db'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 52, 'prompt_tokens': 361, 'total_tokens': 413, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-33535fcc-cb90-4bc5-b8e2-421d7b20e68b-0', tool_calls=[{'name': 'query_weather_from_db', 'args': {'city_name': 'Beijing'}, 'id': 'call_f2h95mzFmIAZ7LwcBaL1QUHP', 'type': 'tool_call'}, {'name': 'query_weather_from_db', 'args': {'city_name': 'Harbin'}, 'id': 'call_8P3qlq6yM9kelaHqRZ9zZ4O0', 'type': 'tool_call'}], usage_metadata={'input_tokens': 361, 'output_tokens': 52, 'total_tokens': 413, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}}),\n", " ToolMessage(content='{\"city_id\": 1816670, \"city_name\": \"Beijing\", \"main_weather\": \"Clear\", \"description\": \"晴\", \"temperature\": 17.72, \"feels_like\": 16.36, \"temp_min\": 14.94, \"temp_max\": 17.72}', name='query_weather_from_db', id='d1b400d9-02f4-4ffd-9f00-0cfc2298bf92', tool_call_id='call_f2h95mzFmIAZ7LwcBaL1QUHP'),\n", " ToolMessage(content='{\"city_id\": 2037013, \"city_name\": \"Harbin\", \"main_weather\": \"Rain\", \"description\": \"小雨\", \"temperature\": 8.62, \"feels_like\": 8.62, \"temp_min\": 8.62, \"temp_max\": 8.62}', name='query_weather_from_db', id='d84861cd-fc8f-44f8-8faa-5b5b8218bace', tool_call_id='call_8P3qlq6yM9kelaHqRZ9zZ4O0'),\n", " AIMessage(content='### 天气对比分析\\n\\n#### 北京\\n- **天气状况**: 晴\\n- **详细描述**: 晴\\n- **当前温度**: 17.72°C\\n- **体感温度**: 16.36°C\\n- **最低温度**: 14.94°C\\n- **最高温度**: 17.72°C\\n\\n#### 哈尔滨\\n- **天气状况**: 雨\\n- **详细描述**: 小雨\\n- **当前温度**: 8.62°C\\n- **体感温度**: 8.62°C\\n- **最低温度**: 8.62°C\\n- **最高温度**: 8.62°C\\n\\n### 出行建议\\n\\n#### 北京\\n1. **穿着建议**: 北京天气晴朗,温度适中,建议穿轻便的春秋装。\\n2. **活动建议**: 适合户外活动,如散步、慢跑或骑行。\\n3. **防晒措施**: 即使是晴天,依然建议使用防晒霜和佩戴太阳镜。\\n\\n#### 哈尔滨\\n1. **穿着建议**: 哈尔滨气温较低且有小雨,建议穿保暖的衣物,并携带雨具。\\n2. **活动建议**: 降雨可能影响户外活动,建议选择室内活动。\\n3. **行车安全**: 雨天路滑,驾车请注意安全,保持车距。\\n\\n这两座城市当前的天气差异较大,出行前请根据实际天气情况做好相应准备。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 355, 'prompt_tokens': 566, 'total_tokens': 921, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_45c6de4934', 'finish_reason': 'stop', 'logprobs': None}, id='run-af1a77c2-2fd4-4549-8683-d8df867221d1-0', usage_metadata={'input_tokens': 566, 'output_tokens': 355, 'total_tokens': 921, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}})]}" ] }, "execution_count": 170, "metadata": {}, "output_type": "execute_result" } ], "source": [ "finan_response = graph.invoke({\"messages\":[\"帮我分析一下数据库中北京和哈尔滨城市天气的信息,做一个详细的对比,并生成出行建议\"]})\n", "\n", "finan_response" ] }, { "cell_type": "code", "execution_count": 174, "id": "1290b10e-186c-41cd-9778-f9d556b217a2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "### 天气对比分析\n", "\n", "#### 北京\n", "- **天气状况**: 晴\n", "- **详细描述**: 晴\n", "- **当前温度**: 17.72°C\n", "- **体感温度**: 16.36°C\n", "- **最低温度**: 14.94°C\n", "- **最高温度**: 17.72°C\n", "\n", "#### 哈尔滨\n", "- **天气状况**: 雨\n", "- **详细描述**: 小雨\n", "- **当前温度**: 8.62°C\n", "- **体感温度**: 8.62°C\n", "- **最低温度**: 8.62°C\n", "- **最高温度**: 8.62°C\n", "\n", "### 出行建议\n", "\n", "#### 北京\n", "1. **穿着建议**: 北京天气晴朗,温度适中,建议穿轻便的春秋装。\n", "2. **活动建议**: 适合户外活动,如散步、慢跑或骑行。\n", "3. **防晒措施**: 即使是晴天,依然建议使用防晒霜和佩戴太阳镜。\n", "\n", "#### 哈尔滨\n", "1. **穿着建议**: 哈尔滨气温较低且有小雨,建议穿保暖的衣物,并携带雨具。\n", "2. **活动建议**: 降雨可能影响户外活动,建议选择室内活动。\n", "3. **行车安全**: 雨天路滑,驾车请注意安全,保持车距。\n", "\n", "这两座城市当前的天气差异较大,出行前请根据实际天气情况做好相应准备。\n" ] } ], "source": [ "print(finan_response[\"messages\"][-1].content)" ] }, { "cell_type": "markdown", "id": "d25ad8f8-0de0-41f1-a3e2-c4e1819f557e", "metadata": {}, "source": [ "  通过对不同复杂程度输入问题的测试,我们发现当前架构能够非常准确且快速地完成任务目标。在涉及多个任务的顺序执行时,`ReAct` 代理能够自主决策并执行,真正实现了完全的自治循环代理。此外,其可扩展性也十分出色。**对于不同的业务需求,我们只需调整接入的大模型实例(可使用其他开源或在线模型)作为 `ReAct` 的基础模型。对于工具的配置,也无需特别进行复杂的编排,只需明确定义每个工具的输入和输出,然后通过工具列表的形式直接注册到大模型实例及 `ToolNode` 实例中**。这种方法在快速构建智能代理方面,非常值得大家尝试。" ] }, { "cell_type": "markdown", "id": "711cdc2d-92cf-42b3-a3ea-1a17436dd335", "metadata": {}, "source": [ "# 3. LangGraph中的事件流" ] }, { "cell_type": "markdown", "id": "9bfc0e23-f020-4a2a-a730-92c0e4d006ae", "metadata": {}, "source": [ "  大模型的流式输出功能我们在《Ch.6 OpenAI Assistant API 高阶应用 - 流式输出功能》中首次提到。这一功能与非流式输出不同,后者在 `Agent` 内部处理完成后一次性输出结果。**流式输出的作用在于,它能实时捕捉并输出任务处理过程中的状态变化。这意味着,任何中间过程中的新状态和值都可以被即时获取到。**所以,流式输出功能本质上不直接参与`Agent`的执行过程,仅仅是用来追踪、记录`Agent`在处理不同任务时产生的各个事件、状态和值。\n", "\n", "  在实际应用中,流式输出尤其适用于需要快速反馈的业务场景,如聊天机器人,因为**大语言模型可能需要几秒钟才能生成对查询的完整响应,这远远慢于应用程序对最终用户的响应速度约为 200-300 毫秒的阈值**,如果是涉及多个大模型调用的复杂应用程序,这种延时会变得更加明显。让应用程序感觉响应更快的关键策略是显示中间进度;即,通过 `token` 流式传输大模型`Token`的输出,以此来显著提升用户体验。而在开发阶段,利用流式输出功能可以准确追踪到事件的具体执行阶段,并捕获相关数据,从而接入不同逻辑的数据处理和决策流程。是我们在应用开发中必须理解和掌握的技术点。" ] }, { "cell_type": "markdown", "id": "99ad66b0-d49f-4adf-bbe0-387be88131b2", "metadata": {}, "source": [ "  流式输出功能在`LangGraph` 框架中的实现方式,相较于`Assistant API`是简单很多的,但基本思路一样。因为`LangGraph`底层是基于 `LangChain` 构建的,所有就直接把`LangChain`中的回调系统拿过来使用了。**在`LangChain`中的流式输出是:以块的形式传输最终输出,即一旦监测到有可用的块,就直接生成它。**最常见和最关键的流数据是大模型本身生成的输出。 大模型通常需要时间才能生成完整的响应,通过实时流式传输输出,用户可以在生成时看到部分结果,这可以提供即时反馈并有助于减少用户的等待时间。如下所示:" ] }, { "cell_type": "code", "execution_count": 201, "id": "63ca6fb8-1c00-41eb-b379-33a5439e4482", "metadata": {}, "outputs": [], "source": [ "import getpass\n", "import os\n", "from langchain_openai import ChatOpenAI\n", "\n", "if not os.environ.get(\"OPENAI_API_KEY\"):\n", " os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"Enter your OpenAI API key: \")\n", "\n", "\n", "llm = ChatOpenAI(model=\"gpt-4o\")" ] }, { "cell_type": "code", "execution_count": 205, "id": "b4e2a8c7-ed27-4aeb-a32e-a20f62a5e757", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "|你好|!|我是|一个|由|人工|智能|驱|动|的|虚|拟|助手|,|旨|在|帮助|回答|问题|和|提供|信息|。我|能够|处理|各种|主题|,包括|常|识|问题|、|技术|支持|、|语言|翻|译|、|写|作|建议|等等|。|我的|设计|目标|是|尽|量|理解|你的|问题|并|提供|有|用|的|答案|。\n", "\n", "|由于|我是|基|于|大|规模|的|语言|模型|构|建|的|,因此|我的|知识|和|能力|是|基|于|对|大量|文本|数据|的|分析|。我|没有|个人|经验|或|感|情|,但|我|会|尽|力|为|你|提供|准确|和|及时|的信息|。\n", "\n", "|如果|你|有|任何|问题|或|需要|帮助|,请|随|时|告诉|我|!||" ] } ], "source": [ "chunks = []\n", "async for chunk in llm.astream(\"你好,请你详细的介绍一下你自己。\"):\n", " chunks.append(chunk)\n", " print(chunk.content, end=\"|\", flush=True)" ] }, { "cell_type": "code", "execution_count": 207, "id": "e86e1e1f-5728-46d8-9656-493ff332f2f8", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "AIMessageChunk(content='', additional_kwargs={}, response_metadata={}, id='run-034a91b3-9509-4442-8c0d-3d17774451bf')" ] }, "execution_count": 207, "metadata": {}, "output_type": "execute_result" } ], "source": [ "chunks[0]" ] }, { "cell_type": "markdown", "id": "21a13fd7-fd41-4d00-82b0-bb6ee6bc545e", "metadata": {}, "source": [ "  每一个块,都是一个`AIMessageChunk`对象,用来代表`AIMessage`对象的一部分。消息块在设计上是可加的,比如:" ] }, { "cell_type": "code", "execution_count": 211, "id": "58b4e258-97fa-45e9-89ac-f8c1fd13004b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "AIMessageChunk(content='你好!我是一个', additional_kwargs={}, response_metadata={}, id='run-034a91b3-9509-4442-8c0d-3d17774451bf')" ] }, "execution_count": 211, "metadata": {}, "output_type": "execute_result" } ], "source": [ "chunks[0] + chunks[1] + chunks[2] + chunks[3] + chunks[4]" ] }, { "cell_type": "markdown", "id": "cc4c9ef6-ac98-4fa8-85f3-16b6fb537d17", "metadata": {}, "source": [ "  而进一步的,除了流式传输大模型的输出之外,通过更复杂的工作流程或管道流式传输进度也很有用,比如 `AI Agent` 中的中间处理过程,这就涉及到工作流概念。" ] }, { "cell_type": "markdown", "id": "72fa8a55-fb39-4987-9845-0b886a9d2c28", "metadata": {}, "source": [ "## 3.1 LangGraph使用流输出" ] }, { "cell_type": "markdown", "id": "00436412-28f9-44dc-819c-fa88c19d45c4", "metadata": {}, "source": [ "  `LangGraph`框架中的工作流中由各个步骤的节点和边组成。这里的流式传输涉及在各个节点请求更新时跟踪图状态的变化。这样可以更精细地监控工作流中当前处于活动状态的节点,并在工作流经过不同阶段时提供有关工作流状态的实时更新。其实现方式也是和`LangChain`一样通过`.stream`和`.astream`方法执行流式输出,只不过适配到了图结构中。调用`.stream`和`.astream`方法时可以指定几种不同的模式,即:\n", "\n", "- \"values\" :在图中的每个步骤之后流式传输**状态**的完整值。\n", "- \"updates\" :在图中的每个步骤之后将更新流式传输到状态。如果在同一步骤中进行多个更新(例如运行多个节点),则这些更新将单独流式传输。\n", "- \"debug\" :在整个图的执行过程中流式传输尽可能多的信息,主要用于调试程序。\n", "- \"messages\":记录每个`messages`中的增量`token`。\n", "- \"custom\":自定义流,通过`LangGraph 的 StreamWriter`方法" ] }, { "cell_type": "markdown", "id": "291ffc21-146c-4b34-9ba9-c631af02241b", "metadata": {}, "source": [ "
" ] }, { "cell_type": "markdown", "id": "a87990aa-6175-4c87-9fc5-9ca8e7db17de", "metadata": {}, "source": [ "  首先来看`Stream`方法,该方法返回一个迭代器,在生成输出块时同步生成它们。我们可以使用`for`循环来实时处理每个块。生成的块的类型取决于正在流式传输的组件。例如,当从大模型流式传输时,每个组件将是一个`AIMessageChunk`,但是,对于其他组件,块可能会有所不同。其`LangGraph`框架中实现的源码如下:" ] }, { "cell_type": "markdown", "id": "17230196-5732-44e7-bd59-f61aafd556f2", "metadata": {}, "source": [ "> LangGraph Graph stream:https://langchain-ai.github.io/langgraph/reference/graphs/#langgraph.graph.graph.CompiledGraph.stream" ] }, { "cell_type": "markdown", "id": "5923fef6-5e03-4fed-ab16-f27e0aae1764", "metadata": {}, "source": [ "```python\n", "def stream(\n", " self,\n", " input: Union[dict[str, Any], Any], # 图中的输入,从状态中取值\n", " config: Optional[RunnableConfig] = None,\n", " *,\n", " stream_mode: Optional[Union[StreamMode, list[StreamMode]]] = None,\n", " output_keys: Optional[Union[str, Sequence[str]]] = None, # 流媒体的键,默认为所有非上下文通道。\n", " interrupt_before: Optional[Union[All, Sequence[str]]] = None, # 中断之前的节点,默认为图中的所有节点。\n", " interrupt_after: Optional[Union[All, Sequence[str]]] = None, # 中断之后的节点,默认为图中的所有节点。\n", " debug: Optional[bool] = None, # 执行过程中是否打印调试信息,默认为False。\n", " subgraphs: bool = False, # 是否流式传输子图\n", ") -> Iterator[Union[dict[str, Any], Any]]:\n", "```" ] }, { "cell_type": "markdown", "id": "1a4f2ed1-4c92-4f7a-ae6b-4893ecda82a2", "metadata": {}, "source": [ "- **values :在图表的每个步骤之后流式传输状态的完整值。**" ] }, { "cell_type": "code", "execution_count": 237, "id": "b643a981-ba97-4138-b630-34f5e08c5593", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "================================\u001b[1m Human Message \u001b[0m=================================\n", "\n", "你好,南京现在的天气怎么样?\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", " query_weather_from_db (call_xfRWSscdr2S6Fgo1GnxAQUFk)\n", " Call ID: call_xfRWSscdr2S6Fgo1GnxAQUFk\n", " Args:\n", " city_name: Nanjing\n", "None\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", "Name: query_weather_from_db\n", "\n", "{\"messages\": [\"未找到城市 'Nanjing' 的天气信息。\"]}\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", " get_weather (call_Wd4DNds56FewTYwx7Z6SEBnw)\n", " Call ID: call_Wd4DNds56FewTYwx7Z6SEBnw\n", " Args:\n", " loc: Nanjing\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", "Name: get_weather\n", "\n", "{\"coord\": {\"lon\": 118.7778, \"lat\": 32.0617}, \"weather\": [{\"id\": 800, \"main\": \"Clear\", \"description\": \"\\u6674\", \"icon\": \"01d\"}], \"base\": \"stations\", \"main\": {\"temp\": 18.75, \"feels_like\": 18.12, \"temp_min\": 18.75, \"temp_max\": 18.75, \"pressure\": 1023, \"humidity\": 55, \"sea_level\": 1023, \"grnd_level\": 1020}, \"visibility\": 10000, \"wind\": {\"speed\": 4.89, \"deg\": 77, \"gust\": 6.84}, \"clouds\": {\"all\": 0}, \"dt\": 1730257590, \"sys\": {\"country\": \"CN\", \"sunrise\": 1730240389, \"sunset\": 1730279826}, \"timezone\": 28800, \"id\": 1799962, \"name\": \"Nanjing\", \"cod\": 200}\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", " insert_weather_to_db (call_rcwMjfSIFM5S1WihC2RD8xIC)\n", " Call ID: call_rcwMjfSIFM5S1WihC2RD8xIC\n", " Args:\n", " city_id: 1799962\n", " city_name: Nanjing\n", " main_weather: Clear\n", " description: 晴\n", " temperature: 18.75\n", " feels_like: 18.12\n", " temp_min: 18.75\n", " temp_max: 18.75\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", "Name: insert_weather_to_db\n", "\n", "{\"messages\": [\"天气数据已成功存储至Mysql数据库。\"]}\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", "南京现在的天气是晴天,气温为18.75°C,体感温度约为18.12°C。空气清新,能见度良好。\n" ] } ], "source": [ "def print_stream(stream):\n", " for sub_stream in stream:\n", " # print(sub_stream) # 就是上面的示例中非流式直接调用的全部信息\n", " message = sub_stream[\"messages\"][-1]\n", " message.pretty_print()\n", "\n", "input_message = {\"messages\": [\"你好,南京现在的天气怎么样?\"]}\n", "print_stream(graph.stream(input_message, stream_mode=\"values\"))" ] }, { "cell_type": "markdown", "id": "ee406825-bcf7-4c9a-85e1-880177651b4e", "metadata": {}, "source": [ "- **updates :在图中的每个步骤之后将更新流式传输到状态。**" ] }, { "cell_type": "code", "execution_count": 246, "id": "91b66439-6c7b-433a-983a-527a64f7faf1", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'agent': {'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Ma7bxsvHjIXCXozwmJieVtJJ', 'function': {'arguments': '{\"loc\": \"Tianjin\"}', 'name': 'get_weather'}, 'type': 'function'}, {'id': 'call_uM3KOsUgIogoBdJbcXwPYEqp', 'function': {'arguments': '{\"loc\": \"Inner Mongolia\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 47, 'prompt_tokens': 347, 'total_tokens': 394, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-5cd03389-b67e-416d-a92f-9c02e4ca75b0-0', tool_calls=[{'name': 'get_weather', 'args': {'loc': 'Tianjin'}, 'id': 'call_Ma7bxsvHjIXCXozwmJieVtJJ', 'type': 'tool_call'}, {'name': 'get_weather', 'args': {'loc': 'Inner Mongolia'}, 'id': 'call_uM3KOsUgIogoBdJbcXwPYEqp', 'type': 'tool_call'}], usage_metadata={'input_tokens': 347, 'output_tokens': 47, 'total_tokens': 394, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}})]}}\n", "{'tools': {'messages': [ToolMessage(content='{\"coord\": {\"lon\": 117.1767, \"lat\": 39.1422}, \"weather\": [{\"id\": 800, \"main\": \"Clear\", \"description\": \"\\\\u6674\", \"icon\": \"01d\"}], \"base\": \"stations\", \"main\": {\"temp\": 18.97, \"feels_like\": 18.46, \"temp_min\": 18.97, \"temp_max\": 18.97, \"pressure\": 1025, \"humidity\": 59, \"sea_level\": 1025, \"grnd_level\": 1024}, \"visibility\": 10000, \"wind\": {\"speed\": 7, \"deg\": 140}, \"clouds\": {\"all\": 0}, \"dt\": 1730258008, \"sys\": {\"type\": 1, \"id\": 9619, \"country\": \"CN\", \"sunrise\": 1730241393, \"sunset\": 1730279591}, \"timezone\": 28800, \"id\": 1792947, \"name\": \"Tianjin\", \"cod\": 200}', name='get_weather', id='d667c453-d237-438f-9735-1b6d7854717d', tool_call_id='call_Ma7bxsvHjIXCXozwmJieVtJJ'), ToolMessage(content='{\"cod\": \"404\", \"message\": \"city not found\"}', name='get_weather', id='c81bf119-461b-4956-aab7-4bcfd67dc461', tool_call_id='call_uM3KOsUgIogoBdJbcXwPYEqp')]}}\n", "{'agent': {'messages': [AIMessage(content='天津的天气情况如下:\\n- 天气:晴\\n- 温度:18.97°C\\n- 体感温度:18.46°C\\n- 风速:7 m/s\\n- 湿度:59%\\n\\n内蒙古的天气信息暂时无法获取,可能是因为城市名不够具体或者数据暂时不可用。如果你有具体的内蒙古城市名称,可以提供给我以便查询。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 90, 'prompt_tokens': 659, 'total_tokens': 749, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'stop', 'logprobs': None}, id='run-e89e91d2-14a3-420c-b19d-08e59a57883f-0', usage_metadata={'input_tokens': 659, 'output_tokens': 90, 'total_tokens': 749, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}})]}}\n" ] } ], "source": [ "def print_stream(stream):\n", " for sub_stream in stream:\n", " print(sub_stream) # 就是上面的示例中非流式直接调用的全部信息\n", "\n", "input_message = {\"messages\": [\"你好,天津、内蒙现在的天气怎么样?\"]}\n", "print_stream(graph.stream(input_message, stream_mode=\"updates\"))" ] }, { "cell_type": "markdown", "id": "488464c6-a476-4662-bca7-78382db3a7dd", "metadata": {}, "source": [ "- **debug :在整个图中的执行过程中流式传输尽可能多的信息**" ] }, { "cell_type": "code", "execution_count": 249, "id": "abf2bf2a-b259-4132-b46f-3626fc23216f", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'type': 'task', 'timestamp': '2024-10-30T03:14:44.538467+00:00', 'step': 1, 'payload': {'id': 'ec74ada5-a269-f1ab-3344-64ef7aa0cb0d', 'name': 'agent', 'input': {'messages': [HumanMessage(content='你好,天津、内蒙现在的天气怎么样?', additional_kwargs={}, response_metadata={}, id='2b048b3c-6ff7-4576-9e97-f5e1986ae085')], 'is_last_step': False}, 'triggers': ['start:agent']}}\n", "{'type': 'task_result', 'timestamp': '2024-10-30T03:14:47.434922+00:00', 'step': 1, 'payload': {'id': 'ec74ada5-a269-f1ab-3344-64ef7aa0cb0d', 'name': 'agent', 'error': None, 'result': [('messages', [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_NaK0YDbmUSm1pSPgmcuaglHl', 'function': {'arguments': '{\"loc\": \"Tianjin\"}', 'name': 'get_weather'}, 'type': 'function'}, {'id': 'call_870GtpjxM8Rgw36N7wIqfRDq', 'function': {'arguments': '{\"loc\": \"Inner Mongolia\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 47, 'prompt_tokens': 347, 'total_tokens': 394, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-6c23f36c-e854-4e3f-beef-04be5ae46041-0', tool_calls=[{'name': 'get_weather', 'args': {'loc': 'Tianjin'}, 'id': 'call_NaK0YDbmUSm1pSPgmcuaglHl', 'type': 'tool_call'}, {'name': 'get_weather', 'args': {'loc': 'Inner Mongolia'}, 'id': 'call_870GtpjxM8Rgw36N7wIqfRDq', 'type': 'tool_call'}], usage_metadata={'input_tokens': 347, 'output_tokens': 47, 'total_tokens': 394, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}})])], 'interrupts': []}}\n", "{'type': 'task', 'timestamp': '2024-10-30T03:14:47.434922+00:00', 'step': 2, 'payload': {'id': '29849df1-72f9-e753-4982-c0ca4403a93d', 'name': 'tools', 'input': {'messages': [HumanMessage(content='你好,天津、内蒙现在的天气怎么样?', additional_kwargs={}, response_metadata={}, id='2b048b3c-6ff7-4576-9e97-f5e1986ae085'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_NaK0YDbmUSm1pSPgmcuaglHl', 'function': {'arguments': '{\"loc\": \"Tianjin\"}', 'name': 'get_weather'}, 'type': 'function'}, {'id': 'call_870GtpjxM8Rgw36N7wIqfRDq', 'function': {'arguments': '{\"loc\": \"Inner Mongolia\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 47, 'prompt_tokens': 347, 'total_tokens': 394, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-6c23f36c-e854-4e3f-beef-04be5ae46041-0', tool_calls=[{'name': 'get_weather', 'args': {'loc': 'Tianjin'}, 'id': 'call_NaK0YDbmUSm1pSPgmcuaglHl', 'type': 'tool_call'}, {'name': 'get_weather', 'args': {'loc': 'Inner Mongolia'}, 'id': 'call_870GtpjxM8Rgw36N7wIqfRDq', 'type': 'tool_call'}], usage_metadata={'input_tokens': 347, 'output_tokens': 47, 'total_tokens': 394, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}})], 'is_last_step': False}, 'triggers': ['branch:agent:should_continue:tools']}}\n", "{'type': 'task_result', 'timestamp': '2024-10-30T03:14:48.719462+00:00', 'step': 2, 'payload': {'id': '29849df1-72f9-e753-4982-c0ca4403a93d', 'name': 'tools', 'error': None, 'result': [('messages', [ToolMessage(content='{\"coord\": {\"lon\": 117.1767, \"lat\": 39.1422}, \"weather\": [{\"id\": 800, \"main\": \"Clear\", \"description\": \"\\\\u6674\", \"icon\": \"01d\"}], \"base\": \"stations\", \"main\": {\"temp\": 18.97, \"feels_like\": 18.46, \"temp_min\": 18.97, \"temp_max\": 18.97, \"pressure\": 1025, \"humidity\": 59, \"sea_level\": 1025, \"grnd_level\": 1024}, \"visibility\": 10000, \"wind\": {\"speed\": 7, \"deg\": 140}, \"clouds\": {\"all\": 0}, \"dt\": 1730258008, \"sys\": {\"type\": 1, \"id\": 9619, \"country\": \"CN\", \"sunrise\": 1730241393, \"sunset\": 1730279591}, \"timezone\": 28800, \"id\": 1792947, \"name\": \"Tianjin\", \"cod\": 200}', name='get_weather', id='ed06d7f2-4d33-4511-be9c-3325dccdc5de', tool_call_id='call_NaK0YDbmUSm1pSPgmcuaglHl'), ToolMessage(content='{\"cod\": \"404\", \"message\": \"city not found\"}', name='get_weather', id='d4f47099-64d4-4071-8956-515ae231cf79', tool_call_id='call_870GtpjxM8Rgw36N7wIqfRDq')])], 'interrupts': []}}\n", "{'type': 'task', 'timestamp': '2024-10-30T03:14:48.719462+00:00', 'step': 3, 'payload': {'id': 'c0a70cf0-9581-d545-c4db-564234efbf12', 'name': 'agent', 'input': {'messages': [HumanMessage(content='你好,天津、内蒙现在的天气怎么样?', additional_kwargs={}, response_metadata={}, id='2b048b3c-6ff7-4576-9e97-f5e1986ae085'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_NaK0YDbmUSm1pSPgmcuaglHl', 'function': {'arguments': '{\"loc\": \"Tianjin\"}', 'name': 'get_weather'}, 'type': 'function'}, {'id': 'call_870GtpjxM8Rgw36N7wIqfRDq', 'function': {'arguments': '{\"loc\": \"Inner Mongolia\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 47, 'prompt_tokens': 347, 'total_tokens': 394, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-6c23f36c-e854-4e3f-beef-04be5ae46041-0', tool_calls=[{'name': 'get_weather', 'args': {'loc': 'Tianjin'}, 'id': 'call_NaK0YDbmUSm1pSPgmcuaglHl', 'type': 'tool_call'}, {'name': 'get_weather', 'args': {'loc': 'Inner Mongolia'}, 'id': 'call_870GtpjxM8Rgw36N7wIqfRDq', 'type': 'tool_call'}], usage_metadata={'input_tokens': 347, 'output_tokens': 47, 'total_tokens': 394, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}}), ToolMessage(content='{\"coord\": {\"lon\": 117.1767, \"lat\": 39.1422}, \"weather\": [{\"id\": 800, \"main\": \"Clear\", \"description\": \"\\\\u6674\", \"icon\": \"01d\"}], \"base\": \"stations\", \"main\": {\"temp\": 18.97, \"feels_like\": 18.46, \"temp_min\": 18.97, \"temp_max\": 18.97, \"pressure\": 1025, \"humidity\": 59, \"sea_level\": 1025, \"grnd_level\": 1024}, \"visibility\": 10000, \"wind\": {\"speed\": 7, \"deg\": 140}, \"clouds\": {\"all\": 0}, \"dt\": 1730258008, \"sys\": {\"type\": 1, \"id\": 9619, \"country\": \"CN\", \"sunrise\": 1730241393, \"sunset\": 1730279591}, \"timezone\": 28800, \"id\": 1792947, \"name\": \"Tianjin\", \"cod\": 200}', name='get_weather', id='ed06d7f2-4d33-4511-be9c-3325dccdc5de', tool_call_id='call_NaK0YDbmUSm1pSPgmcuaglHl'), ToolMessage(content='{\"cod\": \"404\", \"message\": \"city not found\"}', name='get_weather', id='d4f47099-64d4-4071-8956-515ae231cf79', tool_call_id='call_870GtpjxM8Rgw36N7wIqfRDq')], 'is_last_step': False}, 'triggers': ['tools']}}\n", "{'type': 'task_result', 'timestamp': '2024-10-30T03:14:50.707154+00:00', 'step': 3, 'payload': {'id': 'c0a70cf0-9581-d545-c4db-564234efbf12', 'name': 'agent', 'error': None, 'result': [('messages', [AIMessage(content='天津的天气情况如下:\\n\\n- 天气:晴\\n- 温度:18.97°C\\n- 体感温度:18.46°C\\n- 风速:7 m/s\\n- 湿度:59%\\n\\n很遗憾,无法获取内蒙古的具体天气信息。如果你能提供一个具体的城市名称,我可以为你查询更详细的天气信息。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 82, 'prompt_tokens': 659, 'total_tokens': 741, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'stop', 'logprobs': None}, id='run-461964bb-3be1-41b2-9766-305990a9ffec-0', usage_metadata={'input_tokens': 659, 'output_tokens': 82, 'total_tokens': 741, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}})])], 'interrupts': []}}\n" ] } ], "source": [ "def print_stream(stream):\n", " for sub_stream in stream:\n", " print(sub_stream) # 就是上面的示例中非流式直接调用的全部信息\n", "\n", "input_message = {\"messages\": [\"你好,天津、内蒙现在的天气怎么样?\"]}\n", "print_stream(graph.stream(input_message, stream_mode=\"debug\"))" ] }, { "cell_type": "markdown", "id": "e9f87102-bffa-494f-bad8-384a0f7bf5e3", "metadata": {}, "source": [ "  如果在异步开发环境中,则可以使用`astream`方法来实现流式传输,是专为非阻塞工作流程而设计。可使用的模式和`stream`是一致的,只不过需要调整为异步函数的定义方法,代码如下所示:" ] }, { "cell_type": "code", "execution_count": 303, "id": "cbc96b82-4435-4347-9f72-a347b332e145", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "================================\u001b[1m Human Message \u001b[0m=================================\n", "\n", "你好,四川的天气怎么样?\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", " query_weather_from_db (call_jElzMBgWJamba4swVQlUZ5IW)\n", " Call ID: call_jElzMBgWJamba4swVQlUZ5IW\n", " Args:\n", " city_name: Sichuan\n", "<__main__.Weather object at 0x00000222AAA89490>\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", "Name: query_weather_from_db\n", "\n", "{\"city_id\": 1794299, \"city_name\": \"Sichuan\", \"main_weather\": \"Clouds\", \"description\": \"多云\", \"temperature\": 1.67, \"feels_like\": -0.58, \"temp_min\": 1.67, \"temp_max\": 1.67}\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", "四川目前的天气是多云,气温为1.67°C,体感温度接近-0.58°C。\n" ] } ], "source": [ "async for chunk in graph.astream(input={\"messages\": [\"你好,四川的天气怎么样?\"]}, stream_mode=\"values\"):\n", " message = chunk[\"messages\"][-1].pretty_print()" ] }, { "cell_type": "markdown", "id": "c4a6fc1e-0072-445b-a679-e548cb0b0de5", "metadata": {}, "source": [ "  如果只想得到最终结果,可以使用相同的方法并只跟踪收到的最后一个值,代码如下:" ] }, { "cell_type": "code", "execution_count": 305, "id": "2c66c00e-a47f-4ac4-9a77-cf65b21380ad", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "<__main__.Weather object at 0x00000222AAD43F90>\n" ] } ], "source": [ "async for chunk in graph.astream(input={\"messages\": [\"你好,四川的天气怎么样?\"]}, stream_mode=\"values\"):\n", " final_result = chunk" ] }, { "cell_type": "code", "execution_count": 309, "id": "6481a55c-b1e3-4792-a7ef-b309bb827cc0", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", "四川目前的天气是多云。当前气温为1.67°C,体感温度是-0.58°C。\n" ] } ], "source": [ "final_result[\"messages\"][-1].pretty_print()" ] }, { "cell_type": "code", "execution_count": 311, "id": "d5d35ee8-da28-4681-9166-1b2b04f48ab8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "接收到的更新节点: 'agent'\n", "{'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_ieilOQoqoNZDnjLzDwtSiSGa', 'function': {'arguments': '{\"city_name\":\"Urumqi\"}', 'name': 'query_weather_from_db'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 19, 'prompt_tokens': 346, 'total_tokens': 365, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-98d46758-afbd-4c95-965b-8f44d83fe959-0', tool_calls=[{'name': 'query_weather_from_db', 'args': {'city_name': 'Urumqi'}, 'id': 'call_ieilOQoqoNZDnjLzDwtSiSGa', 'type': 'tool_call'}], usage_metadata={'input_tokens': 346, 'output_tokens': 19, 'total_tokens': 365, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}})]}\n", "\n", "\n", "\n", "None\n", "接收到的更新节点: 'tools'\n", "{'messages': [ToolMessage(content='{\"messages\": [\"未找到城市 \\'Urumqi\\' 的天气信息。\"]}', name='query_weather_from_db', id='0852ae9f-b8a8-4a21-926e-765c7f084400', tool_call_id='call_ieilOQoqoNZDnjLzDwtSiSGa')]}\n", "\n", "\n", "\n", "接收到的更新节点: 'agent'\n", "{'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_FA2ZvwygZsZLNgQfWE6zBH8X', 'function': {'arguments': '{\"loc\":\"Urumqi\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 393, 'total_tokens': 409, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-5cd79453-e69d-47b0-a8d8-198be000ad1e-0', tool_calls=[{'name': 'get_weather', 'args': {'loc': 'Urumqi'}, 'id': 'call_FA2ZvwygZsZLNgQfWE6zBH8X', 'type': 'tool_call'}], usage_metadata={'input_tokens': 393, 'output_tokens': 16, 'total_tokens': 409, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}})]}\n", "\n", "\n", "\n", "接收到的更新节点: 'tools'\n", "{'messages': [ToolMessage(content='{\"coord\": {\"lon\": 87.6005, \"lat\": 43.801}, \"weather\": [{\"id\": 801, \"main\": \"Clouds\", \"description\": \"\\\\u6674\\\\uff0c\\\\u5c11\\\\u4e91\", \"icon\": \"02d\"}], \"base\": \"stations\", \"main\": {\"temp\": 5.53, \"feels_like\": 3.99, \"temp_min\": 5.53, \"temp_max\": 5.53, \"pressure\": 1029, \"humidity\": 81, \"sea_level\": 1029, \"grnd_level\": 926}, \"visibility\": 10000, \"wind\": {\"speed\": 2, \"deg\": 110}, \"clouds\": {\"all\": 20}, \"dt\": 1730260508, \"sys\": {\"type\": 1, \"id\": 9677, \"country\": \"CN\", \"sunrise\": 1730248978, \"sunset\": 1730286202}, \"timezone\": 28800, \"id\": 1529102, \"name\": \"\\\\u00dcr\\\\u00fcmqi\", \"cod\": 200}', name='get_weather', id='57b6b3b8-cfc4-435b-ba94-65380b28cf11', tool_call_id='call_FA2ZvwygZsZLNgQfWE6zBH8X')]}\n", "\n", "\n", "\n", "接收到的更新节点: 'agent'\n", "{'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_RiMagTnBGHBCVFdy2ApzG2qt', 'function': {'arguments': '{\"city_id\":1529102,\"city_name\":\"Ürümqi\",\"main_weather\":\"Clouds\",\"description\":\"晴,少云\",\"temperature\":5.53,\"feels_like\":3.99,\"temp_min\":5.53,\"temp_max\":5.53}', 'name': 'insert_weather_to_db'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 68, 'prompt_tokens': 672, 'total_tokens': 740, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-d3fb79a3-b429-4c47-8d72-19d2901f21dc-0', tool_calls=[{'name': 'insert_weather_to_db', 'args': {'city_id': 1529102, 'city_name': 'Ürümqi', 'main_weather': 'Clouds', 'description': '晴,少云', 'temperature': 5.53, 'feels_like': 3.99, 'temp_min': 5.53, 'temp_max': 5.53}, 'id': 'call_RiMagTnBGHBCVFdy2ApzG2qt', 'type': 'tool_call'}], usage_metadata={'input_tokens': 672, 'output_tokens': 68, 'total_tokens': 740, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}})]}\n", "\n", "\n", "\n", "接收到的更新节点: 'tools'\n", "{'messages': [ToolMessage(content='{\"messages\": [\"天气数据已成功存储至Mysql数据库。\"]}', name='insert_weather_to_db', id='124ec142-2441-4865-b75b-b597e2023ce1', tool_call_id='call_RiMagTnBGHBCVFdy2ApzG2qt')]}\n", "\n", "\n", "\n", "接收到的更新节点: 'agent'\n", "{'messages': [AIMessage(content='乌鲁木齐当前的天气情况是:晴,少云。气温为5.53°C,体感温度为3.99°C。天气数据已成功存储到数据库中。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 44, 'prompt_tokens': 766, 'total_tokens': 810, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014', 'finish_reason': 'stop', 'logprobs': None}, id='run-c551db7f-4bd0-4c06-bbe0-59b98bd89fb9-0', usage_metadata={'input_tokens': 766, 'output_tokens': 44, 'total_tokens': 810, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}})]}\n", "\n", "\n", "\n" ] } ], "source": [ "inputs = {\"messages\": [(\"human\", \"你好,乌鲁木齐的天气怎么样?\")]}\n", "async for chunk in graph.astream(inputs, stream_mode=\"updates\"):\n", " for node, values in chunk.items():\n", " print(f\"接收到的更新节点: '{node}'\")\n", " print(values)\n", " print(\"\\n\\n\")" ] }, { "cell_type": "markdown", "id": "0f74a5bb-74b1-44ed-af4b-2afc67db2553", "metadata": {}, "source": [ "  而如果我们想流式传输每个过程中的 `Tokens`, 代码如下:" ] }, { "cell_type": "code", "execution_count": 324, "id": "f8c9e92c-80d0-49e5-a860-bf6fa66d2ca9", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[{'name': 'fetch_real_time_info', 'args': {}, 'id': 'call_fgn93Rd0qvQrb2g8CSBASi5h', 'type': 'tool_call'}]\n", "[{'name': 'fetch_real_time_info', 'args': {}, 'id': 'call_fgn93Rd0qvQrb2g8CSBASi5h', 'type': 'tool_call'}]\n", "[{'name': 'fetch_real_time_info', 'args': {}, 'id': 'call_fgn93Rd0qvQrb2g8CSBASi5h', 'type': 'tool_call'}]\n", "[{'name': 'fetch_real_time_info', 'args': {'query': ''}, 'id': 'call_fgn93Rd0qvQrb2g8CSBASi5h', 'type': 'tool_call'}]\n", "[{'name': 'fetch_real_time_info', 'args': {'query': '查询'}, 'id': 'call_fgn93Rd0qvQrb2g8CSBASi5h', 'type': 'tool_call'}]\n", "[{'name': 'fetch_real_time_info', 'args': {'query': '查询数据库'}, 'id': 'call_fgn93Rd0qvQrb2g8CSBASi5h', 'type': 'tool_call'}]\n", "[{'name': 'fetch_real_time_info', 'args': {'query': '查询数据库中'}, 'id': 'call_fgn93Rd0qvQrb2g8CSBASi5h', 'type': 'tool_call'}]\n", "[{'name': 'fetch_real_time_info', 'args': {'query': '查询数据库中所有'}, 'id': 'call_fgn93Rd0qvQrb2g8CSBASi5h', 'type': 'tool_call'}]\n", "[{'name': 'fetch_real_time_info', 'args': {'query': '查询数据库中所有城市'}, 'id': 'call_fgn93Rd0qvQrb2g8CSBASi5h', 'type': 'tool_call'}]\n", "[{'name': 'fetch_real_time_info', 'args': {'query': '查询数据库中所有城市的'}, 'id': 'call_fgn93Rd0qvQrb2g8CSBASi5h', 'type': 'tool_call'}]\n", "[{'name': 'fetch_real_time_info', 'args': {'query': '查询数据库中所有城市的天气'}, 'id': 'call_fgn93Rd0qvQrb2g8CSBASi5h', 'type': 'tool_call'}]\n", "[{'name': 'fetch_real_time_info', 'args': {'query': '查询数据库中所有城市的天气数据'}, 'id': 'call_fgn93Rd0qvQrb2g8CSBASi5h', 'type': 'tool_call'}]\n", "[{'name': 'fetch_real_time_info', 'args': {'query': '查询数据库中所有城市的天气数据'}, 'id': 'call_fgn93Rd0qvQrb2g8CSBASi5h', 'type': 'tool_call'}]\n", "[{\"title\": \"天气和气象数据网站集合原创 - CSDN博客\", \"link\": \"https://blog.csdn.net/qq_912917507/article/details/104323432\", \"snippet\": \"我们可以使用天气网,全球天气网(www.tianqi.com)提供全国各大城市的历史天气预报查询,历史气温查询,历史天气数据来源于城市当天的天气预报信息。 ... 在IT ...\", \"date\": \"Feb 15, 2020\", \"position\": 1}]|目前|我|无法|直接|从|数据库|中|查询|所有|城市|的|天气|数据|。如果|您|需要|查询|某|个|特|定|城市|的|天气|数据|,请|提供|城市|名称|,我|将|为|您|查询|。|" ] } ], "source": [ "from langchain_core.messages import AIMessageChunk, HumanMessage\n", "\n", "inputs = [HumanMessage(content=\"what is the weather in sf\")]\n", "\n", "first = True\n", "async for msg, metadata in graph.astream({\"messages\": [\"你好,帮我查询一下数据库中都有哪些城市的天气数据\"]}, stream_mode=\"messages\"):\n", " if msg.content and not isinstance(msg, HumanMessage):\n", " print(msg.content, end=\"|\", flush=True)\n", "\n", " if isinstance(msg, AIMessageChunk):\n", " if first:\n", " gathered = msg\n", " first = False\n", " else:\n", " gathered = gathered + msg\n", "\n", " if msg.tool_call_chunks:\n", " print(gathered.tool_calls)" ] }, { "cell_type": "markdown", "id": "f4d7ebd0-4710-4205-831e-d718c63c0c96", "metadata": {}, "source": [ "  `astream`中其他的模式大家可以自行尝试,这里不重复进行说明,总体而言,我们要理解的是,同步`stream`和异步`astream`都是流式传输的默认实现,用于流式传输链中的最终输出。" ] }, { "cell_type": "markdown", "id": "0fca23c1-4d88-481b-abca-2145568a0c5c", "metadata": {}, "source": [ "## 3.2 LangGraph中的事件流" ] }, { "cell_type": "markdown", "id": "b4255ab2-99e8-4678-9543-11d1c851ccd0", "metadata": {}, "source": [ "  对于上述使用的`.stream()`或`.astream()`仅流式传输链中最后一步的输出,这对于一些对话聊天类的应用程序来说基本就足够了,但是当我们的`AI Agent`是一个使用了多个大模型调用的更复杂的链时,我们有时希望在最终输出中也使用到一些中间值。例如,在构建RAG对话应用程序时,很多场景都是最终生成的响应 + 检索到的源文档一起返回给用户,例如:" ] }, { "cell_type": "markdown", "id": "e3c8b0f0-ee6c-4452-bbd6-0fd279f7d742", "metadata": {}, "source": [ "
" ] }, { "cell_type": "markdown", "id": "0fb90e27-b924-4a9a-90df-495b040b4da7", "metadata": {}, "source": [ "  如果想获取到这样的中间事件和步骤,可以使用`LangGraph`框架中的 `astream_events `方法,注意:此方法仅支持异步。用来访问自定义事件和中间输出。使用该方法运行图时,可以得到如下相关事件:" ] }, { "cell_type": "markdown", "id": "f4e04242-4be1-4e25-80ef-bae79a118f42", "metadata": {}, "source": [ "> LangChain CallBack:https://python.langchain.com/docs/concepts/callbacks/" ] }, { "cell_type": "markdown", "id": "8a397012-8e15-4f4e-8512-3ceeaae2fa86", "metadata": {}, "source": [ "
" ] }, { "cell_type": "markdown", "id": "c233eca6-3106-4469-9d1b-7c3bf38a6a9e", "metadata": {}, "source": [ "  如下代码可以打印包含流式聊天模型输出的事件,其中 version=\"v2\" 参数是指定使用 测试版 API 的版本,现在必须指定。 \r\n" ] }, { "cell_type": "code", "execution_count": 339, "id": "dadce5a7-9bb3-4a52-acf3-82a280dde01b", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "on_chain_start: LangGraph\n", "on_chain_start: __start__\n", "on_chain_end: __start__\n", "on_chain_start: agent\n", "on_chain_start: call_model\n", "on_chain_start: RunnableSequence\n", "on_chain_start: StateModifier\n", "on_chain_end: StateModifier\n", "on_chat_model_start: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_stream: ChatOpenAI\n", "on_chat_model_end: ChatOpenAI\n", "on_chain_end: RunnableSequence\n", "on_chain_end: call_model\n", "on_chain_start: _write\n", "on_chain_end: _write\n", "on_chain_start: should_continue\n", "on_chain_end: should_continue\n", "on_chain_stream: agent\n", "on_chain_end: agent\n", "on_chain_stream: LangGraph\n", "on_chain_end: LangGraph\n" ] } ], "source": [ "async for event in graph.astream_events({\"messages\": [\"你好,请你介绍一下你自己\"]}, version=\"v2\"):\n", " kind = event[\"event\"]\n", " print(f\"{kind}: {event['name']}\")" ] }, { "cell_type": "markdown", "id": "dfdf3b30-0fde-4a82-a414-2e75809ccf23", "metadata": {}, "source": [ "  这个过程明确标识了`Agent`执行的每个阶段。从`on_chain_start: LangGraph `开始,写入`__start__`节点,启动`call_model`节点( on_chain_start: call_model )。然后开始聊天模型调用( on_chat_model_start: ChatOpenAI ), 按`token`的增量流式返回 ( on_chat_model_stream: ChatOpenAI ),直到聊天模型( on_chat_model_end: ChatOpenAI )输出完全部内容后停止。继而将结果写回通道( ChannelWrite ),再次回到`call_model`节点做决策,最终完成整个图的运行流程。" ] }, { "cell_type": "markdown", "id": "b491e5a3-e04a-45bd-adbd-cea65e466622", "metadata": {}, "source": [ "  我们可以从中提取具体的某个 `event`(事件),比如:" ] }, { "cell_type": "code", "execution_count": 345, "id": "cb9afb24-7fb0-4f5f-8324-3d90c40f0a0f", "metadata": {}, "outputs": [], "source": [ "events = []\n", "async for event in graph.astream_events({\"messages\": [\"你好,请你介绍一下你自己\"]}, version=\"v2\"):\n", " events.append(event)" ] }, { "cell_type": "code", "execution_count": 358, "id": "9275af64-24d5-46fd-bb99-4276dc430cb4", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'event': 'on_chain_start',\n", " 'data': {'input': {'messages': ['你好,请你介绍一下你自己']}},\n", " 'name': 'LangGraph',\n", " 'tags': [],\n", " 'run_id': '82a8cc1d-4607-47bc-88e5-cf4027857fd0',\n", " 'metadata': {},\n", " 'parent_ids': []}" ] }, "execution_count": 358, "metadata": {}, "output_type": "execute_result" } ], "source": [ "events[0]" ] }, { "cell_type": "code", "execution_count": 360, "id": "a2321c00-63da-4465-9467-d68ed780e4ff", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'event': 'on_chat_model_stream',\n", " 'data': {'chunk': AIMessageChunk(content='您好', additional_kwargs={}, response_metadata={}, id='run-fd2157b0-6ff4-40df-8749-84deab32adbd')},\n", " 'run_id': 'fd2157b0-6ff4-40df-8749-84deab32adbd',\n", " 'name': 'ChatOpenAI',\n", " 'tags': ['seq:step:2'],\n", " 'metadata': {'langgraph_step': 1,\n", " 'langgraph_node': 'agent',\n", " 'langgraph_triggers': ['start:agent'],\n", " 'langgraph_path': ('__pregel_pull', 'agent'),\n", " 'langgraph_checkpoint_ns': 'agent:b43bb514-1964-53fd-a747-94d646ccd17c',\n", " 'checkpoint_ns': 'agent:b43bb514-1964-53fd-a747-94d646ccd17c',\n", " 'ls_provider': 'openai',\n", " 'ls_model_name': 'gpt-4o',\n", " 'ls_model_type': 'chat',\n", " 'ls_temperature': 0.7},\n", " 'parent_ids': ['82a8cc1d-4607-47bc-88e5-cf4027857fd0',\n", " '4d367cb2-9d0a-4d5b-9f31-b0295ea3fce1',\n", " 'aa31ad82-b577-4b8c-bbb9-cb8879b42dda',\n", " 'aa2387eb-9371-4e02-a238-bb7fd8185cba']}" ] }, "execution_count": 360, "metadata": {}, "output_type": "execute_result" } ], "source": [ "events[10]" ] }, { "cell_type": "markdown", "id": "f1be8087-6205-4fdb-b256-70fd921daaaa", "metadata": {}, "source": [ "  所有事件都会包含`event` 、 `name`和`data`字段,其中:\n", "- event :正在发出的事件类型。\n", "- name :这是事件的名称\n", "- data :这是与事件关联的数据。" ] }, { "cell_type": "markdown", "id": "52e1b958-2eb0-4e79-9a08-784087c45ee6", "metadata": {}, "source": [ "  基于此就可以按照`name`、`tags`或`type`等不同的字段来进行事件过滤,比如我们现在选择仅包含聊天模型的输出:" ] }, { "cell_type": "code", "execution_count": 367, "id": "d611ab76-b98c-416a-9c0c-f808620de149", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='你好', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='!', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='我是', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='一个', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='由', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='人工', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='智能', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='驱', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='动', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='的', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='助手', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content=',可以', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='帮助', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='回答', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='问题', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='、', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='提供', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='信息', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='、', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='进行', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='任务', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='自动', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='化', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='等', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='。我', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='可以', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='处理', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='多', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='种', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='语言', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='的', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='文本', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content=',并', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='能', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='与', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='各种', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='应用', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='程序', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='和', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='工具', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='进行', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='集', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='成', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content=',以', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='满足', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='不同', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='的', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='需求', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='。如果', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='你', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='有', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='任何', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='问题', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='或者', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='需要', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='帮助', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content=',', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='随', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='时', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='告诉', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='我', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='!', additional_kwargs={}, response_metadata={}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|{'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_72bbfa6014'}, id='run-599ff39e-910f-48a7-a87b-eaabd1154d29')}, 'run_id': '599ff39e-910f-48a7-a87b-eaabd1154d29', 'name': 'ChatOpenAI', 'tags': ['seq:step:2'], 'metadata': {'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_path': ('__pregel_pull', 'agent'), 'langgraph_checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'checkpoint_ns': 'agent:79d639f6-6c89-f63f-9d09-4c0be3bd44d7', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['c1ebffad-910f-4fe7-914f-832bf6967cad', '85a346c6-9632-4039-902b-abad71845000', 'e7c1e9cf-b472-4e13-bdac-197b1981df38', 'b7763813-9d0b-488c-b6bf-cc035dacad80']}|" ] } ], "source": [ "async for event in graph.astream_events({\"messages\": [\"你好,请你介绍一下你自己\"]}, version=\"v2\"):\n", " kind = event[\"event\"]\n", " if kind == \"on_chat_model_stream\":\n", " print(event, end=\"|\", flush=True)" ] }, { "cell_type": "markdown", "id": "5508aaaf-ec6c-49f9-af51-bf5b1de86766", "metadata": {}, "source": [ "  每种类型的事件都包含不同格式的数据。而其中`data`是一个非常重要的,包含此事件的实际数据。在`on_chat_model_stream`事件中,就是需要响应的流式`Token`,如上图所示是一个 `AIMessageChunk`,其中包含消息的`content`以及`id` ,提取的代码就非常简单了,和我们上面实现的方式一致,即直接采用如下代码:" ] }, { "cell_type": "code", "execution_count": 372, "id": "f1a6c28b-55f4-4d50-924a-9295b4bba43a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "你好|!|我是|一个|由|人工|智能|驱|动|的|助手|,|旨|在|帮助|回答|各种|问题|和|执行|特|定|任务|。我|能够|进行|自然|语言|处理|,|提供|信息|查询|、|天气|预|报|、|简单|计算|等|服务|。如果|你|有|任何|问题|或|需要|帮助|,|随|时|可以|告诉|我|!|" ] } ], "source": [ "first = True\n", "async for msg, metadata in graph.astream({\"messages\": [\"你好,请你介绍一下你自己\"]}, stream_mode=\"messages\"):\n", " if msg.content and not isinstance(msg, HumanMessage):\n", " print(msg.content, end=\"|\", flush=True)\n", "\n", " if isinstance(msg, AIMessageChunk):\n", " if first:\n", " gathered = msg\n", " first = False\n", " else:\n", " gathered = gathered + msg\n", "\n", " if msg.tool_call_chunks:\n", " print(gathered.tool_calls)" ] }, { "cell_type": "markdown", "id": "d485b8a7-d747-4d70-bc58-3c8532ce0949", "metadata": {}, "source": [ "  stream_mode=\"messages\"模式是直接做的格式化提取的实现过程,当然,理解了上述事件流,我们也可以直接在当前的流程下自定义数据流,比如:" ] }, { "cell_type": "code", "execution_count": 382, "id": "5867047a-ba03-4b28-b860-0ad8363d0c85", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "|你好|!|我是|一个|由|人工|智能|驱|动|的|助手|,|旨|在|帮助|回答|问题|、|提供|信息|和|执行|任务|。|我的|能力|包括|自然|语言|理解|、|信息|检|索|、|数据|处理|等等|。如果|你|有|任何|问题|或者|需要|帮助|,|随|时|可以|问|我|!||" ] } ], "source": [ "async for event in graph.astream_events({\"messages\": [\"你好,请你介绍一下你自己\"]}, version=\"v2\"):\n", " kind = event[\"event\"]\n", " if kind == \"on_chat_model_stream\":\n", " print(event[\"data\"][\"chunk\"].content, end=\"|\", flush=True)" ] }, { "cell_type": "markdown", "id": "e114d6b6-20ab-457b-9bc5-9f8f33987d3e", "metadata": {}, "source": [ "  由此可见,在处理事件流中的信息时,我们可以根据实际需求灵活地选择输出和展示的内容格式。这种灵活性正是在复杂业务流程中引入事件流的核心原因。" ] }, { "cell_type": "markdown", "id": "ea2d58e7-4569-47b6-8d17-829e0424541b", "metadata": {}, "source": [ "  至此,我们就完整实现了在`LangGraph`中`ReAct`自治代理的完整构建,对于这个预构建的`ReAct`组件,它是集成了外部工具、记忆和规划三个核心概念,所以除了我们可以自定义外部工具以外,还可以给它制定不同的步骤规划方式,即`Planning`,以及通过`Memory`去赋予`Agent`多轮对话的能力,而这两部分的内容,我们将在下一节课程中展开详细的探讨和实践。" ] } ], "metadata": { "kernelspec": { "display_name": "agent", "language": "python", "name": "agent" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.0" } }, "nbformat": 4, "nbformat_minor": 5 }