第5章:工具使用(函数调用)
通过函数调用使AI智能体能够与外部系统和现实世界工具交互
第5章:工具使用(函数调用)
工具使用模式概述
到目前为止,我们讨论了主要涉及编排语言模型之间交互和管理智能体内部工作流中信息流的智能体模式(链接、路由、并行化、反思)。然而,为了让智能体真正有用并与现实世界或外部系统交互,它们需要使用工具的能力。
工具使用模式,通常通过称为函数调用的机制实现,使智能体能够与外部API、数据库、服务交互,甚至执行代码。它允许智能体核心的LLM根据用户请求或任务的当前状态决定何时以及如何使用特定的外部函数。
该过程通常涉及:
- 工具定义: 外部函数或能力被定义并描述给LLM。此描述包括函数的用途、其名称以及它接受的参数,以及它们的类型和描述。
- LLM决策: LLM接收用户的请求和可用的工具定义。基于其对请求和工具的理解,LLM决定调用一个或多个工具是否必要来满足请求。
- 函数调用生成: 如果LLM决定使用工具,它生成结构化输出(通常是JSON对象),指定要调用的工具名称和要传递给它的参数(从用户请求中提取)。
- 工具执行: 智能体框架或编排层拦截此结构化输出。它识别请求的工具并使用提供的参数执行实际的外部函数。
- 观察/结果: 工具执行的输出或结果返回给智能体。
- LLM处理(可选但常见): LLM接收工具的输出作为上下文,并使用它来制定对用户的最终响应或决定工作流中的下一步(这可能涉及调用另一个工具、反思或提供最终答案)。
这种模式是基础的,因为它打破了LLM训练数据的限制,允许它访问最新信息、执行内部无法进行的计算、与用户特定数据交互或触发现实世界行动。函数调用是连接LLM推理能力和可用的大量外部功能的技术机制。
虽然"函数调用"恰当地描述了调用特定的、预定义的代码函数,但考虑更广泛的"工具调用"概念是有用的。这个更广泛的术语承认智能体的能力可以远远超出简单的函数执行。一个"工具"可以是传统函数,但它也可以是复杂的API端点、对数据库的请求,甚至是针对另一个专门智能体的指令。这种视角允许我们设想更复杂的系统,其中例如,主智能体可能将复杂的数据分析任务委托给专门的"分析师智能体"或通过其API查询外部知识库。从"工具调用"的角度思考更好地捕捉了智能体作为跨多样化数字资源和其他智能实体生态系统的编排者的全部潜力。
LangChain、LangGraph和Google Agent Developer Kit(ADK)等框架为定义工具并将其集成到智能体工作流中提供健壮支持,通常利用现代LLM(如Gemini或OpenAI系列中的那些)的原生函数调用能力。在这些框架的"画布"上,您定义工具,然后配置智能体(通常是LLM智能体)以了解并能够使用这些工具。
工具使用是构建强大、交互式和外部感知智能体的基石模式。
实际应用和用例
工具使用模式适用于智能体需要超越生成文本来执行行动或检索特定、动态信息的几乎所有场景:
- 从外部来源检索信息:
访问LLM训练数据中不存在的实时数据或信息。
- 用例: 天气智能体。
- 工具: 接受位置并返回当前天气条件的天气API。
- 智能体流程: 用户问"伦敦的天气如何?",LLM识别需要天气工具,用"伦敦"调用工具,工具返回数据,LLM将数据格式化为用户友好的响应。
- 与数据库和API交互:
对结构化数据执行查询、更新或其他操作。
- 用例: 电子商务智能体。
- 工具: 检查产品库存、获取订单状态或处理支付的API调用。
- 智能体流程: 用户问"产品X有库存吗?",LLM调用库存API,工具返回库存计数,LLM告诉用户库存状态。
- 执行计算和数据分析:
使用外部计算器、数据分析库或统计工具。
- 用例: 金融智能体。
- 工具: 计算器函数、股票市场数据API、电子表格工具。
- 智能体流程: 用户问"AAPL的当前价格是什么,如果我在150美元买入100股,计算潜在利润?",LLM调用股票API,获取当前价格,然后调用计算器工具,获取结果,格式化响应。
- 发送通信:
发送电子邮件、消息或对外部通信服务进行API调用。
- 用例: 个人助手智能体。
- 工具: 电子邮件发送API。
- 智能体流程: 用户说"给John发一封关于明天会议的电子邮件。",LLM调用电子邮件工具,从请求中提取收件人、主题和正文。
- 执行代码:
在安全环境中运行代码片段以执行特定任务。
- 用例: 编码助手智能体。
- 工具: 代码解释器。
- 智能体流程: 用户提供Python代码片段并问"这段代码做什么?",LLM使用解释器工具运行代码并分析其输出。
- 控制其他系统或设备:
与智能家居设备、IoT平台或其他连接系统交互。
- 用例: 智能家居智能体。
- 工具: 控制智能灯的API。
- 智能体流程: 用户说"关闭客厅的灯。"LLM调用智能家居工具,使用命令和目标设备。
工具使用是将语言模型从文本生成器转变为能够在数字或物理世界中感知、推理和行动的智能体的关键(见图1)

