Callbacks: Observar, Personalizar y Controlar el Comportamiento del Agente¶
Los callbacks son una característica fundamental de ADK, proporcionando un mecanismo poderoso para conectarse al proceso de ejecución de un agente. Te permiten observar, personalizar e incluso controlar el comportamiento del agente en puntos específicos predefinidos sin modificar el código del framework principal de ADK.
¿Qué son? En esencia, los callbacks son funciones estándar que tú defines. Luego asocias estas funciones con un agente cuando lo creas. El framework de ADK llama automáticamente a tus funciones en etapas clave, permitiéndote observar o intervenir. Piénsalo como puntos de control durante el proceso del agente:
- Antes de que el agente comience su trabajo principal en una solicitud, y después de que termine: Cuando le pides a un agente que haga algo (por ejemplo, responder una pregunta), ejecuta su lógica interna para determinar la respuesta.
- El callback
Before Agentse ejecuta justo antes de que comience este trabajo principal para esa solicitud específica. - El callback
After Agentse ejecuta justo después de que el agente haya terminado todos sus pasos para esa solicitud y haya preparado el resultado final, pero justo antes de que se devuelva el resultado. - Este "trabajo principal" abarca el proceso completo del agente para manejar esa única solicitud. Esto puede involucrar decidir llamar a un LLM, realmente llamar al LLM, decidir usar una herramienta, usar la herramienta, procesar los resultados y finalmente armar la respuesta. Estos callbacks esencialmente envuelven toda la secuencia desde recibir la entrada hasta producir la salida final para esa interacción.
- Antes de enviar una solicitud a, o después de recibir una respuesta del, Modelo de Lenguaje Grande (LLM): Estos callbacks (
Before Model,After Model) te permiten inspeccionar o modificar los datos que van hacia y vienen del LLM específicamente. - Antes de ejecutar una herramienta (como una función de Python u otro agente) o después de que termine: De manera similar, los callbacks
Before ToolyAfter Toolte dan puntos de control específicamente alrededor de la ejecución de herramientas invocadas por el agente.

