Brand Search Optimization

A multi-agent system that helps e-commerce teams optimize product titles for search visibility. A root router agent orchestrates three sub-agents: keyword finding (BigQuery), search results browsing (Selenium), and comparison reporting (generator + critic loop).

Architecture

root_agent (router) → keyword_finding_agent, search_results_agent, comparison_root_agent → [comparison_generator_agent, comparison_critic_agent]

Native ADK

The original uses 12 files across 6 directories:

brand_search_optimization/
├── __init__.py
├── agent.py
├── prompt.py
├── shared_libraries/
│   ├── __init__.py
│   └── constants.py
├── tools/
│   ├── __init__.py
│   └── bq_connector.py
└── sub_agents/
    ├── keyword_finding/
    │   ├── __init__.py
    │   ├── agent.py
    │   └── prompt.py
    ├── search_results/
    │   ├── __init__.py
    │   ├── agent.py
    │   └── prompt.py
    └── comparison/
        ├── __init__.py
        ├── agent.py
        └── prompt.py
shared_libraries/constants.py (click to expand)
MODEL = "gemini-2.5-flash"
AGENT_NAME = "brand_search_optimization"
DESCRIPTION = "A helpful assistant for brand search optimization."
sub_agents/keyword_finding/agent.py (click to expand)
from google.adk.agents.llm_agent import Agent
from ...shared_libraries import constants
from ...tools import bq_connector
from . import prompt

keyword_finding_agent = Agent(
    model=constants.MODEL,
    name="keyword_finding_agent",
    description="A helpful agent to find keywords",
    instruction=prompt.KEYWORD_FINDING_AGENT_PROMPT,
    tools=[bq_connector.get_product_details_for_brand],
)
sub_agents/search_results/agent.py (click to expand)
from google.adk.agents.llm_agent import Agent
from google.adk.tools.load_artifacts_tool import load_artifacts_tool
from ...shared_libraries import constants
from . import prompt
# ... 9 Selenium tool function imports ...

search_results_agent = Agent(
    model=constants.MODEL,
    name="search_results_agent",
    description="Get top 3 search results info for a keyword using web browsing",
    instruction=prompt.SEARCH_RESULT_AGENT_PROMPT,
    tools=[go_to_url, take_screenshot, find_element_with_text,
           click_element_with_text, enter_text_into_element,
           scroll_down_screen, get_page_source, load_artifacts_tool,
           analyze_webpage_and_determine_action],
)
sub_agents/comparison/agent.py (click to expand)
from google.adk.agents.llm_agent import Agent
from ...shared_libraries import constants
from . import prompt

comparison_generator_agent = Agent(
    model=constants.MODEL,
    name="comparison_generator_agent",
    description="A helpful agent to generate comparison.",
    instruction=prompt.COMPARISON_AGENT_PROMPT,
)

comparsion_critic_agent = Agent(
    model=constants.MODEL,
    name="comparison_critic_agent",
    description="A helpful agent to critique comparison.",
    instruction=prompt.COMPARISON_CRITIC_AGENT_PROMPT,
)

comparison_root_agent = Agent(
    model=constants.MODEL,
    name="comparison_root_agent",
    description="A helpful agent to compare titles",
    instruction=prompt.COMPARISON_ROOT_AGENT_PROMPT,
    sub_agents=[comparison_generator_agent, comparsion_critic_agent],
)
agent.py — root (click to expand)
from google.adk.agents.llm_agent import Agent
from . import prompt
from .shared_libraries import constants
from .sub_agents.comparison.agent import comparison_root_agent
from .sub_agents.keyword_finding.agent import keyword_finding_agent
from .sub_agents.search_results.agent import search_results_agent

root_agent = Agent(
    model=constants.MODEL,
    name=constants.AGENT_NAME,
    description=constants.DESCRIPTION,
    instruction=prompt.ROOT_PROMPT,
    sub_agents=[keyword_finding_agent, search_results_agent, comparison_root_agent],
)

Fluent API

4 files, flat directory:

# agent.py
from adk_fluent import Agent
from dotenv import load_dotenv

from .prompt import (
    COMPARISON_CRITIC_PROMPT, COMPARISON_PROMPT, COMPARISON_ROOT_PROMPT,
    KEYWORD_FINDING_PROMPT, ROOT_PROMPT, SEARCH_RESULTS_PROMPT,
)
from .tools import (
    analyze_webpage, click_element_with_text, enter_text_into_element,
    find_element_with_text, get_page_source, get_product_details_for_brand,
    go_to_url, scroll_down_screen, take_screenshot,
)

load_dotenv()

MODEL = "gemini-2.5-flash"

# --- Sub-agents ---

keyword_finding = (
    Agent("keyword_finding_agent", MODEL)
    .describe("A helpful agent to find keywords")
    .instruct(KEYWORD_FINDING_PROMPT)
    .tool(get_product_details_for_brand)
)

search_results = (
    Agent("search_results_agent", MODEL)
    .describe("Get top 3 search results info for a keyword using web browsing")
    .instruct(SEARCH_RESULTS_PROMPT)
    .tool(go_to_url)
    .tool(take_screenshot)
    .tool(find_element_with_text)
    .tool(click_element_with_text)
    .tool(enter_text_into_element)
    .tool(scroll_down_screen)
    .tool(get_page_source)
    .tool(analyze_webpage)
)

# Comparison: generator + critic loop, managed by a routing sub-agent

comparison_generator = (
    Agent("comparison_generator_agent", MODEL)
    .describe("A helpful agent to generate comparison.")
    .instruct(COMPARISON_PROMPT)
)

comparison_critic = (
    Agent("comparison_critic_agent", MODEL)
    .describe("A helpful agent to critique comparison.")
    .instruct(COMPARISON_CRITIC_PROMPT)
)

comparison_root = (
    Agent("comparison_root_agent", MODEL)
    .describe("A helpful agent to compare titles")
    .instruct(COMPARISON_ROOT_PROMPT)
    .sub_agents([comparison_generator.build(), comparison_critic.build()])
)

# --- Root agent ---

root_agent = (
    Agent("brand_search_optimization", MODEL)
    .describe("A helpful assistant for brand search optimization.")
    .instruct(ROOT_PROMPT)
    .sub_agents([
        keyword_finding.build(),
        search_results.build(),
        comparison_root.build(),
    ])
    .build()
)

What Changed

  • constants.MODEL referenced everywhere → single MODEL variable at top of agent.py

  • constants.AGENT_NAME, constants.DESCRIPTION → inline strings

  • instruction=prompt.X.instruct(X)

  • description="...".describe("...")

  • tools=[fn1, fn2, ...].tool(fn1).tool(fn2)...

  • Agent(sub_agents=[...]).sub_agents([...])

  • Nested sub-agents (comparison_root with generator + critic) work the same way with .sub_agents() accepting .build() results

  • 12 original files across 6 directories → 4 files in 1 directory

  • No shared_libraries/constants.py needed

  • No __init__.py chain through sub-agent directories

  • Tool functions simplified with stubs (no BigQuery/Selenium dependencies)

Metrics

Metric

Native

Fluent

Reduction

Agent definition files

4

1

75%

Total files

12

4

67%

Directories

6

1

83%

import statements (agent files)

15

12

20%

Constants/config files

1

0

100%

__init__.py files

6

1

83%