Building Composable AI Agents: ADK Agents powered by Agentspace
How we built a multi-agent system that combines semantic search with agentic execution, using custom ADK agents and Google Agentspace
Introduction
In the insanely fast evolving landscape of AI development, many leading software teams are focusing efforts on building modular, composable agents that can truly impact how their business is run. This blog explores how we integrated Google's Agent Development Kit (ADK) and Agentspace to create a multi-agent system that combines strong semantic search capabilities with real-world API integrations.
Our journey began with a simple question: "What if we could have an AI assistant that lets us quickly find job candidates via natural language search but also takes actions on that information?" The answer led us to build Pepper, an ADK-powered AI assistant that demonstrates the power of composable agent architectures.
The Challenge: Beyond Simple Retrieval
Traditional AI assistants often fall into two categories:
- Search-focused systems that excel at finding information but can't act on it
- Action-focused systems that can perform operations but lack sophisticated search capabilities
Our goal was to bridge this gap, creating a system that could:
- Perform semantic search across our large, structured HR data stored in BigQuery
- Take contextual actions based on search results (i.e. finding specific candidates and then updating or changing those candidate’s statuses)
- Maintain conversational context across multiple interactions
- Route requests intelligently to specialized agents
The Solution: Agentspace for Search, ADK agents for Actions
Our solution combines the best of both worlds: Agentspace for search and ADK function tools for actions. Note that Agentspace is just another tool in our ADK agent's toolkit.
The Pepper Routing Agent has two main responsibilities:
invoke_workable_agent
: Routes all Workable-related queries to the specialized Workable agenttranslate_genz_lingo
: A fun tool for translating the very strange gen-z lingo these days. Highlighting here just to show that the routing agent can call its own set of other tools or invoke other agents as tools.
The Workable Agent contains:
query_workable_agentspace
: Semantic search tool for querying BigQuery via Agentspace- Direct API tools: Functions for candidate operations (disqualify, update, etc.)
When a user asks "find candidates who applied last month from outside of the US", the flow is:
- Pepper recognizes this as a Workable-related query and routes the entire request to the Workable Agent
- Workable Agent determines it's a search query and uses its Agentspace tool
- Stores the full response (not just IDs) in its context
When the user follows up with "disqualify them all":
- Pepper again routes to the Workable Agent
- Workable Agent recognizes this as an action query
- Uses the stored context from the previous search
- Executes the appropriate API tools with the context
This pattern - search via Agentspace, act via API tools, maintain context between them - is the core of our architecture. Agentspace becomes a powerful semantic search layer that feeds context to our action tools.
Architecture & Demo

