Skip to main content

Lab 16: Building a Hybrid News Aggregator

Goalโ€‹

In this lab, you will build a sophisticated News Aggregator using a hybrid graph architecture. You will learn how to:

  1. Run two specialized research nodes in parallel (Fan-out).
  2. Synchronize the results using a JoinNode (Fan-in).
  3. Process the combined data in a sequential step.

The Architectureโ€‹

        โ”Œโ”€โ”€โ”€โ”€ Node A (Tech News) โ”€โ”€โ”€โ”€โ”
START โ”€โ”€โ”ผ โ”œโ”€โ”€โ†’ JoinNode โ†’ Summarizer โ†’ END
โ””โ”€โ”€โ”€โ”€ Node B (Market News) โ”€โ”€โ”˜

Step 1: Create the Projectโ€‹

Environment setup:

  • Local machine: If you're continuing the course in sequence, cd into the adk-training project you created in Module 2 and skip the block below -- your environment and dependencies already carry forward. If you're starting fresh at this module, create that project first:
    uv init adk-training --python 3.10 && cd adk-training
    uv add "google-adk>=2.1.0" python-dotenv
  • DevContainer / Codespaces (Alternative): If you opened this repository in a DevContainer or GitHub Codespaces, Python, uv, and the core ADK dependencies are already pre-installed in your container environment.
uv run adk create news_aggregator
cd news_aggregator

Step 2: Implement the Specialist Nodesโ€‹

Open agent.py. Your task is to define three agents: two for research and one for synthesis.

# In agent.py (Starter Code)
from google.adk import Agent, Workflow
from google.adk.workflow import JoinNode

# TODO: Define 'tech_researcher'
# Instruction: Find 3 headlines about AI and Robotics.
# Output key: "tech_news"
tech_researcher = ...

# TODO: Define 'market_researcher'
# Instruction: Find 3 headlines about Stock Market trends.
# Output key: "market_news"
market_researcher = ...

# TODO: Define 'summarizer'
# Instruction: Combine {tech_news} and {market_news} into a newsletter.
summarizer = ...

Step 3: Assemble the Hybrid Graphโ€‹

Exercise: Complete the root_agent definition using the edges parameter to implement the Fan-out/Join pattern.

Tip: A 3-element edge tuple (A, B, C) is shorthand for two chained edges, A โ†’ B and B โ†’ C. So a parallel branch that both starts at "START" and converges on the syncer can be written as one tuple, ("START", tech_researcher, syncer), instead of two separate tuples.

# 1. Create the synchronization point
syncer = JoinNode(name="news_sync")

# 2. Build the Workflow
# TODO: Define the edges to:
# - Start both researchers in parallel from "START".
# - Converge both researchers at the 'syncer'.
# - Connect the 'syncer' to the 'summarizer'.
root_agent = Workflow(
name="NewsSystem",
edges=[
# Parallel Branch 1
("START", ..., ...),

# Parallel Branch 2
(..., ..., ...),

# Sequential Final Step
(..., ...)
]
)

Step 4: Run and Verifyโ€‹

  1. Start the Dev UI: uv run adk web .
  2. Test: Send the prompt "Give me today's update."
  3. Inspect: Open the Graph View. Verify that the researchers start at the same time and the summarizer only runs after both finish.

Known issue (upstream google-adk 2.8.0): the main static Graph View tab works fine for Workflow/JoinNode graphs like this one. However, clicking into an individual event's Graph inspector (the per-event highlight view in the Trace) currently throws a 500 error -- AttributeError: 'Workflow' object has no attribute '_graph' -- for this graph type specifically. This is an upstream ADK tooling bug, not something you did wrong. Workaround: use the main Graph View tab to inspect the overall structure instead of the per-event inspector.

Lab Summaryโ€‹

You have built a hybrid multi-agent graph!

  • You mastered Fan-out by defining multiple edges from START.
  • You mastered Fan-in using the JoinNode.
  • You learned that sequential and parallel execution are just different edge configurations.
  • A few things worth internalizing: the JoinNode waits for every incoming edge -- if one parallel branch fails, it won't fire, so production graphs need retry/error handling to guarantee convergence. Adding a third parallel branch (e.g. a sports_researcher) is just one more ("START", sports_researcher, syncer) edge -- the syncer automatically waits for all of them. And output_key matters specifically because a JoinNode merges multiple upstream outputs into one: it saves each branch's result into session state under its own name, so the summarizer can reliably pull {tech_news} and {market_news} as separate variables instead of one ambiguous blob.

๐Ÿ” Reference Solution Vaultโ€‹

๐Ÿ”

Module 16 Reference Solution Vault

Active Comprehension Gate โ€ข Zero Guessable URLs โ€ข Guided Verification

Step 1/3: Question 1 of 3

In Module 16 (Static Orchestration), what is the primary role of sequential chaining in ADK architectures?