Google ADK
Give Google ADK agents persistent memory with automatic callbacks or explicit save and fetch tools.
GoodMem stores and indexes your agent's conversations and documents. Choose
GoodmemSaveTool and GoodmemFetchTool when the agent should decide what to
remember, or GoodmemPlugin for automatic conversation capture and retrieval.
Both use the official asynchronous GoodMem Python SDK.
Install and configure
pip install 'goodmem-adk>=0.2.0'Configure GOODMEM_BASE_URL and GOODMEM_API_KEY. An embedder must already exist
on your GoodMem server; optionally select it with GOODMEM_EMBEDDER_ID.
Constructors read these variables directly, or accept explicit arguments.
Your agent can use any ADK-supported model. For the Cohere example below, install
litellm>=1.84 and set COHERE_API_KEY. A Google login or Gemini key is not needed
when using Cohere.
Let the agent save and search
from google.adk.agents import LlmAgent
from google.adk.apps import App
from google.adk.models.lite_llm import LiteLlm
from goodmem_adk import GoodmemFetchTool, GoodmemSaveTool
root_agent = LlmAgent(
name="assistant",
model=LiteLlm(model="cohere_chat/command-a-03-2025"),
instruction=(
"Save facts when asked to remember them. Before answering questions "
"about saved facts, call goodmem_fetch, even in a fresh conversation. "
"Check tool results and report errors honestly."
),
tools=[GoodmemSaveTool(), GoodmemFetchTool()],
)
app = App(name="memory_agent", root_agent=root_agent)Save this as memory_agent/agent.py, then run adk run memory_agent.
Ask it to remember a unique fact. Start another session with the same user ID,
then ask for that fact.
Capture conversations automatically
For automatic memory, attach the plugin to an agent without memory tools:
from google.adk.agents import LlmAgent
from google.adk.apps import App
from google.adk.models.lite_llm import LiteLlm
from goodmem_adk import GoodmemPlugin
root_agent = LlmAgent(
name="assistant",
model=LiteLlm(model="cohere_chat/command-a-03-2025"),
instruction="Answer using relevant memory context. Report retrieval limitations honestly.",
)
app = App(name="memory_agent", root_agent=root_agent, plugins=[GoodmemPlugin()])The plugin persists visible messages and inline attachments, and supplies relevant chunks before model calls. Thoughts and streaming deltas are excluded. Retrieved context does not get written back into session history.
Scope and connection settings
Default space names are adk_tool_{user_id} for tools and adk_chat_{user_id} for
the plugin. They separate users, not applications. To share memory deliberately,
configure the same space_id or space_name on the components. Everyone using
that explicit scope shares its memory.
Explicit scope arguments override environment scope settings. If both an ID and a
name are supplied, they must agree. An injected AsyncGoodmem always retains its
own connection and credentials. Keep it open while the ADK app runs; the integration
will not close a caller-owned SDK. Without injection, each operation creates and
closes its own async SDK.
For private certificate authorities, pass an SDK using a configured trust store:
import ssl
import httpx
from goodmem import AsyncGoodmem
from goodmem_adk import GoodmemFetchTool
async def run_with_memory(run_agent, base_url, api_key, ca_file):
async with httpx.AsyncClient(
base_url=base_url,
headers={"x-api-key": api_key},
verify=ssl.create_default_context(cafile=ca_file),
) as http, AsyncGoodmem(http_client=http) as client:
await run_agent([GoodmemFetchTool(client=client)])Handle results
Save tools report accepted IDs and processing states, plus individual attachment
errors. Acceptance precedes indexing. Empty searches are never retried automatically;
applications that need readiness should inspect each accepted ID through SDK
memories.get(). Automatic persistence failures raise an error containing accepted
IDs, so callers can recover without uploading confirmed writes again.
Fetch results contain distinct matching chunks, original source metadata, statuses,
and a partial flag. Usable chunks remain available when another part of retrieval
fails. Unknown status codes are nonfatal and appear as UNKNOWN.
See the migration notes and complete examples. Google also maintains an ADK integration page.