Skip to main content

ADK Research Agent

Research assistant with stateful multi-turn conversation.

Difficulty: Intermediate

What You'll Learn

  • ADK LlmAgent with a custom tool (fetch_webpage)
  • agent.yaml for automatic framework detection
  • register_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

  1. Discovery: AgentVisor detects agent.yaml and selects the ADK framework provider
  2. Execution: python3 -m agentvisor.adk.runner discovers root_agent from agent.py
  3. Session wiring: AgentVisorSessionService is automatically injected — your agent code never instantiates it directly
  4. Research: The agent calls fetch_webpage → HTTP proxy → policy check → host fetches URL → content returned
  5. 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 ScopePrefixBackendNotes
Session state(none)Thread State APIPer-thread, durable across runs
User stateuser:Store APIPer-principal, cross-thread
App stateapp:Store APIGlobal, cross-thread
Temp statetemp:In-memoryNot 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