ADK Research Agent
Research assistant with stateful multi-turn conversation.
Difficulty: Intermediate
What You'll Learn
- ADK
LlmAgentwith a custom tool (fetch_webpage) agent.yamlfor automatic framework detectionregister_agent()for schema extraction- Stateful multi-turn conversation via the Thread State API
- Credential brokering for
GOOGLE_API_KEY(never exposed in sandbox)
Setup
agentvisor template create adk/research-agent
cd research-agent
export GOOGLE_API_KEY=your-api-key-here
temporal server start-dev &
agentvisor serve . --sandbox=none
Test It
THREAD=$(curl -sX POST http://localhost:8090/threads | jq -r '.thread_id')
# Research a topic
curl -sX POST "http://localhost:8090/threads/$THREAD/runs?wait=120s" \
-H "Content-Type: application/json" \
-d '{"input": {"messages": [{"role": "user", "content": "Research the history of the Python programming language"}]}}' \
| jq '.output'
# Follow-up — agent remembers the previous research
curl -sX POST "http://localhost:8090/threads/$THREAD/runs?wait=120s" \
-H "Content-Type: application/json" \
-d '{"input": {"messages": [{"role": "user", "content": "What were the key design decisions that made Python popular?"}]}}' \
| jq '.output'
How It Works
- Discovery: AgentVisor detects
agent.yamland selects the ADK framework provider - Execution:
python3 -m agentvisor.adk.runnerdiscoversroot_agentfromagent.py - Session wiring:
AgentVisorSessionServiceis automatically injected — your agent code never instantiates it directly - Research: The agent calls
fetch_webpage→ HTTP proxy → policy check → host fetches URL → content returned - State persistence: Session state is stored in Temporal via the Thread State API, enabling multi-turn conversation across runs
Credential Flow
Guest Agent (GOOGLE_API_KEY=mav-tok-xxx)
└─► Guest Proxy (TLS termination)
└─► Host Proxy (token substitution: mav-tok-xxx → real key)
└─► generativelanguage.googleapis.com (x-goog-api-key: real-key)
The real GOOGLE_API_KEY is never visible inside the guest sandbox.
State Management
| ADK State Scope | Prefix | Backend | Notes |
|---|---|---|---|
| Session state | (none) | Thread State API | Per-thread, durable across runs |
| User state | user: | Store API | Per-principal, cross-thread |
| App state | app: | Store API | Global, cross-thread |
| Temp state | temp: | In-memory | Not persisted |
Project Files
research-agent/
├── agent.yaml # ADK discovery file (name + description)
├── agent.py # Root agent: LlmAgent with fetch_webpage tool
├── requirements.txt # Python dependencies
├── agentvisor.yaml # AgentVisor config (proxy credential substitution)
└── policies/
└── domain.yml # Authorization policies
agent.yaml
name: research-agent
description: >
A research assistant that fetches and summarizes web content.
Maintains stateful conversation history across turns within a thread.
When agent.yaml is present, AgentVisor automatically selects the adk framework provider. No mav-agent-config.yaml is required.
agent.py (key section)
from google.adk.agents import LlmAgent
from agentvisor.adk import register_agent
root_agent = LlmAgent(
name="researcher",
model="gemini-2.0-flash",
instruction="...",
tools=[fetch_webpage],
)
register_agent(root_agent)
Next Steps
- Google ADK Agents Guide: Deep dive into ADK integration
- CrewAI Research Agent: Same pattern with CrewAI
- LangGraph Research Agent: Same pattern with LangGraph