¿Por qué usarlos? Los callbacks desbloquean flexibilidad significativa y habilitan capacidades avanzadas del agente:
- Observar y Depurar: Registrar información detallada en pasos críticos para monitoreo y resolución de problemas.
- Personalizar y Controlar: Modificar datos que fluyen a través del agente (como solicitudes LLM o resultados de herramientas) o incluso omitir ciertos pasos completamente basándose en tu lógica.
- Implementar Barreras de Seguridad: Aplicar reglas de seguridad, validar entradas/salidas o prevenir operaciones no permitidas.
- Gestionar Estado: Leer o actualizar dinámicamente el estado de sesión del agente durante la ejecución.
- Integrar y Mejorar: Activar acciones externas (llamadas API, notificaciones) o agregar características como caché.
Tip
Al implementar barreras de seguridad y políticas, usa Plugins de ADK para mejor modularidad y flexibilidad que los Callbacks. Para más detalles, consulta Callbacks and Plugins for Security Guardrails.
Cómo se agregan:
Code
from google.adk.agents import LlmAgent
from google.adk.agents.callback_context import CallbackContext
from google.adk.models import LlmResponse, LlmRequest
from typing import Optional
# --- Define your callback function ---
def my_before_model_logic(
callback_context: CallbackContext, llm_request: LlmRequest
) -> Optional[LlmResponse]:
print(f"Callback running before model call for agent: {callback_context.agent_name}")
# ... your custom logic here ...
return None # Allow the model call to proceed
# --- Register it during Agent creation ---
my_agent = LlmAgent(
name="MyCallbackAgent",
model="gemini-2.0-flash", # Or your desired model
instruction="Be helpful.",
# Other agent parameters...
before_model_callback=my_before_model_logic # Pass the function here
)
import {
LlmAgent,
InMemoryRunner,
CallbackContext,
LlmRequest,
LlmResponse,
Event,
isFinalResponse,
} from "@google/adk";
import { createUserContent } from "@google/genai";
import type { Content } from "@google/genai";
const MODEL_NAME = "gemini-2.5-flash";
const APP_NAME = "basic_callback_app";
const USER_ID = "test_user_basic";
const SESSION_ID = "session_basic_001";
// --- Define your callback function ---
function myBeforeModelLogic({
context,
request,
}: {
context: CallbackContext;
request: LlmRequest;
}): LlmResponse | undefined {
console.log(
`Callback running before model call for agent: ${context.agentName}`
);
// ... your custom logic here ...
return undefined; // Allow the model call to proceed
}
// --- Register it during Agent creation ---
const myAgent = new LlmAgent({
name: "MyCallbackAgent",
model: MODEL_NAME,
instruction: "Be helpful.",
beforeModelCallback: myBeforeModelLogic,
});
package main
import (
"context"
"fmt"
"log"
"strings"
"google.golang.org/adk/agent"
"google.golang.org/adk/agent/llmagent"
"google.golang.org/adk/model"
"google.golang.org/adk/model/gemini"
"google.golang.org/adk/runner"
"google.golang.org/adk/session"
"google.golang.org/genai"
)
// onBeforeModel is a callback function that gets triggered before an LLM call.
func onBeforeModel(ctx agent.CallbackContext, req *model.LLMRequest) (*model.LLMResponse, error) {
log.Println("--- onBeforeModel Callback Triggered ---")
log.Printf("Model Request to be sent: %v\n", req)
// Returning nil allows the default LLM call to proceed.
return nil, nil
}
func runBasicExample() {
const (
appName = "CallbackBasicApp"
userID = "test_user_123"
)
ctx := context.Background()
geminiModel, err := gemini.NewModel(ctx, modelName, &genai.ClientConfig{})
if err != nil {
log.Fatalf("Failed to create model: %v", err)
}
// Register the callback function in the agent configuration.
agentCfg := llmagent.Config{
Name: "SimpleAgent",
Model: geminiModel,
BeforeModelCallbacks: []llmagent.BeforeModelCallback{onBeforeModel},
}
simpleAgent, err := llmagent.New(agentCfg)
if err != nil {
log.Fatalf("Failed to create agent: %v", err)
}
sessionService := session.InMemoryService()
r, err := runner.New(runner.Config{
AppName: appName,
Agent: simpleAgent,
SessionService: sessionService,
})
if err != nil {
log.Fatalf("Failed to create runner: %v", err)
}
import com.google.adk.agents.CallbackContext;
import com.google.adk.agents.Callbacks;
import com.google.adk.agents.LlmAgent;
import com.google.adk.models.LlmRequest;
import java.util.Optional;
public class AgentWithBeforeModelCallback {
public static void main(String[] args) {
// --- Define your callback logic ---
Callbacks.BeforeModelCallbackSync myBeforeModelLogic =
(CallbackContext callbackContext, LlmRequest llmRequest) -> {
System.out.println(
"Callback running before model call for agent: " + callbackContext.agentName());
// ... your custom logic here ...
// Return Optional.empty() to allow the model call to proceed,
// similar to returning None in the Python example.
// If you wanted to return a response and skip the model call,
// you would return Optional.of(yourLlmResponse).
return Optional.empty();
};
// --- Register it during Agent creation ---
LlmAgent myAgent =
LlmAgent.builder()
.name("MyCallbackAgent")
.model("gemini-2.0-flash") // Or your desired model
.instruction("Be helpful.")
// Other agent parameters...
.beforeModelCallbackSync(myBeforeModelLogic) // Pass the callback implementation here
.build();
}
}
El Mecanismo de Callback: Intercepción y Control¶
Cuando el framework de ADK encuentra un punto donde un callback puede ejecutarse (por ejemplo, justo antes de llamar al LLM), verifica si proporcionaste una función de callback correspondiente para ese agente. Si lo hiciste, el framework ejecuta tu función.
El Contexto es Clave: Tu función de callback no se llama de forma aislada. El framework proporciona objetos de contexto especiales (CallbackContext o ToolContext) como argumentos. Estos objetos contienen información vital sobre el estado actual de la ejecución del agente, incluyendo los detalles de invocación, el estado de sesión y potencialmente referencias a servicios como artifacts o memoria. Usas estos objetos de contexto para entender la situación e interactuar con el framework. (Consulta la sección dedicada "Objetos de Contexto" para detalles completos).
Controlando el Flujo (El Mecanismo Principal): El aspecto más poderoso de los callbacks radica en cómo su valor de retorno influye en las acciones subsecuentes del agente. Así es como interceptas y controlas el flujo de ejecución:
-
return None(Permitir Comportamiento Predeterminado):- El tipo de retorno específico puede variar dependiendo del lenguaje. En Java, el tipo de retorno equivalente es
Optional.empty(). Consulta la documentación de la API para orientación específica del lenguaje. - Esta es la forma estándar de señalar que tu callback ha terminado su trabajo (por ejemplo, registro, inspección, modificaciones menores a argumentos de entrada mutables como
llm_request) y que el agente de ADK debería proceder con su operación normal. - Para callbacks
before_*(before_agent,before_model,before_tool), retornarNonesignifica que el siguiente paso en la secuencia (ejecutar la lógica del agente, llamar al LLM, ejecutar la herramienta) ocurrirá. - Para callbacks
after_*(after_agent,after_model,after_tool), retornarNonesignifica que el resultado recién producido por el paso precedente (la salida del agente, la respuesta del LLM, el resultado de la herramienta) se usará tal cual.
- El tipo de retorno específico puede variar dependiendo del lenguaje. En Java, el tipo de retorno equivalente es
-
return <Objeto Específico>(Anular Comportamiento Predeterminado):- Retornar un tipo específico de objeto (en lugar de
None) es cómo anulas el comportamiento predeterminado del agente de ADK. El framework usará el objeto que retornas y omitirá el paso que normalmente seguiría o reemplazará el resultado que acaba de ser generado. before_agent_callback→types.Content: Omite la lógica de ejecución principal del agente (_run_async_impl/_run_live_impl). El objetoContentretornado se trata inmediatamente como la salida final del agente para este turno. Útil para manejar solicitudes simples directamente o aplicar control de acceso.before_model_callback→LlmResponse: Omite la llamada al Modelo de Lenguaje Grande externo. El objetoLlmResponseretornado se procesa como si fuera la respuesta real del LLM. Ideal para implementar barreras de seguridad de entrada, validación de prompts o servir respuestas en caché.before_tool_callback→dictoMap: Omite la ejecución de la función de herramienta real (o sub-agente). Eldictretornado se usa como el resultado de la llamada a la herramienta, que luego típicamente se pasa de vuelta al LLM. Perfecto para validar argumentos de herramientas, aplicar restricciones de políticas o retornar resultados de herramientas simulados/en caché.after_agent_callback→types.Content: Reemplaza elContentque la lógica de ejecución del agente acaba de producir.after_model_callback→LlmResponse: Reemplaza elLlmResponserecibido del LLM. Útil para sanitizar salidas, agregar avisos estándar o modificar la estructura de respuesta del LLM.after_tool_callback→dictoMap: Reemplaza el resultadodictretornado por la herramienta. Permite post-procesamiento o estandarización de salidas de herramientas antes de que se envíen de vuelta al LLM.
- Retornar un tipo específico de objeto (en lugar de
Ejemplo de Código Conceptual (Barrera de Seguridad):
Este ejemplo demuestra el patrón común para una barrera de seguridad usando before_model_callback.
Code
# 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.
from google.adk.agents import LlmAgent
from google.adk.agents.callback_context import CallbackContext
from google.adk.models import LlmResponse, LlmRequest
from google.adk.runners import Runner
from typing import Optional
from google.genai import types
from google.adk.sessions import InMemorySessionService
GEMINI_2_FLASH="gemini-2.0-flash"
# --- Define the Callback Function ---
def simple_before_model_modifier(
callback_context: CallbackContext, llm_request: LlmRequest
) -> Optional[LlmResponse]:
"""Inspects/modifies the LLM request or skips the call."""
agent_name = callback_context.agent_name
print(f"[Callback] Before model call for agent: {agent_name}")
# Inspect the last user message in the request contents
last_user_message = ""
if llm_request.contents and llm_request.contents[-1].role == 'user':
if llm_request.contents[-1].parts:
last_user_message = llm_request.contents[-1].parts[0].text
print(f"[Callback] Inspecting last user message: '{last_user_message}'")
# --- Modification Example ---
# Add a prefix to the system instruction
original_instruction = llm_request.config.system_instruction or types.Content(role="system", parts=[])
prefix = "[Modified by Callback] "
# Ensure system_instruction is Content and parts list exists
if not isinstance(original_instruction, types.Content):
# Handle case where it might be a string (though config expects Content)
original_instruction = types.Content(role="system", parts=[types.Part(text=str(original_instruction))])
if not original_instruction.parts:
original_instruction.parts.append(types.Part(text="")) # Add an empty part if none exist
# Modify the text of the first part
modified_text = prefix + (original_instruction.parts[0].text or "")
original_instruction.parts[0].text = modified_text
llm_request.config.system_instruction = original_instruction
print(f"[Callback] Modified system instruction to: '{modified_text}'")
# --- Skip Example ---
# Check if the last user message contains "BLOCK"
if "BLOCK" in last_user_message.upper():
print("[Callback] 'BLOCK' keyword found. Skipping LLM call.")
# Return an LlmResponse to skip the actual LLM call
return LlmResponse(
content=types.Content(
role="model",
parts=[types.Part(text="LLM call was blocked by before_model_callback.")],
)
)
else:
print("[Callback] Proceeding with LLM call.")
# Return None to allow the (modified) request to go to the LLM
return None
# Create LlmAgent and Assign Callback
my_llm_agent = LlmAgent(
name="ModelCallbackAgent",
model=GEMINI_2_FLASH,
instruction="You are a helpful assistant.", # Base instruction
description="An LLM agent demonstrating before_model_callback",
before_model_callback=simple_before_model_modifier # Assign the function here
)
APP_NAME = "guardrail_app"
USER_ID = "user_1"
SESSION_ID = "session_001"
# Session and Runner
async def setup_session_and_runner():
session_service = InMemorySessionService()
session = await session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
runner = Runner(agent=my_llm_agent, app_name=APP_NAME, session_service=session_service)
return session, runner
# Agent Interaction
async def call_agent_async(query):
content = types.Content(role='user', parts=[types.Part(text=query)])
session, runner = await setup_session_and_runner()
events = runner.run_async(user_id=USER_ID, session_id=SESSION_ID, new_message=content)
async for event in events:
if event.is_final_response():
final_response = event.content.parts[0].text
print("Agent Response: ", final_response)
# 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.
await call_agent_async("write a joke on BLOCK")
/**
* 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,
InMemoryRunner,
CallbackContext,
isFinalResponse,
} from "@google/adk";
import { createUserContent } from "@google/genai";
const MODEL_NAME = "gemini-2.5-flash";
const APP_NAME = "before_model_callback_app";
const USER_ID = "test_user_before_model";
const SESSION_ID_BLOCK = "session_block_model_call";
const SESSION_ID_NORMAL = "session_normal_model_call";
// --- Define the Callback Function ---
function simpleBeforeModelModifier({
context,
request,
}: {
context: CallbackContext;
request: any;
}): any | undefined {
console.log(`[Callback] Before model call for agent: ${context.agentName}`);
// Inspect the last user message in the request contents
const lastUserMessage = request.contents?.at(-1)?.parts?.[0]?.text ?? "";
console.log(`[Callback] Inspecting last user message: '${lastUserMessage}'`);
// --- Modification Example ---
// Add a prefix to the system instruction.
// We create a deep copy to avoid modifying the original agent's config object.
const modifiedConfig = JSON.parse(JSON.stringify(request.config));
const originalInstructionText =
modifiedConfig.systemInstruction?.parts?.[0]?.text ?? "";
const prefix = "[Modified by Callback] ";
modifiedConfig.systemInstruction = {
role: "system",
parts: [{ text: prefix + originalInstructionText }],
};
request.config = modifiedConfig; // Assign the modified config back to the request
console.log(
`[Callback] Modified system instruction to: '${modifiedConfig.systemInstruction.parts[0].text}'`
);
// --- Skip Example ---
// Check if the last user message contains "BLOCK"
if (lastUserMessage.toUpperCase().includes("BLOCK")) {
console.log("[Callback] 'BLOCK' keyword found. Skipping LLM call.");
// Return an LlmResponse to skip the actual LLM call
return {
content: {
role: "model",
parts: [
{ text: "LLM call was blocked by the before_model_callback." },
],
},
};
}
console.log("[Callback] Proceeding with LLM call.");
// Return undefined to allow the (modified) request to go to the LLM
return undefined;
}
// --- Create LlmAgent and Assign Callback ---
const myLlmAgent = new LlmAgent({
name: "ModelCallbackAgent",
model: MODEL_NAME,
instruction: "You are a helpful assistant.", // Base instruction
description: "An LLM agent demonstrating before_model_callback",
beforeModelCallback: simpleBeforeModelModifier, // Assign the function here
});
// --- Agent Interaction Logic ---
async function callAgentAndPrint(
runner: InMemoryRunner,
query: string,
sessionId: string
) {
console.log(`\n>>> Calling Agent with query: "${query}"`);
let finalResponseContent = "No final response received.";
const events = runner.runAsync({ userId: USER_ID, sessionId, newMessage: createUserContent(query) });
for await (const event of events) {
if (isFinalResponse(event) && event.content?.parts?.length) {
finalResponseContent = event.content.parts
.map((part: { text?: string }) => part.text ?? "")
.join("");
}
}
console.log("<<< Agent Response: ", finalResponseContent);
}
// --- Run Interactions ---
async function main() {
const runner = new InMemoryRunner({ agent: myLlmAgent, appName: APP_NAME });
// Scenario 1: The callback will find "BLOCK" and skip the model call
await runner.sessionService.createSession({
appName: APP_NAME,
userId: USER_ID,
sessionId: SESSION_ID_BLOCK,
});
await callAgentAndPrint(
runner,
"write a joke about BLOCK",
SESSION_ID_BLOCK
);
// Scenario 2: The callback will modify the instruction and proceed
await runner.sessionService.createSession({
appName: APP_NAME,
userId: USER_ID,
sessionId: SESSION_ID_NORMAL,
});
await callAgentAndPrint(runner, "write a short poem", SESSION_ID_NORMAL);
}
main();
package main
import (
"context"
"fmt"
"log"
"strings"
"google.golang.org/adk/agent"
"google.golang.org/adk/agent/llmagent"
"google.golang.org/adk/model"
"google.golang.org/adk/model/gemini"
"google.golang.org/adk/runner"
"google.golang.org/adk/session"
"google.golang.org/genai"
)
// onBeforeModelGuardrail is a callback that inspects the LLM request.
// If it contains a forbidden topic, it blocks the request and returns a
// predefined response. Otherwise, it allows the request to proceed.
func onBeforeModelGuardrail(ctx agent.CallbackContext, req *model.LLMRequest) (*model.LLMResponse, error) {
log.Println("--- onBeforeModelGuardrail Callback Triggered ---")
// Inspect the request content for forbidden topics.
for _, content := range req.Contents {
for _, part := range content.Parts {
if strings.Contains(part.Text, "finance") {
log.Println("Forbidden topic 'finance' detected. Blocking LLM call.")
// By returning a non-nil response, we override the default behavior
// and prevent the actual LLM call.
return &model.LLMResponse{
Content: &genai.Content{
Parts: []*genai.Part{{Text: "I'm sorry, but I cannot discuss financial topics."}},
Role: "model",
},
}, nil
}
}
}
log.Println("No forbidden topics found. Allowing LLM call to proceed.")
// Returning nil allows the default LLM call to proceed.
return nil, nil
}
func runGuardrailExample() {
const (
appName = "GuardrailApp"
userID = "test_user_456"
)
ctx := context.Background()
geminiModel, err := gemini.NewModel(ctx, modelName, &genai.ClientConfig{})
if err != nil {
log.Fatalf("Failed to create model: %v", err)
}
agentCfg := llmagent.Config{
Name: "ChatAgent",
Model: geminiModel,
BeforeModelCallbacks: []llmagent.BeforeModelCallback{onBeforeModelGuardrail},
}
chatAgent, err := llmagent.New(agentCfg)
if err != nil {
log.Fatalf("Failed to create agent: %v", err)
}
sessionService := session.InMemoryService()
r, err := runner.New(runner.Config{
AppName: appName,
Agent: chatAgent,
SessionService: sessionService,
})
if err != nil {
log.Fatalf("Failed to create runner: %v", err)
}
import com.google.adk.agents.CallbackContext;
import com.google.adk.agents.LlmAgent;
import com.google.adk.events.Event;
import com.google.adk.models.LlmRequest;
import com.google.adk.models.LlmResponse;
import com.google.adk.runner.InMemoryRunner;
import com.google.adk.sessions.Session;
import com.google.genai.types.Content;
import com.google.genai.types.GenerateContentConfig;
import com.google.genai.types.Part;
import io.reactivex.rxjava3.core.Flowable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class BeforeModelGuardrailExample {
private static final String MODEL_ID = "gemini-2.0-flash";
private static final String APP_NAME = "guardrail_app";
private static final String USER_ID = "user_1";
public static void main(String[] args) {
BeforeModelGuardrailExample example = new BeforeModelGuardrailExample();
example.defineAgentAndRun("Tell me about quantum computing. This is a test.");
}
// --- Define your callback logic ---
// Looks for the word "BLOCK" in the user prompt and blocks the call to LLM if found.
// Otherwise the LLM call proceeds as usual.
public Optional<LlmResponse> simpleBeforeModelModifier(
CallbackContext callbackContext, LlmRequest llmRequest) {
System.out.println("[Callback] Before model call for agent: " + callbackContext.agentName());
// Inspect the last user message in the request contents
String lastUserMessageText = "";
List<Content> requestContents = llmRequest.contents();
if (requestContents != null && !requestContents.isEmpty()) {
Content lastContent = requestContents.get(requestContents.size() - 1);
if (lastContent.role().isPresent() && "user".equals(lastContent.role().get())) {
lastUserMessageText =
lastContent.parts().orElse(List.of()).stream()
.flatMap(part -> part.text().stream())
.collect(Collectors.joining(" ")); // Concatenate text from all parts
}
}
System.out.println("[Callback] Inspecting last user message: '" + lastUserMessageText + "'");
String prefix = "[Modified by Callback] ";
GenerateContentConfig currentConfig =
llmRequest.config().orElse(GenerateContentConfig.builder().build());
Optional<Content> optOriginalSystemInstruction = currentConfig.systemInstruction();
Content conceptualModifiedSystemInstruction;
if (optOriginalSystemInstruction.isPresent()) {
Content originalSystemInstruction = optOriginalSystemInstruction.get();
List<Part> originalParts =
new ArrayList<>(originalSystemInstruction.parts().orElse(List.of()));
String originalText = "";
if (!originalParts.isEmpty()) {
Part firstPart = originalParts.get(0);
if (firstPart.text().isPresent()) {
originalText = firstPart.text().get();
}
originalParts.set(0, Part.fromText(prefix + originalText));
} else {
originalParts.add(Part.fromText(prefix));
}
conceptualModifiedSystemInstruction =
originalSystemInstruction.toBuilder().parts(originalParts).build();
} else {
conceptualModifiedSystemInstruction =
Content.builder()
.role("system")
.parts(List.of(Part.fromText(prefix)))
.build();
}
// This demonstrates building a new LlmRequest with the modified config.
llmRequest =
llmRequest.toBuilder()
.config(
currentConfig.toBuilder()
.systemInstruction(conceptualModifiedSystemInstruction)
.build())
.build();
System.out.println(
"[Callback] Conceptually modified system instruction is: '"
+ llmRequest.config().get().systemInstruction().get().parts().get().get(0).text().get());
// --- Skip Example ---
// Check if the last user message contains "BLOCK"
if (lastUserMessageText.toUpperCase().contains("BLOCK")) {
System.out.println("[Callback] 'BLOCK' keyword found. Skipping LLM call.");
LlmResponse skipResponse =
LlmResponse.builder()
.content(
Content.builder()
.role("model")
.parts(
List.of(
Part.builder()
.text("LLM call was blocked by before_model_callback.")
.build()))
.build())
.build();
return Optional.of(skipResponse);
}
System.out.println("[Callback] Proceeding with LLM call.");
// Return Optional.empty() to allow the (modified) request to go to the LLM
return Optional.empty();
}
public void defineAgentAndRun(String prompt) {
// --- Create LlmAgent and Assign Callback ---
LlmAgent myLlmAgent =
LlmAgent.builder()
.name("ModelCallbackAgent")
.model(MODEL_ID)
.instruction("You are a helpful assistant.") // Base instruction
.description("An LLM agent demonstrating before_model_callback")
.beforeModelCallbackSync(this::simpleBeforeModelModifier) // Assign the callback here
.build();
// Session and Runner
InMemoryRunner runner = new InMemoryRunner(myLlmAgent, APP_NAME);
// InMemoryRunner automatically creates a session service. Create a session using the service
Session session = runner.sessionService().createSession(APP_NAME, USER_ID).blockingGet();
Content userMessage =
Content.fromParts(Part.fromText(prompt));
// Run the agent
Flowable<Event> eventStream = runner.runAsync(USER_ID, session.id(), userMessage);
// Stream event response
eventStream.blockingForEach(
event -> {
if (event.finalResponse()) {
System.out.println(event.stringifyContent());
}
});
}
}
Al entender este mecanismo de retornar None versus retornar objetos específicos, puedes controlar con precisión la ruta de ejecución del agente, haciendo de los callbacks una herramienta esencial para construir agentes sofisticados y confiables con ADK.