
LangGraph中Tool Calling Agent学习笔记

一、引言
在LangGraph的代理架构体系中,Router Agent是基础形式,在此之上构建的Tool Calling Agent(工具调用代理)极大地拓展了大模型的应用能力。它允许大模型根据用户输入自主选择并调用合适工具,有效解决与外部系统交互时自然语言与特定输入模式不匹配的问题,显著增强了模型处理复杂任务的能力。
二、Tool Calling Agent核心概念
(一)基于Router Agent的拓展
Router Agent为LangGraph提供了基本的代理功能,而Tool Calling Agent在其基础上进行了关键扩展。通过赋予大模型自主选择和使用多种工具的能力,Tool Calling Agent能够更好地应对复杂任务场景,尤其是需要与外部系统交互的情况。在处理涉及特定领域知识或需要调用外部API的任务时,Tool Calling Agent可借助工具获取准确信息,提升任务处理的准确性和效率。
(二)Function Calling机制剖析
Function Calling是Tool Calling Agent的核心机制,分为两个紧密相连的阶段:
- 工具调用指令生成阶段:大模型接收用户输入的自然语言问题后,对问题进行深入理解和分析。在这个过程中,模型判断是否需要调用外部工具来获取更准确的信息或执行特定操作。如果需要,模型会生成包含调用工具名称和所需参数的JSON格式指令。当用户询问“夏威夷的天气怎么样”时,模型可能生成如下指令:
{
"name": "get_weather",
"args": {
"location": "Hawaii"
}
}
这条指令明确指示了要调用名为“get_weather”的工具,并传入参数“location”,其值为“Hawaii”。
2. 工具调用与结果处理阶段:根据生成的指令,系统实际调用相应工具,并获取工具返回的结果。随后,大模型将工具返回的结果与用户原始问题相结合,经过进一步处理和分析,生成最终的回答。在上述天气查询的例子中,“get_weather”工具返回夏威夷的天气信息,大模型将其与原始问题整合,给出如“夏威夷目前天气晴朗,气温XX摄氏度”的最终答案。
三、ToolNode的使用方法
(一)使用前提条件
在LangGraph中运用ToolNode接入工具时,必须严格满足以下三个条件:
- 状态包含消息列表:全局状态中必须包含消息列表,且该消息列表需通过带有reducer的message键进行定义。这是确保ToolNode能够正确读取和处理相关信息的基础,只有满足这一条件,ToolNode才能与LangGraph的整体架构协同工作,实现信息的有效传递和处理。
- 最后消息为AIMessage:消息列表中的最后一条消息必须是AIMessage类型。这是因为在Tool Calling的流程中,执行工具的相关输出由大模型生成,而大模型生成的这类内容均为AIMessage对象类型。这种设计保证了工具调用过程中消息类型的一致性和规范性,便于系统准确识别和处理工具调用指令。
- AIMessage填充tool_calls:AIMessage中必须填充tool_calls信息。AIMessage不仅可以生成函数调用参数,还能直接生成文本回应。只有当AIMessage包含tool_calls信息时,系统才会触发相关函数调用逻辑,执行工具调用操作。若AIMessage中没有tool_calls信息,系统会认为无需调用外部工具,直接处理AIMessage中的文本内容。
(二)接入工具的详细步骤
- 定义外部函数:以使用Serper API进行实时联网检索为例,编写如下Python函数:
import requests
import json
def fetch_real_time_info(query):
url = "https://google.serper.dev/search"
payload = json.dumps({
"q": query,
"num": 1
})
headers = {
'X-API-KEY': 'fb165ecfaaab69a115ccae620c21576980309eed',
'Content-Type': 'application/json'
}
response = requests.post(url, headers=headers, data=payload)
data = json.loads(response.text)
if 'organic' in data:
return json.dumps(data['organic'], ensure_ascii=False)
else:
return json.dumps({"error": "No organic results found"}, ensure_ascii=False)
该函数通过向指定的Serper API地址发送POST请求,携带查询关键词query和指定返回结果数量num,并在请求头中包含有效的API密钥X-API-KEY。函数根据API返回的数据进行处理,若返回数据中包含organic字段,则返回该字段内容;否则,返回错误信息。

- 添加装饰器转换函数:从
langchain_core.tools导入tool装饰器,并添加到上述函数定义前,将其转换为LangGraph可识别的外部函数:
from langchain_core.tools import tool
@tool
def fetch_real_time_info(query):
# 函数体保持不变
url = "https://google.serper.dev/search"
payload = json.dumps({
"q": query,
"num": 1
})
headers = {
'X-API-KEY': 'fb165ecfaaab69a115ccae620c21576980309eed',
'Content-Type': 'application/json'
}
response = requests.post(url, headers=headers, data=payload)
data = json.loads(response.text)
if 'organic' in data:
return json.dumps(data['organic'], ensure_ascii=False)
else:
return json.dumps({"error": "No organic results found"}, ensure_ascii=False)
添加装饰器后,函数被标记为可用于工具调用的函数,LangGraph框架能够识别和处理该函数,使其符合工具调用的规范和要求。

