neo4j-agent-memory Examples
September 11, 2026 · View on GitHub
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 Project
These 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.
How to choose an example
| 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 with no LLM at all (air-gapped, offline, deterministic) | 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/ |
Hosted backend (NAMS)
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 (47 scope-gated 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.py
See Use NAMS and Bolt vs NAMS for the trade-offs.
Standalone scripts
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
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
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
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
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
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
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.
Directory examples — v0.2 features
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
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
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
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
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.
Directory examples — runtime + tooling
Run without an LLM
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
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).
Directory examples — framework integrations
Strands session manager
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
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
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
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
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.
Directory examples — full-stack reference apps
Full-stack chat agent
full-stack-chat-agent/ — FastAPI + PydanticAI 2.x + Next.js over two Neo4j graphs (memory plus a seeded local news graph); 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.
Lenny's Podcast Memory Explorer
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
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.
TypeScript examples
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.
Conventions across examples
- 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.
Running the test suite for examples
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/examples
Equivalent 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.
Contributing a new example
- 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, and the date.
- 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.
Contributing a TypeScript example
- 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.
Support
License
Apache 2.0 — see the main neo4j-agent-memory repository for details.
Verified against neo4j-agent-memory 0.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-memory 0.4.1 on npm, on 2026-09-10. Examples pinning unreleased surface say so in their own footers.