# Practical and Useful Patterns with ADK
## ๐จ Battle-Tested Callback Patterns for Production ADK Agents
You know how callbacks work โ but how do you actually use them in production? Master ADK's **Callback Design Patterns** for logging, caching, security, and more! ๐ช
## ๐ Title
Callback Patterns (Design Patterns and Best Practices)
## ๐ URL
## ๐งฉ Overview
ADK callbacks have well-established patterns that recur in production systems: logging, caching, state management, security guardrails, request/response modification, conditional skipping, and artifact handling.
The documentation also defines best practices โ single responsibility, performance awareness, idempotency, and error handling โ to keep callbacks robust.
A critical guideline: **for cross-agent security guardrails, prefer Plugins over Callbacks**.
## ๐ How to Use
**Pattern 1: Logging & Monitoring**
`logging_before_tool(ctx, tool, args)` logs the `ctx.invocation_id`, ` and `args` via ` then returns `None` to observe without altering the flow. `logging_after_model(ctx, response)` logs the length of ` with the invocation ID, and likewise returns `None`.
**Pattern 2: Caching Strategy**
`cache_before_tool(ctx, tool, args)` builds a cache key from ` and `hash(str(args))`, then checks `ctx.state.get(cache_key)`. On a cache hit, it returns the cached value to skip tool execution. On a miss, it returns `None` to proceed. `cache_after_tool(ctx, tool, args, tool_ctx, result)` stores the result in `ctx.state[cache_key]` using the same key, then returns `None` to continue without modification.
**Pattern 3: State Management**
`state_aware_callback(ctx, req)` retrieves the user tier from `ctx.state.get("user:tier", "free")`, and if the tier is `"premium"`, appends additional instructions to `req.config.system_instruction`. It returns `None` to continue the normal flow.
## ๐ Practical Usage
**Multi-layer defense pattern for production:**
As a security guardrail (Plugins are preferred for cross-agent use), `security_before_model(ctx, req)` extracts user input from `req.contents[-1].parts[0].text`, runs `detect_pii()` to check for personal information, and if found, calls `audit_log()` and returns an `LlmResponse` with a rejection message to skip the LLM call. It also runs `detect_injection()` for prompt injection detection, blocking with a similar `LlmResponse` if detected. If neither check triggers, it returns `None` to continue. For tool argument sanitization, `sanitize_before_tool(ctx, tool, args)` checks if ` is `"database_query"` and whether `args.get("query", "")` contains `"DROP"`, returning an error dictionary to block dangerous queries. For artifact persistence, `save_artifact_after_agent(ctx)` calls `generate_report(ctx)` and saves the result via `"execution_report.json", report)`, returning `None`.
## ๐ก Use Cases
- ๐ **Structured logging**: Emit structured logs with invocation IDs at every execution point
- ๐พ **API cost reduction**: Cache tool results with before/after patterns to avoid redundant calls
- ๐ **Layered security**: Place PII detection, injection prevention, and SQL sanitization at different layers
- ๐ฆ **Artifact management**: Auto-save execution results and reports as artifacts
- ๐๏ธ **Dynamic behavior**: Adjust instructions dynamically based on user tier or session state
## โ ๏ธ Caveats
- **Single responsibility**: Give each callback one purpose โ don't mix logging with validation
- **Performance**: Callbacks execute synchronously; avoid blocking I/O or heavy computation
- **Idempotency**: Design callbacks with external side effects to be safe when retried
- **Error handling**: Always wrap in try-except to prevent callback errors from crashing the process
- **Prefer Plugins**: For cross-agent security policies, consider **Plugins** over per-agent callbacks
## โจ Closing
Knowing callback patterns dramatically levels up your ADK skills. Combine logging, caching, security, and state management patterns to build robust, cost-efficient agents. And for cross-cutting security concerns, don't forget Plugins!
#
ADK# #
AIAgent#