图1:智能体使用工具的一些示例
实践代码示例(LangChain)
LangChain框架内工具使用的实现是一个两阶段过程。最初,定义一个或多个工具,通常通过封装现有的Python函数或其他可运行组件。随后,这些工具绑定到语言模型,从而授予模型在确定需要外部函数调用来满足用户查询时生成结构化工具使用请求的能力。
以下实现将通过首先定义一个简单的函数来模拟信息检索工具来演示此原理。随后,将构建并配置智能体以利用此工具响应用户输入。此示例的执行需要安装核心LangChain库和模型特定的提供者包。此外,通常通过配置在本地环境中的API密钥与所选语言模型服务进行适当身份验证是必要的先决条件。
import os, getpass
import asyncio
import nest_asyncio
from typing import List
from dotenv import load_dotenv
import logging
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool as langchain_tool
from langchain.agents import create_tool_calling_agent, AgentExecutor
# 取消注释
# 安全地提示用户并将API密钥设置为环境变量
os.environ["GOOGLE_API_KEY"] = getpass.getpass("输入您的Google API密钥: ")
os.environ["OPENAI_API_KEY"] = getpass.getpass("输入您的OpenAI API密钥: ")
try:
# 需要具有函数/工具调用能力的模型。
llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash", temperature=0)
print(f"✅ 语言模型已初始化: {llm.model}")
except Exception as e:
print(f"🛑 初始化语言模型时出错: {e}")
llm = None
# --- 定义工具 ---
@langchain_tool
def search_information(query: str) -> str:
"""
提供给定主题的事实信息。使用此工具查找
如'法国首都'或'伦敦天气?'等短语的答案。
"""
print(f"\n--- 🛠️ 工具调用: search_information,查询: '{query}' ---")
# 使用预定义结果字典模拟搜索工具。
simulated_results = {
"伦敦天气": "伦敦目前多云,温度15°C。",
"法国首都": "法国的首都是巴黎。",
"地球人口": "地球的估计人口约为80亿人。",
"最高山峰": "珠穆朗玛峰是海拔最高的山峰。",
"默认": f"'{query}'的模拟搜索结果: 未找到特定信息,但主题似乎很有趣。"
}
result = simulated_results.get(query.lower(), simulated_results["默认"])
print(f"--- 工具结果: {result} ---")
return result
tools = [search_information]
# --- 创建工具调用智能体 ---
if llm:
# 此提示模板需要`agent_scratchpad`占位符用于智能体的内部步骤。
agent_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个有用的助手。"),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
])
# 创建智能体,将LLM、工具和提示绑定在一起。
agent = create_tool_calling_agent(llm, tools, agent_prompt)
# AgentExecutor是调用智能体并执行所选工具的运行时。
# 这里不需要'tools'参数,因为它们已经绑定到智能体。
agent_executor = AgentExecutor(agent=agent, verbose=True, tools=tools)
async def run_agent_with_tool(query: str):
"""使用查询调用智能体执行器并打印最终响应。"""
print(f"\n--- 🏃 使用查询运行智能体: '{query}' ---")
try:
response = await agent_executor.ainvoke({"input": query})
print("\n--- ✅ 最终智能体响应 ---")
print(response["output"])
except Exception as e:
print(f"\n🛑 智能体执行期间发生错误: {e}")
async def main():
"""并发运行所有智能体查询。"""
tasks = [
run_agent_with_tool("法国的首都是什么?"),
run_agent_with_tool("伦敦的天气如何?"),
run_agent_with_tool("告诉我关于狗的事情。") # 应该触发默认工具响应
]
await asyncio.gather(*tasks)
nest_asyncio.apply()
asyncio.run(main())代码使用LangChain库和Google Gemini模型设置了一个工具调用智能体。它定义了一个search_information工具,模拟为特定查询提供事实答案。该工具对"伦敦天气"、"法国首都"和"地球人口"有预定义响应,对其他查询有默认响应。初始化ChatGoogleGenerativeAI模型,确保它具有工具调用能力。创建ChatPromptTemplate来指导智能体交互。create_tool_calling_agent函数用于将语言模型、工具和提示组合成智能体。然后设置AgentExecutor来管理智能体的执行和工具调用。定义run_agent_with_tool异步函数来使用给定查询调用智能体并打印结果。main异步函数准备多个查询并发运行。这些查询设计为测试search_information工具的特定和默认响应。最后,asyncio.run(main())调用执行所有智能体任务。代码包括在继续智能体设置和执行之前检查LLM初始化成功的检查。
实践代码示例(CrewAI)
此代码提供了如何在CrewAI框架内实现函数调用(工具)的实用示例。它设置了一个简单场景,其中智能体配备了一个查找信息的工具。该示例特别演示了使用此智能体和工具获取模拟股票价格。
# pip install crewai langchain-openai
import os
from crewai import Agent, Task, Crew
from crewai.tools import tool
import logging
# --- 最佳实践: 配置日志记录 ---
# 基本日志设置有助于调试和跟踪crew的执行。
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# --- 设置您的API密钥 ---
# 对于生产,建议使用更安全的密钥管理方法
# 如运行时加载的环境变量或密钥管理器。
#
# 设置您选择的LLM提供商的环境变量(如OPENAI_API_KEY)
# os.environ["OPENAI_API_KEY"] = "YOUR_API_KEY"
# os.environ["OPENAI_MODEL_NAME"] = "gpt-4o"
# --- 1. 重构工具: 返回清洁数据 ---
# 工具现在返回原始数据(浮点数)或引发标准Python错误。
# 这使其更可重用并强制智能体正确处理结果。
@tool("股票价格查找工具")
def get_stock_price(ticker: str) -> float:
"""
获取给定股票代码符号的最新模拟股票价格。
返回价格作为浮点数。如果未找到代码,则引发ValueError。
"""
logging.info(f"工具调用: get_stock_price,代码 '{ticker}'")
simulated_prices = {
"AAPL": 178.15,
"GOOGL": 1750.30,
"MSFT": 425.50,
}
price = simulated_prices.get(ticker.upper())
if price is not None:
return price
else:
# 引发特定错误比返回字符串更好。
# 智能体配备处理异常并可以决定下一步行动。
raise ValueError(f"未找到代码 '{ticker.upper()}' 的模拟价格。")
# --- 2. 定义智能体 ---
# 智能体定义保持不变,但现在将利用改进的工具。
financial_analyst_agent = Agent(
role='高级金融分析师',
goal='使用提供的工具分析股票数据并报告关键价格。',
backstory="你是一名经验丰富的金融分析师,擅长使用数据源查找股票信息。你提供清晰、直接的答案。",
verbose=True,
tools=[get_stock_price],
# 允许委托可能有用,但对于此简单任务不是必需的。
allow_delegation=False,
)
# --- 3. 完善任务: 更清晰的指令和错误处理 ---
# 任务描述更具体,指导智能体如何反应
# 成功数据检索和潜在错误。
analyze_aapl_task = Task(
description=(
"苹果(代码: AAPL)的当前模拟股票价格是什么? "
"使用'股票价格查找工具'查找它。 "
"如果未找到代码,你必须报告你无法检索价格。"
),
expected_output=(
"关于AAPL模拟股票价格的单一、清晰句子。 "
"例如: 'AAPL的模拟股票价格是$178.15。' "
"如果找不到价格,请明确说明。"
),
agent=financial_analyst_agent,
)
# --- 4. 制定Crew ---
# crew编排智能体和任务如何协同工作。
financial_crew = Crew(
agents=[financial_analyst_agent],
tasks=[analyze_aapl_task],
verbose=True # 在生产中设置为False以减少详细日志
)
# --- 5. 在主执行块内运行Crew ---
# 使用__name__ == "__main__":块是标准Python最佳实践。
def main():
"""运行crew的主函数。"""
# 在开始前检查API密钥以避免运行时错误。
if not os.environ.get("OPENAI_API_KEY"):
print("错误: 未设置OPENAI_API_KEY环境变量。")
print("请在运行脚本之前设置它。")
return
print("\n## 启动金融Crew...")
print("---------------------------------")
# kickoff方法开始执行。
result = financial_crew.kickoff()
print("\n---------------------------------")
print("## Crew执行完成。")
print("\n最终结果:\n", result)
if __name__ == "__main__":
main()此代码演示了使用Crew.ai库模拟金融分析任务的简单应用程序。它定义了一个自定义工具get_stock_price,模拟查找预定义代码的股票价格。该工具设计为对有效代码返回浮点数或对无效代码引发ValueError。创建名为financial_analyst_agent的Crew.ai智能体,角色为高级金融分析师。此智能体被给予get_stock_price工具进行交互。定义任务analyze_aapl_task,专门指示智能体使用工具查找AAPL的模拟股票价格。任务描述包括关于如何处理使用工具时的成功和失败情况的清晰指令。组装Crew,包括financial_analyst_agent和analyze_aapl_task。为智能体和crew启用详细设置以在执行期间提供详细日志记录。脚本的主要部分在标准if name == "main":块内使用kickoff()方法运行crew的任务。在开始crew之前,它检查OPENAI_API_KEY环境变量是否设置,这是智能体运行所必需的。crew执行的结果,即任务的输出,然后打印到控制台。代码还包括基本日志配置以更好地跟踪crew的行动和工具调用。它使用环境变量进行API密钥管理,尽管它指出生产环境推荐更安全的方法。简而言之,核心逻辑展示了如何定义工具、智能体和任务以在Crew.ai中创建协作工作流。
实践代码(Google ADK)
Google Agent Developer Kit(ADK)包含一个原生集成工具库,可以直接纳入智能体的能力。
Google搜索: 此类组件的主要示例是Google搜索工具。此工具作为Google搜索引擎的直接接口,为智能体提供执行网络搜索和检索外部信息的功能。
from google.adk.agents import Agent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.tools import google_search
from google.genai import types
import nest_asyncio
import asyncio
# 定义会话设置和智能体执行所需的变量
APP_NAME="Google Search_agent"
USER_ID="user1234"
SESSION_ID="1234"
# 定义具有搜索工具访问权限的智能体
root_agent = ADKAgent(
name="basic_search_agent",
model="gemini-2.0-flash-exp",
description="使用Google搜索回答问题的智能体。",
instruction="我可以通过搜索互联网来回答你的问题。只要问我任何问题!",
tools=[google_search] # Google搜索是执行Google搜索的预构建工具。
)
# 智能体交互
async def call_agent(query):
"""
使用查询调用智能体的辅助函数。
"""
# 会话和运行器
session_service = InMemorySessionService()
session = await session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
runner = Runner(agent=root_agent, app_name=APP_NAME, session_service=session_service)
content = types.Content(role='user', parts=[types.Part(text=query)])
events = runner.run(user_id=USER_ID, session_id=SESSION_ID, new_message=content)
for event in events:
if event.is_final_response():
final_response = event.content.parts[0].text
print("智能体响应: ", final_response)
nest_asyncio.apply()
asyncio.run(call_agent("最新的AI新闻是什么?"))此代码演示了如何使用Google ADK for Python创建和使用基本智能体。智能体设计为通过利用Google搜索作为工具来回答问题。首先,从IPython、google.adk和google.genai导入必要的库。定义应用程序名称、用户ID和会话ID的常量。创建名为"basic_search_agent"的Agent实例,具有描述和指示其用途的指令。它配置为使用Google搜索工具,这是ADK提供的预构建工具。初始化InMemorySessionService(见第8章)来管理智能体的会话。为指定的应用程序、用户和会话ID创建新会话。实例化Runner,将创建的智能体与会话服务链接。此运行器负责在会话内执行智能体的交互。定义辅助函数call_agent来简化向智能体发送查询和处理响应的过程。在call_agent内,用户的查询格式化为types.Content对象,角色为'user'。使用用户ID、会话ID和新消息内容调用runner.run方法。runner.run方法返回表示智能体行动和响应的事件列表。代码迭代这些事件以找到最终响应。如果事件被识别为最终响应,则提取该响应的文本内容。然后将提取的智能体响应打印到控制台。最后,使用查询"最新的AI新闻是什么?"调用call_agent函数来演示智能体的行动。
代码执行: Google ADK具有用于专门任务的集成组件,包括动态代码执行环境。built_in_code_execution工具为智能体提供沙盒Python解释器。这允许模型编写和运行代码以执行计算任务、操作数据结构和执行程序脚本。这种功能对于解决需要确定性逻辑和精确计算的问题至关重要,这些问题超出了概率语言生成的范围。
import os, getpass
import asyncio
import nest_asyncio
from typing import List
from dotenv import load_dotenv
import logging
from google.adk.agents import Agent as ADKAgent, LlmAgent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.tools import google_search
from google.adk.code_executors import BuiltInCodeExecutor
from google.genai import types
# 定义会话设置和智能体执行所需的变量
APP_NAME="calculator"
USER_ID="user1234"
SESSION_ID="session_code_exec_async"
# 智能体定义
code_agent = LlmAgent(
name="calculator_agent",
model="gemini-2.0-flash",
code_executor=BuiltInCodeExecutor(),
instruction="""你是一个计算器智能体。
当给定数学表达式时,编写并执行Python代码来计算结果。
仅以纯文本返回最终数值结果,不带markdown或代码块。
""",
description="执行Python代码进行计算。",
)
# 智能体交互(异步)
async def call_agent_async(query):
# 会话和运行器
session_service = InMemorySessionService()
session = await session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
runner = Runner(agent=code_agent, app_name=APP_NAME, session_service=session_service)
content = types.Content(role='user', parts=[types.Part(text=query)])
print(f"\n--- 运行查询: {query} ---")
final_response_text = "未捕获最终文本响应。"
try:
# 使用run_async
async for event in runner.run_async(user_id=USER_ID, session_id=SESSION_ID, new_message=content):
print(f"事件ID: {event.id}, 作者: {event.author}")
# --- 首先检查特定部分 ---
# has_specific_part = False
if event.content and event.content.parts and event.is_final_response():
for part in event.content.parts: # 迭代所有部分
if part.executable_code:
# 通过.code访问实际代码字符串
print(f" 调试: 智能体生成代码:\n```python\n{part.executable_code.code}\n```")
has_specific_part = True
elif part.code_execution_result:
# 正确访问结果和输出
print(f" 调试: 代码执行结果: {part.code_execution_result.outcome} - 输出:\n{part.code_execution_result.output}")
has_specific_part = True
# 也为调试打印在任何事件中找到的任何文本部分
elif part.text and not part.text.isspace():
print(f" 文本: '{part.text.strip()}'")
# 不要在这里设置has_specific_part=True,因为我们想要下面的最终响应逻辑
# --- 在特定部分之后检查最终响应 ---
text_parts = [part.text for part in event.content.parts if part.text]
final_result = "".join(text_parts)
print(f"==> 最终智能体响应: {final_result}")
except Exception as e:
print(f"智能体运行期间出错: {e}")
print("-" * 30)
# 运行示例的主异步函数
async def main():
await call_agent_async("计算(5 + 7) * 3的值")
await call_agent_async("10的阶乘是什么?")
# 执行主异步函数
try:
nest_asyncio.apply()
asyncio.run(main())
except RuntimeError as e:
# 处理在已运行事件循环(如Jupyter/Colab)中调用asyncio.run时的特定错误
if "cannot be called from a running event loop" in str(e):
print("\n在现有事件循环中运行(如Colab/Jupyter)。")
print("请在笔记本单元格中运行`await main()`。")
# 如果在交互式环境(如笔记本)中,您可能需要运行:
# await main()
else:
raise e # 重新引发其他运行时错误此脚本使用Google的Agent Development Kit(ADK)创建通过编写和执行Python代码解决数学问题的智能体。它定义了一个专门指示充当计算器的LlmAgent,配备built_in_code_execution工具。主要逻辑位于call_agent_async函数中,该函数向智能体的运行器发送用户查询并处理结果事件。在此函数内,异步循环迭代事件,为调试打印生成的Python代码及其执行结果。代码仔细区分这些中间步骤和包含数值答案的最终事件。最后,主函数使用两个不同的数学表达式运行智能体以演示其执行计算的能力。
企业搜索: 此代码定义了使用Python中google.adk库的Google ADK应用程序。它专门使用VSearchAgent,该智能体设计为通过搜索指定的Vertex AI搜索数据存储来回答问题。代码初始化名为"q2_strategy_vsearch_agent"的VSearchAgent,提供描述、要使用的模型("gemini-2.0-flash-exp")和Vertex AI搜索数据存储的ID。DATASTORE_ID预期设置为环境变量。然后它使用InMemorySessionService管理对话历史为智能体设置Runner。定义异步函数call_vsearch_agent_async来与智能体交互。此函数接受查询,构建消息内容对象,并调用运行器的run_async方法将查询发送给智能体。然后函数将智能体的响应流式传输回控制台。它还打印关于最终响应的信息,包括来自数据存储的任何源归因。包括错误处理以捕获智能体执行期间的异常,提供关于潜在问题(如错误的数据存储ID或缺少权限)的信息性消息。提供另一个异步函数run_vsearch_example来演示如何使用示例查询调用智能体。主执行块检查是否设置了DATASTORE_ID,然后使用asyncio.run运行示例。它包括处理在已有运行事件循环的环境(如Jupyter笔记本)中运行代码的情况的检查。
import asyncio
from google.genai import types
from google.adk import agents
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
import os
# --- 配置 ---
# 确保您设置了GOOGLE_API_KEY和DATASTORE_ID环境变量
# 例如:
# os.environ["GOOGLE_API_KEY"] = "YOUR_API_KEY"
# os.environ["DATASTORE_ID"] = "YOUR_DATASTORE_ID"
DATASTORE_ID = os.environ.get("DATASTORE_ID")
# --- 应用程序常量 ---
APP_NAME = "vsearch_app"
USER_ID = "user_123" # 示例用户ID
SESSION_ID = "session_456" # 示例会话ID
# --- 智能体定义(使用指南中较新的模型更新) ---
vsearch_agent = agents.VSearchAgent(
name="q2_strategy_vsearch_agent",
description="使用Vertex AI搜索回答Q2策略文档问题。",
model="gemini-2.0-flash-exp", # 基于指南示例的更新模型
datastore_id=DATASTORE_ID,
model_parameters={"temperature": 0.0}
)
# --- 运行器和会话初始化 ---
runner = Runner(
agent=vsearch_agent,
app_name=APP_NAME,
session_service=InMemorySessionService(),
)
# --- 智能体调用逻辑 ---
async def call_vsearch_agent_async(query: str):
"""初始化会话并流式传输智能体的响应。"""
print(f"用户: {query}")
print("智能体: ", end="", flush=True)
try:
# 正确构造消息内容
content = types.Content(role='user', parts=[types.Part(text=query)])
# 处理从异步运行器到达的事件
async for event in runner.run_async(
user_id=USER_ID,
session_id=SESSION_ID,
new_message=content
):
# 用于响应文本的逐令牌流式传输
if hasattr(event, 'content_part_delta') and event.content_part_delta:
print(event.content_part_delta.text, end="", flush=True)
# 处理最终响应及其关联元数据
if event.is_final_response():
print() # 流式响应后的换行
if event.grounding_metadata:
print(f" (源归因: 找到{len(event.grounding_metadata.grounding_attributions)}个源)")
else:
print(" (未找到基础元数据)")
print("-" * 30)
except Exception as e:
print(f"\n发生错误: {e}")
print("请确保您的数据存储ID正确且服务账户具有必要权限。")
print("-" * 30)
# --- 运行示例 ---
async def run_vsearch_example():
# 替换为与您的数据存储内容相关的问题
await call_vsearch_agent_async("总结Q2策略文档的要点。")
await call_vsearch_agent_async("实验室X提到了哪些安全程序?")
# --- 执行 ---
if __name__ == "__main__":
if not DATASTORE_ID:
print("错误: 未设置DATASTORE_ID环境变量。")
else:
try:
asyncio.run(run_vsearch_example())
except RuntimeError as e:
# 这处理在已有运行事件循环的环境(如Jupyter笔记本)中调用asyncio.run的情况。
if "cannot be called from a running event loop" in str(e):
print("在运行事件循环中跳过执行。请直接运行此脚本。")
else:
raise e总体而言,此代码为构建利用Vertex AI搜索基于数据存储中存储的信息回答问题的对话AI应用程序提供了基本框架。它演示了如何定义智能体、设置运行器以及与智能体异步交互同时流式传输响应。重点是检索和综合来自特定数据存储的信息以回答用户查询。
Vertex扩展: Vertex AI扩展是一个结构化API包装器,使模型能够连接到外部API进行实时数据处理和行动执行。扩展提供企业级安全性、数据隐私和性能保证。它们可用于生成和运行代码、查询网站以及分析来自私有数据存储的信息等任务。Google为常见用例(如代码解释器和Vertex AI搜索)提供预构建扩展,并提供创建自定义扩展的选项。扩展的主要好处包括强大的企业控制和与其他Google产品的无缝集成。扩展和函数调用之间的关键区别在于它们的执行:Vertex AI自动执行扩展,而函数调用需要用户或客户端手动执行。
一览
什么: LLM是强大的文本生成器,但它们从根本上与外部世界断开连接。它们的知识是静态的,仅限于它们训练的数据,它们缺乏执行行动或检索实时信息的能力。这种固有限制阻止它们完成需要与外部API、数据库或服务交互的任务。没有与这些外部系统的桥梁,它们解决现实世界问题的效用严重受限。
为什么: 工具使用模式,通常通过函数调用实现,为此问题提供标准化解决方案。它通过以LLM可以理解的方式描述可用的外部函数或"工具"来工作。基于用户的请求,智能体LLM然后可以决定是否需要工具并生成指定要调用哪个函数以及使用什么参数的结构化数据对象(如JSON)。编排层执行此函数调用,检索结果,并将其反馈给LLM。这允许LLM将最新的外部信息或行动结果纳入其最终响应,有效地赋予它行动的能力。
经验法则: 每当智能体需要突破LLM的内部知识并与外部世界交互时使用工具使用模式。这对于需要实时数据(如检查天气、股票价格)、访问私有或专有信息(如查询公司数据库)、执行精确计算、执行代码或触发其他系统行动(如发送电子邮件、控制智能设备)的任务至关重要。
视觉总结:

图2:工具使用设计模式
关键要点
- 工具使用(函数调用)允许智能体与外部系统交互并访问动态信息。
- 它涉及定义具有清晰描述和参数的工具,LLM可以理解。
- LLM决定何时使用工具并生成结构化函数调用。
- 智能体框架执行实际工具调用并将结果返回给LLM。
- 工具使用对于构建能够执行现实世界行动并提供最新信息的智能体至关重要。
- LangChain使用@tool装饰器简化工具定义,并提供create_tool_calling_agent和AgentExecutor来构建使用工具的智能体。
- Google ADK有许多非常有用的预构建工具,如Google搜索、代码执行和Vertex AI搜索工具。
结论
工具使用模式是扩展大语言模型功能范围超越其固有文本生成能力的关键架构原则。通过为模型配备与外部软件和数据源接口的能力,此范式允许智能体执行行动、执行计算并从其他系统检索信息。此过程涉及模型在确定需要外部工具来满足用户查询时生成调用外部工具的结构化请求。LangChain、Google ADK和Crew AI等框架提供结构化抽象和组件,促进这些外部工具的集成。这些框架管理向模型公开工具规范并解析其后续工具使用请求的过程。这简化了能够与外部数字环境交互并采取行动的复杂智能体系统的开发。
参考文献
- LangChain文档(工具):https://python.langchain.com/docs/integrations/tools/
- Google Agent Developer Kit(ADK)文档(工具):https://google.github.io/adk-docs/tools/
- OpenAI函数调用文档:https://platform.openai.com/docs/guides/function-calling
- CrewAI文档(工具):https://docs.crewai.com/concepts/tools