roo agent

This commit is contained in:
Alfredo Oliviero 2025-09-04 12:00:51 +02:00
parent 4274c0cec2
commit 9fd350a580
8 changed files with 587 additions and 815 deletions

View File

@ -1,6 +1,6 @@
<workflow_instructions>
<mode_overview>
This mode guides you through building, testing, and deploying AI agents using the Google Agent Development Kit (ADK). It covers everything from single-agent creation to complex multi-agent systems.
This mode guides you through building, testing, deploying, and maintaining AI agents using the Google Agent Development Kit (ADK). It covers everything from single-agent creation to complex multi-agent systems, deployment, and ongoing observability.
</mode_overview>
<initialization_steps>
@ -8,19 +8,19 @@
<action>Understand the User's Request</action>
<details>
Parse the user's input to identify:
- The primary goal of the agent.
- The required capabilities and tools.
- The primary goal of the agent (e.g., RAG, data analysis, workflow automation).
- The required capabilities, tools, and data sources.
- Whether it's a new agent or a modification of an existing one.
- Any constraints (e.g., specific models, data sources).
- Any constraints (e.g., specific models, deployment targets, security requirements).
</details>
</step>
<step number="2">
<action>Gather Necessary Context</action>
<tools>
<tool>list_files - Understand the existing project structure, especially the `python/agents` directory.</tool>
<tool>read_file - Examine existing `agent.py`, `prompts.py`, and `tools.py` files for relevant patterns.</tool>
<tool>codebase_search - Find existing implementations of similar agents or tools.</tool>
<tool>list_files - Understand the existing project structure, especially `src/` and sub-agent directories.</tool>
<tool>read_file - Examine existing `agent.py`, `tools.py`, and `pyproject.toml` files for relevant patterns and dependencies.</tool>
<tool>codebase_search - Find existing implementations of similar agents or tools to reuse code and patterns.</tool>
</tools>
</step>
</initialization_steps>
@ -29,45 +29,53 @@
<phase name="architecture_and_design">
<description>Analyze the user's request and design a comprehensive agent architecture. This phase concludes with a design proposal that must be approved by the user.</description>
<steps>
<step>**Analyze Requirements**: Deconstruct the user's goal into a set of functional requirements, paying close attention to whether the task requires flexible reasoning or a deterministic, predictable workflow.</step>
<step>**Choose Agent Types**: Based on the requirements, select the appropriate agent types. Use **LLM Agents** for tasks requiring reasoning and dynamic tool use. Use **Workflow Agents** (`SequentialAgent`, `ParallelAgent`, `LoopAgent`) to orchestrate tasks in a predictable sequence. Use **Custom Agents** for highly specialized logic.</step>
<step>**Propose Architecture**: Design the overall system, deciding between a single-agent or multi-agent architecture. For multi-agent systems, define the role of each agent and how they will be composed (e.g., a root `LlmAgent` as a router delegating to specialized sub-agents).</step>
<step>**Plan Tools**: Identify the necessary tools for the LLM Agents, specifying whether to use pre-built ADK tools or create custom ones.</step>
<step>**Plan State Management**: Determine what information needs to be remembered across conversational turns. Plan how to use the session `state` to store this data (e.g., user preferences, conversation history). Decide if tools will need `ToolContext` to access state or if agents will use `output_key` to save results.</step>
<step>**Outline Control Flow**: Describe how the agent(s) will interact, including delegation logic for multi-agent systems, any HITL checkpoints for user approval, or critique loops for self-correction.</step>
<step>**Seek Approval**: Present the proposed architecture to the user for feedback and approval before proceeding to implementation. Use `ask_followup_question` to get explicit approval.</step>
<step>**Analyze Requirements**: Deconstruct the user's goal into functional requirements. Determine if the task requires flexible reasoning (`LlmAgent`) or a deterministic, predictable workflow (`SequentialAgent`, `LoopAgent`).</step>
<step>**Choose Agent Architecture**: Decide between a single-agent or multi-agent architecture. For multi-agent systems, define the role of each agent (e.g., a root `LlmAgent` as a router delegating to specialized sub-agents). Consider if remote agents are needed and if the A2A protocol is appropriate.</step>
<step>**Plan Tools**: Identify necessary tools. Decide between built-in tools (`google_search`, `VertexAiSearchTool`), custom `FunctionTool`s, `OpenAPIToolset` for existing REST APIs, or `MCPToolset` for database access.</step>
<step>**Plan State and Memory**: Determine what information needs to be remembered. Plan session `state` for short-term memory (e.g., user preferences) and `VertexAiMemoryBankService` for long-term, cross-session memory. Decide if tools will need `ToolContext` to access state or if agents will use `output_key`.</step>
<step>**Plan Data Handling**: For workflows involving files or binary data, plan to use the `ArtifactService` to pass data between tools, avoiding filesystem dependencies.</step>
<step>**Outline Control Flow**: Describe how agents will interact, including delegation logic, Human-in-the-Loop (HITL) checkpoints using `LongRunningFunctionTool`, or critique loops for self-correction.</step>
<step>**Seek Approval**: Present the proposed architecture to the user for feedback and approval before proceeding to implementation. Use `ask_followup_question`.</step>
</steps>
</phase>
<phase name="implementation">
<description>Write the Python code for the agent and its components.</description>
<description>Write the Python code for the agent and its components following ADK best practices.</description>
<steps>
<step>Create the main agent directory (e.g., `python/agents/my-new-agent/`).</step>
<step>Set up the `pyproject.toml` file with the necessary dependencies (`google-adk`, etc.).</step>
<step>Implement custom tools in the `tools/` directory.</step>
<step>Write the instructional prompts in `prompts.py`.</step>
<step>Create the main agent directory (e.g., `src/dave_agent/my_new_agent/`).</step>
<step>Update `pyproject.toml` with any new dependencies (e.g., `google-cloud-aiplatform` for Vertex AI services).</step>
<step>Implement custom tools in a `tools.py` file, ensuring they are asynchronous for I/O operations and have clear docstrings.</step>
<step>Implement the agent logic in `agent.py`, initializing the agent with its model, instructions, and tools.</step>
<step>For multi-agent systems, implement each sub-agent in its own directory under `sub_agents/`.</step>
<step>For multi-agent systems, implement each sub-agent in its own directory (e.g., `sub_agents/specialist_agent/`).</step>
</steps>
</phase>
<phase name="validation_and_evaluation">
<description>Verify the agent's functionality through testing and systematic evaluation.</description>
<description>Verify the agent's functionality through local testing and systematic evaluation.</description>
<steps>
<step>**Unit Testing**: Create unit tests for all custom tools in the `tests/` directory to ensure they function correctly in isolation.</step>
<step>**Evaluation Suite**: Create a comprehensive evaluation suite in the `eval/` directory. This involves creating a `*.test.json` file containing representative test cases, each with a golden `final_response` and expected `intermediate_data` (tool calls).</step>
<step>**Local Testing**: Run the agent locally using `adk run .` for CLI interaction or `adk web .` to use the interactive Trace View for in-depth debugging.</step>
<step>**Performance Measurement**: Use the `adk eval` command to run the evaluation suite. Analyze the `tool_trajectory_avg_score` and `response_match_score` to quantitatively measure performance and identify areas for improvement.</step>
<step>**Local Testing**: Run the agent locally using `adk web .` to use the interactive Trace View for in-depth debugging of agent reasoning, tool calls, and state changes.</step>
<step>**Evaluation Suite**: Use the `adk web` UI to create an evaluation set (`*.evalset.json`). Capture successful and failed interactions as test cases, refining the expected tool trajectories and final responses.</step>
<step>**Performance Measurement**: Use the `adk eval` command to run the evaluation suite against the agent. Analyze the `tool_trajectory_avg_score` and `response_match_score` to quantitatively measure performance and identify regressions after making changes.</step>
</steps>
</phase>
<phase name="security_and_deployment">
<description>Implement security best practices and deploy the agent.</description>
<description>Implement security best practices and deploy the agent to a target environment.</description>
<steps>
<step>**Implement Authentication**: For any tools that access protected resources, implement the appropriate authentication mechanism. Prefer **User-Auth** (e.g., OAuth2) for tools handling user-specific data.</step>
<step>**Add Guardrails**: Implement in-tool guardrails to validate inputs and prevent misuse. Leverage Gemini's built-in safety features.</step>
<step>**Choose Deployment Target**: Use the scripts in the `deployment/` directory to deploy the agent. Common targets include **Vertex AI Agent Engine** for managed execution or **Cloud Run** for web applications.</step>
<step>**Configure Network Controls**: For production deployments handling sensitive data, configure VPC Service Controls to create a security perimeter and prevent data exfiltration.</step>
<step>**Implement Authentication**: For tools accessing protected resources, configure the appropriate authentication mechanism (e.g., OAuth2 for user-data, Service Accounts for GCP services). See `tools/authentication` docs.</step>
<step>**Add Guardrails**: Implement `before_tool_callback` or in-tool validation to prevent misuse. Use `before_model_callback` to screen prompts. Leverage Gemini's built-in safety features.</step>
<step>**Choose Deployment Target**: Decide on the best deployment target: **Vertex AI Agent Engine** for a fully managed serverless experience, **Cloud Run** for containerized web applications, or **GKE** for Kubernetes-based deployments.</step>
<step>**Deploy**: Use the `adk deploy` CLI commands (e.g., `adk deploy agent_engine ...`) to package and deploy your agent.</step>
<step>**Configure Network Controls**: For production, consider VPC Service Controls to create a security perimeter and prevent data exfiltration.</step>
</steps>
</phase>
<phase name="observability_and_maintenance">
<description>Monitor the deployed agent's performance and maintain it over time.</description>
<steps>
<step>**Enable Tracing**: For deployed agents, enable tracing to Google Cloud Trace to monitor performance and debug issues in production.</step>
<step>**Integrate Observability Tools**: Integrate with tools like AgentOps or Arize AX for advanced monitoring, session replays, and cost tracking.</step>
<step>**Update and Retest**: As requirements change or new ADK versions are released, update the agent code and re-run the evaluation suite to ensure continued high performance and prevent regressions.</step>
</steps>
</phase>
</main_workflow>
@ -75,7 +83,8 @@
<completion_criteria>
<criterion>The agent successfully accomplishes the user's primary goal.</criterion>
<criterion>The code follows ADK best practices and project structure conventions.</criterion>
<criterion>The agent is well-documented with a clear `README.md`.</criterion>
<criterion>The agent has a corresponding set of tests and evaluations.</criterion>
<criterion>The agent is well-documented with a clear `README.md` explaining its purpose and how to run it.</criterion>
<criterion>The agent has a comprehensive evaluation suite to validate its behavior.</criterion>
<criterion>The agent is deployed to the specified target environment and is functioning correctly.</criterion>
</completion_criteria>
</workflow_instructions>

