Use with Google ADK

Connect Neo4jMemoryService to an ADK Runner, consume one complete run, persist its session, and verify the user’s exact text. This recipe selects Bolt with OpenAI embeddings and a Gemini model through the Google AI API.

1. Prepare the selected environment

Use Python 3.10+ and a POSIX shell. The recipe uses BoltSettings to select Neo4j explicitly and disables entity extraction so that message persistence can be checked independently. Use a dedicated AuraDB instance with vector-index support and no incompatible existing vectors. Follow the Aura connection setup and copy its connection values into the exports below. These recipes use the published Python 0.7.0 package; provider access and database execution must be verified in your environment.

Create a local folder and virtual environment for the examples on this page. The commands reuse an existing environment without changing its files:

mkdir -p ~/agent-memory-tutorials
cd ~/agent-memory-tutorials
if [ -e .venv ]; then
  printf '%s\n' 'Using the existing virtual environment.'
else
  python3 -m venv .venv
fi
source .venv/bin/activate

Expected: ~/agent-memory-tutorials is your working directory and its virtual environment is active. Install the published SDK with the command below.

Each complete code block labelled Save as names a file to create in this folder using your editor. Copy the entire block, including imports and the entry point. Expand each helper disclosure and use Copy code to copy its full source. Keep all files together so their imports resolve.

When continuing from another tutorial or guide, retain the existing environment, configuration, session files, and .tutorial-state/. Reuse unchanged helper files; compare an existing file before replacing it, and finish any pending cleanup or recovery before changing the code that owns its state.

python -m pip install 'neo4j-agent-memory[google-adk,openai]==0.7.0'
export NEO4J_URI='neo4j+s://<instance-id>.databases.neo4j.io'
export NEO4J_USERNAME='neo4j'
export NEO4J_PASSWORD='replace-with-your-Aura-password'
export NEO4J_DATABASE='neo4j'
export OPENAI_API_KEY='replace-with-your-provider-key'
export GOOGLE_API_KEY='replace-with-your-Google-AI-key'
unset GOOGLE_GENAI_USE_VERTEXAI
export ADK_MODEL='replace-with-an-available-Gemini-model-id'

The OpenAI-backed recipes require access to text-embedding-3-small; it produces 1536-dimensional vectors in this configuration. Configure the selected chat model separately where required. Credentials are read from the environment, never printed by the programs.

2. Read the complete recipe

Create the subdirectory below, then save both complete files under the displayed names. Run commands from agent-memory-tutorials/; Python resolves common from the script’s integrations/ directory.

mkdir -p integrations

The script imports this shared helper from the same directory. It supplies explicit database settings, closes clients through the calling context manager, and fails when expected records are absent. Task traces record the observable outcome of the call; they do not expose hidden model reasoning.

Complete integrations/common.py
Save as integrations/common.py
"""Shared Aura connection through Bolt and readback for integration recipes."""

import os


def settings(embedding=None):
    from neo4j_agent_memory import BoltSettings
    from neo4j_agent_memory.llm import from_provider

    return BoltSettings(
        neo4j={
            "uri": os.environ["NEO4J_URI"],
            "username": os.environ["NEO4J_USERNAME"],
            "password": os.environ["NEO4J_PASSWORD"],
            "database": os.getenv("NEO4J_DATABASE", "neo4j"),
        },
        embedding=embedding or from_provider("openai/text-embedding-3-small", kind="embedding"),
        extraction={"extractor_type": "none"},
    )


async def verify_messages(client, session_id, expected):
    conversation = await client.short_term.get_conversation(session_id)
    contents = [message.content for message in conversation.messages]
    if not all(text in contents for text in expected):
        raise RuntimeError(f"Message readback failed for {session_id}")
    print(f"Verified stored messages; session={session_id}")


async def recorded_turn(client, session_id, prompt, respond):
    """Record a task outcome; this does not claim to capture hidden model reasoning."""
    trace = await client.reasoning.start_trace(session_id=session_id, task=prompt)
    try:
        await client.short_term.add_message(session_id, "user", prompt, extract_entities=False)
        context = await client.get_context(prompt, session_id=session_id)
        reply = str(await respond(context))
        if not reply.strip():
            raise RuntimeError("The framework returned no text")
        await client.short_term.add_message(session_id, "assistant", reply, extract_entities=False)
        await verify_messages(client, session_id, [prompt, reply])
    except Exception as exc:
        await client.reasoning.complete_trace(
            trace.id, success=False, outcome=f"Run failed: {type(exc).__name__}"
        )
        raise
    await client.reasoning.complete_trace(
        trace.id, success=True, outcome="Reply stored and read back"
    )
    stored = await client.reasoning.get_trace_with_steps(trace.id)
    if stored is None or stored.success is not True:
        raise RuntimeError(f"Trace readback failed for {trace.id}")
    print(f"Verified task trace: {trace.id}")
    return reply