- 实例化ToolNode并传入函数列表:将装饰后的函数放入列表,然后实例化ToolNode并传入该列表:
from langgraph.prebuilt import ToolNode
tools = [fetch_real_time_info]
tool_node = ToolNode(tools)
通过上述代码,创建了一个ToolNode实例tool_node,并将fetch_real_time_info函数作为可用工具传递给它。此时,tool_node已配置好可调用的工具,准备执行工具调用操作。

(三)调用测试
- 单个工具调用测试:手动构建包含tool_calls的AIMessage对象,调用ToolNode的invoke方法进行测试:
from langchain_core.messages import AIMessage
message_with_single_tool_call = AIMessage(
#因为调用工具,content是空
content="",
tool_calls=[
{
"name": "fetch_real_time_info",
"args": {"query": "Cloud3.5的最新新闻"},
"id": "tool_call_id",
"type": "tool_call"
}
]
)
result = tool_node.invoke({"messages": [message_with_single_tool_call]})
print(result)

在这段代码中,创建了一个AIMessage对象message_with_single_tool_call,其tool_calls字段包含一个工具调用指令,指定调用fetch_real_time_info工具并传入查询关键词“Cloud3.5的最新新闻”。通过调用tool_node的invoke方法,执行工具调用操作,并将结果打印输出。
2. 多个工具调用测试:定义新函数并添加@tool装饰,更新tools列表,在tool_calls中传入多个工具调用信息,测试多工具并行调用:
@tool
def get_weather(location):
if location.lower() in ["beijing"]:
return "北京的温度是16度,天气晴朗。"
elif location.lower() in ["shanghai"]:
return "上海的温度是20度,部分多云。"
else:
return "不好意思,并未查询到具体的天气信息。"
tools = [fetch_real_time_info, get_weather]
tool_node = ToolNode(tools)
message_with_multiple_tool_calls = AIMessage(
content="",
tool_calls=[
{
"name": "fetch_real_time_info",
"args": {"query": "Cloud3.5的最新新闻"},
"id": "tool_call_id",
"type": "tool_call"
},
{
"name": "get_weather",
"args": {"location": "beijing"},
"id": "tool_call_id_2",
"type": "tool_call"
}
]
)
result = tool_node.invoke({"messages": [message_with_multiple_tool_calls]})
print(result)

在上述代码中,定义了一个新的工具函数get_weather,用于查询指定地点的天气信息。将get_weather函数和fetch_real_time_info函数添加到tools列表中,并更新tool_node。创建包含两个工具调用指令的AIMessage对象message_with_multiple_tool_calls,分别调用fetch_real_time_info工具查询“Cloud3.5的最新新闻”,以及调用get_weather工具查询北京的天气。调用tool_node的invoke方法执行多工具并行调用,并输出结果。
四、完整实现案例
(一)绑定大模型与工具
以ChatOpenAI的gpt - 4模型为例,展示如何将工具绑定到模型上:
import getpass
import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
if not os.environ.get("OPENAI_API_KEY"):
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")
llm = ChatOpenAI(model="gpt-40")
tools = [fetch_real_time_info, get_weather]
# 绑定工具到大模型身上
model_with_tools = llm.bind_tools(tools)
在这段代码中,首先检查环境变量中是否存在OpenAI API密钥,如果不存在,则通过用户输入获取。然后创建ChatOpenAI模型实例llm,指定使用“gpt - 40”模型。将之前定义的fetch_real_time_info和get_weather工具函数添加到tools列表中,并使用llm的bind_tools方法将工具列表绑定到模型上,得到model_with_tools实例。此时,模型已具备识别和调用这些工具的能力。


(二)构建Function Agent架构
结合模型生成的tool_calls信息与ToolNode的invoke方法,构建完整的Function Agent架构:
# 模型生成tool_calls信息
tool_calls_1 = model_with_tools.invoke("Cloud3.5的最新新闻").tool_calls
tool_calls_2 = model_with_tools.invoke("北京现在多少度呀?").tool_calls
# 结合ToolNode执行
result_1 = tool_node.invoke({"messages": [model_with_tools.invoke("Cloud3.5的最新新闻")]})
result_2 = tool_node.invoke({"messages": [model_with_tools.invoke("北京现在多少度呀")]})
print(result_1)
print(result_2)


在上述代码中,首先通过调用model_with_tools的invoke方法,分别输入“Cloud3.5的最新新闻”和“北京现在多少度呀?”两个问题,获取模型生成的tool_calls信息。然后,将模型针对每个问题生成的输出作为消息输入到tool_node的invoke方法中,执行工具调用操作。最后,打印输出两个问题的工具调用结果,展示完整的Function Agent架构的运行效果。
87

被折叠的 条评论
为什么被折叠?



