Agente LLM¶
El LlmAgent (a menudo abreviado simplemente como Agent) es un componente central en ADK,
actuando como la parte "pensante" de tu aplicación. Aprovecha el poder de un
Modelo de Lenguaje Grande (LLM) para razonar, comprender lenguaje natural, tomar
decisiones, generar respuestas e interactuar con herramientas.
A diferencia de los Agentes de Flujo de Trabajo deterministas que siguen
rutas de ejecución predefinidas, el comportamiento del LlmAgent es no determinista. Utiliza
el LLM para interpretar instrucciones y contexto, decidiendo dinámicamente cómo
proceder, qué herramientas usar (si las hay), o si transferir el control a otro
agente.
Construir un LlmAgent efectivo implica definir su identidad, guiar claramente
su comportamiento a través de instrucciones, y equiparlo con las herramientas y
capacidades necesarias.
Definiendo la Identidad y Propósito del Agente¶
Primero, necesitas establecer qué es el agente y para qué sirve.
-
name(Requerido): Cada agente necesita un identificador de cadena único. Estenamees crucial para operaciones internas, especialmente en sistemas multi-agente donde los agentes necesitan referirse o delegar tareas entre sí. Elige un nombre descriptivo que refleje la función del agente (por ejemplo,customer_support_router,billing_inquiry_agent). Evita nombres reservados comouser. -
description(Opcional, Recomendado para Multi-Agente): Proporciona un resumen conciso de las capacidades del agente. Esta descripción es utilizada principalmente por otros agentes LLM para determinar si deben dirigir una tarea a este agente. Hazla lo suficientemente específica para diferenciarlo de sus pares (por ejemplo, "Maneja consultas sobre estados de facturación actuales", no solo "Agente de facturación"). -
model(Requerido): Especifica el LLM subyacente que impulsará el razonamiento de este agente. Este es un identificador de cadena como"gemini-2.5-flash". La elección del modelo impacta las capacidades del agente, el costo y el rendimiento. Consulta la página de Modelos para opciones disponibles y consideraciones.
Guiando al Agente: Instrucciones (instruction)¶
El parámetro instruction es probablemente el más crítico para moldear el
comportamiento de un LlmAgent. Es una cadena (o una función que devuelve una cadena) que
le dice al agente:
- Su tarea u objetivo principal.
- Su personalidad o persona (por ejemplo, "Eres un asistente útil", "Eres un pirata ingenioso").
- Restricciones en su comportamiento (por ejemplo, "Solo responde preguntas sobre X", "Nunca reveles Y").
- Cómo y cuándo usar sus
tools. Debes explicar el propósito de cada herramienta y las circunstancias bajo las cuales debe ser llamada, complementando cualquier descripción dentro de la herramienta misma. - El formato deseado para su salida (por ejemplo, "Responde en JSON", "Proporciona una lista con viñetas").
Consejos para Instrucciones Efectivas:
- Sé Claro y Específico: Evita la ambigüedad. Declara claramente las acciones y resultados deseados.
- Usa Markdown: Mejora la legibilidad para instrucciones complejas usando encabezados, listas, etc.
- Proporciona Ejemplos (Few-Shot): Para tareas complejas o formatos de salida específicos, incluye ejemplos directamente en la instrucción.
- Guía el Uso de Herramientas: No solo listes herramientas; explica cuándo y por qué el agente debe usarlas.
Estado:
- La instrucción es una plantilla de cadena, puedes usar la sintaxis
{var}para insertar valores dinámicos en la instrucción. {var}se usa para insertar el valor de la variable de estado llamada var.{artifact.var}se usa para insertar el contenido de texto del artefacto llamado var.- Si la variable de estado o artefacto no existe, el agente generará un error. Si quieres ignorar el error, puedes agregar un
?al nombre de la variable como en{var?}.
# Ejemplo: Agregando instrucciones
capital_agent = LlmAgent(
model="gemini-2.5-flash",
name="capital_agent",
description="Responde preguntas de usuarios sobre la ciudad capital de un país dado.",
instruction="""Eres un agente que proporciona la ciudad capital de un país.
Cuando un usuario pregunte por la capital de un país:
1. Identifica el nombre del país de la consulta del usuario.
2. Usa la herramienta `get_capital_city` para encontrar la capital.
3. Responde claramente al usuario, indicando la ciudad capital.
Ejemplo de Consulta: "¿Cuál es la capital de {country}?"
Ejemplo de Respuesta: "La capital de Francia es París."
""",
# tools se agregarán a continuación
)
// Ejemplo: Agregando instrucciones
const capitalAgent = new LlmAgent({
model: 'gemini-2.5-flash',
name: 'capital_agent',
description: 'Responde preguntas de usuarios sobre la ciudad capital de un país dado.',
instruction: `Eres un agente que proporciona la ciudad capital de un país.
Cuando un usuario pregunte por la capital de un país:
1. Identifica el nombre del país de la consulta del usuario.
2. Usa la herramienta \`getCapitalCity\` para encontrar la capital.
3. Responde claramente al usuario, indicando la ciudad capital.
Ejemplo de Consulta: "¿Cuál es la capital de {country}?"
Ejemplo de Respuesta: "La capital de Francia es París."
`,
// tools se agregarán a continuación
});
// 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
})
// Ejemplo: Agregando instrucciones
LlmAgent capitalAgent =
LlmAgent.builder()
.model("gemini-2.5-flash")
.name("capital_agent")
.description("Responde preguntas de usuarios sobre la ciudad capital de un país dado.")
.instruction(
"""
Eres un agente que proporciona la ciudad capital de un país.
Cuando un usuario pregunte por la capital de un país:
1. Identifica el nombre del país de la consulta del usuario.
2. Usa la herramienta `get_capital_city` para encontrar la capital.
3. Responde claramente al usuario, indicando la ciudad capital.
Ejemplo de Consulta: "¿Cuál es la capital de {country}?"
Ejemplo de Respuesta: "La capital de Francia es París."
""")
// tools se agregarán a continuación
.build();
(Nota: Para instrucciones que se aplican a todos los agentes en un sistema, considera usar
global_instruction en el agente raíz, detallado más adelante en la
sección Multi-Agentes.)
Equipando al Agente: Herramientas (tools)¶
Las herramientas le dan a tu LlmAgent capacidades más allá del conocimiento integrado del LLM o
razonamiento. Permiten al agente interactuar con el mundo exterior, realizar
cálculos, obtener datos en tiempo real o ejecutar acciones específicas.
tools(Opcional): Proporciona una lista de herramientas que el agente puede usar. Cada elemento en la lista puede ser:- Una función o método nativo (envuelto como un
FunctionTool). Python ADK automáticamente envuelve la función nativa en unFunctionToolmientras que, debes envolver explícitamente tus métodos Java usandoFunctionTool.create(...) - Una instancia de una clase que hereda de
BaseTool. - Una instancia de otro agente (
AgentTool, habilitando delegación agente-a-agente - ver Multi-Agentes).
- Una función o método nativo (envuelto como un
El LLM usa los nombres de función/herramienta, descripciones (de docstrings o el
campo description), y esquemas de parámetros para decidir qué herramienta llamar basándose
en la conversación y sus instrucciones.
# Define una función de herramienta
def get_capital_city(country: str) -> str:
"""Recupera la ciudad capital para un país dado."""
# Reemplaza con lógica real (ej., llamada API, búsqueda en base de datos)
capitals = {"france": "Paris", "japan": "Tokyo", "canada": "Ottawa"}
return capitals.get(country.lower(), f"Lo siento, no conozco la capital de {country}.")
# Agrega la herramienta al agente
capital_agent = LlmAgent(
model="gemini-2.5-flash",
name="capital_agent",
description="Responde preguntas de usuarios sobre la ciudad capital de un país dado.",
instruction="""Eres un agente que proporciona la ciudad capital de un país... (texto de instrucción anterior)""",
tools=[get_capital_city] # Proporciona la función directamente
)
import {z} from 'zod';
import { LlmAgent, FunctionTool } from '@google/adk';
// Define el esquema para los parámetros de entrada de la herramienta
const getCapitalCityParamsSchema = z.object({
country: z.string().describe('El país para el cual obtener la capital.'),
});
// Define la función de herramienta en sí
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()] ??
`Lo siento, no conozco la capital de ${params.country}.`;
return {capitalCity: result}; // Las herramientas deben devolver un objeto
}
// Crea una instancia del FunctionTool
const getCapitalCityTool = new FunctionTool({
name: 'getCapitalCity',
description: 'Recupera la ciudad capital para un país dado.',
parameters: getCapitalCityParamsSchema,
execute: getCapitalCity,
});
// Agrega la herramienta al agente
const capitalAgent = new LlmAgent({
model: 'gemini-2.5-flash',
name: 'capitalAgent',
description: 'Responde preguntas de usuarios sobre la ciudad capital de un país dado.',
instruction: 'Eres un agente que proporciona la ciudad capital de un país...', // Nota: la instrucción completa se omite por brevedad
tools: [getCapitalCityTool], // Proporciona la instancia de FunctionTool en un array
});
// Define a tool function
type getCapitalCityArgs struct {
Country string `json:"country" jsonschema:"The country to get the capital of."`
}
getCapitalCity := func(ctx tool.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},
})
// Define una función de herramienta
// Recupera la ciudad capital de un país dado.
public static Map<String, Object> getCapitalCity(
@Schema(name = "country", description = "El país para el cual obtener la capital")
String country) {
// Reemplaza con lógica real (ej., llamada API, búsqueda en base de datos)
Map<String, String> countryCapitals = new HashMap<>();
countryCapitals.put("canada", "Ottawa");
countryCapitals.put("france", "Paris");
countryCapitals.put("japan", "Tokyo");
String result =
countryCapitals.getOrDefault(
country.toLowerCase(), "Lo siento, no pude encontrar la capital de " + country + ".");
return Map.of("result", result); // Las herramientas deben devolver un Map
}
// Agrega la herramienta al agente
FunctionTool capitalTool = FunctionTool.create(experiment.getClass(), "getCapitalCity");
LlmAgent capitalAgent =
LlmAgent.builder()
.model("gemini-2.5-flash")
.name("capital_agent")
.description("Responde preguntas de usuarios sobre la ciudad capital de un país dado.")
.instruction("Eres un agente que proporciona la ciudad capital de un país... (texto de instrucción anterior)")
.tools(capitalTool) // Proporciona la función envuelta como un FunctionTool
.build();
Aprende más sobre Herramientas en la sección Herramientas.
Configuración y Control Avanzados¶
Más allá de los parámetros centrales, LlmAgent ofrece varias opciones para un control más fino:
Ajustando la Generación del LLM (generate_content_config)¶
Puedes ajustar cómo el LLM subyacente genera respuestas usando generate_content_config.
generate_content_config(Opcional): Pasa una instancia degoogle.genai.types.GenerateContentConfigpara controlar parámetros comotemperature(aleatoriedad),max_output_tokens(longitud de respuesta),top_p,top_k, y configuraciones de seguridad.
from google.genai import types
agent = LlmAgent(
# ... otros parámetros
generate_content_config=types.GenerateContentConfig(
temperature=0.2, # Salida más determinista
max_output_tokens=250,
safety_settings=[
types.SafetySetting(
category=types.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
threshold=types.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE,
)
]
)
)
Estructurando Datos (input_schema, output_schema, output_key)¶
Para escenarios que requieren intercambio de datos estructurados con un LLM Agent, el ADK proporciona mecanismos para definir formatos de entrada esperados y salida deseados usando definiciones de esquema.
-
input_schema(Opcional): Define un esquema que representa la estructura de entrada esperada. Si se establece, el contenido del mensaje del usuario pasado a este agente debe ser una cadena JSON que cumpla con este esquema. Tus instrucciones deben guiar al usuario o agente precedente en consecuencia. -
output_schema(Opcional): Define un esquema que representa la estructura de salida deseada. Si se establece, la respuesta final del agente debe ser una cadena JSON que cumpla con este esquema. -
output_key(Opcional): Proporciona una clave de cadena. Si se establece, el contenido de texto de la respuesta final del agente se guardará automáticamente en el diccionario de estado de la sesión bajo esta clave. Esto es útil para pasar resultados entre agentes o pasos en un flujo de trabajo.- En Python, esto podría verse como:
session.state[output_key] = agent_response_text - En Java:
session.state().put(outputKey, agentResponseText) - En Golang, dentro de un manejador de callback:
ctx.State().Set(output_key, agentResponseText)
- En Python, esto podría verse como:
El esquema de entrada y salida es típicamente un Pydantic BaseModel.
from pydantic import BaseModel, Field
class CapitalOutput(BaseModel):
capital: str = Field(description="La capital del país.")
structured_capital_agent = LlmAgent(
# ... name, model, description
instruction="""Eres un Agente de Información de Capitales. Dado un país, responde SOLO con un objeto JSON que contenga la capital. Formato: {"capital": "nombre_capital"}""",
output_schema=CapitalOutput, # Forzar salida JSON
output_key="found_capital" # Almacenar resultado en state['found_capital']
# No se puede usar tools=[get_capital_city] efectivamente aquí
)
import {z} from 'zod';
import { Schema, Type } from '@google/genai';
// Define el esquema para la salida
const CapitalOutputSchema: Schema = {
type: Type.OBJECT,
properties: {
capital: {
type: Type.STRING,
description: 'La capital del país.',
},
},
required: ['capital'],
};
// Crea la instancia de LlmAgent
const structuredCapitalAgent = new LlmAgent({
// ... name, model, description
instruction: `Eres un Agente de Información de Capitales. Dado un país, responde SOLO con un objeto JSON que contenga la capital. Formato: {"capital": "nombre_capital"}`,
outputSchema: CapitalOutputSchema, // Forzar salida JSON
outputKey: 'found_capital', // Almacenar resultado en state['found_capital']
// No se pueden usar tools efectivamente aquí
});
El esquema de entrada y salida es un objeto 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
})
El esquema de entrada y salida es un objeto google.genai.types.Schema.
private static final Schema CAPITAL_OUTPUT =
Schema.builder()
.type("OBJECT")
.description("Esquema para información de ciudad capital.")
.properties(
Map.of(
"capital",
Schema.builder()
.type("STRING")
.description("La ciudad capital del país.")
.build()))
.build();
LlmAgent structuredCapitalAgent =
LlmAgent.builder()
// ... name, model, description
.instruction(
"Eres un Agente de Información de Capitales. Dado un país, responde SOLO con un objeto JSON que contenga la capital. Formato: {\"capital\": \"nombre_capital\"}")
.outputSchema(capitalOutput) // Forzar salida JSON
.outputKey("found_capital") // Almacenar resultado en state.get("found_capital")
// No se puede usar tools(getCapitalCity) efectivamente aquí
.build();
Gestionando Contexto (include_contents)¶
Controla si el agente recibe el historial de conversación anterior.
include_contents(Opcional, Por defecto:'default'): Determina si loscontents(historial) se envían al LLM.'default': El agente recibe el historial de conversación relevante.'none': El agente no recibecontentsanteriores. Opera basándose únicamente en su instrucción actual y cualquier entrada proporcionada en el turno actual (útil para tareas sin estado o para aplicar contextos específicos).
Planificador¶
planner (Opcional): Asigna una instancia de BasePlanner para habilitar razonamiento y planificación de múltiples pasos antes de la ejecución. Hay dos planificadores principales:
-
BuiltInPlanner: Aprovecha las capacidades de planificación integradas del modelo (por ejemplo, la función de pensamiento de Gemini). Ver Gemini Thinking para detalles y ejemplos.Aquí, el parámetro
thinking_budgetguía al modelo sobre el número de tokens de pensamiento a usar al generar una respuesta. El parámetroinclude_thoughtscontrola si el modelo debe incluir sus pensamientos crudos y proceso de razonamiento interno en la respuesta. -
PlanReActPlanner: Este planificador instruye al modelo para seguir una estructura específica en su salida: primero crear un plan, luego ejecutar acciones (como llamar herramientas), y proporcionar razonamiento para sus pasos. Es particularmente útil para modelos que no tienen una función de "pensamiento" integrada.from google.adk import Agent from google.adk.planners import PlanReActPlanner my_agent = Agent( model="gemini-2.5-flash", planner=PlanReActPlanner(), # ... tus herramientas aquí )La respuesta del agente seguirá un formato estructurado:
[user]: ai news [google_search_agent]: /*PLANNING*/ 1. Realizar una búsqueda en Google de "últimas noticias de IA" para obtener actualizaciones actuales y titulares relacionados con inteligencia artificial. 2. Sintetizar la información de los resultados de búsqueda para proporcionar un resumen de noticias recientes de IA. /*ACTION*/ /*REASONING*/ Los resultados de búsqueda proporcionan una visión general completa de noticias recientes de IA, cubriendo varios aspectos como desarrollos de empresas, avances en investigación y aplicaciones. Tengo suficiente información para responder a la solicitud del usuario. /*FINAL_ANSWER*/ Aquí hay un resumen de noticias recientes de IA: ....
Ejemplo para usar built-in-planner:
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 # Opcional
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:
"""Recupera el reporte de clima actual para una ciudad especificada.
Args:
city (str): El nombre de la ciudad para la cual recuperar el reporte de clima.
Returns:
dict: status y result o msg de error.
"""
if city.lower() == "new york":
return {
"status": "success",
"report": (
"El clima en Nueva York es soleado con una temperatura de 25 grados"
" Celsius (77 grados Fahrenheit)."
),
}
else:
return {
"status": "error",
"error_message": f"La información del clima para '{city}' no está disponible.",
}
def get_current_time(city: str) -> dict:
"""Devuelve la hora actual en una ciudad especificada.
Args:
city (str): El nombre de la ciudad para la cual recuperar la hora actual.
Returns:
dict: status y result o msg de error.
"""
if city.lower() == "new york":
tz_identifier = "America/New_York"
else:
return {
"status": "error",
"error_message": (
f"Lo siento, no tengo información de zona horaria para {city}."
),
}
tz = ZoneInfo(tz_identifier)
now = datetime.datetime.now(tz)
report = (
f'La hora actual en {city} es {now.strftime("%Y-%m-%d %H:%M:%S %Z%z")}'
)
return {"status": "success", "report": report}
# Paso 1: Crear un ThinkingConfig
thinking_config = ThinkingConfig(
include_thoughts=True, # Pedir al modelo que incluya sus pensamientos en la respuesta
thinking_budget=256 # Limitar el 'pensamiento' a 256 tokens (ajustar según sea necesario)
)
print("ThinkingConfig:", thinking_config)
# Paso 2: Instanciar BuiltInPlanner
planner = BuiltInPlanner(
thinking_config=thinking_config
)
print("BuiltInPlanner creado.")
# Paso 3: Envolver el planificador en un LlmAgent
agent = LlmAgent(
model="gemini-2.5-pro-preview-03-25", # Establece el nombre de tu modelo
name="weather_and_time_agent",
instruction="Eres un agente que devuelve hora y clima",
planner=planner,
tools=[get_weather, get_current_time]
)
# Session y Runner
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)
# Interacción con el Agente
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("Si está lloviendo en Nueva York ahora mismo, ¿cuál es la temperatura actual?")
Ejecución de Código¶
code_executor(Opcional): Proporciona una instancia deBaseCodeExecutorpara permitir al agente ejecutar bloques de código encontrados en la respuesta del LLM. Para más información, ver Ejecución de Código con 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?");
}
}
Juntando Todo: Ejemplo¶
Code
Aquí está el capital_agent básico completo:
# 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/agent"
"google.golang.org/adk/agent/llmagent"
"google.golang.org/adk/model/gemini"
"google.golang.org/adk/runner"
"google.golang.org/adk/session"
"google.golang.org/adk/tool"
"google.golang.org/adk/tool/functiontool"
"google.golang.org/genai"
)
// --- Main Runnable Example ---
const (
modelName = "gemini-2.0-flash"
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 tool.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());
}
}
}
(Este ejemplo demuestra los conceptos centrales. Agentes más complejos podrían incorporar esquemas, control de contexto, planificación, etc.)
Conceptos Relacionados (Temas Diferidos)¶
Mientras esta página cubre la configuración central de LlmAgent, varios conceptos relacionados proporcionan control más avanzado y se detallan en otros lugares:
- Callbacks: Interceptar puntos de ejecución (antes/después de llamadas al modelo, antes/después de llamadas a herramientas) usando
before_model_callback,after_model_callback, etc. Ver Callbacks. - Control Multi-Agente: Estrategias avanzadas para interacción de agentes, incluyendo planificación (
planner), control de transferencia de agentes (disallow_transfer_to_parent,disallow_transfer_to_peers), e instrucciones de todo el sistema (global_instruction). Ver Multi-Agentes.