View File

@ -2,221 +2,139 @@
<general_principles>
<principle priority="high">
<name>Separation of Concerns</name>
<description>Keep your agent's logic, prompts, and tools in separate files. This makes the code easier to read, maintain, and test.</description>
<description>Keep your agent's logic, prompts, and tools in separate files/modules. This makes the code easier to read, maintain, and test.</description>
<rationale>A clear project structure is essential for managing complexity, especially in multi-agent systems.</rationale>
<example>
<scenario>Defining a new agent.</scenario>
<good>Create `agent.py` for the agent definition, `prompts.py` for the instructional prompts, and `tools.py` for any custom tools.</good>
<good>Create `agent.py` for the agent definition, and a `tools.py` for any custom tools. For multi-agent systems, place each agent in its own subdirectory.</good>
<bad>Putting all the code in a single `main.py` file.</bad>
</example>
</principle>
<principle priority="medium">
<name>Clear and Concise Prompts</name>
<description>Write detailed and unambiguous instructional prompts. The quality of the prompt directly impacts the agent's performance.</description>
<rationale>A well-crafted prompt provides the agent with the necessary context and guidance to perform its tasks effectively.</rationale>
<principle priority="high">
<name>Clear and Specific Instructions and Docstrings</name>
<description>Write detailed and unambiguous `instructions` for agents and `docstrings` for tools. The quality of this text directly impacts the agent's performance and ability to reason.</description>
<rationale>The LLM relies entirely on this text to understand its role, when to use a tool, what arguments to provide, and how to interpret the result.</rationale>
<example>
<scenario>Creating a RAG agent.</scenario>
<good>Provide specific instructions on when to use the retrieval tool, how to handle uncertainty, and how to format citations.</good>
<bad>A generic prompt like "You are a helpful assistant."</bad>
<scenario>Creating a tool to find a user's email.</scenario>
<good>Docstring: "Finds the email address for a user given their unique user ID. Returns a dictionary with an 'email' key on success or an 'error' key on failure."</good>
<bad>Docstring: "Gets user data." (This is too vague).</bad>
</example>
</principle>
<principle priority="high">
<name>Design for Human Oversight (Human-in-the-Loop)</name>
<description>For complex, multi-step, or costly operations, design the agent to seek user approval at critical checkpoints. This ensures alignment and prevents the agent from proceeding with an incorrect plan.</description>
<rationale>HITL is essential for building trust and ensuring that the agent's actions are aligned with the user's intent, especially before executing long-running or resource-intensive tasks.</rationale>
<name>Choose the Right Agent for the Job</name>
<description>Use `LlmAgent` for reasoning and dynamic tool use. Use `WorkflowAgent`s (`SequentialAgent`, `LoopAgent`) for orchestrating predictable, deterministic processes. Use a custom `BaseAgent` only for highly specialized logic.</description>
<rationale>Mixing agent types allows you to build robust and efficient systems. Using an LLM for a simple, deterministic workflow is inefficient and unpredictable.</rationale>
<example>
<scenario>An agent that generates and executes a research plan.</scenario>
<good>The agent first generates the plan, presents it to the user, and waits for explicit approval (e.g., "Looks good, proceed") before starting the research pipeline.</good>
<bad>The agent immediately executes a plan without user validation, potentially wasting time and resources on a flawed strategy.</bad>
<scenario>Building a research agent.</scenario>
<good>Use a `SequentialAgent` to define the high-level workflow (e.g., plan -> research -> write). Each step is implemented as a specialized `LlmAgent`.</good>
<bad>Trying to implement the entire multi-step workflow inside a single, complex prompt for one `LlmAgent`.</bad>
</example>
</principle>
<principle priority="medium">
<name>Iterative Refinement with Critique Loops</name>
<description>Implement self-correction loops where one agent evaluates the output of another (a "critic"). If the output is subpar, the critic provides feedback, and the original agent refines its work.</description>
<rationale>This pattern significantly improves the quality and reliability of the agent's final output by allowing it to iteratively refine its work based on critical feedback.</rationale>
<example>
<scenario>A research agent writing a report.</scenario>
<good>A `researcher` agent gathers information. A `critic` agent evaluates the research. If the critic finds gaps, it generates follow-up questions, and the `researcher` runs again. This continues until the critic grades the research as "pass".</good>
<bad>A single agent performs research and writes a report in one step, with no mechanism for quality control.</bad>
</example>
</principle>
<principle priority="medium">
<name>Use Asynchronous Tools for I/O Operations</name>
<name>Use Asynchronous Tools for I/O</name>
<description>Define tools that perform network requests or other I/O-bound operations as asynchronous functions (`async def`).</description>
<rationale>The ADK is built on asyncio. Using async tools prevents the agent from blocking while waiting for I/O, improving overall performance and responsiveness, especially in a server environment.</rationale>
<rationale>The ADK is built on asyncio. Using async tools prevents the agent from blocking while waiting for I/O, improving performance, and enabling parallel tool execution.</rationale>
<example>
<scenario>A tool that fetches data from an external API.</scenario>
<good>`async def get_user_data(user_id: int) -> dict: ...`</good>
<bad>`def get_user_data(user_id: int) -> dict: ...` (This will block the event loop).</bad>
</example>
</principle>
<principle priority="high">
<name>Choose the Right Agent for the Job</name>
<description>Use the appropriate agent type for each component of your system. Use `LlmAgent` for tasks requiring reasoning, flexibility, and dynamic tool use. Use `WorkflowAgent`s (`SequentialAgent`, `LoopAgent`) for orchestrating predictable, deterministic processes. Use a custom `BaseAgent` only for highly specialized logic not covered by the other types.</description>
<rationale>Mixing agent types allows you to build robust and efficient systems. Using an LLM for a simple, deterministic workflow is inefficient, while using a rigid workflow agent for a dynamic, reasoning-based task is ineffective. A clear separation of concerns between reasoning (LLM) and orchestration (Workflow) leads to more maintainable and predictable systems.</rationale>
<example>
<scenario>Building a research agent.</scenario>
<good>Use a `SequentialAgent` to define the high-level workflow (e.g., plan -> research -> write). The "plan," "research," and "write" steps are implemented as `LlmAgent`s, which can reason and use tools to accomplish their specific tasks.</good>
<bad>Trying to implement the entire multi-step workflow inside a single, complex prompt for one `LlmAgent`.</bad>
</example>
</principle>
</general_principles>
<code_conventions>
<convention category="state_management">
<rule>Use the `state` dictionary to pass data between agents.</rule>
<description>The `state` dictionary is the primary mechanism for passing data and maintaining context across turns. Use it to store user preferences, task progress, and accumulated information.</description>
<examples>
<good>A tool reads `tool_context.state.get('user_preference_unit', 'Celsius')` to personalize its output. An agent saves its summary to `state` using `output_key="summary"`.</good>
<bad>Relying on global variables or trying to parse information from the raw conversation history to maintain context.</bad>
</examples>
</convention>
<convention category="state_scoping">
<rule>Use state prefixes to define data scope.</rule>
<description>
Use prefixes to control the scope and persistence of your state variables.
- **No Prefix** (e.g., `'task_status'`): Scoped to the current session.
- **`user:`** (e.g., `'user:theme'`): Shared across all sessions for a specific user.
- **`app:`** (e.g., `'app:api_endpoint'`): Shared globally across the entire application.
- **`temp:`** (e.g., `'temp:validation_id'`): Scoped only to the current invocation (turn) and is not persisted.
</description>
<examples>
<good>`tool_context.state['user:preferred_language'] = 'fr'`</good>
<bad>`tool_context.state['preferred_language'] = 'fr'` (This is session-scoped, not user-scoped, and may not persist as intended across different sessions for the same user).</bad>
</examples>
</convention>
<convention category="structured_outputs">
<rule>Use Pydantic models as `output_schema` for reliable agent-to-agent communication.</rule>
<examples>
<good>A critic agent's output is constrained to a Pydantic model with `grade: Literal["pass", "fail"]` and `comment: str`. This ensures its output can be reliably parsed by the control-flow logic.</good>
<bad>The critic agent returns a natural language string, which is difficult to parse reliably and can break the application flow.</bad>
</examples>
</convention>
<convention category="project_structure">
<rule>Follow the standard ADK project structure.</rule>
<template>
python/agents/
└── my-agent/
├── my_agent/
│ ├── __init__.py
│ ├── agent.py
│ ├── prompts.py
│ └── tools/
│ ├── __init__.py
│ └── my_tool.py
├── tests/
├── eval/
├── deployment/
├── .env.example
└── pyproject.toml
</template>
</convention>
<convention category="dependency_management">
<rule>Use Poetry to manage dependencies.</rule>
<examples>
<good>Define all dependencies in `pyproject.toml` and use `poetry install` to create a virtual environment.</good>
<bad>Using `pip install` directly, which can lead to dependency conflicts.</bad>
</examples>
</convention>
</code_conventions>
<common_pitfalls>
<pitfall>
<description>Overly complex root agents in multi-agent systems.</description>
<why_problematic>The root agent should act as a router, delegating tasks to specialized sub-agents. Its LLM uses the `description` of each sub-agent to make routing decisions. Adding too much direct tool-use logic to the root agent makes the system harder to debug and less modular.</why_problematic>
<correct_approach>Keep the root agent's instructions focused on task decomposition and delegation. Ensure each sub-agent has a clear, descriptive `description` so the root agent can route effectively. The root agent should only have tools for its own core, non-delegatable tasks.</correct_approach>
</pitfall>
<pitfall>
<description>Not providing adequate descriptions for tools.</description>
<why_problematic>The agent relies on the tool's description to understand its purpose and when to use it. A vague or inaccurate description will lead to incorrect tool usage.</why_problematic>
<correct_approach>Write clear, concise, and accurate descriptions for all custom tools.</correct_approach>
</pitfall>
</common_pitfalls>
<quality_checklist>
<category name="before_starting">
<item>Have I clearly defined the agent's purpose and capabilities?</item>
<item>Have I reviewed existing sample agents for relevant patterns?</item>
</category>
<category name="during_implementation">
<item>Am I following the standard project structure?</item>
<item>Are my prompts clear, concise, and unambiguous?</item>
<item>Are my tool descriptions accurate and informative?</item>
</category>
<category name="before_completion">
<item>Have I written unit tests for my custom tools?</item>
<item>Have I created an evaluation suite to measure the agent's performance?</item>
<item>Have I documented the agent's setup and usage in a `README.md` file?</item>
</category>
</quality_checklist>
<evaluation_guidelines>
<state_management>
<guideline priority="high">
<name>Create Comprehensive Eval Sets</name>
<description>Create a dedicated `eval/` directory with a `*.test.json` file. This file should contain a representative set of `eval_cases`, each with a golden `final_response` and the expected `intermediate_data` (tool calls and outputs).</description>
<rationale>A high-quality evaluation set is the foundation for reliable performance measurement. It allows you to test your agent against a consistent baseline and track regressions or improvements.</rationale>
<name>Use State for Data Flow</name>
<description>The `state` dictionary, accessible via `ToolContext` or `CallbackContext`, is the primary mechanism for passing data and maintaining context across turns. Use it to store user preferences, task progress, and intermediate results.</description>
<correct_approach>A tool reads `tool_context.state.get('user_preference_unit', 'Celsius')`. An agent saves its summary to `state` using `output_key="summary"`.</correct_approach>
<incorrect_approach>Relying on global variables or trying to parse information from the raw conversation history.</incorrect_approach>
</guideline>
<guideline priority="high">
<name>Use `adk eval` for Performance Measurement</name>
<description>Regularly use the `adk eval` command to measure your agent's performance. Pay close attention to the `tool_trajectory_avg_score` (for tool usage accuracy) and `response_match_score` (for final response quality).</description>
<rationale>Quantitative metrics provide an objective way to assess your agent's performance and guide your development efforts. They help you identify whether changes to prompts, tools, or models are having the desired effect.</rationale>
</guideline>
</evaluation_guidelines>
<debugging_guidelines>
<guideline priority="high">
<name>Use the Interactive Trace View</name>
<description>For local development and debugging, use the `adk web` command to launch the web UI. The "Trace View" provides a step-by-step visualization of the agent's execution, including model inputs/outputs and tool calls.</description>
<rationale>The Trace View is the most effective tool for understanding an agent's reasoning process and identifying the root cause of unexpected behavior without needing to add extensive logging or breakpoints.</rationale>
<name>Use State Prefixes for Scoping</name>
<description>
Use prefixes to control the scope and persistence of state variables.
- **`temp:`**: Scoped only to the current invocation (turn). Ideal for passing data between tools in a single step. Not persisted.
- **No Prefix**: Scoped to the current session. Persists across turns within the same session.
- **`user:`**: Tied to the user ID. Persists across all sessions for that user (requires a persistent `SessionService`).
- **`app:`**: Global to the application. Shared across all users and sessions (requires a persistent `SessionService`).
</description>
<example>`tool_context.state['user:preferred_language'] = 'it'`</example>
</guideline>
<guideline priority="medium">
<name>Enable Cloud Trace for Deployed Agents</name>
<description>For agents deployed on Google Cloud, enable distributed tracing by using the `--trace_to_cloud` flag. This provides a consolidated view of requests in Cloud Trace, which is essential for debugging issues in a production environment.</description>
<rationale>In a distributed system, requests can span multiple services. Cloud Trace helps you visualize the entire request lifecycle, making it easier to pinpoint bottlenecks and errors.</rationale>
<name>Use Artifacts for Binary Data</name>
<description>For files, images, or other non-serializable data, use the `ArtifactService` via `context.save_artifact()` and `context.load_artifact()`. Pass the artifact name between tools using the `temp:` state.</description>
<rationale>Artifacts provide a robust, managed way to handle binary data without relying on a shared filesystem, which is critical for scalable deployments.</rationale>
</guideline>
</debugging_guidelines>
</state_management>
<security_guidelines>
<multi_agent_design>
<guideline priority="high">
<name>Prefer User-Auth for Sensitive Data</name>
<description>When a tool needs to access user-specific data, always use **User-Auth** (e.g., OAuth2). This delegates authentication to the end-user, ensuring that the agent operates with the user's permissions.</description>
<rationale>Using Agent-Auth (a single service account) for user-specific data is a security risk. User-Auth enforces the principle of least privilege, ensuring that the agent can only access data that the user is authorized to see.</rationale>
<name>Keep the Root Agent as a Router</name>
<description>The root agent in a multi-agent system should primarily act as a router or orchestrator. Its main job is to understand the user's intent and delegate the task to the appropriate specialized sub-agent.</description>
<rationale>This makes the system more modular, easier to debug, and scalable. Overloading the root agent with too many tools and complex logic makes it a bottleneck.</rationale>
</guideline>
<guideline priority="high">
<name>Write Clear Sub-Agent Descriptions</name>
<description>The root agent's LLM uses the `description` field of each sub-agent to make its routing decisions. These descriptions must be clear, concise, and accurately reflect the sub-agent's capabilities.</description>
<example>Good: "Handles all database queries related to user profiles." Bad: "Sub-agent 1."</example>
</guideline>
<guideline priority="medium">
<name>Choose Between Agent-as-Tool and Sub-Agent Delegation</name>
<description>
- Use **Sub-Agent Delegation** when you want to transfer control completely to another agent for a turn.
- Use **Agent-as-a-Tool** when you want the calling agent to get a result back from the specialist agent and then continue its own reasoning process.
</description>
<rationale>This choice determines the control flow. Delegation is a hand-off, while Agent-as-a-Tool is a consultation.</rationale>
</guideline>
</multi_agent_design>
<security_and_safety>
<guideline priority="high">
<name>Implement In-Tool Guardrails</name>
<description>Implement validation and sanity checks within your custom tools to prevent misuse. For example, a tool that executes shell commands should have an allowlist of safe commands or blocklist dangerous ones.</description>
<rationale>Do not trust the LLM to always use tools in the intended manner. In-tool guardrails are a critical defense layer against prompt injection and other attacks that could lead to unintended tool execution.</rationale>
<description>Implement validation and sanity checks within your custom tools to prevent misuse. For example, a tool that executes shell commands should have an allowlist of safe commands.</description>
<rationale>Do not trust the LLM to always use tools as intended. In-tool guardrails are a critical defense against prompt injection and unintended tool execution.</rationale>
</guideline>
<guideline priority="high">
<name>Use Callbacks for Pre-Execution Checks</name>
<description>Use `before_tool_callback` to validate tool arguments before execution. Use `before_model_callback` to screen prompts for policy violations.</description>
<rationale>Callbacks provide a centralized way to enforce policies across multiple tools or agents without modifying their core logic.</rationale>
</guideline>
<guideline priority="high">
<name>Prefer User-Auth for Sensitive Data</name>
<description>When a tool needs to access user-specific data, use **User-Auth** (e.g., OAuth2). This delegates authentication to the end-user, ensuring the agent operates with the user's permissions.</description>
<rationale>Using a single service account (Agent-Auth) for user-specific data is a security risk. User-Auth enforces the principle of least privilege.</rationale>
</guideline>
<guideline priority="medium">
<name>Use Sandboxed Code Execution</name>
<description>For any agent that needs to execute code, use a sandboxed environment like the `code_execution` tool. This isolates the code execution from the host system.</description>
<rationale>Executing untrusted, LLM-generated code directly on the host system is a major security vulnerability. A sandbox ensures that the code cannot access sensitive files or perform malicious operations.</rationale>
<description>For any agent that needs to execute code, use a sandboxed environment like the `BuiltInCodeExecutor` or the Vertex AI Code Interpreter Extension.</description>
<rationale>Executing untrusted, LLM-generated code directly on the host system is a major security vulnerability.</rationale>
</guideline>
</security_guidelines>
</security_and_safety>
<integration_guidelines>
<performance>
<guideline priority="medium">
<name>Collaboratively Analyze Third-Party Integrations</name>
<description>Before using wrappers for third-party frameworks like LangChain (`LangchainTool`) or CrewAI (`CrewaiTool`), you must present this option to the user. Collaboratively analyze the pros (e.g., leveraging existing tools, faster prototyping) and cons (e.g., adding external dependencies, potential for instability). Proceed only with explicit user approval.</description>
<rationale>While third-party wrappers can be powerful, they introduce dependencies that may not be desirable in the long term. A joint analysis ensures that the decision to use them is a conscious trade-off, aligning with the project's goals for maintainability and stability.</rationale>
<name>Enable Parallel Tool Execution</name>
<description>Define I/O-bound tools as `async` functions. Instruct the agent's LLM that it can and should call multiple tools in parallel when appropriate to gather information more efficiently.</description>
<rationale>Parallel execution can dramatically reduce latency for queries that require information from multiple independent sources.</rationale>
<example>Prompt Snippet: "When the user asks to compare two stocks, call the `get_stock_price` tool for both symbols in parallel."</example>
</guideline>
</integration_guidelines>
</performance>
<callback_guidelines>
<evaluation_and_debugging>
<guideline priority="high">
<name>Use Callbacks for Guardrails and Validation</name>
<description>Implement `before_model_callback` or `before_tool_callback` to act as gatekeepers. Inspect the incoming request or tool arguments for policy violations (e.g., forbidden topics, dangerous arguments). If a violation is found, return a predefined response to block the operation and prevent execution.</description>
<rationale>Callbacks are the primary mechanism for enforcing security and policy constraints. They allow you to intercept and block potentially harmful or unintended operations before they are executed by the LLM or a tool.</rationale>
<name>Use the Interactive Trace View for Debugging</name>
<description>For local development, use `adk web` to launch the web UI. The "Trace View" provides a step-by-step visualization of the agent's execution, including model inputs/outputs, tool calls, and state changes.</description>
<rationale>The Trace View is the most effective tool for understanding an agent's reasoning process and identifying the root cause of unexpected behavior.</rationale>
</guideline>
<guideline priority="high">
<name>Create Comprehensive Eval Sets</name>
<description>Use the `adk web` UI to capture interactions and build an evaluation set (`*.evalset.json`). Include a variety of test cases covering both success and failure paths, with golden `final_response` and expected `intermediate_data` (tool calls).</description>
<rationale>A high-quality evaluation set is the foundation for reliable performance measurement and preventing regressions.</rationale>
</guideline>
<guideline priority="medium">
<name>Manage State Dynamically</name>
<description>Use callbacks to read from and write to `callback_context.state` or `tool_context.state`. This allows you to pass data between agent steps, make behavior context-aware, and implement caching logic.</description>
<rationale>State management within callbacks is essential for building complex, multi-turn conversational agents that need to remember information and adapt their behavior over time.</rationale>
<name>Use `adk eval` for Performance Measurement</name>
<description>Regularly use the `adk eval` command to measure your agent's performance against your evaluation set. Track the `tool_trajectory_avg_score` and `response_match_score`.</description>
<rationale>Quantitative metrics provide an objective way to assess your agent's performance and guide improvements to prompts, tools, or models.</rationale>
</guideline>
<guideline priority="low">
<name>Keep Callbacks Focused and Performant</name>
<description>Design each callback for a single, well-defined purpose (e.g., logging, validation). Avoid long-running or blocking operations within callbacks, as they execute synchronously and can impact agent performance.</description>
<rationale>Monolithic and slow callbacks make the agent difficult to debug and can introduce significant latency. Keeping them small and fast is crucial for a responsive and maintainable system.</rationale>
</guideline>
</callback_guidelines>
</evaluation_and_debugging>
</best_practices>

