Google ADK Memory
A real BaseMemoryService for Google’s Agent Development Kit: ingestion methods that actually store your sessions, powering ADK’s native load_memory and preload_memory tools with durable cross-session memory — plus a guaranteed per-turn context tool, a capture callback, five agent memory tools, and async helpers.
What the integration provides
| Layer | What it does |
|---|---|
MemorySyncMemoryService | All four BaseMemoryService methods implemented — add_session_to_memory, add_events_to_memory, add_memory, search_memory. Plug it into Runner(memory_service=...) and ADK’s own memory tools just work. |
MemorySyncContextTool | Guaranteed memory injection before every turn plus automatic user-turn persistence — no memory-tool calls left to model discretion. |
create_memory_callbacks | An after_model_callback that sends the user’s message once the model has answered it. The model’s reply is not sent. |
| Agent tools + helpers | create_memorysync_tools() — five never-raise tools — and async helpers get_memory_context, search_memories, save_turn. |
| Mem0 | Supermemory | Zep | MemorySync | |
|---|---|---|---|---|
| ADK integration exists | ✗ nothing shipped | ✗ nothing shipped | △ zep-adk | ✓ google-adk-memorysync |
add_session_to_memory stores the session | — | — | ✗ documented no-op | ✓ real ingestion, raises on failure |
| Works without pre-provisioning | — | — | ✗ silent skip unless user/thread pre-created | ✓ first call just works |
| Guaranteed per-turn injection | — | — | ✗ | ✓ context tool, deduped per invocation |
| Repeated ingestion converges | — | — | — | ✓ event-id idempotency seeds |
Install
pip install google-adk-memorysync google-adk
Set MEMORYSYNC_API_KEY in the environment, or pass api_key explicitly. Requires google-adk 2.0+ (Python 3.10+). This is a Python surface — for TypeScript agents use the Vercel AI SDK or Mastra integrations.
The memory service
from google.adk.agents import LlmAgentfrom google.adk.runners import Runnerfrom google.adk.sessions import InMemorySessionServicefrom google.adk.tools import preload_memoryfrom google_adk_memorysync import MemorySyncMemoryServicememory_service = MemorySyncMemoryService() # reads MEMORYSYNC_API_KEYagent = LlmAgent(name="assistant",model="your-model",instruction="You are a helpful assistant.",tools=[preload_memory], # ADK's native tool — our service powers it)runner = Runner(agent=agent,app_name="support",session_service=InMemorySessionService(),memory_service=memory_service,)# ... after a conversation, ingest the session:session = await runner.session_service.get_session(app_name="support", user_id="customer-7", session_id="thread-42")await memory_service.add_session_to_memory(session)# The next run — any session, any process, any deploy —# preload_memory injects what MemorySync knows about the user.
add_events_to_memory sends the user’s messages as they happen and converges with a later whole-session add_session_to_memory — same seeds on both paths, each message extracted exactly once. search_memory returns proper MemoryEntry objects (content, author, timestamp) so ADK’s preload_memory formatter renders them natively; search failures degrade to an empty response through on_error, never a crashed turn. By default the same user’s memory follows them across ADK apps; pass scope_to_app=True to silo each app_name.
Guaranteed context injection
from google.adk.agents import LlmAgentfrom google_adk_memorysync import MemorySyncContextToolagent = LlmAgent(name="assistant",model="your-model",instruction="You are a helpful assistant.",tools=[MemorySyncContextTool()], # inject memory + persist user turns)# Read-only variant (inject, never write):LlmAgent(name="assistant",model="your-model",instruction="You are a helpful assistant.",tools=[MemorySyncContextTool(persist=False)],)
ADK’s load_memory leaves recall to model discretion and preload_memory only injects — nothing captures what the user says as it happens. The context tool does both: it recalls for tool_context.user_id and appends the block to the request instructions — once per invocation, even when a multi-tool turn makes several model calls — then sends the user’s message to fact extraction. Outages degrade to a memoryless turn through on_error, never a crashed one.
Capture after the model answers
from google.adk.agents import LlmAgentfrom google_adk_memorysync import (MemorySyncContextTool,create_memory_callbacks,)agent = LlmAgent(name="assistant",model="your-model",instruction="You are a helpful assistant.",tools=[MemorySyncContextTool(persist=False)], # recall only**create_memory_callbacks(), # after_model_callback sends the user's message)# The model's reply is not sent: assistant replies are not stored# as memories.
Retries are recognised server-side and extract nothing twice: the context tool, the callback and save_turn derive the same content seed for a message in a session (so the tool and the callback together still extract it once, and the second send is not counted), and the memory service keys each message on its ADK event id. The two seed schemes are separate — when you also call add_session_to_memory for a session, turn per-turn capture off (persist=False, no callback) so each message is sent once.
Agent memory tools
from google.adk.agents import LlmAgentfrom google_adk_memorysync import create_memorysync_toolsagent = LlmAgent(name="assistant",model="your-model",instruction="Use the memory tools to remember durable facts.",tools=create_memorysync_tools(user_id="customer-7"),)# Untrusted agents: search + list only.create_memorysync_tools(user_id="customer-7", read_only=True)
| Tool | What it does | Failure behaviour |
|---|---|---|
add_memory | Save one durable fact; duplicate saves answer “already stored”. | Readable error string — never raises. |
search_memory | Semantic search with relevance scores. | Readable error string. |
list_memories | Newest-first listing. | Readable error string. |
update_memory | Change tags/importance. Memory text is immutable. | Readable error string. |
delete_memory | Permanent delete by id, scoped to the configured user. | Readable error string. |
Same five operations, same response strings as the LangChain, AI SDK, CrewAI, Mastra, OpenAI Agents and LlamaIndex tool sets — an agent moved between frameworks keeps behaving the same way. Plain async callables that ADK auto-wraps as FunctionTools.
Standalone helpers
from google_adk_memorysync import (get_memory_context,save_turn,search_memories,)# 1. Prompt-ready context block ("" for a new user)context = await get_memory_context("what should I cook?", user_id="customer-7")# 2. Scored raw resultshits = await search_memories("dietary preferences", user_id="customer-7")# 3. Explicit persistence — RAISES on failure (an explicit call is# owed the truth), unlike the degrading tool and callback planes.await save_turn(user_id="customer-7",user="I'm vegetarian",assistant="Noted!",session_id="thread-42",)
Public API
| Export | Kind | Notes |
|---|---|---|
MemorySyncMemoryService | BaseMemoryService | All four methods real; scope_to_app, k, on_error optional. Ingestion raises on failure; search never does. |
MemorySyncContextTool | BaseTool | Guaranteed injection + sending the user’s message; persist=False for read-only; deduped per invocation. |
create_memory_callbacks | Callback factory | Returns {after_model_callback: ...} that sends the user’s message once the model has answered; unpack into LlmAgent(**...). |
create_memorysync_tools | Tool factory | Five plain async callables ADK auto-wraps; read_only=True returns search + list only. |
get_memory_context / search_memories / save_turn | Async helpers | save_turn sends only the user message and raises on failure; it shares the context tool’s seeds. |
MemorySyncAPIError | Exception | Carries the HTTP status and server detail. |
Supported versions
| Package | Registry | Requires | Runtime |
|---|---|---|---|
google-adk-memorysync 1.1.0 | PyPI | google-adk 2.0+ | Python 3.10+ |
The test suite drives a REAL ADK Runner — the native preload_memory flow end to end, repeated-ingestion convergence, partial-event filtering, per-invocation injection dedup — and CI re-runs it against the latest google-adk 2.x release on every push, so an interface change upstream fails our pipeline before it can fail your agent.