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, persistence callbacks, 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 persists assistant turns as they happen. |
| 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.x (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="gemini-2.5-flash",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 persists events incrementally as they happen and converges with a later whole-session add_session_to_memory — same seeds on both paths, each event stored 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="gemini-2.5-flash",instruction="You are a helpful assistant.",tools=[MemorySyncContextTool()], # inject memory + persist user turns)# Read-only variant (inject, never write):LlmAgent(name="assistant",model="gemini-2.5-flash",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 persists turns as they happen. 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 persists the user’s message. Outages degrade to a memoryless turn through on_error, never a crashed one.
Assistant-side persistence
from google.adk.agents import LlmAgentfrom google_adk_memorysync import (MemorySyncContextTool,create_memory_callbacks,)agent = LlmAgent(name="assistant",model="gemini-2.5-flash",instruction="You are a helpful assistant.",tools=[MemorySyncContextTool()], # user turns + recall**create_memory_callbacks(), # assistant turns)# Every exchange now persists automatically — and every surface# shares the same idempotency seeds, so nothing double-stores.
Agent memory tools
from google.adk.agents import LlmAgentfrom google_adk_memorysync import create_memorysync_toolsagent = LlmAgent(name="assistant",model="gemini-2.5-flash",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 + user-turn persistence; persist=False for read-only; deduped per invocation. |
create_memory_callbacks | Callback factory | Returns {after_model_callback: ...} persisting assistant turns; 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 raises on failure; all share the same idempotency seeds. |
MemorySyncAPIError | Exception | Carries the HTTP status and server detail. |
Supported versions
| Package | Registry | Requires | Runtime |
|---|---|---|---|
google-adk-memorysync 1.0.0 | PyPI | google-adk >=2 <3 | 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.