Files
Amaad Martin c75e61f5f3 docs: normalize Typescript to TypeScript across the site (#2079)
The site rendered one language under two names. Tab labels were split
137 `TypeScript` / 53 `Typescript`, with three pages carrying both
spellings at once (custom-agents.md 7/7, patterns.md 1/7,
function-tools.md 4/1), and the language-support badges were split 67/18
the same way. Because pymdownx.tabbed slugifies tab labels to lowercase,
both variants rendered and linked fine, so no link check or build warning
ever flagged it -- it was visible only to readers, as two names for one
SDK.

Every user-visible occurrence is normalized to `TypeScript`, plus the two
inconsistencies that turned up while doing it. 80 changed lines, accounted
for exactly:

  53  tab label       === "Typescript"            -> === "TypeScript"
  18  badge span      lst-typescript">Typescript  -> TypeScript
   3  prose mention   cloud-run.md, mcp-tools.md, workflows/patterns.md
   2  api-reference/index.md card heading and link text
   1  badge div attr  title="...Python and Typescript."
   1  mkdocs.yml nav  Typescript ADK              -> TypeScript ADK
   1  code fence      ```javascript -> ```typescript on a .ts include
   1  artifacts/index.md closing summary sentence
  ---
  80

The first six rows are pure casing: 78 lines that differ from their
originals by nothing but `Typescript` -> `TypeScript`. The last two are
not, and are the reason this is not a `sed`:

llm-agents.md:872 fenced `--8<-- ".../capital_agent.ts"` as ```javascript.
It was the only javascript-fenced `.ts` include in docs/ (the other 189
TypeScript fences are correct), and it cost that one snippet its
TypeScript highlighting.

artifacts/index.md:1084 closed the page by naming languages and got the
list wrong. It described reaching the artifact methods "using Python's
context objects or directly interacting with the `BaseArtifactService` in
Java" -- a two-language enumeration at the end of a page that carries
Python, TypeScript, Go, Java and Kotlin tabs (11/10/10/10/11), and one
that contradicts :556, which correctly names four of them. The
enumeration is dropped rather than extended: the sentence now describes
the two ways to reach these methods -- through the context object, or
through `BaseArtifactService` -- which is what the page actually teaches
and does not rot when a sixth language is added.

docs/api-reference/index.md is included even though the rest of
docs/api-reference/ is generated output that must not be touched. That
tree holds 3,140 generated HTML files and exactly one hand-authored page:
this one. It is Markdown, it is the only api-reference entry mkdocs.yml
lists as `.md` rather than `index.html` (:272, :441), it uses Material
`grid cards` and `:fontawesome-*:` shortcodes, and it carries a
`CONTRIBUTORS:` note citing issues #1716 and #1717. Its TypeScript card
already said "TypeScript" twice in its body text while its heading and
link text said "Typescript"; those two are now consistent with the body.
No generated file is modified.

Not in this change: the broken `SseConnectionParams` sample in
mcp-tools.md (docs-ts/p6c-mcp-ts-sample) and the `@google/adk` example
version bumps (docs-ts/p6b-example-versions). Only the casing of the
prose line above that sample is touched here.

Verified: `mkdocs build` exits 0 with an empty warning set on both main
and this branch, and the two warning sets are identical. A rendered
before/after diff of the whole site shows every `__tabbed_*` id, every
tab radio id and every heading anchor unchanged. Zero `=== "Typescript"`
and zero `lst-typescript">Typescript` remain anywhere in the repo.

Co-authored-by: Amaad Martin <amaadmartin@google.com>
2026-08-05 15:58:49 -07:00

9.1 KiB

Callbacks: Observe, Customize, and Control Agent Behavior

Supported in ADKPython v0.1.0TypeScript v0.2.0Go v0.1.0Java v0.1.0Kotlin v0.1.0

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 Agent callback executes right before this main work begins for that specific request.
    • The After Agent callback 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.
  • 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 Tool and After Tool callbacks give you control points specifically around the execution of tools invoked by the agent.

intro_components.png

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:

  1. 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 is CallbackChoice.Continue(value) (for before_* callbacks) or returning the original object (for after_* 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), returning CallbackChoice.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.
  2. 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) (for before_* callbacks) or a replacement object (for after_* 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 returned Content object 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 returned LlmResponse object 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 returned Map is 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 the Content that the agent's run logic just produced.
    • after_model_callback → LlmResponse: Replaces the LlmResponse received from the LLM. Useful for sanitizing outputs, adding standard disclaimers, or modifying the LLM's response structure.
    • after_tool_callback → Map<String, Any>: Replaces the Map result returned by the tool. Allows for post-processing or standardization of tool outputs before they are sent back to the LLM.

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.