使用 LlmAgent 构建简单智能体¶
LlmAgent 类通常简称为 Agent,是 ADK 的核心组件,充当智能体应用程序的核心。它利用大语言模型 (LLM) 或生成式 AI 模型进行推理、理解自然语言、制定决策、生成响应以及与工具交互。由于这种类型的智能体使用 AI 模型来解释指令和上下文,AI 模型会动态决定如何继续、使用哪些工具(如果有的话)以及提供什么输出。因此,这种类型的智能体行为是非确定性的,必须在构建和评估时考虑到这一点。
构建一个高效的 LlmAgent 涉及定义其身份、通过指令清晰引导其行为,以及为其配备必要的工具和能力。
定义智能体身份和目的¶
首先,你需要确定智能体的身份和用途。
-
name(必填): 每个智能体需要一个唯一的字符串标识符。这个name对内部操作至关重要,尤其是在多智能体系统中,智能体之间 需要相互引用或委派任务时。请选择一个能反映智能体功能的描述性名称 (例如customer_support_router、billing_inquiry_agent)。避免使用user等保留名称。 -
description(可选,推荐用于多智能体): 提供智能体能力的简要 摘要。此描述主要用于其他 LLM 智能体来判断是否应该将任务路由给 此智能体。使其足够具体以区别于同级智能体(例如,"处理关于当前 账单的查询",而不仅仅是"账单智能体")。 -
model(必填): 指定驱动此智能体推理的底层 LLM。这是一个 字符串标识符,如"gemini-flash-latest"。模型的选择会影响智能体的 能力、成本和性能。请参阅模型页面了解可用选项和 相关注意事项。
通过指令引导智能体¶
instruction 参数可以说是塑造 LlmAgent 行为最为关键的参数。它是一个
字符串(或返回字符串的函数),用于告诉智能体:
- 其核心任务或目标。
- 其个性或人设(例如"你是一个乐于助人的助手","你是一个机智的海盗")。
- 行为约束(例如"只回答关于 X 的问题","永远不要透露 Y")。
- 如何以及何时使用其
tools。你应该解释每个工具的用途以及在什么情况下 应该调用它,以补充工具本身的描述。 - 期望的输出格式(例如"以 JSON 格式响应","提供带项目符号的列表")。
编写高效指令的建议:
- 清晰且具体: 避免歧义。明确说明期望的操作和结果。
- 使用 Markdown: 使用标题、列表等改善复杂指令的可读性。
- 提供示例(少样本): 对于复杂任务或特定输出格式,在指令中直接 包含示例。
- 引导工具使用: 不要仅仅列出工具;解释智能体何时以及为什么 应该使用它们。
使用动态状态变量:
- 指令是一个字符串模板,你可以使用
{var}语法向指令中插入动态值。 {var}用于插入名为 var 的状态变量的值。{artifact.var}用于插入名为 var 的制品的文本内容。- 如果状态变量或制品不存在,智能体会抛出错误。如果你想忽略错误,
可以在变量名后附加
?,如{var?}。
# 示例:添加指令
capital_agent = LlmAgent(
model="gemini-flash-latest",
name="capital_agent",
description="Answers user questions about the capital city of a given country.",
instruction="""You are an agent that provides the capital city of a country.
When a user asks for the capital of a country:
1. Identify the country name from the user's query.
2. Use the `get_capital_city` tool to find the capital.
3. Respond clearly to the user, stating the capital city.
Example Query: "What's the capital of {country}?"
Example Response: "The capital of France is Paris."
""",
# 工具将在后面添加
)
// 示例:添加指令
const capitalAgent = new LlmAgent({
model: 'gemini-flash-latest',
name: 'capital_agent',
description: 'Answers user questions about the capital city of a given country.',
instruction: `You are an agent that provides the capital city of a country.
When a user asks for the capital of a country:
1. Identify the country name from the user's query.
2. Use the \`getCapitalCity\` tool to find the capital.
3. Respond clearly to the user, stating the capital city.
Example Query: "What's the capital of {country}?"
Example Response: "The capital of France is Paris."
`,
// 工具将在后面添加
});
// Example: Adding instructions
agent, err := llmagent.New(llmagent.Config{
Name: "capital_agent",
Model: model,
Description: "Answers user questions about the capital city of a given country.",
Instruction: `You are an agent that provides the capital city of a country.
When a user asks for the capital of a country:
1. Identify the country name from the user's query.
2. Use the 'get_capital_city' tool to find the capital.
3. Respond clearly to the user, stating the capital city.
Example Query: "What's the capital of {country}?"
Example Response: "The capital of France is Paris."`,
// tools will be added next
})
// 示例:添加指令
LlmAgent capitalAgent =
LlmAgent.builder()
.model("gemini-flash-latest")
.name("capital_agent")
.description("Answers user questions about the capital city of a given country.")
.instruction(
"""
You are an agent that provides the capital city of a country.
When a user asks for the capital of a country:
1. Identify the country name from the user's query.
2. Use the `get_capital_city` tool to find the capital.
3. Respond clearly to the user, stating the capital city.
Example Query: "What's the capital of {country}?"
Example Response: "The capital of France is Paris."
""")
// 工具将在后面添加
.build();
val instructedAgent =
LlmAgent(
name = "capital_agent",
model = Gemini(name = "gemini-flash-latest"),
instruction =
Instruction(
"""
You are an agent that provides the capital city of a country.
When a user asks for the capital of a country:
1. Identify the country name from the user's query.
2. Use the `getCapitalCity` tool to find the capital.
3. Respond clearly to the user, stating the capital city.
Example Query: "What's the capital of {country}?"
Example Response: "The capital of France is Paris."
""".trimIndent(),
),
)
注意: 对于适用于系统中所有智能体的指令,请考虑在根智能体上使用
global_instruction。
为智能体配备工具¶
工具赋予你的 LlmAgent 超越 LLM 内置知识或推理能力的额外功能。
它们使智能体能够与外部世界交互、执行计算、获取实时数据或执行
特定操作。
tools(可选): 提供智能体可以使用的工具列表。列表中的每个 项目可以是:- 一个原生函数或方法(包装为
FunctionTool)。Python ADK 会自动 将原生函数包装为FunctionTool,而在 Java 中你必须使用FunctionTool.create(...)显式包装你的方法。在 Kotlin 中,你可以 使用@Tool注解在编译时自动生成FunctionTool。 - 继承自
BaseTool的类实例。 - 另一个智能体的实例(
AgentTool,支持智能体之间的委派——参见 自定义智能体工作流)。
- 一个原生函数或方法(包装为
LLM 使用函数/工具名称、描述(来自文档字符串或 description 字段)
以及参数模式来根据对话内容和指令决定调用哪个工具。
# 定义工具函数
def get_capital_city(country: str) -> str:
"""检索给定国家的首都城市。"""
# 替换为实际逻辑(例如 API 调用、数据库查询)
capitals = {"france": "Paris", "japan": "Tokyo", "canada": "Ottawa"}
return capitals.get(country.lower(), f"Sorry, I don't know the capital of {country}.")
# 将工具添加到智能体
capital_agent = LlmAgent(
model="gemini-flash-latest",
name="capital_agent",
description="Answers user questions about the capital city of a given country.",
instruction="""You are an agent that provides the capital city of a country... (previous instruction text)""",
tools=[get_capital_city] # 直接提供函数
)
import {z} from 'zod';
import { LlmAgent, FunctionTool } from '@google/adk';
// 定义工具输入参数的模式
const getCapitalCityParamsSchema = z.object({
country: z.string().describe('The country to get capital for.'),
});
// 定义工具函数本身
async function getCapitalCity(params: z.infer<typeof getCapitalCityParamsSchema>): Promise<{ capitalCity: string }> {
const capitals: Record<string, string> = {
'france': 'Paris',
'japan': 'Tokyo',
'canada': 'Ottawa',
};
const result = capitals[params.country.toLowerCase()] ??
`Sorry, I don't know the capital of ${params.country}.`;
return {capitalCity: result}; // 工具必须返回一个对象
}
// 创建 FunctionTool 实例
const getCapitalCityTool = new FunctionTool({
name: 'getCapitalCity',
description: 'Retrieves the capital city for a given country.',
parameters: getCapitalCityParamsSchema,
execute: getCapitalCity,
});
// 将工具添加到智能体
const capitalAgent = new LlmAgent({
model: 'gemini-flash-latest',
name: 'capitalAgent',
description: 'Answers user questions about the capital city of a given country.',
instruction: 'You are an agent that provides the capital city of a country...', // 注意:为简洁起见省略了完整指令
tools: [getCapitalCityTool], // 在数组中提供 FunctionTool 实例
});
// Define a tool function
type getCapitalCityArgs struct {
Country string `json:"country" jsonschema:"The country to get the capital of."`
}
getCapitalCity := func(ctx agent.Context, args getCapitalCityArgs) (map[string]any, error) {
// Replace with actual logic (e.g., API call, database lookup)
capitals := map[string]string{"france": "Paris", "japan": "Tokyo", "canada": "Ottawa"}
capital, ok := capitals[strings.ToLower(args.Country)]
if !ok {
return nil, fmt.Errorf("Sorry, I don't know the capital of %s.", args.Country)
}
return map[string]any{"result": capital}, nil
}
// Add the tool to the agent
capitalTool, err := functiontool.New(
functiontool.Config{
Name: "get_capital_city",
Description: "Retrieves the capital city for a given country.",
},
getCapitalCity,
)
if err != nil {
log.Fatal(err)
}
agent, err := llmagent.New(llmagent.Config{
Name: "capital_agent",
Model: model,
Description: "Answers user questions about the capital city of a given country.",
Instruction: "You are an agent that provides the capital city of a country... (previous instruction text)",
Tools: []tool.Tool{capitalTool},
})
// 定义工具函数
// 检索给定国家的首都城市。
public static Map<String, Object> getCapitalCity(
@Schema(name = "country", description = "The country to get capital for")
String country) {
// 替换为实际逻辑(例如 API 调用、数据库查询)
Map<String, String> countryCapitals = new HashMap<>();
countryCapitals.put("canada", "Ottawa");
countryCapitals.put("france", "Paris");
countryCapitals.put("japan", "Tokyo");
String result =
countryCapitals.getOrDefault(
country.toLowerCase(), "Sorry, I couldn't find the capital for " + country + ".");
return Map.of("result", result); // 工具必须返回一个 Map
}
// 将工具添加到智能体
FunctionTool capitalTool = FunctionTool.create(experiment.getClass(), "getCapitalCity");
LlmAgent capitalAgent =
LlmAgent.builder()
.model("gemini-flash-latest")
.name("capital_agent")
.description("Answers user questions about the capital city of a given country.")
.instruction("You are an agent that provides the capital city of a country... (previous instruction text)")
.tools(capitalTool) // 提供包装为 FunctionTool 的函数
.build();
class CapitalService {
@Tool(description = "Retrieves the capital city for a given country.")
fun getCapitalCity(
@Param("The country to get capital for.") country: String,
): String {
val capitals = mapOf("france" to "Paris", "japan" to "Tokyo", "canada" to "Ottawa")
return capitals[country.lowercase()] ?: "Sorry, I don't know the capital of $country."
}
}
// 将工具添加到智能体
// Note: generatedTools() is generated by KSP for classes containing @Tool annotated functions.
// In a real project, you would need to set up the ADK KSP processor.
// val agentWithTools = LlmAgent(
// name = "capital_agent",
// model = Gemini(name = "gemini-flash-latest"),
// tools = capitalService.generatedTools()
// )
在自定义工具中了解更多关于工具的信息。
高级配置与控制¶
除了核心参数外,LlmAgent 还提供了多个用于更精细控制的选项:
微调 AI 模型操作¶
你可以使用 generate_content_config 调整底层 AI 模型生成响应的方式。
generate_content_config(可选): 传递一个google.genai.types.GenerateContentConfig实例来控制temperature(随机性)、max_output_tokens(响应长度)、top_p、top_k和安全设置等参数。
from google.genai import types
agent = LlmAgent(
# ... 其他参数
generate_content_config=types.GenerateContentConfig(
temperature=0.2, # 更确定性的输出
max_output_tokens=250,
safety_settings=[
types.SafetySetting(
category=types.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
threshold=types.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE,
)
]
)
)
配置默认模型¶
你可以使用 set_default_model 类方法为所有 LlmAgent 实例设置系统级
默认模型。如果你在创建智能体时未指定模型,它会回退到 ADK 的内置默认
模型。此设置有助于避免冗余的模型指定,并轻松地一次性更改所有智能体的
模型。
from google.adk.agents import LlmAgent
# 为所有智能体设置新的默认模型
LlmAgent.set_default_model("gemini-flash-latest")
# 此智能体现在将默认使用 "gemini-flash-latest"
agent_with_default_model = LlmAgent(
name="default_model_agent",
instruction="You are a helpful assistant."
)
# 你仍然可以为特定智能体覆盖默认值
specific_agent = LlmAgent(
name="specific_model_agent",
model="gemini-pro-latest",
instruction="You are a creative writer."
)
结构化数据输入和输出¶
对于需要与 LLM 智能体进行结构化数据交换的场景,ADK 提供了使用模式定义 来定义期望输入和期望输出格式的机制。
-
input_schema(可选): 定义表示期望输入结构的模式。如果设置了, 传递给此智能体的用户消息内容必须是符合此模式的 JSON 字符串。 你的指令应相应地引导用户或前序智能体。 -
output_schema(可选): 定义表示期望输出结构的模式。如果设置了, 智能体的最终响应必须是符合此模式的 JSON 字符串。
警告:将 output_schema 与 tools 一起使用
在同一个 LLM 请求中同时使用 output_schema 和 tools 仅受特定
模型支持,包括 Gemini
3.0。
对于其他模型,ADK 中使用函数工具的
解决方法可能无法可靠运行。在这种情况下,请考虑使用单独处理输出
格式的子智能体。
output_key(可选): 提供一个字符串键。如果设置了,智能体最终 响应的文本内容将自动保存到会话状态字典的此键下。这对于在工作流中 的智能体或步骤之间传递结果非常有用。- 在 Python 中,这可能类似于:
session.state[output_key] = agent_response_text - 在 Java 中:
session.state().put(outputKey, agentResponseText) - 在 Golang 中,在回调处理器中:
ctx.State().Set(output_key, agentResponseText)
- 在 Python 中,这可能类似于:
输入和输出模式通常是 Pydantic BaseModel。
from pydantic import BaseModel, Field
class CapitalOutput(BaseModel):
capital: str = Field(description="The capital of the country.")
structured_capital_agent = LlmAgent(
# ... 名称、模型、描述
instruction="""You are a Capital Information Agent. Given a country, respond ONLY with a JSON object containing the capital. Format: {"capital": "capital_name"}""",
output_schema=CapitalOutput, # 强制 JSON 输出
output_key="found_capital" # 将结果存储在 state['found_capital'] 中
# 此处无法有效使用 tools=[get_capital_city]
)
import {z} from 'zod';
import { Schema, Type } from '@google/genai';
// 定义输出的模式
const CapitalOutputSchema: Schema = {
type: Type.OBJECT,
properties: {
capital: {
type: Type.STRING,
description: 'The capital of the country.',
},
},
required: ['capital'],
};
// 创建 LlmAgent 实例
const structuredCapitalAgent = new LlmAgent({
// ... 名称、模型、描述
instruction: `You are a Capital Information Agent. Given a country, respond ONLY with a JSON object containing the capital. Format: {"capital": "capital_name"}`,
outputSchema: CapitalOutputSchema, // 强制 JSON 输出
outputKey: 'found_capital', // 将结果存储在 state['found_capital'] 中
// 此处无法有效使用工具
});
输入和输出模式是 google.genai.types.Schema 对象。
capitalOutput := &genai.Schema{
Type: genai.TypeObject,
Description: "Schema for capital city information.",
Properties: map[string]*genai.Schema{
"capital": {
Type: genai.TypeString,
Description: "The capital city of the country.",
},
},
}
agent, err := llmagent.New(llmagent.Config{
Name: "structured_capital_agent",
Model: model,
Description: "Provides capital information in a structured format.",
Instruction: `You are a Capital Information Agent. Given a country, respond ONLY with a JSON object containing the capital. Format: {"capital": "capital_name"}`,
OutputSchema: capitalOutput,
OutputKey: "found_capital",
// Cannot use the capitalTool tool effectively here
})
输入和输出模式是 google.genai.types.Schema 对象。
private static final Schema CAPITAL_OUTPUT =
Schema.builder()
.type("OBJECT")
.description("Schema for capital city information.")
.properties(
Map.of(
"capital",
Schema.builder()
.type("STRING")
.description("The capital city of the country.")
.build()))
.build();
LlmAgent structuredCapitalAgent =
LlmAgent.builder()
// ... 名称、模型、描述
.instruction(
"You are a Capital Information Agent. Given a country, respond ONLY with a JSON object containing the capital. Format: {\"capital\": \"capital_name\"}")
.outputSchema(CAPITAL_OUTPUT) // 强制 JSON 输出
.outputKey("found_capital") // 将结果存储在 state.get("found_capital") 中
// 此处无法有效使用 tools(getCapitalCity)
.build();
管理智能体上下文¶
控制智能体是否接收先前的对话历史记录。
include_contents(可选,默认值:'default'): 确定是否将contents(历史记录)发送给 LLM。'default':智能体接收相关的对话历史记录。'none':智能体不接收先前的contents。它仅根据当前指令和 当前轮次提供的输入进行操作(适用于无状态任务或强制特定上下文)。
Go v2.0.0:智能体执行模式
ADK Go v2.0.0 在 llmagent.Config 上引入了一个显式的 Mode 字段,
用于控制智能体在基于图或动态工作流中运行时的行为。三种可用模式:
ModeChat(用作子智能体时的默认值):智能体参与与用户的 多轮对话,并可通过transfer_to_agent被同级智能体访问。ModeSingleTurn(用作工作流节点时的默认值):智能体在单轮 对话中完成任务,不与用户进行聊天。ModeTask:一个与用户聊天以完成任务的任务智能体——与ModeSingleTurn不同,它可以跨轮次与用户交互以完成工作。
当你使用 workflow.NewAgentNode 包装 llmagent 时,如果未指定模式,
工作流引擎会自动将模式设置为 ModeSingleTurn——等同于 Python 中
在用作工作流节点的智能体上设置 mode="single_turn"。有关在基于图的
工作流中组合智能体的更多信息,请参阅基于图的智能体工作流。
配置规划器¶
planner(可选): 分配一个 BasePlanner 实例以在执行前启用多步推理
和规划。主要有两种规划器:
-
BuiltInPlanner: 利用模型的内置规划能力(例如 Gemini 的思考功能)。 详情和示例请参阅 Gemini Thinking。此处,
thinking_budget参数引导模型在生成响应时使用的思考 token 数量。include_thoughts参数控制模型是否在响应中包含其原始思考和内部推理过程。 -
PlanReActPlanner: 此规划器指示模型遵循特定的输出结构:首先创建 计划,然后执行操作(如调用工具),并为其步骤提供推理说明。对于没有 内置"思考"功能的模型特别有用。from google.adk import Agent from google.adk.planners import PlanReActPlanner my_agent = Agent( model="gemini-flash-latest", planner=PlanReActPlanner(), # ... 你的工具 )智能体的响应将遵循以下结构化格式:
[user]: ai news [google_search_agent]: /*PLANNING*/ 1. Perform a Google search for "latest AI news" to get current updates and headlines related to artificial intelligence. 2. Synthesize the information from the search results to provide a summary of recent AI news. /*ACTION*/ /*REASONING*/ The search results provide a comprehensive overview of recent AI news, covering various aspects like company developments, research breakthroughs, and applications. I have enough information to answer the user's request. /*FINAL_ANSWER*/ Here's a summary of recent AI news: ....
使用内置规划器的示例:
from dotenv import load_dotenv
import asyncio
import os
from google.genai import types
from google.adk.agents.llm_agent import LlmAgent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService # 可选
from google.adk.planners import BasePlanner, BuiltInPlanner, PlanReActPlanner
from google.adk.models import LlmRequest
from google.genai.types import ThinkingConfig
from google.genai.types import GenerateContentConfig
import datetime
from zoneinfo import ZoneInfo
APP_NAME = "weather_app"
USER_ID = "1234"
SESSION_ID = "session1234"
def get_weather(city: str) -> dict:
"""检索指定城市的当前天气报告。
Args:
city (str): 要检索天气报告的城市名称。
Returns:
dict: 状态和结果或错误消息。
"""
if city.lower() == "new york":
return {
"status": "success",
"report": (
"The weather in New York is sunny with a temperature of 25 degrees"
" Celsius (77 degrees Fahrenheit)."
),
}
else:
return {
"status": "error",
"error_message": f"Weather information for '{city}' is not available.",
}
def get_current_time(city: str) -> dict:
"""返回指定城市的当前时间。
Args:
city (str): 要检索当前时间的城市名称。
Returns:
dict: 状态和结果或错误消息。
"""
if city.lower() == "new york":
tz_identifier = "America/New_York"
else:
return {
"status": "error",
"error_message": (
f"Sorry, I don't have timezone information for {city}."
),
}
tz = ZoneInfo(tz_identifier)
now = datetime.datetime.now(tz)
report = (
f'The current time in {city} is {now.strftime("%Y-%m-%d %H:%M:%S %Z%z")}'
)
return {"status": "success", "report": report}
# 步骤 1:创建 ThinkingConfig
thinking_config = ThinkingConfig(
include_thoughts=True, # 要求模型在响应中包含其思考过程
thinking_budget=256 # 将"思考"限制为 256 个 token(根据需要调整)
)
print("ThinkingConfig:", thinking_config)
# 步骤 2:实例化 BuiltInPlanner
planner = BuiltInPlanner(
thinking_config=thinking_config
)
print("BuiltInPlanner created.")
# 步骤 3:将规划器包装在 LlmAgent 中
agent = LlmAgent(
model="gemini-flash-latest", # 设置你的模型名称
name="weather_and_time_agent",
instruction="You are an agent that returns time and weather",
planner=planner,
tools=[get_weather, get_current_time]
)
# 会话和运行器
session_service = InMemorySessionService()
session = session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
runner = Runner(agent=agent, app_name=APP_NAME, session_service=session_service)
# 智能体交互
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:
print(f"\nDEBUG EVENT: {event}\n")
if event.is_final_response() and event.content:
final_answer = event.content.parts[0].text.strip()
print("\n🟢 FINAL ANSWER\n", final_answer, "\n")
call_agent("If it's raining in New York right now, what is the current temperature?")
代码执行¶
code_executor(可选): 提供一个BaseCodeExecutor实例以允许 智能体执行 LLM 响应中找到的代码块。更多信息请参阅 使用 Gemini API 执行代码。
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import asyncio
from google.adk.agents import LlmAgent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.code_executors import BuiltInCodeExecutor
from google.genai import types
AGENT_NAME = "calculator_agent"
APP_NAME = "calculator"
USER_ID = "user1234"
SESSION_ID = "session_code_exec_async"
GEMINI_MODEL = "gemini-2.0-flash"
# Agent Definition
code_agent = LlmAgent(
name=AGENT_NAME,
model=GEMINI_MODEL,
code_executor=BuiltInCodeExecutor(),
instruction="""You are a calculator agent.
When given a mathematical expression, write and execute Python code to calculate the result.
Return only the final numerical result as plain text, without markdown or code blocks.
""",
description="Executes Python code to perform calculations.",
)
# Session and Runner
session_service = InMemorySessionService()
session = asyncio.run(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)
# Agent Interaction (Async)
async def call_agent_async(query):
content = types.Content(role="user", parts=[types.Part(text=query)])
print(f"\n--- Running Query: {query} ---")
final_response_text = "No final text response captured."
try:
# Use run_async
async for event in runner.run_async(
user_id=USER_ID, session_id=SESSION_ID, new_message=content
):
print(f"Event ID: {event.id}, Author: {event.author}")
# --- Check for specific parts FIRST ---
has_specific_part = False
if event.content and event.content.parts:
for part in event.content.parts: # Iterate through all parts
if part.executable_code:
# Access the actual code string via .code
print(
f" Debug: Agent generated code:\n```python\n{part.executable_code.code}\n```"
)
has_specific_part = True
elif part.code_execution_result:
# Access outcome and output correctly
print(
f" Debug: Code Execution Result: {part.code_execution_result.outcome} - Output:\n{part.code_execution_result.output}"
)
has_specific_part = True
# Also print any text parts found in any event for debugging
elif part.text and not part.text.isspace():
print(f" Text: '{part.text.strip()}'")
# Do not set has_specific_part=True here, as we want the final response logic below
# --- Check for final response AFTER specific parts ---
# Only consider it final if it doesn't have the specific code parts we just handled
if not has_specific_part and event.is_final_response():
if (
event.content
and event.content.parts
and event.content.parts[0].text
):
final_response_text = event.content.parts[0].text.strip()
print(f"==> Final Agent Response: {final_response_text}")
else:
print(
"==> Final Agent Response: [No text content in final event]")
except Exception as e:
print(f"ERROR during agent run: {e}")
print("-" * 30)
# Main async function to run the examples
async def main():
await call_agent_async("Calculate the value of (5 + 7) * 3")
await call_agent_async("What is 10 factorial?")
# Execute the main async function
try:
asyncio.run(main())
except RuntimeError as e:
# Handle specific error when running asyncio.run in an already running loop (like Jupyter/Colab)
if "cannot be called from a running event loop" in str(e):
print("\nRunning in an existing event loop (like Colab/Jupyter).")
print("Please run `await main()` in a notebook cell instead.")
# If in an interactive environment like a notebook, you might need to run:
# await main()
else:
raise e # Re-raise other runtime errors
import com.google.adk.agents.BaseAgent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.runner.Runner;
import com.google.adk.sessions.InMemorySessionService;
import com.google.adk.sessions.Session;
import com.google.adk.tools.BuiltInCodeExecutionTool;
import com.google.common.collect.ImmutableList;
import com.google.genai.types.Content;
import com.google.genai.types.Part;
public class CodeExecutionAgentApp {
private static final String AGENT_NAME = "calculator_agent";
private static final String APP_NAME = "calculator";
private static final String USER_ID = "user1234";
private static final String SESSION_ID = "session_code_exec_sync";
private static final String GEMINI_MODEL = "gemini-2.0-flash";
/**
* Calls the agent with a query and prints the interaction events and final response.
*
* @param runner The runner instance for the agent.
* @param query The query to send to the agent.
*/
public static void callAgent(Runner runner, String query) {
Content content =
Content.builder().role("user").parts(ImmutableList.of(Part.fromText(query))).build();
InMemorySessionService sessionService = (InMemorySessionService) runner.sessionService();
Session session =
sessionService
.createSession(APP_NAME, USER_ID, /* state= */ null, SESSION_ID)
.blockingGet();
System.out.println("\n--- Running Query: " + query + " ---");
final String[] finalResponseText = {"No final text response captured."};
try {
runner
.runAsync(session.userId(), session.id(), content)
.forEach(
event -> {
System.out.println("Event ID: " + event.id() + ", Author: " + event.author());
boolean hasSpecificPart = false;
if (event.content().isPresent() && event.content().get().parts().isPresent()) {
for (Part part : event.content().get().parts().get()) {
if (part.executableCode().isPresent()) {
System.out.println(
" Debug: Agent generated code:\n```python\n"
+ part.executableCode().get().code()
+ "\n```");
hasSpecificPart = true;
} else if (part.codeExecutionResult().isPresent()) {
System.out.println(
" Debug: Code Execution Result: "
+ part.codeExecutionResult().get().outcome()
+ " - Output:\n"
+ part.codeExecutionResult().get().output());
hasSpecificPart = true;
} else if (part.text().isPresent() && !part.text().get().trim().isEmpty()) {
System.out.println(" Text: '" + part.text().get().trim() + "'");
}
}
}
if (!hasSpecificPart && event.finalResponse()) {
if (event.content().isPresent()
&& event.content().get().parts().isPresent()
&& !event.content().get().parts().get().isEmpty()
&& event.content().get().parts().get().get(0).text().isPresent()) {
finalResponseText[0] =
event.content().get().parts().get().get(0).text().get().trim();
System.out.println("==> Final Agent Response: " + finalResponseText[0]);
} else {
System.out.println(
"==> Final Agent Response: [No text content in final event]");
}
}
});
} catch (Exception e) {
System.err.println("ERROR during agent run: " + e.getMessage());
e.printStackTrace();
}
System.out.println("------------------------------");
}
public static void main(String[] args) {
BuiltInCodeExecutionTool codeExecutionTool = new BuiltInCodeExecutionTool();
BaseAgent codeAgent =
LlmAgent.builder()
.name(AGENT_NAME)
.model(GEMINI_MODEL)
.tools(ImmutableList.of(codeExecutionTool))
.instruction(
"""
You are a calculator agent.
When given a mathematical expression, write and execute Python code to calculate the result.
Return only the final numerical result as plain text, without markdown or code blocks.
""")
.description("Executes Python code to perform calculations.")
.build();
InMemorySessionService sessionService = new InMemorySessionService();
Runner runner = new Runner(codeAgent, APP_NAME, null, sessionService);
callAgent(runner, "Calculate the value of (5 + 7) * 3");
callAgent(runner, "What is 10 factorial?");
}
}
代码示例¶
以下示例展示了本页讨论的核心概念。 更复杂的智能体可能会包含模式、上下文控制和规划功能。
代码
以下是完整的基础 capital_agent:
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# --- Full example code demonstrating LlmAgent with Tools vs. Output Schema ---
import json # Needed for pretty printing dicts
import asyncio
from google.adk.agents import LlmAgent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types
from pydantic import BaseModel, Field
# --- 1. Define Constants ---
APP_NAME = "agent_comparison_app"
USER_ID = "test_user_456"
SESSION_ID_TOOL_AGENT = "session_tool_agent_xyz"
SESSION_ID_SCHEMA_AGENT = "session_schema_agent_xyz"
MODEL_NAME = "gemini-2.0-flash"
# --- 2. Define Schemas ---
# Input schema used by both agents
class CountryInput(BaseModel):
country: str = Field(description="The country to get information about.")
# Output schema ONLY for the second agent
class CapitalInfoOutput(BaseModel):
capital: str = Field(description="The capital city of the country.")
# Note: Population is illustrative; the LLM will infer or estimate this
# as it cannot use tools when output_schema is set.
population_estimate: str = Field(description="An estimated population of the capital city.")
# --- 3. Define the Tool (Only for the first agent) ---
def get_capital_city(country: str) -> str:
"""Retrieves the capital city of a given country."""
print(f"\n-- Tool Call: get_capital_city(country='{country}') --")
country_capitals = {
"united states": "Washington, D.C.",
"canada": "Ottawa",
"france": "Paris",
"japan": "Tokyo",
}
result = country_capitals.get(country.lower(), f"Sorry, I couldn't find the capital for {country}.")
print(f"-- Tool Result: '{result}' --")
return result
# --- 4. Configure Agents ---
# Agent 1: Uses a tool and output_key
capital_agent_with_tool = LlmAgent(
model=MODEL_NAME,
name="capital_agent_tool",
description="Retrieves the capital city using a specific tool.",
instruction="""You are a helpful agent that provides the capital city of a country using a tool.
The user will provide the country name in a JSON format like {"country": "country_name"}.
1. Extract the country name.
2. Use the `get_capital_city` tool to find the capital.
3. Respond clearly to the user, stating the capital city found by the tool.
""",
tools=[get_capital_city],
input_schema=CountryInput,
output_key="capital_tool_result", # Store final text response
)
# Agent 2: Uses output_schema (NO tools possible)
structured_info_agent_schema = LlmAgent(
model=MODEL_NAME,
name="structured_info_agent_schema",
description="Provides capital and estimated population in a specific JSON format.",
instruction=f"""You are an agent that provides country information.
The user will provide the country name in a JSON format like {{"country": "country_name"}}.
Respond ONLY with a JSON object matching this exact schema:
{json.dumps(CapitalInfoOutput.model_json_schema(), indent=2)}
Use your knowledge to determine the capital and estimate the population. Do not use any tools.
""",
# *** NO tools parameter here - using output_schema prevents tool use ***
input_schema=CountryInput,
output_schema=CapitalInfoOutput, # Enforce JSON output structure
output_key="structured_info_result", # Store final JSON response
)
# --- 5. Set up Session Management and Runners ---
session_service = InMemorySessionService()
# Create a runner for EACH agent
capital_runner = Runner(
agent=capital_agent_with_tool,
app_name=APP_NAME,
session_service=session_service
)
structured_runner = Runner(
agent=structured_info_agent_schema,
app_name=APP_NAME,
session_service=session_service
)
# --- 6. Define Agent Interaction Logic ---
async def call_agent_and_print(
runner_instance: Runner,
agent_instance: LlmAgent,
session_id: str,
query_json: str
):
"""Sends a query to the specified agent/runner and prints results."""
print(f"\n>>> Calling Agent: '{agent_instance.name}' | Query: {query_json}")
user_content = types.Content(role='user', parts=[types.Part(text=query_json)])
final_response_content = "No final response received."
async for event in runner_instance.run_async(user_id=USER_ID, session_id=session_id, new_message=user_content):
# print(f"Event: {event.type}, Author: {event.author}") # Uncomment for detailed logging
if event.is_final_response() and event.content and event.content.parts:
# For output_schema, the content is the JSON string itself
final_response_content = event.content.parts[0].text
print(f"<<< Agent '{agent_instance.name}' Response: {final_response_content}")
current_session = await session_service.get_session(app_name=APP_NAME,
user_id=USER_ID,
session_id=session_id)
stored_output = current_session.state.get(agent_instance.output_key)
# Pretty print if the stored output looks like JSON (likely from output_schema)
print(f"--- Session State ['{agent_instance.output_key}']: ", end="")
try:
# Attempt to parse and pretty print if it's JSON
parsed_output = json.loads(stored_output)
print(json.dumps(parsed_output, indent=2))
except (json.JSONDecodeError, TypeError):
# Otherwise, print as string
print(stored_output)
print("-" * 30)
# --- 7. Run Interactions ---
async def main():
# Create separate sessions for clarity, though not strictly necessary if context is managed
print("--- Creating Sessions ---")
await session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID_TOOL_AGENT)
await session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID_SCHEMA_AGENT)
print("--- Testing Agent with Tool ---")
await call_agent_and_print(capital_runner, capital_agent_with_tool, SESSION_ID_TOOL_AGENT, '{"country": "France"}')
await call_agent_and_print(capital_runner, capital_agent_with_tool, SESSION_ID_TOOL_AGENT, '{"country": "Canada"}')
print("\n\n--- Testing Agent with Output Schema (No Tool Use) ---")
await call_agent_and_print(structured_runner, structured_info_agent_schema, SESSION_ID_SCHEMA_AGENT, '{"country": "France"}')
await call_agent_and_print(structured_runner, structured_info_agent_schema, SESSION_ID_SCHEMA_AGENT, '{"country": "Japan"}')
# --- Run the Agent ---
# Note: In Colab, you can directly use 'await' at the top level.
# If running this code as a standalone Python script, you'll need to use asyncio.run() or manage the event loop.
if __name__ == "__main__":
asyncio.run(main())
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { LlmAgent, FunctionTool, InMemoryRunner, isFinalResponse } from '@google/adk';
import { createUserContent, Schema, Type } from '@google/genai';
import type { Part } from '@google/genai';
import { z } from 'zod';
// --- 1. Define Constants ---
const APP_NAME = "capital_app_ts";
const USER_ID = "test_user_789";
const SESSION_ID_TOOL_AGENT = "session_tool_agent_ts";
const SESSION_ID_SCHEMA_AGENT = "session_schema_agent_ts";
const MODEL_NAME = "gemini-2.5-flash"; // Using flash for speed
// --- 2. Define Schemas ---
// A. Schema for the Tool's parameters (using Zod)
const CountryInput = z.object({
country: z.string().describe('The country to get the capital for.'),
});
// B. Output schema ONLY for the second agent (using ADK's Schema type)
const CapitalInfoOutputSchema: Schema = {
type: Type.OBJECT,
description: "Schema for capital city information.",
properties: {
capital: {
type: Type.STRING,
description: "The capital city of the country."
},
population_estimate: {
type: Type.STRING,
description: "An estimated population of the capital city."
},
},
required: ["capital", "population_estimate"],
};
// --- 3. Define the Tool (Only for the first agent) ---
async function getCapitalCity(params: z.infer<typeof CountryInput>): Promise<{ result: string }> {
console.log(`\n-- Tool Call: getCapitalCity(country='${params.country}') --`);
const capitals: Record<string, string> = {
'united states': 'Washington, D.C.',
'canada': 'Ottawa',
'france': 'Paris',
'japan': 'Tokyo',
};
const result = capitals[params.country.toLowerCase()] ??
`Sorry, I couldn't find the capital for ${params.country}.`;
console.log(`-- Tool Result: '${result}' --`);
return { result: result }; // Tools must return an object
}
// --- 4. Configure Agents ---
// Agent 1: Uses a tool and outputKey
const getCapitalCityTool = new FunctionTool({
name: 'get_capital_city',
description: 'Retrieves the capital city for a given country',
parameters: CountryInput,
execute: getCapitalCity,
});
const capitalAgentWithTool = new LlmAgent({
model: MODEL_NAME,
name: 'capital_agent_tool',
description: 'Retrieves the capital city using a specific tool.',
instruction: `You are a helpful agent that provides the capital city of a country using a tool.
The user will provide the country name in a JSON format like {"country": "country_name"}.
1. Extract the country name.
2. Use the \`get_capital_city\` tool to find the capital.
3. Respond with a JSON object with the key 'capital' and the value as the capital city.
`,
tools: [getCapitalCityTool],
outputKey: "capital_tool_result", // Store final text response
});
// Agent 2: Uses outputSchema (NO tools possible)
const structuredInfoAgentSchema = new LlmAgent({
model: MODEL_NAME,
name: 'structured_info_agent_schema',
description: 'Provides capital and estimated population in a specific JSON format.',
instruction: `You are an agent that provides country information.
The user will provide the country name in a JSON format like {"country": "country_name"}.
Respond ONLY with a JSON object matching this exact schema:
${JSON.stringify(CapitalInfoOutputSchema, null, 2)}
Use your knowledge to determine the capital and estimate the population. Do not use any tools.
`,
// *** NO tools parameter here - using outputSchema prevents tool use ***
outputSchema: CapitalInfoOutputSchema,
outputKey: "structured_info_result",
});
// --- 5. Define Agent Interaction Logic ---
async function callAgentAndPrint(
runner: InMemoryRunner,
agent: LlmAgent,
sessionId: string,
queryJson: string
) {
console.log(`\n>>> Calling Agent: '${agent.name}' | Query: ${queryJson}`);
const message = createUserContent(queryJson);
let finalResponseContent = "No final response received.";
for await (const event of runner.runAsync({ userId: USER_ID, sessionId: sessionId, newMessage: message })) {
if (isFinalResponse(event) && event.content?.parts?.length) {
finalResponseContent = event.content.parts.map((part: Part) => part.text ?? '').join('');
}
}
console.log(`<<< Agent '${agent.name}' Response: ${finalResponseContent}`);
// Check the session state
const currentSession = await runner.sessionService.getSession({ appName: APP_NAME, userId: USER_ID, sessionId: sessionId });
if (!currentSession) {
console.log(`--- Session not found: ${sessionId} ---`);
return;
}
const storedOutput = currentSession.state[agent.outputKey!];
console.log(`--- Session State ['${agent.outputKey}']: `);
try {
// Attempt to parse and pretty print if it's JSON
const parsedOutput = JSON.parse(storedOutput as string);
console.log(JSON.stringify(parsedOutput, null, 2));
} catch (e) {
// Otherwise, print as a string
console.log(storedOutput);
}
console.log("-".repeat(30));
}
// --- 6. Run Interactions ---
async function main() {
// Set up runners for each agent
const capitalRunner = new InMemoryRunner({ appName: APP_NAME, agent: capitalAgentWithTool });
const structuredRunner = new InMemoryRunner({ appName: APP_NAME, agent: structuredInfoAgentSchema });
// Create sessions
console.log("--- Creating Sessions ---");
await capitalRunner.sessionService.createSession({ appName: APP_NAME, userId: USER_ID, sessionId: SESSION_ID_TOOL_AGENT });
await structuredRunner.sessionService.createSession({ appName: APP_NAME, userId: USER_ID, sessionId: SESSION_ID_SCHEMA_AGENT });
console.log("\n--- Testing Agent with Tool ---");
await callAgentAndPrint(capitalRunner, capitalAgentWithTool, SESSION_ID_TOOL_AGENT, '{"country": "France"}');
await callAgentAndPrint(capitalRunner, capitalAgentWithTool, SESSION_ID_TOOL_AGENT, '{"country": "Canada"}');
console.log("\n\n--- Testing Agent with Output Schema (No Tool Use) ---");
await callAgentAndPrint(structuredRunner, structuredInfoAgentSchema, SESSION_ID_SCHEMA_AGENT, '{"country": "France"}');
await callAgentAndPrint(structuredRunner, structuredInfoAgentSchema, SESSION_ID_SCHEMA_AGENT, '{"country": "Japan"}');
}
main();
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"strings"
"google.golang.org/adk/v2/agent"
"google.golang.org/adk/v2/agent/llmagent"
"google.golang.org/adk/v2/model/gemini"
"google.golang.org/adk/v2/runner"
"google.golang.org/adk/v2/session"
"google.golang.org/adk/v2/tool"
"google.golang.org/adk/v2/tool/functiontool"
"google.golang.org/genai"
)
// --- Main Runnable Example ---
const (
modelName = "gemini-flash-latest"
appName = "agent_comparison_app"
userID = "test_user_456"
)
type getCapitalCityArgs struct {
Country string `json:"country" jsonschema:"The country to get the capital of."`
}
// getCapitalCity retrieves the capital city of a given country.
func getCapitalCity(ctx agent.Context, args getCapitalCityArgs) (map[string]any, error) {
fmt.Printf("\n-- Tool Call: getCapitalCity(country='%s') --\n", args.Country)
capitals := map[string]string{
"united states": "Washington, D.C.",
"canada": "Ottawa",
"france": "Paris",
"japan": "Tokyo",
}
capital, ok := capitals[strings.ToLower(args.Country)]
if !ok {
result := fmt.Sprintf("Sorry, I couldn't find the capital for %s.", args.Country)
fmt.Printf("-- Tool Result: '%s' --\n", result)
return nil, errors.New(result)
}
fmt.Printf("-- Tool Result: '%s' --\n", capital)
return map[string]any{"result": capital}, nil
}
// callAgent is a helper function to execute an agent with a given prompt and handle its output.
func callAgent(ctx context.Context, a agent.Agent, outputKey string, prompt string) {
fmt.Printf("\n>>> Calling Agent: '%s' | Query: %s\n", a.Name(), prompt)
// Create an in-memory session service to manage agent state.
sessionService := session.InMemoryService()
// Create a new session for the agent interaction.
sessionCreateResponse, err := sessionService.Create(ctx, &session.CreateRequest{
AppName: appName,
UserID: userID,
})
if err != nil {
log.Fatalf("Failed to create the session service: %v", err)
}
session := sessionCreateResponse.Session
// Configure the runner with the application name, agent, and session service.
config := runner.Config{
AppName: appName,
Agent: a,
SessionService: sessionService,
}
// Create a new runner instance.
r, err := runner.New(config)
if err != nil {
log.Fatalf("Failed to create the runner: %v", err)
}
// Prepare the user's message to send to the agent.
sessionID := session.ID()
userMsg := &genai.Content{
Parts: []*genai.Part{
genai.NewPartFromText(prompt),
},
Role: string(genai.RoleUser),
}
// Run the agent and process the streaming events.
for event, err := range r.Run(ctx, userID, sessionID, userMsg, agent.RunConfig{
StreamingMode: agent.StreamingModeSSE,
}) {
if err != nil {
fmt.Printf("\nAGENT_ERROR: %v\n", err)
} else if event.Partial {
// Print partial responses as they are received.
for _, p := range event.Content.Parts {
fmt.Print(p.Text)
}
}
}
// After the run, check if there's an expected output key in the session state.
if outputKey != "" {
storedOutput, error := session.State().Get(outputKey)
if error == nil {
// Pretty-print the stored output if it's a JSON string.
fmt.Printf("\n--- Session State ['%s']: ", outputKey)
storedString, isString := storedOutput.(string)
if isString {
var prettyJSON map[string]interface{}
if err := json.Unmarshal([]byte(storedString), &prettyJSON); err == nil {
indentedJSON, err := json.MarshalIndent(prettyJSON, "", " ")
if err == nil {
fmt.Println(string(indentedJSON))
} else {
fmt.Println(storedString)
}
} else {
fmt.Println(storedString)
}
} else {
fmt.Println(storedOutput)
}
fmt.Println(strings.Repeat("-", 30))
}
}
}
func main() {
ctx := context.Background()
model, err := gemini.NewModel(ctx, modelName, &genai.ClientConfig{})
if err != nil {
log.Fatalf("Failed to create model: %v", err)
}
capitalTool, err := functiontool.New(
functiontool.Config{
Name: "get_capital_city",
Description: "Retrieves the capital city for a given country.",
},
getCapitalCity,
)
if err != nil {
log.Fatalf("Failed to create function tool: %v", err)
}
countryInputSchema := &genai.Schema{
Type: genai.TypeObject,
Description: "Input for specifying a country.",
Properties: map[string]*genai.Schema{
"country": {
Type: genai.TypeString,
Description: "The country to get information about.",
},
},
Required: []string{"country"},
}
capitalAgentWithTool, err := llmagent.New(llmagent.Config{
Name: "capital_agent_tool",
Model: model,
Description: "Retrieves the capital city using a specific tool.",
Instruction: `You are a helpful agent that provides the capital city of a country using a tool.
The user will provide the country name in a JSON format like {"country": "country_name"}.
1. Extract the country name.
2. Use the 'get_capital_city' tool to find the capital.
3. Respond clearly to the user, stating the capital city found by the tool.`,
Tools: []tool.Tool{capitalTool},
InputSchema: countryInputSchema,
OutputKey: "capital_tool_result",
})
if err != nil {
log.Fatalf("Failed to create capital agent with tool: %v", err)
}
capitalInfoOutputSchema := &genai.Schema{
Type: genai.TypeObject,
Description: "Schema for capital city information.",
Properties: map[string]*genai.Schema{
"capital": {
Type: genai.TypeString,
Description: "The capital city of the country.",
},
"population_estimate": {
Type: genai.TypeString,
Description: "An estimated population of the capital city.",
},
},
Required: []string{"capital", "population_estimate"},
}
schemaJSON, _ := json.Marshal(capitalInfoOutputSchema)
structuredInfoAgentSchema, err := llmagent.New(llmagent.Config{
Name: "structured_info_agent_schema",
Model: model,
Description: "Provides capital and estimated population in a specific JSON format.",
Instruction: fmt.Sprintf(`You are an agent that provides country information.
The user will provide the country name in a JSON format like {"country": "country_name"}.
Respond ONLY with a JSON object matching this exact schema:
%s
Use your knowledge to determine the capital and estimate the population. Do not use any tools.`, string(schemaJSON)),
InputSchema: countryInputSchema,
OutputSchema: capitalInfoOutputSchema,
OutputKey: "structured_info_result",
})
if err != nil {
log.Fatalf("Failed to create structured info agent: %v", err)
}
fmt.Println("--- Testing Agent with Tool ---")
callAgent(ctx, capitalAgentWithTool, "capital_tool_result", `{"country": "France"}`)
callAgent(ctx, capitalAgentWithTool, "capital_tool_result", `{"country": "Canada"}`)
fmt.Println("\n\n--- Testing Agent with Output Schema (No Tool Use) ---")
callAgent(ctx, structuredInfoAgentSchema, "structured_info_result", `{"country": "France"}`)
callAgent(ctx, structuredInfoAgentSchema, "structured_info_result", `{"country": "Japan"}`)
}
// --- Full example code demonstrating LlmAgent with Tools vs. Output Schema ---
import com.google.adk.agents.LlmAgent;
import com.google.adk.events.Event;
import com.google.adk.runner.Runner;
import com.google.adk.sessions.InMemorySessionService;
import com.google.adk.sessions.Session;
import com.google.adk.tools.Annotations;
import com.google.adk.tools.FunctionTool;
import com.google.genai.types.Content;
import com.google.genai.types.Part;
import com.google.genai.types.Schema;
import io.reactivex.rxjava3.core.Flowable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class LlmAgentExample {
// --- 1. Define Constants ---
private static final String MODEL_NAME = "gemini-2.0-flash";
private static final String APP_NAME = "capital_agent_tool";
private static final String USER_ID = "test_user_456";
private static final String SESSION_ID_TOOL_AGENT = "session_tool_agent_xyz";
private static final String SESSION_ID_SCHEMA_AGENT = "session_schema_agent_xyz";
// --- 2. Define Schemas ---
// Input schema used by both agents
private static final Schema COUNTRY_INPUT_SCHEMA =
Schema.builder()
.type("OBJECT")
.description("Input for specifying a country.")
.properties(
Map.of(
"country",
Schema.builder()
.type("STRING")
.description("The country to get information about.")
.build()))
.required(List.of("country"))
.build();
// Output schema ONLY for the second agent
private static final Schema CAPITAL_INFO_OUTPUT_SCHEMA =
Schema.builder()
.type("OBJECT")
.description("Schema for capital city information.")
.properties(
Map.of(
"capital",
Schema.builder()
.type("STRING")
.description("The capital city of the country.")
.build(),
"population_estimate",
Schema.builder()
.type("STRING")
.description("An estimated population of the capital city.")
.build()))
.required(List.of("capital", "population_estimate"))
.build();
// --- 3. Define the Tool (Only for the first agent) ---
// Retrieves the capital city of a given country.
public static Map<String, Object> getCapitalCity(
@Annotations.Schema(name = "country", description = "The country to get capital for")
String country) {
System.out.printf("%n-- Tool Call: getCapitalCity(country='%s') --%n", country);
Map<String, String> countryCapitals = new HashMap<>();
countryCapitals.put("united states", "Washington, D.C.");
countryCapitals.put("canada", "Ottawa");
countryCapitals.put("france", "Paris");
countryCapitals.put("japan", "Tokyo");
String result =
countryCapitals.getOrDefault(
country.toLowerCase(), "Sorry, I couldn't find the capital for " + country + ".");
System.out.printf("-- Tool Result: '%s' --%n", result);
return Map.of("result", result); // Tools must return a Map
}
public static void main(String[] args){
LlmAgentExample agentExample = new LlmAgentExample();
FunctionTool capitalTool = FunctionTool.create(agentExample.getClass(), "getCapitalCity");
// --- 4. Configure Agents ---
// Agent 1: Uses a tool and output_key
LlmAgent capitalAgentWithTool =
LlmAgent.builder()
.model(MODEL_NAME)
.name("capital_agent_tool")
.description("Retrieves the capital city using a specific tool.")
.instruction(
"""
You are a helpful agent that provides the capital city of a country using a tool.
1. Extract the country name.
2. Use the `get_capital_city` tool to find the capital.
3. Respond clearly to the user, stating the capital city found by the tool.
""")
.tools(capitalTool)
.inputSchema(COUNTRY_INPUT_SCHEMA)
.outputKey("capital_tool_result") // Store final text response
.build();
// Agent 2: Uses an output schema
LlmAgent structuredInfoAgentSchema =
LlmAgent.builder()
.model(MODEL_NAME)
.name("structured_info_agent_schema")
.description("Provides capital and estimated population in a specific JSON format.")
.instruction(
String.format("""
You are an agent that provides country information.
Respond ONLY with a JSON object matching this exact schema: %s
Use your knowledge to determine the capital and estimate the population. Do not use any tools.
""", CAPITAL_INFO_OUTPUT_SCHEMA.toJson()))
// *** NO tools parameter here - using output_schema prevents tool use ***
.inputSchema(COUNTRY_INPUT_SCHEMA)
.outputSchema(CAPITAL_INFO_OUTPUT_SCHEMA) // Enforce JSON output structure
.outputKey("structured_info_result") // Store final JSON response
.build();
// --- 5. Set up Session Management and Runners ---
InMemorySessionService sessionService = new InMemorySessionService();
sessionService.createSession(APP_NAME, USER_ID, null, SESSION_ID_TOOL_AGENT).blockingGet();
sessionService.createSession(APP_NAME, USER_ID, null, SESSION_ID_SCHEMA_AGENT).blockingGet();
Runner capitalRunner = new Runner(capitalAgentWithTool, APP_NAME, null, sessionService);
Runner structuredRunner = new Runner(structuredInfoAgentSchema, APP_NAME, null, sessionService);
// --- 6. Run Interactions ---
System.out.println("--- Testing Agent with Tool ---");
agentExample.callAgentAndPrint(
capitalRunner, capitalAgentWithTool, SESSION_ID_TOOL_AGENT, "{\"country\": \"France\"}");
agentExample.callAgentAndPrint(
capitalRunner, capitalAgentWithTool, SESSION_ID_TOOL_AGENT, "{\"country\": \"Canada\"}");
System.out.println("\n\n--- Testing Agent with Output Schema (No Tool Use) ---");
agentExample.callAgentAndPrint(
structuredRunner,
structuredInfoAgentSchema,
SESSION_ID_SCHEMA_AGENT,
"{\"country\": \"France\"}");
agentExample.callAgentAndPrint(
structuredRunner,
structuredInfoAgentSchema,
SESSION_ID_SCHEMA_AGENT,
"{\"country\": \"Japan\"}");
}
// --- 7. Define Agent Interaction Logic ---
public void callAgentAndPrint(Runner runner, LlmAgent agent, String sessionId, String queryJson) {
System.out.printf(
"%n>>> Calling Agent: '%s' | Session: '%s' | Query: %s%n",
agent.name(), sessionId, queryJson);
Content userContent = Content.fromParts(Part.fromText(queryJson));
final String[] finalResponseContent = {"No final response received."};
Flowable<Event> eventStream = runner.runAsync(USER_ID, sessionId, userContent);
// Stream event response
eventStream.blockingForEach(event -> {
if (event.finalResponse() && event.content().isPresent()) {
event
.content()
.get()
.parts()
.flatMap(parts -> parts.isEmpty() ? Optional.empty() : Optional.of(parts.get(0)))
.flatMap(Part::text)
.ifPresent(text -> finalResponseContent[0] = text);
}
});
System.out.printf("<<< Agent '%s' Response: %s%n", agent.name(), finalResponseContent[0]);
// Retrieve the session again to get the updated state
Session updatedSession =
runner
.sessionService()
.getSession(APP_NAME, USER_ID, sessionId, Optional.empty())
.blockingGet();
if (updatedSession != null && agent.outputKey().isPresent()) {
// Print to verify if the stored output looks like JSON (likely from output_schema)
System.out.printf("--- Session State ['%s']: ", agent.outputKey().get());
}
}
}
val finalAgent =
LlmAgent(
name = "capital_agent",
model = Gemini(name = "gemini-flash-latest"),
description = "Answers user questions about the capital city of a given country.",
instruction =
Instruction(
"You are an agent that provides the capital city of a country...",
),
// tools = capitalService.generatedTools() // Assuming tools are added
)
val sessionService = InMemorySessionService()
val runner = InMemoryRunner(finalAgent, "capital_app", sessionService)
val userMessage = Content(parts = listOf(Part(text = "What is the capital of France?")))
// Use runAsync to get a Flow of events
runner.runAsync(
userId = "user123",
sessionId = "session456",
newMessage = userMessage,
).collect {
event ->
if (event.isFinalResponse) {
val finalResponse = event.content?.parts?.firstOrNull()?.text
println(finalResponse)
}
}
附加功能¶
ADK 为本指南未涵盖的智能体提供了附加功能,包括以下内容:
- 回调: 通过拦截智能体执行点来添加更多控制,包括模型调用之前和 之后,以及工具调用之前和之后,参见回调。
- 基于图的工作流: 使用基于图的智能体工作流将 LLM 智能体
组合为确定性、基于图的流水线中的步骤。在 Go v2.0.0 中,使用
workflow.NewAgentNode将任何 LLM 智能体包装为工作流节点。 - 多智能体系统: 智能体交互的高级策略,包括智能体转移
(
disallow_transfer_to_parent、disallow_transfer_to_peers)和 共享指令(global_instruction)。参见多智能体工作流和 协作智能体团队。