chore: import upstream snapshot with attribution
Continuous Integration / Pre-commit Linter (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.10) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.11) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.12) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.10) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.11) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.12) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.14) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Waiting to run
Copybara PR Handler / close-imported-pr (push) Waiting to run
Continuous Integration / Pre-commit Linter (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.10) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.11) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.12) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.10) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.11) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.12) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.14) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Waiting to run
Copybara PR Handler / close-imported-pr (push) Waiting to run
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
# ADK Workflow Loop Sample
|
||||
|
||||
## Overview
|
||||
|
||||
This sample demonstrates how to create a feedback loop between different nodes in **ADK Workflows**.
|
||||
|
||||
It takes a user-provided topic and uses the `generate_headline` agent to write a headline. The `evaluate_headline` agent then grades the headline as either "tech-related" or "unrelated", providing feedback if it's unrelated. The `route_headline` function checks this grade. If the headline is "unrelated", the workflow loops back to the `generate_headline` agent, passing the feedback so it can try again. This process repeats until a "tech-related" headline is generated.
|
||||
|
||||
In ADK Workflows, loops allow for iterative refinement and evaluation by conditionally routing execution back to an earlier node in the sequence.
|
||||
|
||||
## Sample Inputs
|
||||
|
||||
- `flower`
|
||||
|
||||
- `quantum mechanics`
|
||||
|
||||
- `renewable energy`
|
||||
|
||||
## Graph
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
START --> process_input
|
||||
process_input --> generate_headline
|
||||
generate_headline --> evaluate_headline
|
||||
evaluate_headline --> route_headline
|
||||
route_headline -->|unrelated| generate_headline
|
||||
route_headline -->|tech-related| END[Loop ends]
|
||||
```
|
||||
|
||||
## How To
|
||||
|
||||
1. Define a node (like `route_headline`) that yields an `Event` with a specific route based on a condition:
|
||||
|
||||
```python
|
||||
def route_headline(node_input: Feedback):
|
||||
return Event(route=node_input.grade)
|
||||
```
|
||||
|
||||
1. In the `Workflow` edges definition, create a conditional edge that connects the routing node back to a previous node in the workflow, using a routing map dict:
|
||||
|
||||
```python
|
||||
(route_headline, {"unrelated": generate_headline})
|
||||
```
|
||||
|
||||
This creates the cycle. If the route yielded by `route_headline` is "unrelated", execution jumps back to `generate_headline`.
|
||||
@@ -0,0 +1,80 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from google.adk import Agent
|
||||
from google.adk import Event
|
||||
from google.adk import Workflow
|
||||
from pydantic import BaseModel
|
||||
from pydantic import Field
|
||||
|
||||
|
||||
class Feedback(BaseModel):
|
||||
grade: Literal["tech-related", "unrelated"] = Field(
|
||||
description=(
|
||||
"Decide if the headline is related to technology or software"
|
||||
" engineering."
|
||||
),
|
||||
)
|
||||
feedback: str = Field(
|
||||
description=(
|
||||
"If the headline is unrelated to technology, provide feedback on how"
|
||||
" to make it more tech-focused."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def process_input(node_input: str):
|
||||
"""Puts user input in the state."""
|
||||
return Event(state={"topic": node_input})
|
||||
|
||||
|
||||
generate_headline = Agent(
|
||||
name="generate_headline",
|
||||
instruction="""
|
||||
Write a headline about the topic "{topic}".
|
||||
If feedback is provided, take it into account.
|
||||
The feedback: {feedback?}
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
evaluate_headline = Agent(
|
||||
name="evaluate_headline",
|
||||
instruction="""
|
||||
Grade whether the headline is related to technology or software engineering.
|
||||
""",
|
||||
output_schema=Feedback,
|
||||
output_key="feedback",
|
||||
)
|
||||
|
||||
|
||||
def route_headline(node_input: Feedback):
|
||||
return Event(route=node_input.grade)
|
||||
|
||||
|
||||
root_agent = Workflow(
|
||||
name="root_agent",
|
||||
edges=[
|
||||
(
|
||||
"START",
|
||||
process_input,
|
||||
generate_headline,
|
||||
evaluate_headline,
|
||||
route_headline,
|
||||
),
|
||||
(route_headline, {"unrelated": generate_headline}),
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"events": [
|
||||
{
|
||||
"author": "user",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"text": "computer"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"id": "e-1",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"path": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"actions": {
|
||||
"stateDelta": {
|
||||
"topic": "computer"
|
||||
}
|
||||
},
|
||||
"author": "root_agent",
|
||||
"id": "e-2",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"path": "root_agent@1/process_input@1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"author": "generate_headline",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"text": "Computers: Shaping Our World"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"finishReason": "STOP",
|
||||
"id": "e-3",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"messageAsOutput": true,
|
||||
"outputFor": [
|
||||
"root_agent@1/generate_headline@1"
|
||||
],
|
||||
"path": "root_agent@1/generate_headline@1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"actions": {
|
||||
"stateDelta": {
|
||||
"feedback": {
|
||||
"feedback": "This headline is clearly tech-related as it directly discusses computers.",
|
||||
"grade": "tech-related"
|
||||
}
|
||||
}
|
||||
},
|
||||
"author": "evaluate_headline",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"text": "{\"grade\": \"tech-related\", \"feedback\": \"This headline is clearly tech-related as it directly discusses computers.\"}"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"finishReason": "STOP",
|
||||
"id": "e-4",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"messageAsOutput": true,
|
||||
"outputFor": [
|
||||
"root_agent@1/evaluate_headline@1"
|
||||
],
|
||||
"path": "root_agent@1/evaluate_headline@1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"actions": {
|
||||
"route": "tech-related"
|
||||
},
|
||||
"author": "root_agent",
|
||||
"id": "e-5",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"path": "root_agent@1/route_headline@1"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
{
|
||||
"appName": "loop",
|
||||
"events": [
|
||||
{
|
||||
"author": "user",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"text": "flower"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"id": "e-1",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"path": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"actions": {
|
||||
"stateDelta": {
|
||||
"topic": "flower"
|
||||
}
|
||||
},
|
||||
"author": "root_agent",
|
||||
"id": "e-2",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"path": "root_agent@1/process_input@1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"author": "generate_headline",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"text": "\"Petal Power: The Timeless Allure of Flowers\""
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"finishReason": "STOP",
|
||||
"id": "e-3",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"messageAsOutput": true,
|
||||
"outputFor": [
|
||||
"root_agent@1/generate_headline@1"
|
||||
],
|
||||
"path": "root_agent@1/generate_headline@1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"actions": {
|
||||
"stateDelta": {
|
||||
"feedback": {
|
||||
"feedback": "To make this headline more tech-focused, consider incorporating elements like AI for plant recognition, robotics in gardening, biotechnology in horticulture, or data analytics related to flower cultivation and sales. For example, 'AI-Driven Botany: The Tech Unlocking Petal Power' or 'Smart Gardens: Engineering the Timeless Allure of Flowers'.",
|
||||
"grade": "unrelated"
|
||||
}
|
||||
}
|
||||
},
|
||||
"author": "evaluate_headline",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"text": "{\"grade\": \"unrelated\", \"feedback\": \"To make this headline more tech-focused, consider incorporating elements like AI for plant recognition, robotics in gardening, biotechnology in horticulture, or data analytics related to flower cultivation and sales. For example, 'AI-Driven Botany: The Tech Unlocking Petal Power' or 'Smart Gardens: Engineering the Timeless Allure of Flowers'.\"}"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"finishReason": "STOP",
|
||||
"id": "e-4",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"messageAsOutput": true,
|
||||
"outputFor": [
|
||||
"root_agent@1/evaluate_headline@1"
|
||||
],
|
||||
"path": "root_agent@1/evaluate_headline@1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"actions": {
|
||||
"route": "unrelated"
|
||||
},
|
||||
"author": "root_agent",
|
||||
"id": "e-5",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"path": "root_agent@1/route_headline@1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"author": "generate_headline",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"text": "AI-Powered Petals: The Tech Revolution Blooming in Modern Floriculture"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"finishReason": "STOP",
|
||||
"id": "e-6",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"messageAsOutput": true,
|
||||
"outputFor": [
|
||||
"root_agent@1/generate_headline@2"
|
||||
],
|
||||
"path": "root_agent@1/generate_headline@2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"actions": {
|
||||
"stateDelta": {
|
||||
"feedback": {
|
||||
"feedback": "This headline is strongly tech-related, explicitly mentioning 'AI-Powered' and 'Tech Revolution'. It effectively combines a traditional field (floriculture) with advanced technology.",
|
||||
"grade": "tech-related"
|
||||
}
|
||||
}
|
||||
},
|
||||
"author": "evaluate_headline",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"text": "{\"grade\": \"tech-related\", \"feedback\": \"This headline is strongly tech-related, explicitly mentioning 'AI-Powered' and 'Tech Revolution'. It effectively combines a traditional field (floriculture) with advanced technology.\"}"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"finishReason": "STOP",
|
||||
"id": "e-7",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"messageAsOutput": true,
|
||||
"outputFor": [
|
||||
"root_agent@1/evaluate_headline@2"
|
||||
],
|
||||
"path": "root_agent@1/evaluate_headline@2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"actions": {
|
||||
"route": "tech-related"
|
||||
},
|
||||
"author": "root_agent",
|
||||
"id": "e-8",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"path": "root_agent@1/route_headline@2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "2779014d-3ee3-429e-951d-e69756ddf789",
|
||||
"state": {
|
||||
"__session_metadata__": {
|
||||
"displayName": "flower"
|
||||
},
|
||||
"feedback": {
|
||||
"feedback": "This headline is already strongly tech-focused due to the explicit mention of AI and 'Tech Revolution'.",
|
||||
"grade": "tech-related"
|
||||
},
|
||||
"topic": "flower"
|
||||
},
|
||||
"userId": "user"
|
||||
}
|
||||
Reference in New Issue
Block a user