* Add Kotlin to hero / front page * Add quickstart page for Kotlin * Complete Kotlin quickstart guide and fix hero code sample (#2) * Replace GitHub repo links with language icons in header (#3) * Fix header icon FOUC and homepage font weight regression (#4) * Testing staging pipeline * Revert test edit (for staging pipeline) * Update language icon tooltips to indicate GitHub destination (#5) * Add link to ADK Kotlin release notes (#7) * Initial commit of ADK Kotlin API reference docs (#6) * Add script to generate ADK Kotlin API reference docs (#8) * Update links and link checker ignore list (temporarily) (#9) * Add ADK Kotlin for Android getting started guide to Advanced setup page (#10) * Add advanced setup page with steps to "Use ADK Kotlin in Android projects" * Update temp link checker rules * Add placeholder folder for adk-samples (#13) * adding linter/compilation checks for kotlin snippets (#12) * adding linter/compilation checks for kotlin snippets * Add Kotlin validation scripts * Initial commit of Kotlin sample agents for adk-samples (#15) * Adding kotlin snippet for llm agents (#16) * Adding kotlin snippets to Events (#17) * Pull changes to docs/events/index.md from glaforge-kotlin-snippets * fixing kotlin event timestamp and longRunningToolIds * Fix language tags (#19) * Fix language tags * Update * Fix wrapping * Fix wrapping (again) * Fix wrapping/format * Fix language tag on integration page * Enable check_paths in PyMdown Snippets Extension to make the build fail if a snippet can't be found (#20) * Update mkdocs config (#21) * Fix broken links, update URLs to adk.dev, and improve (temp) lychee config (#22) * Add Kotlin/maven badge to README (#23) * Adding Kotlin snippets for artifacts (#18) * Pull Kotlin snippets for artifacts from glaforge-kotlin-snippets * Add comprehensive Kotlin snippets for artifacts * Refactor artifacts documentation to use external Kotlin snippets * Update Kotlin model to gemini-flash-latest * Fix GCS initialization in Kotlin artifact snippet * afixi failing test with capital-agent added to files_to_check * Fix snippet label syntax for MkDocs build * Configure proper Gradle project for Kotlin snippets and fix dependencies * Add KSP support and generated sources to Kotlin snippets build * fixing capital_agent turnComplete * Fix syntax error in build.gradle.kts by removing invalid placeholders (#25) * Adding Kotlin snippets to google-gemini.md (#27) Pulling kotlin changes to google-gemini.md from glaforge-kotlin-snippets * Add a warning about not adding an api key to production code. (#28) * Add a warning about not adding an api key to production code. * Update note --------- Co-authored-by: Kristopher Overholt <koverholt@google.com> * Add ADK Demo App sample showcasing Gemini-powered agents (#29) This sample demonstrates how to use the Google ADK (Agent Development Kit) in an Android application to create a chat interface powered by a Gemini-based "Fun Facts" agent. The implementation features: * Integration with the Kotlin ADK core and processor libraries. * A `FunFactsAgent` defined using `LlmAgent` and the Gemini model. * A `ChatViewModel` utilizing `InMemoryRunner` for asynchronous message streaming. * A modern UI built with Jetpack Compose and Material 3. * Build configuration logic for secure API key management via environment variables or `local.properties`. * Update Kotlin docs and samples to align with adk-kotlin API changes (#30) Rename GeminiModel to Gemini, @AdkTool/@AdkParam to @Tool/@Param, adkTools() to generatedTools(), replace DebugRunner with InMemoryRunner, fix AgentLoader import path, use SingleAgentLoader, bump Kotlin to 2.3.21 and KSP to 2.3.7, and update Android minSdk from 24 to 26. * adding kotlin info to READMEs (#14) * Reorganize Android sample agent and add READMEs (#31) * Move Android sample agent * Update repo README, add Android README, update sample agent README * Minor edit to language support tags (#32) * Remove blog post link (#33) Will re-add after it's published * Remove examples link (#34) * Adding Kotlin snippets for Sessions docs (#26) * initial kotlins snippets additions to sessions docs * Updating memory docs with kotlin snippets * Adding kotlin snippets to session state docs. * update model to gemini-flash-latest * sessions examples clean-up * fixing sessions snippet markers * adding kotlin session snippets to files to test * adding callback to memory_example * Fixing capital agent snippet (#35) Fixing file name Updating adkTool > Tool Updating GeminiModel > Gemini * Adding kotlin snippets for tools docs (#36) * adding function tool kotlin snippets * adding function_tools snippets to files to test * Adding kotlin snippets to observability docs (#37) * initial kotlin observability updates * adding observability snippets to file check (#38) * Adding Kotlin snippets to Callbacks docs (#39) * kotlin callbacks snippets * adding callbacks snippets to file check * Align Kotlin and KSP versions with published 0.1.0 artifacts (#40) * switch CLI entry points from InMemoryRunner to ReplRunner (#41) * Switch CLI entry points from InMemoryRunner to ReplRunner * Fix wording * Update API reference docs for Kotlin, 2026-05-18 (#42) * Remove ADK on Android note until published (#43) * Update Kotlin code samples (#44) * Rename GeminiModel to Gemini in Kotlin snippets and docs * Remove broken SessionKey call and use sessionId directly in AgentTool snippet * Rewrite Go hero snippet to use llmagent API * Use isFinalResponse with safe access in CapitalAgent snippet * Use Role.USER constant instead of raw string in SetupExample * Use full semver v0.1.0 in Kotlin language support tags * Remove Android setup steps, moving to new property (#45) * Tutorial Kotlin agent (#46) * Adding multi-tool-agent snippet and updating tutorial * Fixing Go language order on tutorial page * adding multi tool agent example to files to test * Inline Kotlin get-started code sample * Kotlin Multi agents snippets (#47) * Multi-agent kotlin snippets * Fixing docs tags in multiagent example * Fix Kotlin language support tags, code samples, and google-gemini.md cleanup (#48) * Add Kotlin v0.1.0 to language support tags across docs * Fix MultiToolAgent.kt model string and argument style * Update MultiAgentExample.kt to use gemini-flash-latest model string * Fix google-gemini.md: add Kotlin sample, remove unsupported Java tabs * Remove explicit apiKey from CallbackBasic.kt for consistency * Standardize Gemini() constructor to use named args in all snippets * Remove adk-samples directory (moved to google/adk-samples#1969) * Remove adk-samples directory (moved to google/adk-samples#1969) (#49) * Update API reference docs for ADK Kotlin 0.1.0 (#50) * Remove adk-samples directory (moved to google/adk-samples#1969) * Update API reference docs for ADK Kotlin 0.1.0 * Remove kotlin lycheeignore config (#51) * Remove adk-samples directory (moved to google/adk-samples#1969) * Remove Kotlin .lycheeignore config links --------- Co-authored-by: Toni Klopfenstein <2359976+ToniCorinne@users.noreply.github.com> Co-authored-by: Jolanda Verhoef <JolandaVerhoef@users.noreply.github.com>
9.1 KiB
Callbacks: Observe, Customize, and Control Agent Behavior
Callbacks are a cornerstone feature of ADK, providing a powerful mechanism to hook into an agent's execution process. They allow you to observe, customize, and even control the agent's behavior at specific, predefined points without modifying the core ADK framework code.
What are they? In essence, callbacks are standard functions that you define. You then associate these functions with an agent when you create it. The ADK framework automatically calls your functions at key stages, letting you observe or intervene. Think of it like checkpoints during the agent's process:
- Before the agent starts its main work on a request, and after it finishes: When you ask an agent to do something (e.g., answer a question), it runs its internal logic to figure out the response.
- The
Before Agentcallback executes right before this main work begins for that specific request. - The
After Agentcallback executes right after the agent has finished all its steps for that request and has prepared the final result, but just before the result is returned. - This "main work" encompasses the agent's entire process for handling that single request. This might involve deciding to call an LLM, actually calling the LLM, deciding to use a tool, using the tool, processing the results, and finally putting together the answer. These callbacks essentially wrap the whole sequence from receiving the input to producing the final output for that one interaction.
- The
- Before sending a request to, or after receiving a response from, the Large Language Model (LLM): These callbacks (
Before Model,After Model) allow you to inspect or modify the data going to and coming from the LLM specifically. - Before executing a tool (like a Python function or another agent) or after it finishes: Similarly,
Before ToolandAfter Toolcallbacks give you control points specifically around the execution of tools invoked by the agent.
Why use them? Callbacks unlock significant flexibility and enable advanced agent capabilities:
- Observe & Debug: Log detailed information at critical steps for monitoring and troubleshooting.
- Customize & Control: Modify data flowing through the agent (like LLM requests or tool results) or even bypass certain steps entirely based on your logic.
- Implement Guardrails: Enforce safety rules, validate inputs/outputs, or prevent disallowed operations.
- Manage State: Read or dynamically update the agent's session state during execution.
- Integrate & Enhance: Trigger external actions (API calls, notifications) or add features like caching.
!!! tip When implementing security guardrails and policies, use ADK Plugins for better modularity and flexibility than Callbacks. For more details, see Callbacks and Plugins for Security Guardrails.
How are they added:
??? "Code" === "Python"
```python
--8<-- "examples/python/snippets/callbacks/callback_basic.py:callback_basic"
```
=== "Typescript"
```typescript
--8<-- "examples/typescript/snippets/callbacks/callback_basic.ts:callback_basic"
```
=== "Go"
```go
--8<-- "examples/go/snippets/callbacks/main.go:imports"
--8<-- "examples/go/snippets/callbacks/main.go:callback_basic"
```
=== "Java"
```java
--8<-- "examples/java/snippets/src/main/java/callbacks/AgentWithBeforeModelCallback.java:init"
```
=== "Kotlin"
```kotlin
--8<-- "examples/kotlin/snippets/callbacks/CallbackBasic.kt:callback_basic"
```
The Callback Mechanism: Interception and Control
When the ADK framework encounters a point where a callback can run (e.g., just before calling the LLM), it checks if you provided a corresponding callback function for that agent. If you did, the framework executes your function.
Context is Key: Your callback function isn't called in isolation. The framework provides special context objects (CallbackContext or ToolContext) as arguments. These objects contain vital information about the current state of the agent's execution, including the invocation details, session state, and potentially references to services like artifacts or memory. You use these context objects to understand the situation and interact with the framework. (See the dedicated "Context Objects" section for full details).
Controlling the Flow (The Core Mechanism): The most powerful aspect of callbacks lies in how their return value influences the agent's subsequent actions. This is how you intercept and control the execution flow:
-
return None(Allow Default Behavior):- The specific return type can vary depending on the language. In Java, the equivalent return type is
Optional.empty(). In Kotlin, it isCallbackChoice.Continue(value)(forbefore_*callbacks) or returning the original object (forafter_*callbacks). Refer to the API documentation for language specific guidance. - This is the standard way to signal that your callback has finished its work (e.g., logging, inspection, minor modifications to input arguments) and that the ADK agent should proceed with its normal operation.
- For
before_*callbacks (before_agent,before_model,before_tool), returningCallbackChoice.Continue(...)means the next step in the sequence (running the agent logic, calling the LLM, executing the tool) will occur. - For
after_*callbacks (after_agent,after_model,after_tool), returning the result just produced (the agent's output, the LLM's response, the tool's result) as is means the framework will continue processing.
- The specific return type can vary depending on the language. In Java, the equivalent return type is
-
return <Specific Object>(Override Default Behavior):- Returning a specific type of object (instead of signaling "Continue") is how you override the ADK agent's default behavior. In Kotlin, this is achieved by returning
CallbackChoice.Break(value)(forbefore_*callbacks) or a replacement object (forafter_*callbacks). The framework will use the object you return and skip the step that would normally follow or replace the result that was just generated. before_agent_callback→CallbackChoice.Break(Content): Skips the agent's main execution logic. The returnedContentobject is immediately treated as the agent's final output for this turn. Useful for handling simple requests directly or enforcing access control.before_model_callback→CallbackChoice.Break(LlmResponse): Skips the call to the external Large Language Model. The returnedLlmResponseobject is processed as if it were the actual response from the LLM. Ideal for implementing input guardrails, prompt validation, or serving cached responses.before_tool_callback→CallbackChoice.Break(Map<String, Any>): Skips the execution of the actual tool function (or sub-agent). The returnedMapis used as the result of the tool call, which is then typically passed back to the LLM. Perfect for validating tool arguments, applying policy restrictions, or returning mocked/cached tool results.after_agent_callback→Content: Replaces theContentthat the agent's run logic just produced.after_model_callback→LlmResponse: Replaces theLlmResponsereceived from the LLM. Useful for sanitizing outputs, adding standard disclaimers, or modifying the LLM's response structure.after_tool_callback→Map<String, Any>: Replaces theMapresult returned by the tool. Allows for post-processing or standardization of tool outputs before they are sent back to the LLM.
- Returning a specific type of object (instead of signaling "Continue") is how you override the ADK agent's default behavior. In Kotlin, this is achieved by returning
Conceptual Code Example (Guardrail):
This example demonstrates the common pattern for a guardrail using before_model_callback.
??? "Code" === "Python"
```python
--8<-- "examples/python/snippets/callbacks/before_model_callback.py"
```
=== "Typescript"
```typescript
--8<-- "examples/typescript/snippets/callbacks/before_model_callback.ts"
```
=== "Go"
```go
--8<-- "examples/go/snippets/callbacks/main.go:imports"
--8<-- "examples/go/snippets/callbacks/main.go:guardrail_init"
```
=== "Java"
```java
--8<-- "examples/java/snippets/src/main/java/callbacks/BeforeModelGuardrailExample.java:init"
```
=== "Kotlin"
```kotlin
--8<-- "examples/kotlin/snippets/callbacks/BeforeModelCallback.kt:before_model_callback"
```
By understanding this mechanism of returning None versus returning specific objects, you can precisely control the agent's execution path, making callbacks an essential tool for building sophisticated and reliable agents with ADK.
