diff --git a/contributing/samples/core_basic/README.md b/contributing/samples/core_basic/README.md new file mode 100644 index 00000000..cde68d24 --- /dev/null +++ b/contributing/samples/core_basic/README.md @@ -0,0 +1,7 @@ +# Basic Confg-based Agent + +This sample only covers: + +* name +* description +* model diff --git a/contributing/samples/core_basic/root_agent.yaml b/contributing/samples/core_basic/root_agent.yaml new file mode 100644 index 00000000..0ef21f29 --- /dev/null +++ b/contributing/samples/core_basic/root_agent.yaml @@ -0,0 +1,9 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +name: assistant_agent +model: gemini-2.5-flash +description: A helper agent that can answer users' questions. +instruction: | + You are an agent to help answer users' various questions. + + 1. If the user's intention is not clear, ask clarifying questions to better understand their needs. + 2. Once the intention is clear, provide accurate and helpful answers to the user's questions. diff --git a/contributing/samples/core_callback/callbacks.py b/contributing/samples/core_callback/callbacks.py new file mode 100644 index 00000000..1614a935 --- /dev/null +++ b/contributing/samples/core_callback/callbacks.py @@ -0,0 +1,79 @@ +from google.genai import types + + +async def before_agent_callback(callback_context): + print('@before_agent_callback') + return None + + +async def after_agent_callback(callback_context): + print('@after_agent_callback') + return None + + +async def before_model_callback(callback_context, llm_request): + print('@before_model_callback') + return None + + +async def after_model_callback(callback_context, llm_response): + print('@after_model_callback') + return None + + +def after_agent_callback1(callback_context): + print('@after_agent_callback1') + + +def after_agent_callback2(callback_context): + print('@after_agent_callback2') + # ModelContent (or Content with role set to 'model') must be returned. + # Otherwise, the event will be excluded from the context in the next turn. + return types.ModelContent( + parts=[ + types.Part( + text='(stopped) after_agent_callback2', + ), + ], + ) + + +def after_agent_callback3(callback_context): + print('@after_agent_callback3') + + +def before_agent_callback1(callback_context): + print('@before_agent_callback1') + + +def before_agent_callback2(callback_context): + print('@before_agent_callback2') + + +def before_agent_callback3(callback_context): + print('@before_agent_callback3') + + +def before_tool_callback1(tool, args, tool_context): + print('@before_tool_callback1') + + +def before_tool_callback2(tool, args, tool_context): + print('@before_tool_callback2') + + +def before_tool_callback3(tool, args, tool_context): + print('@before_tool_callback3') + + +def after_tool_callback1(tool, args, tool_context, tool_response): + print('@after_tool_callback1') + + +def after_tool_callback2(tool, args, tool_context, tool_response): + print('@after_tool_callback2') + return {'test': 'after_tool_callback2', 'response': tool_response} + + +def after_tool_callback3(tool, args, tool_context, tool_response): + print('@after_tool_callback3') diff --git a/contributing/samples/core_callback/root_agent.yaml b/contributing/samples/core_callback/root_agent.yaml new file mode 100644 index 00000000..ceeda146 --- /dev/null +++ b/contributing/samples/core_callback/root_agent.yaml @@ -0,0 +1,43 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +name: hello_world_agent +model: gemini-2.0-flash +description: hello world agent that can roll a dice and check prime numbers. +instruction: | + You roll dice and answer questions about the outcome of the dice rolls. + You can roll dice of different sizes. + You can use multiple tools in parallel by calling functions in parallel(in one request and in one round). + It is ok to discuss previous dice roles, and comment on the dice rolls. + When you are asked to roll a die, you must call the roll_die tool with the number of sides. Be sure to pass in an integer. Do not pass in a string. + You should never roll a die on your own. + When checking prime numbers, call the check_prime tool with a list of integers. Be sure to pass in a list of integers. You should never pass in a string. + You should not check prime numbers before calling the tool. + When you are asked to roll a die and check prime numbers, you should always make the following two function calls: + 1. You should first call the roll_die tool to get a roll. Wait for the function response before calling the check_prime tool. + 2. After you get the function response from roll_die tool, you should call the check_prime tool with the roll_die result. + 2.1 If user asks you to check primes based on previous rolls, make sure you include the previous rolls in the list. + 3. When you respond, you must include the roll_die result from step 1. + You should always perform the previous 3 steps when asking for a roll and checking prime numbers. + You should not rely on the previous history on prime results. +tools: + - name: callbacks.tools.roll_die + - name: callbacks.tools.check_prime +before_agent_callbacks: + - name: callbacks.callbacks.before_agent_callback1 + - name: callbacks.callbacks.before_agent_callback2 + - name: callbacks.callbacks.before_agent_callback3 +after_agent_callbacks: + - name: callbacks.callbacks.after_agent_callback1 + - name: callbacks.callbacks.after_agent_callback2 + - name: callbacks.callbacks.after_agent_callback3 +before_model_callbacks: + - name: callbacks.callbacks.before_model_callback +after_model_callbacks: + - name: callbacks.callbacks.after_model_callback +before_tool_callbacks: + - name: callbacks.callbacks.before_tool_callback1 + - name: callbacks.callbacks.before_tool_callback2 + - name: callbacks.callbacks.before_tool_callback3 +after_tool_callbacks: + - name: callbacks.callbacks.after_tool_callback1 + - name: callbacks.callbacks.after_tool_callback2 + - name: callbacks.callbacks.after_tool_callback3 diff --git a/contributing/samples/core_callback/tools.py b/contributing/samples/core_callback/tools.py new file mode 100644 index 00000000..6d6e3111 --- /dev/null +++ b/contributing/samples/core_callback/tools.py @@ -0,0 +1,48 @@ +import random + +from google.adk.tools.tool_context import ToolContext + + +def roll_die(sides: int, tool_context: ToolContext) -> int: + """Roll a die and return the rolled result. + + Args: + sides: The integer number of sides the die has. + + Returns: + An integer of the result of rolling the die. + """ + result = random.randint(1, sides) + if not 'rolls' in tool_context.state: + tool_context.state['rolls'] = [] + + tool_context.state['rolls'] = tool_context.state['rolls'] + [result] + return result + + +def check_prime(nums: list[int]) -> str: + """Check if a given list of numbers are prime. + + Args: + nums: The list of numbers to check. + + Returns: + A str indicating which number is prime. + """ + primes = set() + for number in nums: + number = int(number) + if number <= 1: + continue + is_prime = True + for i in range(2, int(number**0.5) + 1): + if number % i == 0: + is_prime = False + break + if is_prime: + primes.add(number) + return ( + 'No prime numbers found.' + if not primes + else f"{', '.join(str(num) for num in primes)} are prime numbers." + ) diff --git a/contributing/samples/core_config/root_agent.yaml b/contributing/samples/core_config/root_agent.yaml new file mode 100644 index 00000000..6c108539 --- /dev/null +++ b/contributing/samples/core_config/root_agent.yaml @@ -0,0 +1,10 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +name: search_agent +model: gemini-2.0-flash +description: 'an agent whose job it is to perform Google search queries and answer questions about the results.' +instruction: You are an agent whose job is to perform Google search queries and answer questions about the results. +tools: + - name: google_search +generate_content_config: + temperature: 0.1 + max_output_tokens: 2000 diff --git a/contributing/samples/core_custom_agent/__init__.py b/contributing/samples/core_custom_agent/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/contributing/samples/core_custom_agent/my_agents.py b/contributing/samples/core_custom_agent/my_agents.py new file mode 100644 index 00000000..6dfa07e1 --- /dev/null +++ b/contributing/samples/core_custom_agent/my_agents.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from keyword import kwlist +from typing import Any +from typing import AsyncGenerator +from typing import ClassVar +from typing import Dict +from typing import Type + +from google.adk.agents import BaseAgent +from google.adk.agents.base_agent_config import BaseAgentConfig +from google.adk.agents.invocation_context import InvocationContext +from google.adk.events.event import Event +from google.genai import types +from pydantic import ConfigDict +from typing_extensions import override + + +class MyCustomAgentConfig(BaseAgentConfig): + model_config = ConfigDict( + extra="forbid", + ) + agent_class: str = "core_cutom_agent.my_agents.MyCustomAgent" + my_field: str = "" + + +class MyCustomAgent(BaseAgent): + my_field: str = "" + + config_type: ClassVar[type[BaseAgentConfig]] = MyCustomAgentConfig + + @override + @classmethod + def _parse_config( + cls: Type[MyCustomAgent], + config: MyCustomAgentConfig, + config_abs_path: str, + kwargs: Dict[str, Any], + ) -> Dict[str, Any]: + if config.my_field: + kwargs["my_field"] = config.my_field + return kwargs + + async def _run_async_impl( + self, ctx: InvocationContext + ) -> AsyncGenerator[Event, None]: + yield Event( + invocation_id=ctx.invocation_id, + author=self.name, + content=types.ModelContent( + parts=[ + types.Part( + text=f"I feel good! value in my_field: `{self.my_field}`" + ) + ] + ), + ) diff --git a/contributing/samples/core_custom_agent/root_agent.yaml b/contributing/samples/core_custom_agent/root_agent.yaml new file mode 100644 index 00000000..220cff71 --- /dev/null +++ b/contributing/samples/core_custom_agent/root_agent.yaml @@ -0,0 +1,5 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +name: working_agent +agent_class: core_custom_agent.my_agents.MyCustomAgent +description: Handles all the work. +my_field: my_field_value diff --git a/contributing/samples/ma_llm/README.md b/contributing/samples/ma_llm/README.md new file mode 100644 index 00000000..302ccfc3 --- /dev/null +++ b/contributing/samples/ma_llm/README.md @@ -0,0 +1,3 @@ +# Config-based Agent Sample - LLM multi-agent + +http://google3/third_party/py/google/adk/open_source_workspace/contributing/samples/hello_world_ma/ diff --git a/contributing/samples/ma_llm/__init__.py b/contributing/samples/ma_llm/__init__.py new file mode 100644 index 00000000..65158660 --- /dev/null +++ b/contributing/samples/ma_llm/__init__.py @@ -0,0 +1,74 @@ +import random + +from google.adk.examples.example import Example +from google.adk.tools.example_tool import ExampleTool +from google.genai import types + + +def roll_die(sides: int) -> int: + """Roll a die and return the rolled result.""" + return random.randint(1, sides) + + +def check_prime(nums: list[int]) -> str: + """Check if a given list of numbers are prime.""" + primes = set() + for number in nums: + number = int(number) + if number <= 1: + continue + is_prime = True + for i in range(2, int(number**0.5) + 1): + if number % i == 0: + is_prime = False + break + if is_prime: + primes.add(number) + return ( + "No prime numbers found." + if not primes + else f"{', '.join(str(num) for num in primes)} are prime numbers." + ) + + +example_tool = ExampleTool( + examples=[ + Example( + input=types.UserContent( + parts=[types.Part(text="Roll a 6-sided die.")] + ), + output=[ + types.ModelContent( + parts=[types.Part(text="I rolled a 4 for you.")] + ) + ], + ), + Example( + input=types.UserContent( + parts=[types.Part(text="Is 7 a prime number?")] + ), + output=[ + types.ModelContent( + parts=[types.Part(text="Yes, 7 is a prime number.")] + ) + ], + ), + Example( + input=types.UserContent( + parts=[ + types.Part( + text="Roll a 10-sided die and check if it's prime." + ) + ] + ), + output=[ + types.ModelContent( + parts=[types.Part(text="I rolled an 8 for you.")] + ), + types.ModelContent( + parts=[types.Part(text="8 is not a prime number.")] + ), + ], + ), + ] +) diff --git a/contributing/samples/ma_llm/prime_agent.yaml b/contributing/samples/ma_llm/prime_agent.yaml new file mode 100644 index 00000000..e5c94839 --- /dev/null +++ b/contributing/samples/ma_llm/prime_agent.yaml @@ -0,0 +1,12 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +agent_class: LlmAgent +model: gemini-2.5-flash +name: prime_agent +description: Handles checking if numbers are prime. +instruction: | + You are responsible for checking whether numbers are prime. + When asked to check primes, you must call the check_prime tool with a list of integers. + Never attempt to determine prime numbers manually. + Return the prime number results to the root agent. +tools: + - name: ma_llm.check_prime diff --git a/contributing/samples/ma_llm/roll_agent.yaml b/contributing/samples/ma_llm/roll_agent.yaml new file mode 100644 index 00000000..ba0ca6f2 --- /dev/null +++ b/contributing/samples/ma_llm/roll_agent.yaml @@ -0,0 +1,11 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +agent_class: LlmAgent +model: gemini-2.5-flash +name: roll_agent +description: Handles rolling dice of different sizes. +instruction: | + You are responsible for rolling dice based on the user's request. + + When asked to roll a die, you must call the roll_die tool with the number of sides as an integer. +tools: + - name: ma_llm.roll_die diff --git a/contributing/samples/ma_llm/root_agent.yaml b/contributing/samples/ma_llm/root_agent.yaml new file mode 100644 index 00000000..280f7460 --- /dev/null +++ b/contributing/samples/ma_llm/root_agent.yaml @@ -0,0 +1,26 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +agent_class: LlmAgent +model: gemini-2.5-flash +name: root_agent +description: Iterative writing pipeline agent. +# global_instruction: You are DicePrimeBot, ready to roll dice and check prime numbers. +instruction: | + You are a helpful assistant that can roll dice and check if numbers are prime. + + You delegate rolling dice tasks to the roll_agent and prime checking tasks to the prime_agent. + + Follow these steps: + 1. If the user asks to roll a die, delegate to the roll_agent. + 2. If the user asks to check primes, delegate to the prime_agent. + 3. If the user asks to roll a die and then check if the result is prime, call roll_agent first, then pass the result to prime_agent. + + Always clarify the results before proceeding. +sub_agents: + - config_path: roll_agent.yaml + - config_path: prime_agent.yaml +tools: + - name: ma_llm.example_tool +generate_content_config: + safety_settings: + - category: HARM_CATEGORY_DANGEROUS_CONTENT + threshold: 'OFF' diff --git a/contributing/samples/ma_loop/README.md b/contributing/samples/ma_loop/README.md new file mode 100644 index 00000000..136a44ec --- /dev/null +++ b/contributing/samples/ma_loop/README.md @@ -0,0 +1,16 @@ +# Config-based Agent Sample - Sequential and Loop Workflow + +A multi-agent setup with a sequential and loop workflow. + +The whole process is: + +1. An initial writing agent will author a 1-2 sentence as starting point. +2. A critic agent will review and provide feedback. +3. A refiner agent will revise based on critic agent's feedback. +4. Loop back to #2 until critic agent says "No major issues found." + +Sample queries: + +> initial topic: badminton + +> initial topic: AI hurts human diff --git a/contributing/samples/ma_loop/loop_agent.yaml b/contributing/samples/ma_loop/loop_agent.yaml new file mode 100644 index 00000000..944b6a07 --- /dev/null +++ b/contributing/samples/ma_loop/loop_agent.yaml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +agent_class: LoopAgent +name: RefinementLoop +description: Refinement loop agent. +max_iterations: 5 +sub_agents: + - config_path: writer_agents/critic_agent.yaml + - config_path: writer_agents/refiner_agent.yaml diff --git a/contributing/samples/ma_loop/root_agent.yaml b/contributing/samples/ma_loop/root_agent.yaml new file mode 100644 index 00000000..92c7d0c9 --- /dev/null +++ b/contributing/samples/ma_loop/root_agent.yaml @@ -0,0 +1,7 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +agent_class: SequentialAgent +name: IterativeWritingPipeline +description: Iterative writing pipeline agent. +sub_agents: + - config_path: writer_agents/initial_writer_agent.yaml + - config_path: loop_agent.yaml diff --git a/contributing/samples/ma_loop/writer_agents/critic_agent.yaml b/contributing/samples/ma_loop/writer_agents/critic_agent.yaml new file mode 100644 index 00000000..b11b0ac5 --- /dev/null +++ b/contributing/samples/ma_loop/writer_agents/critic_agent.yaml @@ -0,0 +1,32 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +agent_class: LlmAgent +name: CriticAgent +model: gemini-2.5-pro +description: Reviews the current draft, providing critique if clear improvements are needed, otherwise signals completion. +instruction: | + You are a Constructive Critic AI reviewing a document draft (typically at least 10 sentences). Your goal is balanced feedback. + + **Document to Review:** + ``` + {{current_document}} + ``` + + **Task:** + Review the document for the following cretiria: + + - content length: at least 10 sentences; + - clarity: the content must be clear; + - engagement: the content should be engaging and relevant to the topic; + - basic coherence according to the initial topic (if known). + + IF you identify 1-2 *clear and actionable* ways the document could be improved to better capture the topic or enhance reader engagement (e.g., "Needs a stronger opening sentence", "Clarify the character's goal"): + Provide these specific suggestions concisely. Output *only* the critique text. + + ELSE IF the document is coherent, addresses the topic adequately for its length, and has no glaring errors or obvious omissions: + Respond *exactly* with the phrase "No major issues found." and nothing else. It doesn't need to be perfect, just functionally complete for this stage. Avoid suggesting purely subjective stylistic preferences if the core is sound. + + Do not add explanations. Output only the critique OR the exact completion phrase. + + IF output the critique, ONLY output JUST ONE aspect each time. +include_contents: none +output_key: criticism diff --git a/contributing/samples/ma_loop/writer_agents/initial_writer_agent.yaml b/contributing/samples/ma_loop/writer_agents/initial_writer_agent.yaml new file mode 100644 index 00000000..bbfe4036 --- /dev/null +++ b/contributing/samples/ma_loop/writer_agents/initial_writer_agent.yaml @@ -0,0 +1,13 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +agent_class: LlmAgent +name: InitialWriterAgent +model: gemini-2.0-flash +description: Writes the initial document draft based on the topic, aiming for some initial substance. +instruction: | + You are a Creative Writing Assistant tasked with starting a story. + + Write the *first draft* of a short story (aim for 1-2 sentences). + Base the content *only* on the topic provided by user. Try to introduce a specific element (like a character, a setting detail, or a starting action) to make it engaging. + + Output *only* the story/document text. Do not add introductions or explanations. +output_key: current_document diff --git a/contributing/samples/ma_loop/writer_agents/refiner_agent.yaml b/contributing/samples/ma_loop/writer_agents/refiner_agent.yaml new file mode 100644 index 00000000..ded3442c --- /dev/null +++ b/contributing/samples/ma_loop/writer_agents/refiner_agent.yaml @@ -0,0 +1,25 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +agent_class: LlmAgent +name: RefinerAgent +model: gemini-2.0-flash +description: Refines the document based on critique, or calls exit_loop if critique indicates completion. +instruction: | + You are a Creative Writing Assistant refining a document based on feedback OR exiting the process. + **Current Document:** + ``` + {{current_document}} + ``` + **Critique/Suggestions:** + {{criticism}} + + **Task:** + Analyze the 'Critique/Suggestions'. + IF the critique is *exactly* "No major issues found.": + You MUST call the 'exit_loop' function. Do not output any text. + ELSE (the critique contains actionable feedback): + Carefully apply the suggestions to improve the 'Current Document'. Output *only* the refined document text. + + Do not add explanations. Either output the refined document OR call the exit_loop function. +output_key: current_document +tools: + - name: exit_loop diff --git a/contributing/samples/ma_seq/README.md b/contributing/samples/ma_seq/README.md new file mode 100644 index 00000000..a60d9af4 --- /dev/null +++ b/contributing/samples/ma_seq/README.md @@ -0,0 +1,13 @@ +# Config-based Agent Sample - Sequential Workflow + +A multi-agent setup with a sequential workflow. + +The whole process is: + +1. An agent backed by a cheap and fast model to write initial version. +2. An agent backed by a smarter and a little more expenstive to review the code. +3. An final agent backed by the smartest and slowest model to write the final revision. + +Sample queries: + +> Write a quicksort method in python diff --git a/contributing/samples/ma_seq/root_agent.yaml b/contributing/samples/ma_seq/root_agent.yaml new file mode 100644 index 00000000..9324b098 --- /dev/null +++ b/contributing/samples/ma_seq/root_agent.yaml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +agent_class: SequentialAgent +name: CodePipelineAgent +description: Executes a sequence of code writing, reviewing, and refactoring. +sub_agents: + - config_path: sub_agents/code_writer_agent.yaml + - config_path: sub_agents/code_reviewer_agent.yaml + - config_path: sub_agents/code_refactorer_agent.yaml diff --git a/contributing/samples/ma_seq/sub_agents/code_refactorer_agent.yaml b/contributing/samples/ma_seq/sub_agents/code_refactorer_agent.yaml new file mode 100644 index 00000000..eed4e3f7 --- /dev/null +++ b/contributing/samples/ma_seq/sub_agents/code_refactorer_agent.yaml @@ -0,0 +1,26 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +agent_class: LlmAgent +name: CodeRefactorerAgent +model: gemini-2.5-pro +description: Refactors code based on review comments. +instruction: | + You are a Python Code Refactoring AI. + Your goal is to improve the given Python code based on the provided review comments. + + **Original Code:** + ```python + {generated_code} + ``` + + **Review Comments:** + {review_comments} + + **Task:** + Carefully apply the suggestions from the review comments to refactor the original code. + If the review comments state "No major issues found," return the original code unchanged. + Ensure the final code is complete, functional, and includes necessary imports and docstrings. + + **Output:** + Output *only* the final, refactored Python code block, enclosed in triple backticks (```python ... ```). + Do not add any other text before or after the code block. +output_key: refactored_code diff --git a/contributing/samples/ma_seq/sub_agents/code_reviewer_agent.yaml b/contributing/samples/ma_seq/sub_agents/code_reviewer_agent.yaml new file mode 100644 index 00000000..267db6d5 --- /dev/null +++ b/contributing/samples/ma_seq/sub_agents/code_reviewer_agent.yaml @@ -0,0 +1,26 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +agent_class: LlmAgent +name: CodeReviewerAgent +model: gemini-2.5-flash +description: Reviews code and provides feedback. +instruction: | + You are an expert Python Code Reviewer. + Your task is to provide constructive feedback on the provided code. + + **Code to Review:** + ```python + {generated_code} + ``` + + **Review Criteria:** + 1. **Correctness:** Does the code work as intended? Are there logic errors? + 2. **Readability:** Is the code clear and easy to understand? Follows PEP 8 style guidelines? + 3. **Efficiency:** Is the code reasonably efficient? Any obvious performance bottlenecks? + 4. **Edge Cases:** Does the code handle potential edge cases or invalid inputs gracefully? + 5. **Best Practices:** Does the code follow common Python best practices? + + **Output:** + Provide your feedback as a concise, bulleted list. Focus on the most important points for improvement. + If the code is excellent and requires no changes, simply state: "No major issues found." + Output *only* the review comments or the "No major issues" statement. +output_key: review_comments diff --git a/contributing/samples/ma_seq/sub_agents/code_writer_agent.yaml b/contributing/samples/ma_seq/sub_agents/code_writer_agent.yaml new file mode 100644 index 00000000..ce57e154 --- /dev/null +++ b/contributing/samples/ma_seq/sub_agents/code_writer_agent.yaml @@ -0,0 +1,11 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +agent_class: LlmAgent +name: CodeWriterAgent +model: gemini-2.0-flash +description: Writes initial Python code based on a specification. +instruction: | + You are a Python Code Generator. + Based *only* on the user's request, write Python code that fulfills the requirement. + Output *only* the complete Python code block, enclosed in triple backticks (```python ... ```). + Do not add any other text before or after the code block. +output_key: generated_code diff --git a/contributing/samples/sub_agents/__init__.py b/contributing/samples/sub_agents/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/contributing/samples/sub_agents/life_agent.py b/contributing/samples/sub_agents/life_agent.py new file mode 100644 index 00000000..a1a1178d --- /dev/null +++ b/contributing/samples/sub_agents/life_agent.py @@ -0,0 +1,10 @@ +from google.adk.agents import LlmAgent + +agent = LlmAgent( + name="life_agent", + description="Life agent", + instruction=( + "You are a life agent. You are responsible for answering" + " questions about life." + ), +) diff --git a/contributing/samples/sub_agents/root_agent.yaml b/contributing/samples/sub_agents/root_agent.yaml new file mode 100644 index 00000000..9ef82795 --- /dev/null +++ b/contributing/samples/sub_agents/root_agent.yaml @@ -0,0 +1,11 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +name: root_agent +model: gemini-2.0-flash +description: Root agent +instruction: | + If the user query is about life, you should route it to the life sub-agent. + If the user query is about work, you should route it to the work sub-agent. + If the user query is about anything else, you should answer it yourself. +sub_agents: + - config_path: ./work_agent.yaml + - code: sub_agents.life_agent.agent diff --git a/contributing/samples/sub_agents/work_agent.yaml b/contributing/samples/sub_agents/work_agent.yaml new file mode 100644 index 00000000..f2faf8ce --- /dev/null +++ b/contributing/samples/sub_agents/work_agent.yaml @@ -0,0 +1,5 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +name: work_agent +description: Work agent +instruction: | + You are a work agent. You are responsible for answering questions about work. diff --git a/contributing/samples/tool_agent_tool/root_agent.yaml b/contributing/samples/tool_agent_tool/root_agent.yaml new file mode 100644 index 00000000..e2d758f7 --- /dev/null +++ b/contributing/samples/tool_agent_tool/root_agent.yaml @@ -0,0 +1,19 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +name: research_assistant_agent +model: gemini-2.0-flash +description: 'research assistant agent that can perform web search and summarize the results.' +instruction: | + You can perform web search and summarize the results. + You should always use the web_search_agent to get the latest information. + You should always use the summarizer_agent to summarize the results. +tools: + - name: AgentTool + args: + agent: + config_path: ./web_search_agent.yaml + skip_summarization: False + - name: AgentTool + args: + agent: + config_path: ./summarizer_agent.yaml + skip_summarization: False diff --git a/contributing/samples/tool_agent_tool/summarizer_agent.yaml b/contributing/samples/tool_agent_tool/summarizer_agent.yaml new file mode 100644 index 00000000..e919f041 --- /dev/null +++ b/contributing/samples/tool_agent_tool/summarizer_agent.yaml @@ -0,0 +1,5 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +name: summarizer_agent +model: gemini-2.0-flash +description: 'summarizer agent that can summarize text.' +instruction: "Given a text, summarize it." diff --git a/contributing/samples/tool_agent_tool/web_search_agent.yaml b/contributing/samples/tool_agent_tool/web_search_agent.yaml new file mode 100644 index 00000000..3476b967 --- /dev/null +++ b/contributing/samples/tool_agent_tool/web_search_agent.yaml @@ -0,0 +1,7 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +name: web_search_agent +model: gemini-2.0-flash +description: 'an agent whose job it is to perform web search and return the results.' +instruction: You are an agent whose job is to perform web search and return the results. +tools: + - name: google_search diff --git a/contributing/samples/tool_builtin/root_agent.yaml b/contributing/samples/tool_builtin/root_agent.yaml new file mode 100644 index 00000000..6986fe4c --- /dev/null +++ b/contributing/samples/tool_builtin/root_agent.yaml @@ -0,0 +1,7 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +name: search_agent +model: gemini-2.0-flash +description: 'an agent whose job it is to perform Google search queries and answer questions about the results.' +instruction: You are an agent whose job is to perform Google search queries and answer questions about the results. +tools: + - name: google_search diff --git a/contributing/samples/tool_functions/__init__.py b/contributing/samples/tool_functions/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/contributing/samples/tool_functions/root_agent.yaml b/contributing/samples/tool_functions/root_agent.yaml new file mode 100644 index 00000000..9ca3befc --- /dev/null +++ b/contributing/samples/tool_functions/root_agent.yaml @@ -0,0 +1,23 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +name: hello_world_agent +model: gemini-2.0-flash +description: 'hello world agent that can roll a dice and check prime numbers.' +instruction: | + You roll dice and answer questions about the outcome of the dice rolls. + You can roll dice of different sizes. + You can use multiple tools in parallel by calling functions in parallel(in one request and in one round). + It is ok to discuss previous dice roles, and comment on the dice rolls. + When you are asked to roll a die, you must call the roll_die tool with the number of sides. Be sure to pass in an integer. Do not pass in a string. + You should never roll a die on your own. + When checking prime numbers, call the check_prime tool with a list of integers. Be sure to pass in a list of integers. You should never pass in a string. + You should not check prime numbers before calling the tool. + When you are asked to roll a die and check prime numbers, you should always make the following two function calls: + 1. You should first call the roll_die tool to get a roll. Wait for the function response before calling the check_prime tool. + 2. After you get the function response from roll_die tool, you should call the check_prime tool with the roll_die result. + 2.1 If user asks you to check primes based on previous rolls, make sure you include the previous rolls in the list. + 3. When you respond, you must include the roll_die result from step 1. + You should always perform the previous 3 steps when asking for a roll and checking prime numbers. + You should not rely on the previous history on prime results. +tools: + - name: tools.tools.roll_die + - name: tools.tools.check_prime diff --git a/contributing/samples/tool_functions/tools.py b/contributing/samples/tool_functions/tools.py new file mode 100644 index 00000000..a87befff --- /dev/null +++ b/contributing/samples/tool_functions/tools.py @@ -0,0 +1,48 @@ +import random + +from google.adk.tools.tool_context import ToolContext + + +def roll_die(sides: int, tool_context: ToolContext) -> int: + """Roll a die and return the rolled result. + + Args: + sides: The integer number of sides the die has. + + Returns: + An integer of the result of rolling the die. + """ + result = random.randint(1, sides) + if not 'rolls' in tool_context.state: + tool_context.state['rolls'] = [] + + tool_context.state['rolls'] = tool_context.state['rolls'] + [result] + return result + + +async def check_prime(nums: list[int]) -> str: + """Check if a given list of numbers are prime. + + Args: + nums: The list of numbers to check. + + Returns: + A str indicating which number is prime. + """ + primes = set() + for number in nums: + number = int(number) + if number <= 1: + continue + is_prime = True + for i in range(2, int(number**0.5) + 1): + if number % i == 0: + is_prime = False + break + if is_prime: + primes.add(number) + return ( + 'No prime numbers found.' + if not primes + else f"{', '.join(str(num) for num in primes)} are prime numbers." + ) diff --git a/contributing/samples/tool_human_in_the_loop/README.md b/contributing/samples/tool_human_in_the_loop/README.md new file mode 100644 index 00000000..432cd9aa --- /dev/null +++ b/contributing/samples/tool_human_in_the_loop/README.md @@ -0,0 +1,3 @@ +# Config-based Agent Sample - Human-In-The-Loop + +http://google3/third_party/py/google/adk/open_source_workspace/contributing/samples/human_in_loop/ \ No newline at end of file diff --git a/contributing/samples/tool_human_in_the_loop/__init__.py b/contributing/samples/tool_human_in_the_loop/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/contributing/samples/tool_human_in_the_loop/root_agent.yaml b/contributing/samples/tool_human_in_the_loop/root_agent.yaml new file mode 100644 index 00000000..9bf47340 --- /dev/null +++ b/contributing/samples/tool_human_in_the_loop/root_agent.yaml @@ -0,0 +1,17 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +name: reimbursement_agent +model: gemini-2.0-flash +instruction: | + You are an agent whose job is to handle the reimbursement process for + the employees. If the amount is less than $100, you will automatically + approve the reimbursement. + + If the amount is greater than $100, you will + ask for approval from the manager. If the manager approves, you will + call reimburse() to reimburse the amount to the employee. If the manager + rejects, you will inform the employee of the rejection. +tools: + - name: tool_human_in_the_loop.tools.reimburse + - name: LongRunningFunctionTool + args: + func: tool_human_in_the_loop.tools.ask_for_approval diff --git a/contributing/samples/tool_human_in_the_loop/tools.py b/contributing/samples/tool_human_in_the_loop/tools.py new file mode 100644 index 00000000..fe5d9e19 --- /dev/null +++ b/contributing/samples/tool_human_in_the_loop/tools.py @@ -0,0 +1,21 @@ +from typing import Any + +from google.adk.tools.tool_context import ToolContext + + +def reimburse(purpose: str, amount: float) -> str: + """Reimburse the amount of money to the employee.""" + return { + 'status': 'ok', + } + + +def ask_for_approval( + purpose: str, amount: float, tool_context: ToolContext +) -> dict[str, Any]: + """Ask for approval for the reimbursement.""" + return { + 'status': 'pending', + 'amount': amount, + 'ticketId': 'reimbursement-ticket-001', + } diff --git a/contributing/samples/tool_mcp_stdio_notion/root_agent.yaml b/contributing/samples/tool_mcp_stdio_notion/root_agent.yaml new file mode 100644 index 00000000..ddd68142 --- /dev/null +++ b/contributing/samples/tool_mcp_stdio_notion/root_agent.yaml @@ -0,0 +1,16 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json +name: notion_agent +model: gemini-2.0-flash +instruction: | + You are my workspace assistant. Use the provided tools to read, search, comment on, or create + Notion pages. Ask clarifying questions when unsure. +tools: +- name: MCPToolset + args: + stdio_server_params: + command: "npx" + args: + - "-y" + - "@notionhq/notion-mcp-server" + env: + OPENAPI_MCP_HEADERS: "{'Authorization': 'Bearer fake_notion_api_key', 'Notion-Version': '2022-06-28'}" \ No newline at end of file