Here's a real example. User says: "Find all software engineers who applied this month and disqualify the ones without work authorization."
- Pepper Agent recognizes Workable intent and calls
invoke_workable_agent("find software engineers who applied this month and disqualify ones without work authorization")
- Workable Agent analyzes the request and determines it needs both search and action:
- First calls its
query_workable_agentspace("backend engineers applied this month")
tool - Agentspace returns rich candidate data from BigQuery, including snippets about visa status
- Stores this context internally
- Parses the results for candidates without authorization
- Calls
list_disqualification_reasons()
to find the right reason ID - Calls
batch_disqualify_candidates()
with the candidate IDs and reason
- First calls its
The user gets one seamless experience, but underneath we've orchestrated semantic search and multiple API calls across different systems.
1. Pepper Agent: the Routing Layer with Intelligent Intent Detection
The Pepper Routing Agent serves as our orchestrator, implemented as an ADK Agent with specialized tools:
root_agent = Agent(
model="gemini-2.5-flash",
name="pepper_routing_agent",
instruction=return_instructions_root(),
tools=[
pepper_tools.invoke_workable_agent,
pepper_tools.translate_genz_lingo,
]
)
This agent analyzes user intent and routes requests to the appropriate specialized service, maintaining context throughout the conversation. For example, if Pepper determines the user intent is related to a job for the Workable Agent, it will simply route the request:
def invoke_workable_agent(request: str) -> dict:
"""Route Workable-related requests to the specialized agent."""
# Get the deployed agent
workable_agent = agent_engines.get(
os.environ.get("WORKABLE_AGENT_ENGINE_ID")
)
# Pass the request directly - Workable agent handles all context
session = workable_agent.start_chat(
context={"workable_api_token": os.environ.get("WORKABLE_API_TOKEN")}
)
response = session.send_message(request)
return process_streaming_response(response)
The Workable Agent maintains its own context throughout the conversation so we don’t need complex context passing between the two agents.
2. Workable Agent for Semantic Search + Execution on HR Stuff
As mentioned, our Workable agent is a specialized agent with tool design to interact with the Workable API and execute actions that we want based on the context gathered from the Agentspace semantic search tool:
root_agent = LlmAgent(
model="gemini-2.5-flash",
name="szns_workable_adk_agent",
instruction=return_instructions_root(),
tools=[
# Semantic Search Tool (Agentspace)
workable_tools.query_workable_agentspace,
# API Tools (Calls the Workable API)
workable_tools.list_candidates,
workable_tools.list_disqualification_reasons,
workable_tools.list_members,
workable_tools.list_stages,
workable_tools.list_jobs,
workable_tools.get_default_member_id,
workable_tools.get_candidate_by_id,
workable_tools.disqualify_candidate_by_identifier,
workable_tools.batch_disqualify_candidates,
workable_tools.update_candidate_by_identifier,
workable_tools.update_custom_attribute_by_identifier,
workable_tools.find_candidate_by_name_or_email,
]
)
For information retrieval, we integrate with Google's Vertex AI Search (Agentspace), which provides semantic search over our BigQuery datastore.
def query_workable_agentspace(query: str) -> dict:
"""Query candidates using natural language via Agentspace.
This tool uses semantic search on connected Datastores. In our case,
we've connected the Agentspace app to a BigQuery datastore with our Workable
data.
"""
# Use Answer API for semantic understanding
endpoint = f"{BASE_URL}/answer"
payload = {
"query": query, # Pass user's natural language as-is
"answerGenerationSpec": {
"modelSpec": {"modelVersion": "GEMINI_1_5_FLASH_001"},
"promptSpec": {"preamble": "You are an HR assistant..."}
},
"searchSpec": {
"searchParams": {
"maxReturnResults": 10,
"filter": "" # Can add metadata filters if needed
}
}
}
response = requests.post(endpoint, json=payload, headers=get_gcp_headers())
# Parse the semantic search results
return parse_agentspace_response(response.json())
We store the entire Agentspace response payload, not just extracted candidate IDs. This broader context enables our action agent to make better decisions. Then the Workable Agent uses relevant tools to execute actions using its Workable API-based function tools.
Why ADK?
We chose ADK over other agent frameworks for several reasons. ADK provides a consistent framework for managing tools, memory, and context across agents. Unlike cobbling together LangChain components or building custom orchestration layers, ADK gives you:
- Built-in tool management with automatic type inference and validation
- Session state that persists across conversations
- Native integration with Google Cloud services
- Standardized patterns for agent-to-agent communication
The composability is what sold us. You can build specialist agents that handle specific domains, then compose them into larger systems. Each agent maintains its own context and tools, but they can work together through well-defined interfaces.
More importantly, ADK handles the plumbing. Tool calling, streaming responses, error handling - it's all standardized. You write Python functions, ADK turns them into tools the LLM can use. Lastly, cloud-native integration with GCP. The SZNS Solutions team are experts at Cloud Run deployments.
Why Agentspace?
For semantic search, we evaluated building our own RAG pipeline versus using Agentspace. While building custom gives you control over embeddings, chunk sizes, and retrieval algorithms, Agentspace won for practical reasons:
- Native BigQuery connector meant zero ETL code
- Natural language search worked out of the box
- No vector database to manage or tune
- Search quality was good enough for our use case
Yes, it is a black box. You can't tweak the embedding model or adjust similarity thresholds. But for our HR search needs, the tradeoff was worth it. We went from BigQuery tables to semantic search in minutes, not days. The performance has been solid. Agentspace handles our queries about candidates, job applications, and work history without needing us to optimize embeddings or tune retrieval parameters. It just works.
If we needed more control, we could use Vertex AI's RAG Engine instead. That would let us:
- Choose specific embedding models
- Tune retrieval parameters like
similarity_top_k
andvector_distance_threshold
- Implement custom chunking strategies
- Add metadata filtering
But Agentspace's simplicity and performance made it the right choice for this project. We can always migrate to RAG Engine later if we need fine-grained control.
Conclusion
Building composable AI agents with Google's ADK and Agentspace has proven to be a powerful approach for creating sophisticated, production-ready AI systems.
SZNS Solutions is one of the first Agentspace Implementation Partners for Google Cloud, and we specialize in building production-ready agentic workflows that help businesses transform the way they work. If you want to learn more or want a demo, contact us at partner@szns.solutions