You've already forked adk-python
mirror of
https://github.com/encounter/adk-python.git
synced 2026-03-30 10:57:20 -07:00
docs: Update ADK agent builder instructions for model callback signatures
PiperOrigin-RevId: 824690587
This commit is contained in:
committed by
Copybara-Service
parent
1ca82068fd
commit
19f52467db
@@ -16,7 +16,7 @@ When users ask informational questions like "find me examples", "show me samples
|
||||
|
||||
**NON-NEGOTIABLE**: `root_agent.yaml` MUST always declare `agent_class: LlmAgent`.
|
||||
**NEVER** set `root_agent.yaml` to any workflow agent type (SequentialAgent,
|
||||
ParallelAgent, LoopAgent). All workflow coordination must stay in sub-agents, not the root file.
|
||||
ParallelAgent, LoopAgent.) All workflow coordination must stay in sub-agents, not the root file.
|
||||
**MODEL CONTRACT**: Every `LlmAgent` (root and sub-agents) must explicitly set
|
||||
`model` to the confirmed model choice (use `{default_model}` only when the user
|
||||
asks for the default). Never omit this field or rely on a global default.
|
||||
@@ -347,6 +347,12 @@ uncertainty about architecture, or you otherwise need authoritative guidance.
|
||||
8. **Follow current ADK patterns**: Always search for and reference the latest examples from contributing/samples
|
||||
9. **Gemini API Usage**: If generating Python code that interacts with Gemini models, use `import google.genai as genai`, not `google.generativeai`.
|
||||
|
||||
### ✅ Fully Qualified Paths Required
|
||||
- Every tool or callback reference in YAML must be a fully qualified dotted path that starts with the project folder name. Use `{project_folder_name}.callbacks.privacy_callbacks.censor_content`, **never** `callbacks.privacy_callbacks.censor_content`.
|
||||
- Only reference packages that actually exist. Before you emit a dotted path, confirm the directory contains an `__init__.py` so Python can import it. Create `__init__.py` files for each subdirectory that should be importable (for example `callbacks/` or `tools/`). The project root itself does not need an `__init__.py`.
|
||||
- When you generate Python modules with `write_files`, make sure the tool adds these `__init__.py` markers for the package directories (skip the project root) so future imports succeed.
|
||||
- If the user already has bare paths like `callbacks.foo`, explain why they must be rewritten with the project prefix and add the missing `__init__.py` files when you generate the Python modules.
|
||||
|
||||
### 🚨 CRITICAL: Callback Correct Signatures
|
||||
ADK supports different callback types with DIFFERENT signatures. Use FUNCTION-based callbacks (never classes):
|
||||
|
||||
@@ -378,17 +384,49 @@ from google.adk.models.llm_request import LlmRequest
|
||||
from google.adk.models.llm_response import LlmResponse
|
||||
from google.adk.agents.callback_context import CallbackContext
|
||||
|
||||
def log_model_request(callback_context: CallbackContext, request: LlmRequest) -> Optional[LlmResponse]:
|
||||
def log_model_request(
|
||||
*, callback_context: CallbackContext, llm_request: LlmRequest
|
||||
) -> Optional[LlmResponse]:
|
||||
"""Before model callback to log requests."""
|
||||
print(f"Model request: {{request.contents}}")
|
||||
print(f"Model request: {{llm_request.contents}}")
|
||||
return None # Return None to proceed with original request
|
||||
|
||||
def modify_model_response(callback_context: CallbackContext, response: LlmResponse) -> Optional[LlmResponse]:
|
||||
from google.adk.events.event import Event
|
||||
|
||||
def modify_model_response(
|
||||
*,
|
||||
callback_context: CallbackContext,
|
||||
llm_response: LlmResponse,
|
||||
model_response_event: Optional[Event] = None,
|
||||
) -> Optional[LlmResponse]:
|
||||
"""After model callback to modify response."""
|
||||
# Modify response if needed
|
||||
return response # Return modified response or None for original
|
||||
_ = callback_context # Access context if you need state or metadata
|
||||
_ = model_response_event # Available for tracing and event metadata
|
||||
if (
|
||||
not llm_response
|
||||
or not llm_response.content
|
||||
or not llm_response.content.parts
|
||||
):
|
||||
return llm_response
|
||||
|
||||
updated_parts = []
|
||||
for part in llm_response.content.parts:
|
||||
text = getattr(part, "text", None)
|
||||
if text:
|
||||
updated_parts.append(
|
||||
types.Part(text=text.replace("dolphins", "[CENSORED]"))
|
||||
)
|
||||
else:
|
||||
updated_parts.append(part)
|
||||
|
||||
llm_response.content = types.Content(
|
||||
parts=updated_parts, role=llm_response.content.role
|
||||
)
|
||||
return llm_response
|
||||
```
|
||||
|
||||
**Callback content handling**: `LlmResponse` exposes a single `content` field (a `types.Content`). ADK already extracts the first candidate for you and does not expose `llm_response.candidates`. When filtering or rewriting output, check `llm_response.content` and mutate its `parts`. Preserve non-text parts and reassign a new `types.Content` rather than mutating undefined attributes.
|
||||
|
||||
## 3. Tool Callbacks (before_tool_callbacks / after_tool_callbacks)
|
||||
|
||||
**✅ CORRECT Tool Callback:**
|
||||
@@ -412,11 +450,13 @@ def log_tool_result(tool: BaseTool, tool_args: Dict[str, Any], tool_context: Too
|
||||
|
||||
## Callback Signature Summary:
|
||||
- **Agent Callbacks**: `(callback_context: CallbackContext) -> Optional[types.Content]`
|
||||
- **Before Model**: `(callback_context: CallbackContext, request: LlmRequest) -> Optional[LlmResponse]`
|
||||
- **After Model**: `(callback_context: CallbackContext, response: LlmResponse) -> Optional[LlmResponse]`
|
||||
- **Before Model**: `(*, callback_context: CallbackContext, llm_request: LlmRequest) -> Optional[LlmResponse]`
|
||||
- **After Model**: `(*, callback_context: CallbackContext, llm_response: LlmResponse, model_response_event: Optional[Event] = None) -> Optional[LlmResponse]`
|
||||
- **Before Tool**: `(tool: BaseTool, tool_args: Dict[str, Any], tool_context: ToolContext) -> Optional[Dict]`
|
||||
- **After Tool**: `(tool: BaseTool, tool_args: Dict[str, Any], tool_context: ToolContext, result: Dict) -> Optional[Dict]`
|
||||
|
||||
**Name Matching Matters**: ADK passes callback arguments by keyword. Always name parameters exactly `callback_context`, `llm_request`, `llm_response`, and `model_response_event` (when used) so they bind correctly. Returning `None` keeps the original value; otherwise return the modified `LlmResponse`.
|
||||
|
||||
## Important ADK Requirements
|
||||
|
||||
**File Naming & Structure:**
|
||||
|
||||
Reference in New Issue
Block a user