功能工具
什么是功能工具?
当现成的工具无法完全满足特定需求时,开发者可以创建自定义功能工具。这允许实现定制化功能,例如连接专有数据库或实现独特算法。
例如,一个名为"myfinancetool"的函数工具可能是一个计算特定财务指标的函数。ADK还支持长时间运行的函数,因此如果该计算需要一些时间,智能体可以继续处理其他任务。
ADK提供了多种创建功能工具的方法,每种方法适用于不同复杂度和控制级别:
- 功能工具
- 长期运行功能工具
- 智能体即工具
1. 功能工具
将函数转化为工具是将自定义逻辑集成到智能体中的一种直接方式。这种方法提供了灵活性和快速集成。
参数
使用标准的JSON可序列化类型(如字符串、整数、列表、字典)来定义您的函数参数。需要注意的是,避免为参数设置默认值,因为语言模型(LLM)目前不支持解析默认值。
返回类型
Python函数工具的首选返回类型是字典。这允许您使用键值对来构建响应,为LLM提供上下文和清晰度。如果您的函数返回的不是字典类型,框架会自动将其包装成一个字典,其中包含一个名为"result"的键。
尽量使返回值尽可能具有描述性。例如,与其返回一个数字错误代码,不如返回一个包含"error_message"键的字典,其中包含人类可读的解释。请记住需要理解结果的是LLM,而不是代码片段。作为最佳实践,在返回字典中包含一个"status"键来指示整体结果(例如"success"、"error"、"pending"),为LLM提供关于操作状态的清晰信号。
文档字符串
函数的文档字符串(docstring)作为工具的描述会被发送给大语言模型(LLM)。因此,编写良好且全面的文档字符串对于LLM理解如何有效使用该工具至关重要。需要清晰说明函数的目的、参数的含义以及预期的返回值。
Example
该工具是一个Python函数,用于获取给定股票代码/符号的股票价格。
注意: 使用此工具前需要先pip install yfinance库。
from google.adk.agents import Agent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types
import yfinance as yf
APP_NAME = "stock_app"
USER_ID = "1234"
SESSION_ID = "session1234"
def get_stock_price(symbol: str):
"""
Retrieves the current stock price for a given symbol.
Args:
symbol (str): The stock symbol (e.g., "AAPL", "GOOG").
Returns:
float: The current stock price, or None if an error occurs.
"""
try:
stock = yf.Ticker(symbol)
historical_data = stock.history(period="1d")
if not historical_data.empty:
current_price = historical_data['Close'].iloc[-1]
return current_price
else:
return None
except Exception as e:
print(f"Error retrieving stock price for {symbol}: {e}")
return None
stock_price_agent = Agent(
model='gemini-2.0-flash',
name='stock_agent',
instruction= 'You are an agent who retrieves stock prices. If a ticker symbol is provided, fetch the current price. If only a company name is given, first perform a Google search to find the correct ticker symbol before retrieving the stock price. If the provided ticker symbol is invalid or data cannot be retrieved, inform the user that the stock price could not be found.',
description='This agent specializes in retrieving real-time stock prices. Given a stock ticker symbol (e.g., AAPL, GOOG, MSFT) or the stock name, use the tools and reliable data sources to provide the most up-to-date price.',
tools=[get_stock_price],
)
# Session and Runner
session_service = InMemorySessionService()
session = session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
runner = Runner(agent=stock_price_agent, app_name=APP_NAME, session_service=session_service)
# Agent Interaction
def call_agent(query):
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("Agent Response: ", final_response)
call_agent("stock price of GOOG")
该工具的返回值将被封装为一个字典。
最佳实践
在定义函数时您拥有相当大的灵活性,但请记住简洁性可以提升大语言模型的使用体验。请考虑以下指导原则:
- 参数越少越好:尽量减少参数数量以降低复杂性。
- 简单数据类型:尽可能优先使用
str和int等基本数据类型,而不是自定义类。 - 有意义的命名: 函数名称和参数名称会显著影响LLM对工具的理解和使用方式。选择能清晰反映函数用途及其输入含义的名称。避免使用像
do_stuff()这样的通用名称。
2. 长时运行函数工具
专为需要大量处理时间但不会阻塞智能体执行的任务而设计。该工具是FunctionTool的子类。
在使用LongRunningFunctionTool时,您的Python函数可以启动长时间运行的操作,并可选地返回一个中间结果,以便让模型和用户了解进度。然后智能体可以继续处理其他任务。例如在人机协作场景中,智能体在执行任务前需要获得人工批准。
工作原理
你可以将一个Python生成器函数(使用yield的函数)用LongRunningFunctionTool进行封装。
-
初始化: 当大语言模型调用该工具时,您的生成器函数开始执行。
-
中间更新 (
yield): 您的函数应定期生成中间Python对象(通常是字典)来报告进度。ADK框架会获取每个生成的值,并将其封装在FunctionResponse中返回给LLM。这使得LLM能够通知用户(例如状态、完成百分比、消息)。 -
完成(
return): 当任务结束时,生成器函数使用return来提供最终的Python对象结果。 -
框架处理: ADK框架负责管理执行过程。它会将每个生成的值作为中间
FunctionResponse返回。当生成器完成时,框架会将返回值作为最终FunctionResponse的内容发送,向LLM标志这个长时间运行的操作已经结束。
创建工具
定义您的生成器函数并使用LongRunningFunctionTool类进行封装:
from google.adk.tools import LongRunningFunctionTool
# Define your generator function (see example below)
def my_long_task_generator(*args, **kwargs):
# ... setup ...
yield {"status": "pending", "message": "Starting task..."} # Framework sends this as FunctionResponse
# ... perform work incrementally ...
yield {"status": "pending", "progress": 50} # Framework sends this as FunctionResponse
# ... finish work ...
return {"status": "completed", "result": "Final outcome"} # Framework sends this as final FunctionResponse
# Wrap the function
my_tool = LongRunningFunctionTool(func=my_long_task_generator)
中间更新
生成结构化的Python对象(如字典)对于提供有意义的更新至关重要。应包含以下键:
-
状态:例如,"待处理"、"运行中"、"等待输入"
-
进度:例如,百分比、已完成步骤
-
message: 面向用户/大语言模型的描述性文本
-
estimated_completion_time: 如果可计算
框架会将您生成的每个值封装成FunctionResponse并发送给LLM。
最终结果
你的生成器函数返回的Python对象被视为工具执行的最终结果。框架会将这个值(即使为None)打包到最终发送回LLM的FunctionResponse内容中,表示工具执行已完成。
Example: File Processing Simulation
import time
from google.adk.agents import Agent
from google.adk.runners import Runner
from google.adk.tools import LongRunningFunctionTool
from google.adk.sessions import InMemorySessionService
from google.genai import types
# 1. Define the generator function
def process_large_file(file_path: str) -> dict:
"""
Simulates processing a large file, yielding progress updates.
Args:
file_path: Path to the file being processed.
Returns:
A final status dictionary.
"""
total_steps = 5
# This dict will be sent in the first FunctionResponse
yield {"status": "pending", "message": f"Starting processing for {file_path}..."}
for i in range(total_steps):
time.sleep(1) # Simulate work for one step
progress = (i + 1) / total_steps
# Each yielded dict is sent in a subsequent FunctionResponse
yield {
"status": "pending",
"progress": f"{int(progress * 100)}%",
"estimated_completion_time": f"~{total_steps - (i + 1)} seconds remaining"
}
# This returned dict will be sent in the final FunctionResponse
return {"status": "completed", "result": f"Successfully processed file: {file_path}"}
# 2. Wrap the function with LongRunningFunctionTool
long_running_tool = LongRunningFunctionTool(func=process_large_file)
# 3. Use the tool in an Agent
file_processor_agent = Agent(
# Use a model compatible with function calling
model="gemini-2.0-flash",
name='file_processor_agent',
instruction="""You are an agent that processes large files. When the user provides a file path, use the 'process_large_file' tool. Keep the user informed about the progress based on the tool's updates (which arrive as function responses). Only provide the final result when the tool indicates completion in its final function response.""",
tools=[long_running_tool]
)
APP_NAME = "file_processor"
USER_ID = "1234"
SESSION_ID = "session1234"
# Session and Runner
session_service = InMemorySessionService()
session = session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
runner = Runner(agent=file_processor_agent, app_name=APP_NAME, session_service=session_service)
# Agent Interaction
def call_agent(query):
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("Agent Response: ", final_response)
call_agent("Replace with a path to your file...")
本示例的关键要点
-
process_large_file: 该生成器模拟一个耗时操作,产出中间状态/进度字典。
-
LongRunningFunctionTool: 封装生成器;框架负责将产生的更新和最终返回值作为连续的FunctionResponse发送。 -
智能体指令: 指导大语言模型使用该工具,并理解传入的FunctionResponse流(进度与完成状态)以更新用户信息。
-
最终返回: 该函数返回最终结果字典,该字典会在结束的FunctionResponse中发送以表示完成。
3. 智能体即工具
这一强大功能允许您通过将其他智能体作为工具调用来利用系统中的能力。智能体即工具(Agent-as-a-Tool)使您可以调用另一个智能体执行特定任务,有效地委派责任。从概念上讲,这类似于创建一个Python函数,该函数调用另一个智能体并将智能体的响应作为函数的返回值。
与子智能体的关键区别
区分工具型智能体和子智能体非常重要。
-
智能体即工具模式: 当智能体A将智能体B作为工具调用时(采用智能体即工具模式),智能体B的应答会被回传给智能体A,由智能体A汇总答案并生成面向用户的响应。智能体A保持控制权,继续处理后续用户输入。
-
子智能体: 当智能体A调用智能体B作为子智能体时,回答用户的责任完全转移给智能体B。智能体A实际上退出了交互循环。所有后续用户输入都将由智能体B来回答。
使用说明
要将智能体作为工具使用,请使用AgentTool类对智能体进行封装。
自定义
AgentTool 类提供以下属性用于自定义其行为:
- skip_summarization: bool: 如果设置为True,框架将跳过基于LLM的总结工具智能体的响应。这在工具的响应已经格式良好且不需要进一步处理时非常有用。
Example
from google.adk.agents import Agent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.tools.agent_tool import AgentTool
from google.genai import types
APP_NAME="summary_agent"
USER_ID="user1234"
SESSION_ID="1234"
summary_agent = Agent(
model="gemini-2.0-flash",
name="summary_agent",
instruction="""You are an expert summarizer. Please read the following text and provide a concise summary.""",
description="Agent to summarize text",
)
root_agent = Agent(
model='gemini-2.0-flash',
name='root_agent',
instruction="""You are a helpful assistant. When the user provides a long text, use the 'summarize' tool to get a summary and then present it to the user.""",
tools=[AgentTool(agent=summary_agent)]
)
# Session and Runner
session_service = InMemorySessionService()
session = session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
runner = Runner(agent=summary_agent, app_name=APP_NAME, session_service=session_service)
# Agent Interaction
def call_agent(query):
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("Agent Response: ", final_response)
long_text = """Quantum computing represents a fundamentally different approach to computation,
leveraging the bizarre principles of quantum mechanics to process information. Unlike classical computers
that rely on bits representing either 0 or 1, quantum computers use qubits which can exist in a state of superposition - effectively
being 0, 1, or a combination of both simultaneously. Furthermore, qubits can become entangled,
meaning their fates are intertwined regardless of distance, allowing for complex correlations. This parallelism and
interconnectedness grant quantum computers the potential to solve specific types of incredibly complex problems - such
as drug discovery, materials science, complex system optimization, and breaking certain types of cryptography - far
faster than even the most powerful classical supercomputers could ever achieve, although the technology is still largely in its developmental stages."""
call_agent(long_text)
工作原理
- 当
main_agent接收到长文本时,其指令会告知它使用'summarize'工具来处理长文本。 - 该框架将'summarize'识别为一个封装了
summary_agent的AgentTool。 - 在幕后,
main_agent会调用summary_agent并将长文本作为输入传递给它。 summary_agent将根据其指令处理文本并生成摘要。- 来自
summary_agent的响应随后被传递回main_agent。 main_agent随后可以获取摘要并制定对用户的最终响应(例如,"这是文本的摘要:...")