Complete integrations/google_adk_recipe.py
Save as integrations/google_adk_recipe.py
"""Consume the ADK Runner stream, persist its session, then verify readback."""

import asyncio
import os

from common import settings, verify_messages


async def exercise(client):
    from google.adk.agents import LlmAgent
    from google.adk.runners import Runner
    from google.adk.sessions import InMemorySessionService
    from google.adk.tools import load_memory
    from google.genai import types

    from neo4j_agent_memory.integrations.google_adk import Neo4jMemoryService

    app_name, user_id = "docs-memory", "fictional-user"
    service = Neo4jMemoryService(client, user_id=user_id, extract_on_store=False)
    sessions = InMemorySessionService()
    agent = LlmAgent(
        name="checklist_agent",
        model=os.environ["ADK_MODEL"],
        instruction="Answer briefly. Use load_memory when prior context is needed.",
        tools=[load_memory],
    )
    runner = Runner(
        app_name=app_name, agent=agent, session_service=sessions, memory_service=service
    )
    session = await sessions.create_session(app_name=app_name, user_id=user_id)
    prompt = "The fictional project code is cedar-lantern-47. Acknowledge it briefly."
    async for event in runner.run_async(
        user_id=user_id,
        session_id=session.id,
        new_message=types.Content(role="user", parts=[types.Part(text=prompt)]),
    ):
        if event.error_code:
            raise RuntimeError(f"ADK run failed: {event.error_code}")
        if event.is_final_response() and event.content and event.content.parts:
            print("".join(part.text or "" for part in event.content.parts))
    stored_session = await sessions.get_session(
        app_name=app_name, user_id=user_id, session_id=session.id
    )
    if stored_session is None:
        raise RuntimeError("ADK session is missing after the run")
    await service.add_session_to_memory(stored_session)
    await verify_messages(client, session.id, [prompt])
    response = await service.search_memory(app_name=app_name, user_id=user_id, query="project code")
    print(f"ADK SearchMemoryResponse: {len(response.memories)} candidate(s)")
    return session.id


async def main():
    from neo4j_agent_memory import MemoryClient

    async with MemoryClient(settings()) as client:
        await exercise(client)


if __name__ == "__main__":
    asyncio.run(main())

3. Run and verify persistence

python integrations/google_adk_recipe.py

Expected: a final response, Verified stored messages, and an ADK search candidate count. run_async() is an async generator and must be iterated; session creation and lookup must be awaited. An error event or exception stops the run. The sample persists the session once after completion; repeatedly ingesting the full session can duplicate messages.

Troubleshooting and cleanup

Stop when the program raises an exception; a printed model response alone does not verify persistence. Check the configured database, provider access and vector dimensions before retrying. The program prints the session or trace IDs needed for inspection and closes the client. Retained example records remain in the dedicated database; follow the Aura tutorial’s cleanup procedure for that dedicated instance only when you no longer need them. Reusing a session groups records; it does not authorize a user to read them.

Adapt the integration

Memory is a Runner-level service: Runner(memory_service=…​). LlmAgent receives load_memory (or a deliberately selected preload_memory tool), not a memory= argument or an invented @agent.tool decorator.

search_memory(app_name=…​, user_id=…​, query=…​) returns SearchMemoryResponse; iterate .memories. Each ADK entry carries content.parts, author, timestamp and custom_metadata. The convenience get_memories_for_session(session_id) returns the library’s own entries with string content, so do not mix the two result shapes.

add_session_to_memory(session) stores text-bearing events. add_events_to_memory(app_name=…​, user_id=…​, events=…​, session_id=…​) supports incremental ingestion. The adapter preserves the agent author in metadata and omits tool-only events; it does not turn tool JSON into extracted facts or automatically record a full reasoning trace.

For NAMS, message search requires a real conversation ID. The service tracks the last session written and uses it for search, or accepts an explicit session_id. Without one it skips message search and can still search workspace entities/preferences. This is not cross-conversation message search, and Bolt’s unscoped semantic message search is not user authorization. The selected complete program above is Bolt; follow the hosted guide before adapting it.

See Vertex AI embeddings and Google Cloud deployment for the distinct cloud credentials and infrastructure path.

See Backend capabilities and scoping before adding hosted or multi-user behavior. Neo4j Agent Memory is a Neo4j Labs project with community support.