View File

@ -1,326 +1,130 @@
<examples>
<example name="single_agent_definition">
<description>A basic single-agent definition with a tool.</description>
<context>Use this pattern as a starting point for any new agent.</context>
<example name="router_agent_delegation">
<description>A root agent that acts as a router, delegating tasks to specialized sub-agents based on their descriptions.</description>
<context>Use this pattern for complex workflows that can be broken down into smaller, specialized tasks. This is the core pattern for building modular and scalable multi-agent systems.</context>
<code language="python"><![CDATA[
from google.adk.agents import Agent
from your_project.tools import your_custom_tool
from your_project.prompts import get_agent_instructions
root_agent = Agent(
model='gemini-2.5-flash',
name='my_agent',
instruction=get_agent_instructions(),
tools=[your_custom_tool]
)
]]></code>
<explanation>
This snippet shows the basic structure of an agent definition. It imports the necessary classes, defines the agent with a model, name, instructions, and a list of tools.
</explanation>
</example>
<example name="multi_agent_definition">
<description>A root agent with two sub-agents.</description>
<context>Use this pattern for complex workflows that can be broken down into smaller, specialized tasks.</context>
<code language="python"><![CDATA[
from google.adk.agents import Agent
from your_project.sub_agents.analyzer import analyzer_agent
from your_project.sub_agents.reporter import reporter_agent
from your_project.prompts import get_router_instructions
from your_project.sub_agents.analyzer import analyzer_agent # Has description: "Analyzes data."
from your_project.sub_agents.reporter import reporter_agent # Has description: "Generates reports."
# The root agent's instructions guide its routing decisions.
router_agent = Agent(
model='gemini-2.5-pro',
name='router_agent',
instruction=get_router_instructions(),
instruction="You are a router. Analyze the user request and delegate to the appropriate sub-agent. If the request is about analysis, use the analyzer. If it's about reporting, use the reporter.",
sub_agents=[analyzer_agent, reporter_agent]
)
]]></code>
<explanation>
This snippet demonstrates how to create a multi-agent system. The `router_agent` acts as a dispatcher, delegating tasks to the `analyzer_agent` and `reporter_agent` based on the user's request.
The `router_agent` uses the `description` of its `sub_agents` to decide where to send a task. This keeps the root agent simple and focused on orchestration.
</explanation>
</example>
<example name="custom_tool_definition">
<description>A custom tool definition using a standard Python function.</description>
<context>Use this pattern to create custom tools that perform specific actions. The ADK uses the function's name, docstring, and type hints to create the tool definition for the LLM.</context>
<example name="sequential_agent_workflow">
<description>Using a `SequentialAgent` to orchestrate a fixed, multi-step workflow.</description>
<context>Use this for deterministic processes where steps must be executed in a specific order, such as a research pipeline (plan -> research -> write).</context>
<code language="python"><![CDATA[
from pydantic import BaseModel, Field
from google.adk.agents import SequentialAgent
from your_project.sub_agents import plan_agent, research_agent, write_agent
class WeatherArgs(BaseModel):
city: str = Field(description="The city for which to get the weather.")
def get_weather(args: WeatherArgs) -> str:
"""Gets the current weather for a specified city."""
# In a real implementation, this would call a weather API.
if "boston" in args.city.lower():
return "The weather in Boston is 15°C and sunny."
else:
return f"Weather data for {args.city} is not available."
# Then, in your agent definition:
# my_agent = Agent(..., tools=[get_weather])
]]></code>
<explanation>
A tool in the ADK is a standard Python function. The agent's LLM uses the function's name (e.g., `get_weather`), its docstring (which serves as the description), and its type-hinted arguments to understand how and when to call it. Using a Pydantic model for the arguments is a best practice for complex tools.
</explanation>
</example>
<example name="code_executor_usage">
<description>An agent that uses the `VertexAiCodeExecutor`.</description>
<context>Use this pattern for agents that need to perform data analysis, computations, or other tasks that require code execution.</context>
<code language="python"><![CDATA[
from google.adk.agents import Agent
from google.adk.code_executors import VertexAiCodeExecutor
from your_project.prompts import get_data_analyst_instructions
data_analyst_agent = Agent(
model='gemini-2.5-pro',
name='data_analyst_agent',
instruction=get_data_analyst_instructions(),
code_executor=VertexAiCodeExecutor(
optimize_data_file=True,
stateful=True,
)
)
]]></code>
<explanation>
This snippet shows how to equip an agent with a `VertexAiCodeExecutor`. This allows the agent to write and execute Python code to answer questions and perform tasks.
</explanation>
</example>
<example name="critique_loop_definition">
<description>An agent that implements a critique and refinement loop.</description>
<context>Use this pattern to improve the quality of generated content by having one agent critique the work of another.</context>
<code language="python"><![CDATA[
from google.adk.agents import LlmAgent, LoopAgent, BaseAgent
from google.adk.agents.callback_context import CallbackContext
from google.adk.events import Event, EventActions
from pydantic import BaseModel, Field
from typing import Literal
# 1. Define the output schema for the critic
class Feedback(BaseModel):
grade: Literal["pass", "fail"]
comment: str
# 2. Define the researcher agent
researcher = LlmAgent(
name="researcher",
instruction="Research the topic and provide a summary.",
output_key="research_summary"
)
# 3. Define the critic agent
critic = LlmAgent(
name="critic",
instruction="Evaluate the research summary. If it's good, grade 'pass'. Otherwise, grade 'fail' and provide comments.",
output_schema=Feedback,
output_key="evaluation"
)
# 4. Define a custom agent to check the grade and stop the loop
class EscalationChecker(BaseAgent):
def __init__(self, name: str):
super().__init__(name=name)
async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]:
if ctx.session.state.get("evaluation", {}).get("grade") == "pass":
yield Event(author=self.name, actions=EventActions(escalate=True))
else:
yield Event(author=self.name)
# 5. Define the refinement agent (could be the same as the researcher)
refiner = LlmAgent(
name="refiner",
instruction="Improve the research summary based on the critic's feedback.",
output_key="research_summary"
)
# 6. Assemble the loop
critique_loop = LoopAgent(
name="critique_loop",
max_iterations=3,
# Each sub_agent is an LlmAgent responsible for one step.
# Data is passed between them using the session state.
research_workflow = SequentialAgent(
name="ResearchWorkflow",
sub_agents=[
researcher, # First iteration
critic,
EscalationChecker(name="escalation_checker"),
refiner # Subsequent iterations
plan_agent, # Step 1: Creates a plan, saves to state.
research_agent, # Step 2: Reads plan from state, executes, saves findings.
write_agent # Step 3: Reads findings from state, writes report.
]
)
]]></code>
<explanation>
This example shows a complete critique loop. The `researcher` does the initial work. The `critic` evaluates it using a structured output. The `EscalationChecker` stops the loop if the work is satisfactory. If not, the `refiner` improves the work based on the feedback.
A `SequentialAgent` is a "Workflow Agent" that doesn't use an LLM for its own logic. It simply executes a list of sub-agents in order, providing a predictable structure for complex tasks.
</explanation>
</example>
<example name="async_tool_definition">
<description>An asynchronous tool for non-blocking I/O operations.</description>
<context>Use this pattern for tools that make network requests to external APIs or databases.</context>
<example name="critique_loop_with_loopagent">
<description>An agent that implements a critique and refinement loop using `LoopAgent`.</description>
<context>Use this pattern to improve the quality of generated content by having one agent critique the work of another, repeating until the work is satisfactory.</context>
<code language="python"><![CDATA[
import httpx
from google.adk.tools import tool
from google.adk.agents import LlmAgent, LoopAgent, BaseAgent
from google.adk.events import Event, EventActions
@tool(
name='get_user_profile',
description='Fetches a user profile from the API.'
# (Researcher and Critic agents are defined as LlmAgents)
# A custom agent to check the critic's grade and stop the loop
class EscalationChecker(BaseAgent):
async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]:
if ctx.session.state.get("evaluation", {}).get("grade") == "pass":
# Escalate signals the LoopAgent to stop.
yield Event(author=self.name, actions=EventActions(escalate=True))
else:
yield Event(author=self.name)
critique_loop = LoopAgent(
name="critique_loop",
max_iterations=3,
sub_agents=[
researcher, # Runs on first iteration
critic,
EscalationChecker(name="escalation_checker"),
refiner # Runs on subsequent iterations
]
)
async def get_user_profile(user_id: int) -> dict:
"""Fetches user data asynchronously."""
async with httpx.AsyncClient() as client:
response = await client.get(f"https://api.example.com/users/{user_id}")
response.raise_for_status()
return response.json()
]]></code>
<explanation>
By defining the tool with `async def`, the ADK's event loop is not blocked while waiting for the API response. This is critical for building responsive, production-ready agents.
The `LoopAgent` repeatedly executes its sub-agents. The `EscalationChecker` is a key component that inspects the state (where the critic's grade is saved) and uses `EventActions(escalate=True)` to terminate the loop when the quality standard is met.
</explanation>
</example>
<example name="streaming_response_handling">
<description>How to stream a response from an agent to a client.</description>
<context>Use this pattern in a server environment (e.g., FastAPI) to provide real-time feedback to the user.</context>
<example name="human_in_the_loop_approval">
<description>Using a `LongRunningFunctionTool` to pause execution and wait for human approval.</description>
<context>This is a critical pattern for tasks that are costly, irreversible, or require human oversight before proceeding.</context>
<code language="python"><![CDATA[
# In your FastAPI application
from fastapi import FastAPI
from sse_starlette.sse import EventSourceResponse
from your_agent import root_agent
from google.adk.tools import LongRunningFunctionTool
app = FastAPI()
# This tool initiates the approval process and pauses.
def request_human_approval(plan: str) -> dict:
"""Requests human approval for a given plan."""
# In a real app, this would trigger a UI notification.
print(f"PLAN SUBMITTED FOR APPROVAL:\n{plan}")
return {'status': 'pending', 'message': 'Waiting for human approval.'}
@app.post("/chat")
async def chat_endpoint(request: Request):
async def event_stream():
# The `astream_events()` method returns an async generator
async for event in root_agent.astream_events(request.body()):
if event.output_content:
yield {"data": event.output_content}
approval_tool = LongRunningFunctionTool(func=request_human_approval)
return EventSourceResponse(event_stream())
# The agent uses this tool. The client application would then:
# 1. Detect the long-running tool call.
# 2. Present the plan to the user in the UI.
# 3. When the user clicks "Approve", the client sends a FunctionResponse
# back to the agent to resume the workflow.
]]></code>
<explanation>
The `astream_events()` method allows you to process the agent's output as a stream of events. In a web server, this can be used to create a Server-Sent Events (SSE) endpoint, which pushes data to the client as it becomes available.
A `LongRunningFunctionTool` signals to the client application that a pause is needed. The agent's execution halts until the client sends a `FunctionResponse` back, allowing the workflow to continue. This decouples the agent's reasoning from the human interaction.
</explanation>
</example>
<example name="mcp_toolset_usage">
<description>An agent that consumes external tools from an MCP server.</description>
<context>Use this pattern to integrate your agent with independently deployed tool servers, such as the MCP Toolbox for Databases.</context>
<example name="rag_with_vertex_ai_search">
<description>An agent that uses `VertexAiSearchTool` to answer questions from a private knowledge base.</description>
<context>This is the standard pattern for building Retrieval-Augmented Generation (RAG) agents that use your own data.</context>
<code language="python"><![CDATA[
from google.adk.agents import Agent
from google.adk.tools.mcp_toolset import MCPToolset
import os
# The MCP server address is typically configured via an environment variable
mcp_server_address = os.environ.get("MCP_SERVER_ADDRESS")
# The MCPToolset will automatically fetch the tool definitions from the server
mcp_tools = MCPToolset(server_address=mcp_server_address)
mcp_client_agent = Agent(
model='gemini-2.5-pro',
name='mcp_client_agent',
instruction="Use the available tools to answer the user's question.",
tools=mcp_tools.get_tools()
)
]]></code>
<explanation>
The `MCPToolset` acts as a client to an MCP server. It fetches the available tools from the server and makes them available to the agent. This decouples the agent from the tool implementation, promoting modularity and scalability.
</explanation>
</example>
<example name="openapi_toolset_usage">
<description>An agent that uses a toolset generated from an OpenAPI specification.</description>
<context>Use this pattern to quickly integrate your agent with any existing REST API that has an OpenAPI (Swagger) definition.</context>
<code language="python"><![CDATA[
from google.adk.agents import Agent
from google.adk.tools.openapi_toolset import OpenAPIToolset
from google.adk.tools import VertexAiSearchTool
# The toolset can be initialized from a local file or a URL
openapi_spec_path = "path/to/your/openapi.json"
api_tools = OpenAPIToolset(openapi_spec_path=openapi_spec_path)
# The datastore path is the full resource name from Google Cloud
DATASTORE_PATH = "projects/.../dataStores/..."
api_agent = Agent(
model='gemini-2.5-pro',
name='api_agent',
instruction="Use the API tools to manage user resources.",
tools=api_tools.get_tools()
rag_agent = Agent(
model='gemini-2.5-flash',
name='internal_docs_agent',
instruction="Answer questions using the internal documentation. Always cite your sources.",
tools=[
VertexAiSearchTool(data_store_id=DATASTORE_PATH)
]
)
]]></code>
<explanation>
The `OpenAPIToolset` parses an OpenAPI specification and automatically creates a corresponding set of tools that the agent can use. This eliminates the need to write boilerplate code for API clients, significantly speeding up integration with existing APIs.
</explanation>
</example>
<example name="third_party_tool_wrapper">
<description>An agent that uses a tool from the LangChain framework via the `LangchainTool` wrapper.</description>
<context>Use this pattern only after explicit user approval. It is useful for leveraging existing, complex tools from the LangChain ecosystem without needing to rewrite them in the ADK.</context>
<code language="python"><![CDATA[
from google.adk.agents import Agent
from google.adk.tools.langchain_tool import LangchainTool
from langchain_community.tools import DuckDuckGoSearchRun
# 1. Instantiate the LangChain tool
langchain_search_tool = DuckDuckGoSearchRun()
# 2. Wrap it with LangchainTool
adk_compatible_tool = LangchainTool(langchain_tool=langchain_search_tool)
# 3. Use the wrapped tool in your ADK agent
search_agent = Agent(
model='gemini-2.5-pro',
name='search_agent',
instruction="Use the search tool to answer the user's question.",
tools=[adk_compatible_tool]
)
]]></code>
<explanation>
The `LangchainTool` class acts as a bridge between the ADK and the LangChain framework. It takes an existing LangChain tool as input and exposes it in a way that is compatible with the ADK's tool-calling mechanism. This allows you to mix and match native ADK tools with tools from other ecosystems.
</explanation>
</example>
<example name="callback_for_guardrails">
<description>Using a `before_tool_callback` to implement a guardrail that prevents a tool from being used with certain arguments.</description>
<context>This is a critical security and safety pattern. Use it to validate inputs before executing tools that could perform sensitive or destructive operations.</context>
<code language="python"><![CDATA[
from google.adk.agents import LlmAgent
from google.adk.tools import FunctionTool
from google.adk.tools.tool_context import ToolContext
from typing import Dict, Any, Optional
# 1. Define a potentially sensitive tool
def execute_shell_command(command: str) -> str:
"""Executes a shell command."""
# In a real implementation, this would run the command.
return f"Executed command: {command}"
# 2. Define the callback function to act as a guardrail
def shell_command_guardrail(
tool: BaseTool, args: Dict[str, Any], tool_context: ToolContext
) -> Optional[Dict]:
"""Prevents dangerous shell commands from being executed."""
command = args.get("command", "")
if "rm -rf" in command:
print(f"[Callback] DANGEROUS COMMAND DETECTED: '{command}'. Blocking execution.")
# By returning a dictionary, we skip the tool's execution
# and use this dictionary as the tool's output instead.
return {"error": "Execution blocked for security reasons."}
# If the command is safe, return None to allow execution to proceed.
print(f"[Callback] Command '{command}' is safe. Allowing execution.")
return None
# 3. Create the tool and the agent with the callback
shell_tool = FunctionTool(func=execute_shell_command)
secure_agent = LlmAgent(
model='gemini-2.5-pro',
name='secure_agent',
instruction="Use the shell tool to execute commands.",
tools=[shell_tool],
before_tool_callback=shell_command_guardrail
)
]]></code>
<explanation>
The `before_tool_callback` (`shell_command_guardrail`) intercepts any call to the `execute_shell_command` tool. It inspects the `args` to check for a dangerous pattern. If the pattern is found, it returns a dictionary, which immediately becomes the tool's output, and the actual `execute_shell_command` function is never called. If the command is safe, the callback returns `None`, allowing the tool to execute as normal.
The `VertexAiSearchTool` connects the agent to a Vertex AI Search datastore. The agent's LLM will automatically decide when to call this tool to find information relevant to the user's query. The response includes citations.
</explanation>
</example>
@ -330,39 +134,24 @@ secure_agent = LlmAgent(
<code language="python"><![CDATA[
from google.adk.tools.tool_context import ToolContext
# This tool reads a temperature unit preference from state,
# formats its output accordingly, and then writes the
# last-checked city back to the state.
def get_weather_stateful(city: str, tool_context: ToolContext) -> dict:
"""
Retrieves weather and formats the temperature unit based on session state.
Also saves the last checked city to state.
"""
# 1. Read from state (with a default value)
preferred_unit = tool_context.state.get("user_preference_temperature_unit", "Celsius")
print(f"[Tool] Reading preference from state: {preferred_unit}")
preferred_unit = tool_context.state.get("user:temperature_unit", "Celsius")
# (Mock weather logic...)
temp_c = 25 # Mock temperature in Celsius
if preferred_unit == "Fahrenheit":
temp_value = (temp_c * 9/5) + 32
temp_unit = "°F"
else:
temp_value = temp_c
temp_unit = "°C"
report = f"The weather in {city} is {temp_value:.0f}{temp_unit}."
report = f"The weather in {city} is 25°C." # Placeholder
# 2. Write to state
tool_context.state["last_city_checked"] = city
print(f"[Tool] Wrote 'last_city_checked' to state: {city}")
return {"status": "success", "report": report}
# In the agent definition, this tool would be added to the tools list.
# The agent's instruction would guide the LLM on how to interpret the output.
]]></code>
<explanation>
By adding `tool_context: ToolContext` as the last argument to the function, the ADK automatically provides access to the current session's context. The tool can then read from `tool_context.state` to get information (like user preferences) and write back to `tool_context.state` to save information for future turns. This is the primary mechanism for creating stateful, memory-aware tools.
By adding `tool_context: ToolContext` as an argument, the ADK automatically provides access to the session's context. The tool can read from `tool_context.state` (using prefixes like `user:` for persistence) and write back to it.
</explanation>
</example>
</examples>

View File

@ -1,138 +1,146 @@
<tool_usage_guide>
<tool_priorities>
<priority level="1">
<tool>adk CLI</tool>
<when>For running, testing, and deploying agents.</when>
<why>The `adk` CLI is the primary interface for interacting with the ADK and managing the agent lifecycle.</why>
<tool>adk CLI (`adk web`, `adk run`, `adk eval`, `adk deploy`)</tool>
<when>For the entire agent lifecycle: interactive debugging, command-line testing, performance evaluation, and deployment.</when>
<why>The `adk` CLI is the primary interface for managing and interacting with your agents.</why>
</priority>
<priority level="2">
<tool>Pre-built ADK Tools</tool>
<when>For common tasks like RAG and code execution.</when>
<why>Using pre-built tools saves development time and ensures that you are using a tested and optimized implementation.</why>
<tool>Built-in Tools (`google_search`, `VertexAiSearchTool`, `BuiltInCodeExecutor`)</tool>
<when>For common, powerful capabilities like web search, RAG, and code execution.</when>
<why>Leveraging pre-built, optimized tools saves significant development time and provides robust functionality out-of-the-box.</why>
</priority>
<priority level="3">
<tool>Custom `FunctionTool`</tool>
<when>When you need to connect the agent to a proprietary API, a database, or implement custom business logic.</when>
<why>This is the most common way to extend an agent's capabilities with your own specific logic.</why>
</priority>
</tool_priorities>
<tool_specific_guidance>
<tool name="adk CLI">
<tool name="adk_cli">
<best_practices>
<practice>Use `adk run .` for command-line interaction with your agent.</practice>
<practice>Use `adk web .` to launch the ADK Dev UI for a more interactive, web-based experience.</practice>
<practice>Use `adk eval` to run your evaluation suite and measure your agent's performance.</practice>
<practice>Always run `poetry install` before running an agent to ensure all dependencies are installed.</practice>
<practice>Use `adk web .` for local development. The Trace View is invaluable for debugging agent reasoning and tool usage.</practice>
<practice>Use `adk run .` for quick command-line tests and scripting interactions.</practice>
<practice>Use `adk eval` to automate performance testing against a predefined evaluation set, ensuring changes don't cause regressions.</practice>
<practice>Use `adk deploy [agent-engine|cloud-run|gke] ...` to deploy your agent to Google Cloud.</practice>
</best_practices>
<example><![CDATA[
# To run an agent from the command line
cd python/agents/my-agent/my_agent/
adk run .
# To launch the Dev UI
cd python/agents/my-agent/
adk web .
]]></example>
</tool>
<tool name="VertexAiRagRetrieval">
<tool name="FunctionTool">
<best_practices>
<practice>Ensure that the `RAG_CORPUS` environment variable is set to the full resource name of your Vertex AI RAG corpus.</practice>
<practice>Fine-tune the `similarity_top_k` and `vector_distance_threshold` parameters to optimize retrieval results.</practice>
<practice>Define tools as `async def` functions for any I/O-bound operations (e.g., API calls, database queries) to enable parallel execution.</practice>
<practice>Write clear, detailed docstrings. The LLM depends on them to understand what the tool does, its parameters, and when to use it.</practice>
<practice>Return a dictionary with a `status` key (e.g., 'success', 'error') to give the LLM clear feedback on the outcome.</practice>
<practice>For stateful operations, include `tool_context: ToolContext` as the last argument to read from and write to the session state.</practice>
</best_practices>
<example><![CDATA[
from google.adk.tools.retrieval.vertex_ai_rag_retrieval import VertexAiRagRetrieval
from vertexai.preview import rag
import os
retrieval_tool = VertexAiRagRetrieval(
name='retrieve_documentation',
description='Retrieves information from the knowledge base.',
rag_resources=[
rag.RagResource(
rag_corpus=os.environ.get("RAG_CORPUS")
)
],
similarity_top_k=5,
vector_distance_threshold=0.7,
)
]]></example>
async def get_weather(city: str, tool_context: ToolContext) -> dict:
"""
Retrieves the current weather for a specified city.
Args:
city (str): The name of the city.
Returns:
A dictionary with 'status' and 'report' or 'error_message'.
"""
# ... async API call ...
tool_context.state['last_city_checked'] = city
return {"status": "success", "report": "..."}
]]>
</example>
</tool>
<tool name="VertexAiCodeExecutor">
<tool name="BuiltInCodeExecutor">
<best_practices>
<practice>Set `stateful=True` to allow the code executor to maintain state between turns, which is useful for multi-step data analysis tasks.</practice>
<practice>Use a dedicated data analyst sub-agent to handle all code execution tasks.</practice>
<practice>Use this for agents that need to perform calculations, data manipulation, or run small scripts in a sandboxed environment.</practice>
<practice>Assign this capability to a specialized sub-agent (e.g., a "Data Analyst" agent) to isolate code execution logic.</practice>
</best_practices>
<example><![CDATA[
from google.adk.code_executors import VertexAiCodeExecutor
from google.adk.agents import Agent
from google.adk.code_executors import BuiltInCodeExecutor
code_executor = VertexAiCodeExecutor(
optimize_data_file=True,
stateful=True,
)
]]></example>
</tool>
<tool name="SequentialAgent">
<best_practices>
<practice>Use `SequentialAgent` to orchestrate a fixed, linear workflow of other agents (typically `LlmAgent`s). It executes a list of sub-agents in a predefined order.</practice>
<practice>This agent is a **Workflow Agent**; it does not use an LLM for its logic. Its purpose is to provide a deterministic structure for a multi-step process.</practice>
<practice>Ensure that the `output_key` of each sub-agent matches the expected input key for the next agent in the sequence to pass data through the pipeline.</practice>
</best_practices>
<example><![CDATA[
from google.adk.agents import SequentialAgent
from .sub_agents import planner, researcher, composer
research_pipeline = SequentialAgent(
name="research_pipeline",
sub_agents=[planner, researcher, composer]
)
]]></example>
</tool>
<tool name="LoopAgent">
<best_practices>
<practice>Use `LoopAgent` to implement iterative processes where a sequence of sub-agents is executed repeatedly until a condition is met.</practice>
<practice>Like `SequentialAgent`, this is a **Workflow Agent** that provides deterministic control flow. It is most powerful when orchestrating `LlmAgent`s in patterns like critique-and-refinement.</practice>
<practice>Always include a custom `BaseAgent` (like the `EscalationChecker` example) to define the loop's exit condition, preventing infinite loops. Set a `max_iterations` limit as a safeguard.</practice>
</best_practices>
<example><![CDATA[
from google.adk.agents import LoopAgent
from .sub_agents import researcher, critic, refiner, escalation_checker
critique_loop = LoopAgent(
name="critique_loop",
max_iterations=3,
sub_agents=[
researcher,
critic,
escalation_checker,
refiner
]
)
]]></example>
</tool>
<tool name="AgentTool">
<best_practices>
<practice>Use `AgentTool` to wrap a specialized agent and make it available as a tool to a higher-level "manager" or "router" agent.</practice>
<practice>This is the primary mechanism for building hierarchical, multi-agent systems where complex tasks are delegated.</practice>
</best_practices>
<example><![CDATA[
from google.adk.agents import LlmAgent
from google.adk.tools.agent_tool import AgentTool
# Define a specialist agent
plan_generator = LlmAgent(
name="plan_generator",
instruction="Create a detailed research plan."
)
# Use the specialist agent as a tool in a manager agent
manager_agent = LlmAgent(
name="manager_agent",
instruction="Use the plan_generator tool to create a plan.",
tools=[AgentTool(plan_generator)]
analyst_agent = Agent(
model='gemini-2.5-pro',
name='analyst_agent',
instruction="You are a data analyst. Write and execute Python code to answer questions.",
code_executor=BuiltInCodeExecutor()
)
]]>
</example>
</tool>
<tool name="VertexAiSearchTool_and_google_search">
<best_practices>
<practice>Use `VertexAiSearchTool` for RAG on your own private, indexed data stores.</practice>
<practice>Use `google_search` for accessing real-time, public information from the web.</practice>
<practice>Your agent's instructions should guide it on when to use which search tool if both are provided.</practice>
</best_practices>
<example><![CDATA[
# For private data RAG
from google.adk.tools import VertexAiSearchTool
rag_tool = VertexAiSearchTool(data_store_id="projects/.../dataStores/...")
# For public web search
from google.adk.tools import google_search
# In agent definition:
# tools=[rag_tool, google_search]
]]>
</example>
</tool>
<tool name="OpenAPIToolset">
<best_practices>
<practice>Use this to instantly create a full set of tools from an existing REST API's OpenAPI v3 specification.</practice>
<practice>Configure authentication (`auth_scheme`, `auth_credential`) on the toolset during initialization to apply it to all generated tools.</practice>
</best_practices>
<example><![CDATA[
from google.adk.tools.openapi_tool.openapi_spec_parser.openapi_toolset import OpenAPIToolset
# Load your OpenAPI spec from a file or string
with open("my_api_spec.yaml", "r") as f:
spec_str = f.read()
api_toolset = OpenAPIToolset(
spec_str=spec_str,
spec_str_type="yaml"
# ... add auth config if needed ...
)
# In agent definition:
# tools=[api_toolset]
]]>
</example>
</tool>
<tool name="MCPToolset">
<best_practices>
<practice>Use `MCPToolset` to connect to external tool servers that follow the Model Context Protocol, such as the MCP Toolbox for Databases.</practice>
<practice>This is the recommended way to give agents access to SQL databases (BigQuery, Postgres, etc.) in a secure and structured manner.</practice>
</best_practices>
<example><![CDATA[
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
from google.adk.tools.mcp_tool.mcp_session_manager import SseConnectionParams
# Connect to a remote MCP server (like the DB Toolbox)
db_tools = MCPToolset(
connection_params=SseConnectionParams(
url="http://your-mcp-db-toolbox-url"
)
)
# In agent definition:
# tools=[db_tools]
]]>
</example>
</tool>
<tool name="Authentication">
<best_practices>
<practice>For custom tools (`FunctionTool`) requiring auth, the tool itself must manage the logic: check state for tokens, request credentials via `tool_context.request_credential()`, and handle the response.</practice>
<practice>For toolsets (`OpenAPIToolset`, `GoogleApiToolSet`), configure authentication at the toolset level. The framework will handle the interactive flow.</practice>
<practice>Always prefer OAuth2 (User-Auth) for tools accessing user-specific data.</practice>
</best_practices>
</tool>
</tool_specific_guidance>
</tool_usage_guide>

View File

@ -1,232 +1,126 @@
<examples>
<example name="simple_rag_agent">
<description>A complete example of a single-agent system that uses the `VertexAiRagRetrieval` tool to answer questions from a knowledge base.</description>
<context>Use this example as a template for building simple RAG-based agents.</context>
<example name="multi_agent_rag_with_hitl">
<description>A complete example of a multi-agent system that combines RAG, a Human-in-the-Loop (HITL) approval step, and state management.</description>
<context>This pattern is suitable for building research assistants or any agent that needs to gather information from a private source, formulate a plan, get user approval, and then execute.</context>
<code language="python"><![CDATA[
# In python/agents/my-rag-agent/my_rag_agent/prompts.py
def get_rag_instructions() -> str:
return """
You are an AI assistant that answers questions based on a knowledge base.
Use the retrieval tool to find relevant information and cite your sources.
"""
# --- sub_agents/planner/tools.py ---
from google.adk.tools import LongRunningFunctionTool
# In python/agents/my-rag-agent/my_rag_agent/agent.py
import os
from google.adk.agents import Agent
from google.adk.tools.retrieval.vertex_ai_rag_retrieval import VertexAiRagRetrieval
from vertexai.preview import rag
from .prompts import get_rag_instructions
def request_human_approval(plan: str) -> dict:
"""Presents a plan to the user and waits for their approval to proceed."""
print(f"--- APPROVAL REQUIRED ---\n{plan}\n-------------------------")
return {'status': 'pending', 'message': 'Waiting for human approval of the plan.'}
retrieval_tool = VertexAiRagRetrieval(
name='retrieve_documentation',
description='Retrieves information from the knowledge base.',
rag_resources=[
rag.RagResource(
rag_corpus=os.environ.get("RAG_CORPUS")
)
approval_tool = LongRunningFunctionTool(func=request_human_approval)
# --- sub_agents/planner/agent.py ---
from google.adk.agents import LlmAgent
from .tools import approval_tool
planner_agent = LlmAgent(
name="PlannerAgent",
model="gemini-2.5-pro",
instruction="Based on the user's request and retrieved context, create a step-by-step execution plan. Then, use the 'request_human_approval' tool to present this plan to the user.",
tools=[approval_tool],
output_key="execution_plan"
)
# --- sub_agents/retriever/agent.py ---
from google.adk.agents import LlmAgent
from google.adk.tools import VertexAiSearchTool
retriever_agent = LlmAgent(
name="RetrieverAgent",
model="gemini-2.5-flash",
instruction="Use the search tool to retrieve context relevant to the user's query.",
tools=[VertexAiSearchTool(data_store_id="...")],
output_key="retrieved_context"
)
# --- sub_agents/executor/agent.py ---
# (This agent would contain tools to execute the steps outlined in the plan)
from google.adk.agents import LlmAgent
executor_agent = LlmAgent(name="ExecutorAgent", model="gemini-2.5-flash", instruction="Execute the plan.")
# --- root_agent.py ---
from google.adk.agents import SequentialAgent
from .sub_agents.retriever import retriever_agent
from .sub_agents.planner import planner_agent
from .sub_agents.executor import executor_agent
root_workflow = SequentialAgent(
name="HITL_RAG_Workflow",
sub_agents=[
retriever_agent, # 1. Retrieve context
planner_agent, # 2. Create a plan and request approval (this will pause)
executor_agent # 3. Execute the plan (resumes after client sends approval)
]
)
root_agent = Agent(
model='gemini-2.5-flash',
name='my_rag_agent',
instruction=get_rag_instructions(),
tools=[retrieval_tool]
)
]]></code>
<explanation>
This example shows a complete implementation of a simple RAG agent. It defines the instructional prompt and the agent itself, which is equipped with the `VertexAiRagRetrieval` tool.
This example shows a `SequentialAgent` that first uses a `retriever_agent` for RAG. The retrieved context is saved to the state. The `planner_agent` then uses this context to create a plan and pauses execution using a `LongRunningFunctionTool` to wait for human approval. Once the client application receives the approval and sends a `FunctionResponse`, the workflow resumes with the `executor_agent`.
</explanation>
</example>
<example name="multi_agent_data_analysis">
<description>A complete example of a multi-agent system for data analysis. A router agent delegates tasks to a BigQuery agent (for data retrieval) and a data analyst agent (for code execution).</description>
<context>Use this example as a template for building complex, multi-agent systems that combine different capabilities.</context>
<example name="artifact_based_file_processing">
<description>A workflow for processing a file using the ArtifactService to pass the file data between agents, avoiding filesystem dependencies.</description>
<context>This is the recommended pattern for any multi-step workflow that involves reading, processing, and writing file data, as it is robust and works in any deployment environment.</context>
<code language="python"><![CDATA[
# In python/agents/my-data-agent/my_data_agent/sub_agents/bigquery/agent.py
# (Assuming a tool `execute_sql` is defined in tools.py)
from google.adk.agents import Agent
from .tools import execute_sql
from .prompts import get_bq_instructions
bq_agent = Agent(
model='gemini-2.5-pro',
name='bigquery_agent',
instruction=get_bq_instructions(),
tools=[execute_sql]
)
# In python/agents/my-data-agent/my_data_agent/sub_agents/analyst/agent.py
from google.adk.agents import Agent
from google.adk.code_executors import VertexAiCodeExecutor
from .prompts import get_analyst_instructions
analyst_agent = Agent(
model='gemini-2.5-pro',
name='analyst_agent',
instruction=get_analyst_instructions(),
code_executor=VertexAiCodeExecutor(stateful=True)
)
# In python/agents/my-data-agent/my_data_agent/agent.py
from google.adk.agents import Agent
from .sub_agents.bigquery import bq_agent
from .sub_agents.analyst import analyst_agent
from .prompts import get_router_instructions
router_agent = Agent(
model='gemini-2.5-pro',
name='router_agent',
instruction=get_router_instructions(),
sub_agents=[bq_agent, analyst_agent]
)
]]></code>
<explanation>
This example illustrates a multi-agent system. The `router_agent` decomposes the user's request and routes it to either the `bq_agent` for SQL execution or the `analyst_agent` for Python-based data analysis.
</explanation>
</example>
<example name="full_stack_research_agent">
<description>A complete example of a full-stack research agent with a Human-in-the-Loop (HITL) planning phase and a critique-refinement loop.</description>
<context>Use this example as a template for building production-ready, full-stack agentic applications.</context>
<code language="python"><![CDATA[
# In python/agents/my-fullstack-agent/app/agent.py
# (Assume all sub-agents like plan_generator, section_planner, researcher, critic, etc., are defined as in the gemini-fullstack example)
# --- Main Pipelines ---
# The autonomous research pipeline
research_pipeline = SequentialAgent(
name="research_pipeline",
description="Executes a pre-approved research plan.",
sub_agents=[
section_planner,
LoopAgent(
name="iterative_refinement_loop",
max_iterations=3,
sub_agents=[
section_researcher,
research_evaluator,
EscalationChecker(name="escalation_checker"),
enhanced_search_executor,
],
),
report_composer,
],
)
# The interactive planner that involves the user
interactive_planner_agent = LlmAgent(
name="interactive_planner_agent",
model="gemini-2.5-pro",
description="Collaborates with the user to create and refine a research plan.",
instruction="""
Your job is to create a research plan based on the user's request.
1. Use the `plan_generator` to create a draft plan.
2. Present the plan to the user for approval.
3. Once the user approves, delegate to the `research_pipeline`.
""",
sub_agents=[research_pipeline],
tools=[AgentTool(plan_generator)],
output_key="research_plan",
)
# The root agent for the application
root_agent = interactive_planner_agent
# In a separate file, e.g., main.py, you would expose this agent via an API
# from google.adk.api_server import serve
# from my_fullstack_agent.app.agent import root_agent
#
# if __name__ == "__main__":
# serve(agents={"my-agent": root_agent})
]]></code>
<explanation>
This example shows the high-level structure of a full-stack agent. The `interactive_planner_agent` handles the HITL phase, collaborating with the user. Once the plan is approved, it delegates to the `research_pipeline`, a `SequentialAgent` that orchestrates the research, critique, and composition phases. The final agent can be served via an API for integration with a web frontend.
</explanation>
</example>
<example name="state_management_and_memory">
<description>A complete example demonstrating how to use session state for short-term memory and a memory service for long-term recall.</description>
<context>Use this pattern for agents that need to remember user preferences within a conversation and recall information from past conversations.</context>
<code language="python"><![CDATA[
# --- 1. Define Services ---
from google.adk.sessions import InMemorySessionService
from google.adk.memory import InMemoryMemoryService
# Services should be shared across runners to maintain context
session_service = InMemorySessionService()
memory_service = InMemoryMemoryService() # Use InMemory for testing
# --- 2. Define a Stateful Tool ---
# --- sub_agents/ingestor/tools.py ---
from google.adk.tools.tool_context import ToolContext
from google.genai import types
import os
def get_user_profile(user_id: str, tool_context: ToolContext) -> dict:
"""Retrieves a user's profile, remembering the last user looked up."""
# Write to session state
tool_context.state["last_user_profile_lookup"] = user_id
return {"user_id": user_id, "preference": "dark_mode"}
def ingest_file(file_path: str, tool_context: ToolContext) -> dict:
"""Reads a file from a local path and saves its content as an artifact."""
try:
with open(file_path, "rb") as f:
file_bytes = f.read()
artifact_part = types.Part(inline_data=types.Blob(mime_type="application/octet-stream", data=file_bytes))
artifact_name = f"ingested_{os.path.basename(file_path)}"
tool_context.save_artifact(artifact_name, artifact_part)
tool_context.state['temp:artifact_to_process'] = artifact_name
return {"status": "success", "artifact_name": artifact_name}
except Exception as e:
return {"status": "error", "error_message": str(e)}
# --- 3. Define Agents ---
from google.adk.agents import LlmAgent
from google.adk.tools import load_memory
# --- sub_agents/summarizer/tools.py ---
def summarize_artifact(tool_context: ToolContext) -> dict:
"""Loads a text artifact from the session and summarizes it."""
artifact_name = tool_context.state.get('temp:artifact_to_process')
if not artifact_name:
return {"status": "error", "error_message": "No artifact found in state."}
# Agent to capture information and populate memory
info_capture_agent = LlmAgent(
model="gemini-2.0-flash",
name="InfoCaptureAgent",
instruction="Acknowledge the user's statement about their favorite color."
artifact_part = tool_context.load_artifact(artifact_name)
if not artifact_part or not artifact_part.inline_data:
return {"status": "error", "error_message": f"Could not load artifact '{artifact_name}'."}
text_content = artifact_part.inline_data.data.decode('utf-8')
summary = f"Summary of '{artifact_name}': {text_content[:100]}..." # Placeholder for actual summarization
tool_context.state['final_summary'] = summary
return {"status": "success", "summary": summary}
# --- root_workflow.py ---
from google.adk.agents import SequentialAgent, LlmAgent
from .sub_agents.ingestor.tools import ingest_file
from .sub_agents.summarizer.tools import summarize_artifact
ingestor_agent = LlmAgent(name="Ingestor", tools=[ingest_file], instruction="Ingest the file.")
summarizer_agent = LlmAgent(name="Summarizer", tools=[summarize_artifact], instruction="Summarize the ingested file.")
file_processing_workflow = SequentialAgent(
name="FileProcessingWorkflow",
sub_agents=[ingestor_agent, summarizer_agent]
)
# Agent that can recall information from long-term memory
memory_recall_agent = LlmAgent(
model="gemini-2.0-flash",
name="MemoryRecallAgent",
instruction="Answer the user's question. Use the 'load_memory' tool if the answer might be in past conversations.",
tools=[load_memory]
)
# --- 4. Run the Scenario ---
from google.adk.runners import Runner
from google.genai.types import Content, Part
async def run_memory_scenario():
# --- Turn 1: Capture information in a session ---
runner1 = Runner(
agent=info_capture_agent,
app_name="memory_app",
session_service=session_service,
memory_service=memory_service
)
session1 = await session_service.create_session(app_name="memory_app", user_id="user1", session_id="session1")
user_input1 = Content(parts=[Part(text="My favorite color is blue.")], role="user")
async for event in runner1.run_async(user_id="user1", session_id="session1", new_message=user_input1):
pass # Run to completion
# Add the completed session to long-term memory
completed_session1 = await session_service.get_session(app_name="memory_app", user_id="user1", session_id="session1")
await memory_service.add_session_to_memory(completed_session1)
print("--- Session 1 added to memory ---")
# --- Turn 2: Recall the information in a new session ---
runner2 = Runner(
agent=memory_recall_agent,
app_name="memory_app",
session_service=session_service,
memory_service=memory_service
)
session2 = await session_service.create_session(app_name="memory_app", user_id="user1", session_id="session2")
user_input2 = Content(parts=[Part(text="What is my favorite color?")], role="user")
async for event in runner2.run_async(user_id="user1", session_id="session2", new_message=user_input2):
if event.is_final_response():
print(f"Agent Response: {event.content.parts[0].text}")
# To run: asyncio.run(run_memory_scenario())
]]></code>
<explanation>
This example shows the full lifecycle of session and memory. In Turn 1, a simple agent has a conversation, and the resulting session is saved to the `memory_service`. In Turn 2, a different agent in a new session uses the `load_memory` tool to search the `memory_service` for the answer to the user's question, demonstrating long-term recall across conversational boundaries.
The `ingestor_agent`'s tool reads a file and uses `tool_context.save_artifact()` to store its content in the `ArtifactService`. It then passes the name of the artifact to the next agent via the `temp:` state. The `summarizer_agent`'s tool reads the artifact name from the state, uses `tool_context.load_artifact()` to retrieve the content, and then processes it.
</explanation>
</example>
</examples>

View File

@ -0,0 +1,78 @@
<architecture_and_deployment>
<pattern name="runtime_event_loop">
<overview>
The ADK Runtime operates on an Event Loop. The `Runner` component orchestrates the execution by yielding and processing `Event` objects. This pattern ensures that state changes are consistently applied and that agent logic always operates on the most recently committed state.
</overview>
<components>
<component name="Runner">Acts as the central coordinator, initiating agent execution, processing events, and committing state changes via services.</component>
<component name="Execution Logic">Your agents, tools, and callbacks. They perform tasks and `yield` `Event` objects to communicate results or request actions.</component>
<component name="Event">The message passed between the Runner and Execution Logic, carrying content and actions (like `state_delta`).</component>
<component name="Services">Backend components (`SessionService`, `ArtifactService`, `MemoryService`) that manage persistence.</component>
</components>
<flow>
<step>1. `Runner` calls the agent's `run_async` method.</step>
<step>2. Agent logic executes until it needs to communicate, then `yields` an `Event`.</step>
<step>3. Agent execution **pauses**.</step>
<step>4. `Runner` receives the event, uses `SessionService` to process its `actions` (e.g., apply `state_delta`), and persists the event to history.</step>
<step>5. `Runner` signals completion, and agent execution **resumes** from where it paused, now able to see the committed state changes.</step>
</flow>
</pattern>
<pattern name="session_and_memory_services">
<overview>ADK provides different services for managing session state and long-term memory, allowing you to choose the right backend for your application's needs.</overview>
<services>
<service name="InMemorySessionService">
<description>Stores all session data in memory. Data is lost on application restart.</description>
<use_case>Ideal for local development, testing, and simple, stateless applications.</use_case>
</service>
<service name="VertexAiSessionService">
<description>Uses Google Cloud's Vertex AI Agent Engine for persistent, scalable session management.</description>
<use_case>Recommended for production applications deployed on Google Cloud, especially when using Vertex AI Express Mode.</use_case>
</service>
<service name="VertexAiMemoryBankService">
<description>Provides long-term, cross-session memory for a user by connecting to a Vertex AI backend.</description>
<use_case>For agents that need to remember user preferences or information from past conversations.</use_case>
</service>
</services>
</pattern>
<pattern name="agent_to_agent_a2a_protocol">
<overview>The A2A protocol enables communication between independent agents running as separate services, potentially in different languages or frameworks.</overview>
<when_to_use>
<item>Integrating with a third-party agent/service.</item>
<item>Building a microservices architecture where agents are independent services.</item>
<item>Enforcing a strong, formal contract between agents maintained by different teams.</item>
</when_to_use>
<components>
<component name="Exposing Agent">Use `adk api_server --a2a` or the `to_a2a()` utility to expose an ADK agent as an A2A server.</component>
<component name="Consuming Agent">Use the `RemoteA2aAgent` class to connect to and interact with a remote A2A agent as if it were a local tool.</component>
</components>
</pattern>
<pattern name="streaming_and_bidi_streaming">
<overview>ADK supports different streaming modes for real-time communication, configured via `RunConfig`.</overview>
<modes>
<mode name="StreamingMode.NONE">Default. Responses are delivered as a single, complete unit.</mode>
<mode name="StreamingMode.SSE">Server-Sent Events. The agent streams its response back to the client token-by-token, creating a "typing" effect.</mode>
<mode name="StreamingMode.BIDI">Bidirectional Streaming (Live API). Enables low-latency, two-way voice and video interaction, allowing the user to interrupt the agent.</mode>
</modes>
</pattern>
<pattern name="deployment_targets">
<overview>ADK provides CLI commands to streamline deployment to various Google Cloud targets.</overview>
<targets>
<target name="Vertex AI Agent Engine">
<command>`adk deploy agent_engine`</command>
<description>A fully managed, serverless platform for deploying and scaling agents. It handles infrastructure, provides persistent session state via `VertexAiSessionService`, and is the recommended target for most production agents.</description>
</target>
<target name="Cloud Run">
<command>`adk deploy cloud_run`</command>
<description>A managed platform for running containerized applications. Use this when you need to package your agent as part of a larger web service (e.g., a FastAPI application) or require more control over the container environment.</description>
</target>
<target name="Google Kubernetes Engine (GKE)">
<command>`adk deploy gke`</command>
<description>A managed Kubernetes service for deploying containerized applications at scale. Use this for complex deployments requiring fine-grained control over networking, scaling, and orchestration, such as using a sidecar pattern for MCP servers.</description>
</target>
</targets>
</pattern>
</architecture_and_deployment>

View File

@ -0,0 +1,66 @@
<security_and_safety>
<overview>
This guide covers best practices for building secure and safe AI agents with the ADK. It is critical to implement multiple layers of defense to mitigate risks such as prompt injection, data exfiltration, and harmful content generation.
</overview>
<principle name="layered_defense">
<description>Security should not rely on a single mechanism. A multi-layered approach combining identity management, input/output screening, sandboxing, and network controls is essential.</description>
</principle>
<layer name="identity_and_authorization">
<description>Control who the agent acts as and what permissions it has.</description>
<pattern name="User-Auth (OAuth2 / OIDC)">
<when_to_use>When a tool needs to access user-specific data in an external service (e.g., Google Calendar, Salesforce).</when_to_use>
<how_it_works>The tool delegates authentication to the end-user. The agent operates with the user's permissions, enforcing the principle of least privilege.</how_it_works>
<implementation>Configure the `auth_scheme` and `auth_credential` on the tool (e.g., `OpenAPIToolset`). The ADK framework manages the interactive OAuth2 flow, signaling the client application when user interaction is needed.</implementation>
</pattern>
<pattern name="Agent-Auth (Service Accounts)">
<when_to_use>When a tool interacts with services where all users share the same level of access (e.g., a project-wide BigQuery dataset).</when_to_use>
<how_it_works>The tool authenticates using a dedicated service account. Access is controlled via IAM policies on the target resource.</how_it_works>
<implementation>Provide a service account JSON key when configuring the tool or rely on Application Default Credentials in a Google Cloud environment.</implementation>
</pattern>
</layer>
<layer name="input_output_screening">
<description>Inspect and validate data at critical points in the execution flow.</description>
<pattern name="In-Tool Guardrails">
<when_to_use>Always. This is a fundamental security practice for any custom tool.</when_to_use>
<how_it_works>The tool's own code validates the arguments provided by the LLM before executing its core logic. It can check for dangerous inputs, validate against allowlists, or enforce business rules.</how_it_works>
<example>A SQL execution tool should parse the incoming SQL to ensure it's a read-only `SELECT` statement and doesn't contain `DROP TABLE`.</example>
</pattern>
<pattern name="Before-Tool Callback (`before_tool_callback`)">
<when_to_use>To enforce centralized, reusable policies across multiple tools without modifying each tool's code.</when_to_use>
<how_it_works>A callback function inspects the tool name and arguments before execution. It can block the call by returning a dictionary, which becomes the tool's result.</how_it_works>
<example>A callback that blocks any tool call if the arguments contain a user ID that doesn't match the one in the session state.</example>
</pattern>
<pattern name="Before-Model Callback (`before_model_callback`)">
<when_to_use>To screen user prompts for harmful content, prompt injection attempts, or policy violations before they are sent to the LLM.</when_to_use>
<how_it_works>A callback function inspects the `LlmRequest`. It can block the LLM call by returning an `LlmResponse`, which is sent to the user instead.</how_it_works>
</pattern>
<pattern name="Gemini Safety Features">
<when_to_use>Always, when using Gemini models.</when_to_use>
<how_it_works>Leverage Gemini's built-in, configurable content filters for harm categories (hate speech, harassment, etc.) and use system instructions to guide the model's behavior and define safety guidelines.</how_it_works>
</pattern>
</layer>
<layer name="sandboxed_code_execution">
<description>Isolate the execution of LLM-generated code to prevent it from compromising the host environment.</description>
<pattern name="BuiltInCodeExecutor">
<when_to_use>When the agent needs to run Python code for data analysis or calculations.</when_to_use>
<how_it_works>The `BuiltInCodeExecutor` uses a secure, sandboxed environment to run code, preventing access to the local filesystem or network.</how_it_works>
<implementation>Assign an instance of `BuiltInCodeExecutor` to the `code_executor` property of your `LlmAgent`.</implementation>
</pattern>
<pattern name="Vertex AI Code Interpreter Extension">
<when_to_use>For more advanced data analysis tasks requiring pre-installed libraries.</when_to_use>
<how_it_works>This is a managed service that provides a sandboxed Python environment with common data analysis libraries available.</how_it_works>
</pattern>
</layer>
<layer name="network_controls">
<description>Confine agent activity within a secure perimeter to prevent data exfiltration.</description>
<pattern name="VPC Service Controls">
<when_to_use>For production agents deployed on Google Cloud that handle sensitive data.</when_to_use>
<how_it_works>VPC-SC creates a service perimeter that restricts data exfiltration by preventing services within the perimeter from communicating with services outside of it, unless explicitly allowed.</how_it_works>
</pattern>
</layer>
</security_and_safety>

View File

@ -26,3 +26,13 @@ customModes:
- edit
- command
source: project
- slug: prompt-engineer
name: "✨ Prompt Engineer Elite"
roleDefinition: |
You are an Expert prompt engineer specializing in designing, optimizing, and managing prompts for large language models. Masters prompt architecture, evaluation frameworks, and production prompt systems with focus on reliability, efficiency, and measurable outcomes.
groups:
- read
- edit
- command
- mcp
source: project