Runnable examples for neo4j-agent-memory — from a thirteen-line round trip to full-stack apps with Next.js frontends and multi-agent orchestration.
⚠️ Neo4j Labs ProjectThese examples are part of
neo4j-agent-memory, a Neo4j Labs project. They are actively maintained but not officially supported. APIs may change. Community support is available via the Neo4j Community Forum.
For examples using the Python bolt backend, first follow the shared Aura setup and cleanup guide. Keep its Aura credentials exported while running the example and update any private .env connection settings the example loads. Hosted NAMS examples use their own workspace setup.
| If you want to… | Start here |
|---|---|
| Run the smallest possible round trip — one file, no checkout, no virtualenv | hello-memory/ |
| Read a guided tour of the whole API surface | basic_usage.py |
| Run against the hosted service with just an API key (no Neo4j) | nams-quickstart/ |
| Share one memory graph across a whole team, with no agent code | claude-code-team-memory/ |
| Evolve a typed domain model and re-type an existing graph | ontology-lifecycle/ |
| Put memory behind an HTTP API | nams-fastapi/ |
| Lay the library on top of a graph you already have in production | existing-graph/ |
| Stop blocking the user-visible response on Neo4j writes | buffered-writes/ |
| Wire 1-hop "what touched this entity?" audit queries | audit-trail/ |
| Gate CI on memory quality like any other regression metric | eval-harness/ |
| Run local models without an LLM API (Aura requires network access) | no_llm/ |
| Tune entity extraction for a specific domain | domain-schemas/ |
| Resolve duplicate entities | entity_resolution.py |
| Enrich entities with Wikipedia/Diffbot data | enrichment_example.py |
| Use it from a framework | langchain_agent.py, pydantic_ai_agent.py, google_adk_demo/, microsoft_agent_retail_assistant/ |
| Persist a Strands agent's conversation automatically (no tool calls) | strands-session-manager/ |
| Give a Strands agent cross-session recall from a graph | strands-memory-store/ |
| Wire it to Google Cloud (Vertex AI, ADK, MCP) | google_cloud_integration/ |
| See a full-stack reference app | full-stack-chat-agent/, lennys-memory/ |
| See a multi-agent compliance workflow | financial-services-advisor/ |
| Write the memory layer in TypeScript instead | ../typescript/examples/ |
Run against the hosted NAMS service — no Neo4j to operate and no LLM/embedding key required (extraction and embeddings run server-side). Set MEMORY_API_KEY and the backend auto-selects NAMS.
| Example | Description |
|---|---|
nams-quickstart/ |
Minimal end-to-end flow over the unified MemoryClient — a conversation, messages, an entity, a reasoning trace, wait_for_extraction(), and a read-only client.query.cypher round-trip. The same script body runs on bolt by flipping backend, so it doubles as the bolt-vs-NAMS diff. |
nams-fastapi/ |
NAMS-backed memory inside a FastAPI service: one lifespan-managed client, per-user scoping from the authenticated request, the library's error taxonomy mapped onto HTTP status codes, a /chat route that reads assembled context before answering, and a /search route. |
nams-langchain/ |
The same create_agent + middleware agent as langchain_agent.py, against hosted NAMS — server-side extraction and embeddings, no Neo4j to run. |
ontology-lifecycle/ |
The full NAMS ontology lifecycle: import an Arrows diagram into a typed schema, activate it, ingest under it, then rename a type and migrate the already-extracted entities — import_, diff, migrate + get_migration polling, with a query.cypher read-back. Hosted-only; the bolt twin is existing-graph/. |
claude-code-team-memory/ |
Shared memory for Claude Code, Claude Desktop and Cursor with no agent code — ready-to-copy .mcp.json / claude_desktop_config.json / .cursor/mcp.json files wiring the hosted NAMS MCP server (scope-dependent tools, OAuth) and the self-hosted mcp serve (6 or 16 tools) side by side, plus provision_keys.py (one rotatable client.auth key per developer), seed_workspace.py (bulk_add_messages → await extraction → read back) and doctor.py (key, config validity, tool surface, reachability, extraction status). |
cp examples/.env.example examples/.env # then set MEMORY_API_KEY=nams_...
uv run python examples/nams-quickstart/main.pySee Use NAMS and Bolt vs NAMS for the trade-offs.
Single-file demos. Run the top-level scripts with uv run python examples/<name>.py; hello-memory/ is a one-file directory because it carries a PEP 723 header.
hello-memory/ — the front door. One file, thirteen lines of body: two messages in, one entity, one preference, the assembled context back out. main.py carries a PEP 723 header, so uv run examples/hello-memory/main.py builds its own environment — no checkout, no virtualenv, no pip install. Runs on hosted NAMS with MEMORY_API_KEY, or on bolt with NEO4J_URI.
basic_usage.py — the guided tour: twelve numbered sections across all three memory types (short-term conversation, long-term entities/preferences/facts, reasoning traces), plus geocoding, batch loading, consolidation and graph export. Read hello-memory/ first if you want the shortest possible round trip, and nams-quickstart/ for the hosted path.
entity_resolution.py — six sections over the resolver strategies: exact, fuzzy, semantic (embedder-backed), and composite chaining, including the type-aware guarantee that a PERSON is never merged into a LOCATION. No Neo4j required — runs purely in-memory. Optional extras: [fuzzy] for the RapidFuzz stage, [sentence-transformers] for the semantic stage (both sections self-skip when the extra is absent).
enrichment_example.py — fetch additional entity data from Wikipedia (free) and Diffbot (API key) and merge it onto your nodes. Demos direct provider use, caching, composite providers, and end-to-end Neo4j integration. Enriched fields land in entity.metadata (see the conventions note below). Needs httpx, which the [nams] extra already installs.
langchain_agent.py — a LangChain 1.x create_agent agent with memory: Neo4jMemoryMiddleware for context injection and turn persistence, a reasoning-trace middleware, and Neo4jMemoryRetriever as a retriever tool. Runs with no API key (scripted model + local embedder). Requires the [langchain-agents] extra; add [openai] plus langchain-openai for a real model turn.
pydantic_ai_agent.py — MemoryDependency, create_memory_tools() and record_agent_trace() wired into a PydanticAI 2.x agent: two real turns where the agent calls the memory tools, the exchange persisted with save_interaction(), each run recorded as a reasoning trace and read back, and the second turn seeing the first. Requires the [pydantic-ai] extra; with OPENAI_API_KEY it runs on OpenAIChatModel (the same model instance also drives memory extraction), and without a key it runs offline on pydantic_ai.models.test.TestModel plus the [sentence-transformers] extra.
These four examples cover the v0.2 feature drop. Each is self-contained, runs with no LLM and a local embedder, and pairs with a how-to in docs/.
existing-graph/ — adopt a pre-existing Neo4j graph (:Person, :Movie, :Genre …) as long-term memory entities via client.schema.adopt_existing_graph(...). Idempotent. Configures SchemaModel.CUSTOM so library writes target your domain types instead of POLE+O. Four scripts: seed.py (behind a --reset safety flag), adopt.py, memory_io.py, retrieve.py.
buffered-writes/ — MemorySettings.memory.write_mode = "buffered", client.buffered.submit(...), client.flush(), client.write_errors, and a back-pressure section that drops max_pending to 4 so the bounded queue is observable. The agent's response to the user is not blocked on Neo4j round-trips.
audit-trail/ — explicit :TOUCHED edges from ReasoningStep → Entity, an @on_tool_call_recorded hook for domain-specific inference, and TraceOutcome with indexable error_kind. Headline payoff: a one-hop MATCH (e)<-[:TOUCHED]-(s) audit query. Bolt only — :TOUCHED edges are a Cypher-level feature.
eval-harness/ — labelled regression cases for memory quality. RetrievalCase, AuditCase and PreferenceCase over a two-tenant fixture, run via client.eval.run(suite, dimensions=[...]), plus a ci_gate.py that exits non-zero below a threshold and writes a JSON report you can upload as a CI artifact.
no_llm/ — llm=None, backend="bolt", sentence-transformers embedder, spaCy + GLiNER extractor with the LLM fallback disabled. Exercises all three memory layers locally, runs a consolidation dry run, and fails fast at construction time when a local model is missing, so you never get a surprise API call.
domain-schemas/ — eight ready-made GLiNER2 schemas (POLE+O, podcast, news, scientific, business, entertainment, medical, legal), a recipe for your own, and shared sample documents under samples/. One runner: uv run python examples/domain-schemas/run.py --schema <name> (the eight per-domain scripts remain as thin deprecated wrappers).
strands-session-manager/ — Neo4jSessionManager on Agent(session_manager=...): every turn persisted and restored automatically, opt-in <user_context> injection from long-term memory, per-analyst user_id= scoping, tool calls mirrored into reasoning memory, and the shared-brain pattern (N agents, one graph). Runs with no LLM or API key.
strands-memory-store/ — Neo4jMemoryStore on MemoryManager(stores=[...]): entity/preference/fact recall fed into the agent loop, plus graph-native tools a MemoryManager cannot provide. Complementary to the session manager. Also runs with no API key.
google_adk_demo/ — a real ADK Runner loop (LlmAgent + load_memory) backed by Neo4jMemoryService; two turns across two sessions prove cross-session recall; runs with no API keys via a scripted model and a local embedder.
google_cloud_integration/ — Vertex AI embeddings (gemini-embedding-001), ADK memory, the FastMCP 4 server (6 core / 16 extended tools), a reasoning-trace :TOUCHED audit query, buffered writes for Cloud Run, and a consolidation dry run. Use this if you have already chosen GCP.
microsoft_agent_retail_assistant/ — full-stack retail shopping assistant on the Microsoft Agent Framework 1.x GA. Neo4jContextProvider, a working entity-deduplication review queue, reasoning audit edges (:TOUCHED), GDS-backed recommendations, a shopper switcher that exercises the preference write path, and a memory graph visualization.
full-stack-chat-agent/ — FastAPI + PydanticAI 2.x + Next.js over two Neo4j graphs (memory plus a seeded news graph in Aura); SSE with live tool events, reasoning traces with :TOUCHED audit edges, entity extraction switched by EXTRACTION_MODE. Bolt only (it uses client.get_graph()). Great middle-weight example; the frontend has its own README, lint/typecheck/test scripts and a Node 22 floor.
lennys-memory/ — the flagship Python demo. A podcast knowledge graph with a 28-tool PydanticAI agent, Wikipedia-enriched entity cards, geospatial map view, NVL graph view and automatic preference learning. The real transcript corpus is not shipped — make load-sample runs against the synthetic fixtures in data/samples/. Live demo →
financial-services-advisor/ — multi-agent KYC/AML compliance investigations. Same architecture implemented twice: AWS Strands + Bedrock, and Google ADK + Gemini on Python 3.12 with the [google-adk,vertex-ai,litellm] extras. A supervisor agent orchestrates four specialists (KYC, AML, Relationship, Compliance), all backed by real Cypher queries against Neo4j.
The TypeScript SDK @neo4j-labs/agent-memory ships its own gallery at ../typescript/examples/ — nine examples covering the Vercel AI SDK middleware, a Next.js 16 App Router flagship app, an MCP server, LangChain JS, Mastra, Strands, the ontology lifecycle, a Cloudflare Worker on fetch alone, and an agentic-commerce assistant. See ../typescript/examples/README.md for the index and the TypeScript contributing checklist.
- Async-only. Every memory operation is a coroutine. From a script, wrap your entry point in
asyncio.run(...). From a notebook, prefix calls withawait. MemoryClientlifecycle. Useasync with MemoryClient(settings) as client:(the recommended pattern), orawait client.connect()/await client.close(). There is noinitialize()method.- Environment loading. Single-file examples do
from _env import NEO4J_URI, NEO4J_PASSWORD, ...;examples/_env.pyloadsexamples/.env(copyexamples/.env.example) and falls back to a tiny parser whenpython-dotenvis absent. Directory examples with their own.env.exampleload that file instead. - Model ids come from the environment. Never hard-code a chat or embedding model id. Read it from an env var with a current default —
OPENAI_MODEL(defaultgpt-5-mini),GEMINI_MODEL(defaultgemini-2.5-flash),VERTEX_EMBEDDING_MODEL(defaultgemini-embedding-001),OPENAI_EMBEDDING_MODEL(defaulttext-embedding-3-small) — so an example survives the next retirement. add_entityreturns a tuple. Since v0.1.1,await client.long_term.add_entity(...)returns(entity, dedup_result). Discard the result with_, _ = await ...or unpack to inspect the dedup outcome.- POLE+O entity types are strings. Use
"PERSON","ORGANIZATION","LOCATION","EVENT","OBJECT"— not the legacyEntityTypeenum. - Custom entity types are uppercase.
add_entitynormalises types, but the:TOUCHEDMERGE storestypeverbatim, soEntityRef(type="Client")creates a second:Entitynode thatadd_entitycan never match. WriteEntityRef(type="CLIENT"). - Message roles are plain strings. Pass
"user"/"assistant"/"system"toadd_message. AMessageRoleenum member is also accepted, andMessageRoleis what reads back off aMessage, so compare withmsg.role.value. - Enrichment lands in
entity.metadata. Wikipedia/Diffbot fields (enriched_description,wikipedia_url,wikidata_id,image_url,enriched_at) are entries in themetadatadict, not attributes onEntity. Read them as(entity.metadata or {}).get("wikipedia_url"). - Vertex AI embedding models. Use
gemini-embedding-001(the default — 3072 native dimensions, truncated to 768 byVertexAIEmbedderso existing vector indexes keep working),text-embedding-005, ortext-multilingual-embedding-002.text-embedding-004(shut down 2026-01-14) and thetextembedding-gecko*family (shut down 2025-04-09) are retired and now raise anEmbeddingErrornaming the replacement. Read the id fromVERTEX_EMBEDDING_MODELrather than hard-coding it. - Google ADK. Memory is a
Runner-level service —Runner(memory_service=...)plus theload_memorytool.LlmAgenthas nomemory=field.Runner.run_async()returns anAsyncGenerator, so iterate it withasync for event in runner.run_async(...); don'tawaitit.search_memory()returns aSearchMemoryResponse— iterateresponse.memories. - Multi-file example directories. A directory example whose scripts import each other adds its own directory to
sys.pathat the top of each entrypoint (sys.path.insert(0, str(Path(__file__).parent))), which is whyexamples/**/*.pycarries anE402ruff exemption. - PEP 723 is allowed in
hello-memory/only. Every other example declares its dependencies in arequirements.txtorpyproject.tomlso the pin is reviewable and Dependabot can see it. - Local development uv source. Backend
pyproject.tomlfiles pinneo4j-agent-memory[...]>=0.5.0,<0.7and add a[tool.uv.sources]entry pointing at the repo root, relative to the backend directory ({ path = "../../..", editable = true }— one more..for a backend nested two levels down). The git URL line is commented out and used for production.
Every example has a smoke-test module under ../tests/examples/. Two entry points:
# Quick (no Neo4j — structure, imports, manifests, phantom methods)
uv run pytest tests/examples -m "syntax or imports"
# Full (needs Neo4j; testcontainers will start one if available)
uv run pytest tests/examplesEquivalent make targets: make test-examples-quick and make test-examples. The exact set CI runs is defined in .github/workflows/ci-python.yml (example-tests-quick with no database, example-tests with a Neo4j service) — that file is the source of truth. tests/examples/test_examples_registry.py fails if an example directory has no test module, no README footer, or no index row in this file.
- Add a directory under
examples/(or a single.pyfor a script). - Pin the library as
neo4j-agent-memory[...]>=0.5.0,<0.7inrequirements.txtorpyproject.toml, and read every model id from an environment variable with a current default. - Include a README following the Neo4j Labs guidelines — Labs badge, status badge, community support badge, disclaimer, prerequisites, run steps, expected output, support section, and a "Verified against …" footer naming the library version, the framework versions you tested, the checks you ran, and the date. Say which checks used mocks and which ran against a live service.
- Add a smoke test under
tests/examples/. Mirror an existing one such astests/examples/test_buffered_writes_example.pyfor the structure, and mark the classes that need a database with@pytest.mark.requires_neo4j. - Register the test in
.github/workflows/ci-python.ymlunderexample-tests-quickif it needs no Neo4j (example-testspicks up the whole directory automatically). - Add a row to the index above —
tests/examples/test_examples_registry.pyenforces it.
- Add a directory under
typescript/examples/<name>/with"@neo4j-labs/agent-memory": "file:../..","engines": {"node": ">=22"}, and caret-pinned frameworks. - Extend
typescript/examples/tsconfig.base.jsonrather than re-declaring compiler options. - Add
lint(tsc --noEmit) andtestscripts; keep the test suite offline (no API key). - Add the directory to the
type-check-examplesmatrix in.github/workflows/ci-typescript.yml. - Add a row to
typescript/examples/README.md.
Apache 2.0 — see the main neo4j-agent-memory repository for details.
Historical verification report — 2026-09-10. The following records a prior checkout/test report. Its development-version labels, passing counts, and release-availability statements are historical, not evidence of current package compatibility.
Verified against
neo4j-agent-memory0.6.0-dev (in-tree; NAMS hosted-backend support shipped in v0.4.0, workspace addressing and the ontology surface in v0.5.0) and@neo4j-labs/agent-memory0.4.1 on npm, on 2026-09-10. Examples pinning unreleased surface say so in their own footers.