|
| 1 | +## `functools.partial` 是什么 |
| 2 | + |
| 3 | +在 Python 中,`partial` 方法是 `functools` 模块中的一个功能,它用于创建一个**新的函数**,这个函数是基于原函数的**部分参数已经固定**的版本。这在需要重复调用同一函数,并且传递相同的某些参数时非常有用。 |
| 4 | + |
| 5 | +通过 `partial`,我们可以预先为函数的某些参数赋值,生成一个新的函数,这个新函数已经预先固定了部分参数,只需要再传递剩下的参数即可。 |
| 6 | + |
| 7 | +### `functools.partial` 在 `create_agent` 函数中的应用 |
| 8 | + |
| 9 | +在你提供的代码中,`partial` 是用在 `ChatPromptTemplate` 的 `from_messages` 函数生成的提示模板上,它通过 `partial` 方法预先固定了部分参数。 |
| 10 | + |
| 11 | +```python |
| 12 | +prompt = prompt.partial(system_message=system_message) |
| 13 | +prompt = prompt.partial(tool_names=", ".join([tool.name for tool in tools])) |
| 14 | +``` |
| 15 | + |
| 16 | +这里的 `partial` 用于创建一个新的提示模板对象,并为 `system_message` 和 `tool_names` 这两个参数提供了值。这相当于对提示模板的“定制”,预先指定了这些参数的值。 |
| 17 | + |
| 18 | +**`partial` 的具体作用:** |
| 19 | + |
| 20 | +1. 调用 `prompt.partial(system_message=system_message)`,预先为 `system_message` 参数赋值,生成一个新的提示模板,固定了系统消息的内容。 |
| 21 | +2. 调用 `prompt.partial(tool_names=", ".join([tool.name for tool in tools]))`,为 `tool_names` 参数赋值,将所有工具的名称合并成一个字符串,并固定在新的模板中。 |
| 22 | + |
| 23 | +通过这两步 `partial` 调用,`prompt` 对象中已经预先填入了 `system_message` 和 `tool_names` 这两个参数,简化了后续的调用过程。 |
| 24 | + |
| 25 | +-------------------- |
| 26 | + |
| 27 | +### `functools.partial` 在 `research_node` 定义时的应用 |
| 28 | + |
| 29 | +在这段代码中,`functools.partial` 用来创建一个新的函数,该函数已经预先绑定了部分参数。这种用法简化了后续调用函数时需要传递的参数,尤其是在重复使用某些固定参数时,`partial` 可以大大减少代码冗余。 |
| 30 | + |
| 31 | +```python |
| 32 | +research_node = functools.partial(agent_node, agent=research_agent, name="Researcher") |
| 33 | +``` |
| 34 | + |
| 35 | +这里的 `functools.partial` 创建了一个新的函数 `research_node`,该函数基于原始的 `agent_node` 函数,且已经为 `agent_node` 的部分参数(`agent` 和 `name`)预先设置了值。新的 `research_node` 函数只需要接收剩余的参数就可以正常运行。 |
| 36 | + |
| 37 | +**`partial` 的具体作用:** |
| 38 | + |
| 39 | +1. **原始函数 `agent_node`**: |
| 40 | + ```python |
| 41 | + def agent_node(state, agent, name): |
| 42 | + # 函数体... |
| 43 | + ``` |
| 44 | + - `agent_node` 是一个接受 `state`, `agent`, 和 `name` 三个参数的函数。 |
| 45 | + |
| 46 | +2. **使用 `functools.partial`**: |
| 47 | + ```python |
| 48 | + research_node = functools.partial(agent_node, agent=research_agent, name="Researcher") |
| 49 | + ``` |
| 50 | + - 通过 `functools.partial`,我们创建了一个新的函数 `research_node`,它仍然是 `agent_node`,但 `agent` 参数和 `name` 参数已经被预先固定: |
| 51 | + - `agent=research_agent` |
| 52 | + - `name="Researcher"` |
| 53 | + - 也就是说,调用 `research_node` 时,只需要传递 `state` 参数,因为 `agent` 和 `name` 已经有默认值了。 |
| 54 | + |
| 55 | +**举个例子** |
| 56 | + |
| 57 | +假设有一个函数 `agent_node`,你经常需要调用它并传递相同的 `agent` 和 `name`,那么每次调用时重复写这些参数会很冗余。使用 `partial` 可以避免这种重复。 |
| 58 | + |
| 59 | +```python |
| 60 | +# 原始函数定义 |
| 61 | +def agent_node(state, agent, name): |
| 62 | + print(f"State: {state}, Agent: {agent}, Name: {name}") |
| 63 | + |
| 64 | +# 预先设置 agent 和 name 参数 |
| 65 | +research_node = functools.partial(agent_node, agent="research_agent_value", name="Researcher") |
| 66 | + |
| 67 | +# 调用时只需要传递剩下的参数 |
| 68 | +research_node(state="current_state") |
| 69 | +# 输出: State: current_state, Agent: research_agent_value, Name: Researcher |
| 70 | +``` |
| 71 | + |
| 72 | +### `functools.partial` 的优势 |
| 73 | + |
| 74 | +1. **减少重复代码**:在你需要多次调用同一个函数并且某些参数不变时,`partial` 可以避免每次都传递相同的参数。 |
| 75 | + |
| 76 | +2. **简化函数调用**:在需要频繁使用相同参数时,`partial` 提供了更简洁的写法,使代码更易于维护。 |
| 77 | + |
| 78 | +### 总结 |
| 79 | + |
| 80 | +在这段代码中,`functools.partial` 的用法预先为 `agent_node` 函数的部分参数(`agent` 和 `name`)赋值,创建了一个新函数 `research_node`。调用 `research_node` 时,只需要传递剩下的参数(`state`),从而简化了函数调用的流程。 |
| 81 | + |
| 82 | +----------------- |
| 83 | + |
| 84 | +## 什么是 ToolNode? |
| 85 | + |
| 86 | +**ToolNode** 是 LangChain 的一个预构建节点,它能够从图状态(`graph state`)中提取消息并调用指定的工具,最后将工具调用的结果反馈回图的状态中。ToolNode 非常适合与 LangGraph 中的 ReAct agent 协同工作,但也可以与任何 `StateGraph` 配合使用,只要状态中有 `messages` 键和合适的消息处理方式。 |
| 87 | + |
| 88 | +## ToolNode 的特点 |
| 89 | +1. **工具调用**:ToolNode 可以根据状态中的消息自动调用指定的工具,并返回工具的执行结果。 |
| 90 | +2. **兼容性**:可以与任意支持工具调用的 LangChain 模型配合使用。 |
| 91 | +3. **并行工具调用**:支持同时调用多个工具,并处理工具返回的多个结果。 |
| 92 | +4. **错误处理**:ToolNode 默认启用了错误处理,可以处理工具在执行过程中的异常情况。 |
| 93 | + |
| 94 | +### ToolNode 的使用步骤 |
| 95 | + |
| 96 | +### 1. 安装和环境设置 |
| 97 | +首先,安装所需的包并设置 API 密钥: |
| 98 | + |
| 99 | +```python |
| 100 | +%%capture --no-stderr |
| 101 | +%pip install --quiet -U langgraph langchain_anthropic |
| 102 | +import getpass |
| 103 | +import os |
| 104 | + |
| 105 | +def _set_env(var: str): |
| 106 | + if not os.environ.get(var): |
| 107 | + os.environ[var] = getpass.getpass(f"{var}: ") |
| 108 | + |
| 109 | +_set_env("ANTHROPIC_API_KEY") |
| 110 | +``` |
| 111 | + |
| 112 | +### 2. 定义工具 |
| 113 | + |
| 114 | +使用 `@tool` 装饰器定义可以被 ToolNode 调用的工具。下面的例子定义了两个工具: |
| 115 | +- `get_weather`:获取某个地点的天气。 |
| 116 | +- `get_coolest_cities`:获取最酷的城市列表。 |
| 117 | + |
| 118 | +```python |
| 119 | +from langchain_core.messages import AIMessage |
| 120 | +from langchain_core.tools import tool |
| 121 | +from langgraph.prebuilt import ToolNode |
| 122 | + |
| 123 | +@tool |
| 124 | +def get_weather(location: str): |
| 125 | + """获取当前的天气。""" |
| 126 | + if location.lower() in ["sf", "san francisco"]: |
| 127 | + return "It's 60 degrees and foggy." |
| 128 | + else: |
| 129 | + return "It's 90 degrees and sunny." |
| 130 | + |
| 131 | +@tool |
| 132 | +def get_coolest_cities(): |
| 133 | + """获取最酷的城市列表""" |
| 134 | + return "nyc, sf" |
| 135 | + |
| 136 | +tools = [get_weather, get_coolest_cities] # 将定义的工具放入列表中 |
| 137 | +tool_node = ToolNode(tools) # 使用工具列表初始化 ToolNode |
| 138 | +``` |
| 139 | + |
| 140 | +### 3. 手动调用 ToolNode |
| 141 | + |
| 142 | +**ToolNode** 通过图状态操作消息列表,它期望列表中的最后一条消息是一个带有 `tool_calls` 参数的 `AIMessage`。以下是手动调用 ToolNode 的示例: |
| 143 | + |
| 144 | +```python |
| 145 | +message_with_single_tool_call = AIMessage( |
| 146 | + content="", |
| 147 | + tool_calls=[ |
| 148 | + { |
| 149 | + "name": "get_weather", |
| 150 | + "args": {"location": "sf"}, |
| 151 | + "id": "tool_call_id", |
| 152 | + "type": "tool_call", |
| 153 | + } |
| 154 | + ], |
| 155 | +) |
| 156 | + |
| 157 | +tool_node.invoke({"messages": [message_with_single_tool_call]}) |
| 158 | +# 返回: {'messages': [ToolMessage(content="It's 60 degrees and foggy.", name='get_weather', tool_call_id='tool_call_id')]} |
| 159 | +``` |
| 160 | + |
| 161 | +### 4. 并行调用多个工具 |
| 162 | + |
| 163 | +ToolNode 也支持并行调用多个工具,只需在 `AIMessage` 的 `tool_calls` 参数中传入多个工具调用: |
| 164 | + |
| 165 | +```python |
| 166 | +message_with_multiple_tool_calls = AIMessage( |
| 167 | + content="", |
| 168 | + tool_calls=[ |
| 169 | + { |
| 170 | + "name": "get_coolest_cities", |
| 171 | + "args": {}, |
| 172 | + "id": "tool_call_id_1", |
| 173 | + "type": "tool_call", |
| 174 | + }, |
| 175 | + { |
| 176 | + "name": "get_weather", |
| 177 | + "args": {"location": "sf"}, |
| 178 | + "id": "tool_call_id_2", |
| 179 | + "type": "tool_call", |
| 180 | + }, |
| 181 | + ], |
| 182 | +) |
| 183 | + |
| 184 | +tool_node.invoke({"messages": [message_with_multiple_tool_calls]}) |
| 185 | +# 返回: |
| 186 | +# {'messages': [ToolMessage(content='nyc, sf', name='get_coolest_cities', tool_call_id='tool_call_id_1'), |
| 187 | +# ToolMessage(content="It's 60 degrees and foggy.", name='get_weather', tool_call_id='tool_call_id_2')]} |
| 188 | +``` |
| 189 | + |
| 190 | +### 与对话模型结合使用 |
| 191 | + |
| 192 | +在使用像 Anthropic 这样的对话模型时,模型可以自动生成带有 `tool_calls` 的 `AIMessage`,这样我们可以直接将模型生成的消息传给 ToolNode 来执行工具调用: |
| 193 | + |
| 194 | +```python |
| 195 | +from langchain_anthropic import ChatAnthropic |
| 196 | +from langgraph.prebuilt import ToolNode |
| 197 | + |
| 198 | +model_with_tools = ChatAnthropic( |
| 199 | + model="claude-3-haiku-20240307", temperature=0 |
| 200 | +).bind_tools(tools) |
| 201 | + |
| 202 | +tool_node.invoke({"messages": [model_with_tools.invoke("what's the weather in sf?")]}) |
| 203 | +# 返回: {'messages': [ToolMessage(content="It's 60 degrees and foggy.", name='get_weather', tool_call_id='toolu_01LFvAVT3xJMeZS6kbWwBGZK')]} |
| 204 | +``` |
| 205 | + |
| 206 | +### ToolNode 与 ReAct Agent 结合 |
| 207 | + |
| 208 | +ReAct Agent 是 LangGraph 中的一种智能体,它会反复调用工具,直到收集到足够的信息来解决问题。以下是 ReAct Agent 的基本工作流,它通过工具节点来完成工具调用: |
| 209 | + |
| 210 | +```python |
| 211 | +from typing import Literal |
| 212 | +from langgraph.graph import StateGraph, MessagesState |
| 213 | + |
| 214 | +def should_continue(state: MessagesState) -> Literal["tools", "__end__"]: |
| 215 | + messages = state["messages"] |
| 216 | + last_message = messages[-1] |
| 217 | + if last_message.tool_calls: |
| 218 | + return "tools" |
| 219 | + return "__end__" |
| 220 | + |
| 221 | +def call_model(state: MessagesState): |
| 222 | + messages = state["messages"] |
| 223 | + response = model_with_tools.invoke(messages) |
| 224 | + return {"messages": [response]} |
| 225 | + |
| 226 | +# 创建状态图 |
| 227 | +workflow = StateGraph(MessagesState) |
| 228 | + |
| 229 | +# 定义两个节点:一个用于调用模型,一个用于调用工具 |
| 230 | +workflow.add_node("agent", call_model) |
| 231 | +workflow.add_node("tools", tool_node) |
| 232 | + |
| 233 | +workflow.add_edge("__start__", "agent") # 从 agent 节点开始 |
| 234 | +workflow.add_conditional_edges("agent", should_continue) # 根据条件判断是否继续调用工具 |
| 235 | +workflow.add_edge("tools", "agent") # 工具调用完成后,返回 agent 节点 |
| 236 | + |
| 237 | +app = workflow.compile() # 编译状态图 |
| 238 | +``` |
| 239 | + |
| 240 | +### 例子:调用单个工具 |
| 241 | + |
| 242 | +当用户输入 `"what's the weather in sf?"` 时,智能体将调用 `get_weather` 工具并返回天气信息: |
| 243 | + |
| 244 | +```python |
| 245 | +for chunk in app.stream( |
| 246 | + {"messages": [("human", "what's the weather in sf?")]}, stream_mode="values" |
| 247 | +): |
| 248 | + chunk["messages"][-1].pretty_print() |
| 249 | +``` |
| 250 | + |
| 251 | +### 例子:连续调用多个工具 |
| 252 | + |
| 253 | +当用户输入 `"what's the weather in the coolest cities?"` 时,智能体将依次调用 `get_coolest_cities` 和 `get_weather` 工具,返回所有城市的天气信息: |
| 254 | + |
| 255 | +```python |
| 256 | +for chunk in app.stream( |
| 257 | + {"messages": [("human", "what's the weather in the coolest cities?")]}, |
| 258 | + stream_mode="values", |
| 259 | +): |
| 260 | + chunk["messages"][-1].pretty_print() |
| 261 | +``` |
| 262 | + |
| 263 | +### 错误处理 |
| 264 | + |
| 265 | +ToolNode 默认启用了错误处理,可以处理工具执行中的异常情况。如果想禁用错误处理,可以设置 `handle_tool_errors=False`。 |
| 266 | + |
| 267 | +### 总结 |
| 268 | + |
| 269 | +**ToolNode** 是一个非常强大的组件,它能够自动调用工具并将结果反馈回工作流。它可以处理单个或多个工具调用,并与 LangChain 模型紧密结合,使得在复杂的多步骤任务中能够更高效地调用外部 API 或工具。 |
0 commit comments