CEO, Versino PsiOmega
24 min read
Semvec, Semvec Cortex, and Neo4j
A clinical assistant that remembers a patient’s full history across a 12-hour shift handover and knows which colleague already investigated the same case three hours earlier behaves nothing like one that treats every conversation as a fresh start. LLMs do not have native memory; they fake it by re-reading the entire conversation on each turn. This means token costs grow linearly. Context windows snap. And the moment a second agent enters the picture, even that illusion breaks down.
This post shows how Semvec gives a single agent persistent semantic memory in roughly three lines of Python, and how Semvec Cortex scales the same engine across teams of agents, with Neo4j as the single source of truth for both the domain and the conversational state. We build a healthcare assistant that starts as a single Python process and grows into a multi-department network with shared memory, drift consensus, cross-shift handover, deliberate consensus voting, and a behavioural-consistency probe that catches algorithmic drift after import. All code comes from the public reference repository MichaelNeuberger/neo4j-agent-integrations, runs on Semvec 0.7.0 from PyPI, and is exercised by a 274-test pytest suite.
About SemvecClient
Every code snippet in this post uses a class called SemvecClient. SemvecClient is not part of pip install semvec. It is the in-process facade defined in this repository at src/semvec_neo4j/core/semvec_client.py. It composes SessionManager, ClusterManager, RegionalManager, GlobalObserver, and NetworkManager from semvec.api.* into one ergonomic surface that returns plain Python dictionaries, well suited for direct mirroring into Neo4j.
The PyPI package semvec exports the underlying primitives: SemvecState, SemvecConfig, MultiResolutionMemory, PhaseDetector, LiteralCache, ResonanceTrigger, the cortex package (SemvecAgentNetwork, ConsensusEngine, AttentionAggregation, …), and the full exception hierarchy. To reproduce the snippets below, clone the integrations repository; pip install semvec alone does not include SemvecClient, the persistence stores, or the demo scenarios.
The numbers, up front #
On the LOCOMO long-term conversational-memory benchmark, the same suite mem0, Zep, Letta, and the GPT-4-turbo full-context baseline report against, Semvec lands at mem0-near quality with zero generative-LLM calls at ingest. The headline numbers below are from the public Semvec benchmark page (semvec-docs.pages.dev/benchmarks), measured against mem0 0.1.x in a 1:1 evaluation setup (gpt-4o-mini reader + LLM-as-Judge, T=0):
Benchmark LOCOMO (10 conversations, 1986 QAs; gpt-4o-mini reader + judge, T=0)
Result J 0.605 (LLM-as-Judge, Cat 1–4, n=1540) at zero ingest LLM calls; F1 0.424 (Porter-stemmed token-F1, n=1986); ~2,000 context tokens per reader call (~8× fewer than full-context replay, 87 % reduction); ~3 min vs. ~24.5 min ingest on conv-44 / 675 turns (~8× faster)
Reference baseline mem0 J 0.669 (paper)
A note on the speed figures: the ~8× above is single-conversation ingest wall-clock (conv-44, 675 turns). The Semvec docs separately report a 17× end-to-end wall-clock vs. mem0 for the full 1986-QA suite, measured on a GPU reference platform with hybrid BM25 enabled — a different measurement, documented at semvec-docs.pages.dev/benchmarks.
Those savings are what make multi-agent scenarios economically practical. A ward of ten specialists making 50 queries each per shift is no longer prohibitive. The per-turn update is sub-millisecond at dimension 384 on a recent x86_64 CPU; the Rust core runs outside the GIL, so the math does not contend with Python.
Architecture in one picture #
Domain knowledge and conversational state coexist in the same Neo4j graph. Patients, medications, providers, and diagnoses live alongside agent sessions, drift events, and cluster memberships. A single Cypher query can join “what does the agent know” with “what was asked, when, and by whom.” That bridge is the INVESTIGATED relationship, and it is the central piece of the architecture.
Semvec does the math; Neo4j stores the result. Embedding generation, drift scoring, cluster aggregation, observer sampling, fact extraction, and consensus voting all happen behind a small in-process API. The Neo4j integration is intentionally thin: stores that persist sessions, states, drift events, cluster relationships, and verbatim facts as the engine produces them. There is no duplicated logic.
Part one. Single-agent memory with Semvec #
The smallest useful agent now fits on a screen. Install the package and wire it up:
pip install semvec# Then, inside the integrations repo:from semvec_neo4j.core.embedder import SentenceTransformerEmbedderfrom semvec_neo4j.core.semvec_client import SemvecClient semvec = SemvecClient(embedder=SentenceTransformerEmbedder()) # default: all-mpnet-base-v2 (768-d) session_id = Noneprev_response = Nonefor user_msg in conversation: result = semvec.run(message=user_msg, session_id=session_id, response=prev_response) session_id = result["session_id"] # captured on first turn answer = call_llm(result["context"], user_msg) # your LLM, your prompt prev_response = answer # stored alongside the next .run()
Two details matter. First, the session_id is allocated by Semvec on the first call and reused thereafter. That is how the same agent picks up where it left off across process restarts. Second, the previous LLM response is buffered and sent with the next run() call. Store-and-retrieve happen together, so there is no race window where an answer is given but not yet remembered.
PydanticAI integration #
The Semvec context becomes part of the agent’s system prompt via the @agent.system_prompt decorator. Each turn, only the memories the engine deems relevant to the current user message are injected:
@agent.system_promptasync def inject_semvec_context(ctx: RunContext[Deps]) -> str: if ctx.deps.semvec_context: return f"Memory context from previous conversations:\n{ctx.deps.semvec_context}" return ""
What you get for free #
Each Semvec response carries more than compressed text. The result dict includes:
• drift_phase: stable / shifting / drifted; a jump signals a topic switch (distinct from the six conversational state phases — initialization through instability)
• drift_score: 0.0–1.0 numeric distance from the accumulated context
• top_similarity: cosine similarity to the rolling top-K of recent embeddings
• short_circuit: true when a paraphrase of an earlier question is recognised and the LLM call can be skipped
The session is also immediately queryable in Neo4j as a chain of SemanticState nodes, with a CURRENT_STATE pointer to the latest one.
Live token-savings panel #
The compression numbers in the table at the top of this post are not a once-and-for-all benchmark. Every turn carries its own measurement. SemvecChatProxy wraps any OpenAI-shaped llm_call callable and returns the PSS-compressed prompt token count next to what the same conversation would have cost if you replayed the full history. The wrapper exposes it via SemvecClient.create_chat_proxy:
proxy = semvec.create_chat_proxy( llm_call=my_llm, # any callable; expose .last_usage for exact counts system_prompt="You are a clinical assistant.",)for question in clinic_questions: turn = proxy.turn(question) print(turn["pss_input_tokens"], "vs", turn["baseline_input_tokens"], "phase", turn["phase"]) print(proxy.summary()) # totals + per-turn breakdown
Scenario 1 of the demo prints both per-turn and cumulative token counts as you type. The compression ratio rises with conversation depth, exactly as the headline benchmark predicts, and it does so against your own questions and your own LLM, not a curated dataset.
Anchors, triggers, and synthetic memory injection #
When an agent has a fixed domain, pin the session with an anchor and inject synthetic memories from the Neo4j domain graph. For an oncology pharmacist, every contraindication in the graph becomes a long-term memory before the session even begins:
session_id = semvec.create_session()["session_id"] for c in contraindications_from_neo4j: # fetched via Cypher text = f"CRITICAL: {c['drug_a']} and {c['drug_b']} are contraindicated." semvec.inject_memory( session_id=session_id, embedding=embedder.get_embedding(text).astype(float).tolist(), text=text, tier="long_term", importance=1.0, ) semvec.add_trigger(session_id, keyword="contraindication")semvec.add_anchor(session_id, embedder.get_embedding("oncology chemotherapy treatment").astype(float).tolist())semvec.set_isolation(session_id, level="QUARANTINE", similarity_threshold=0.55)
Verbatim facts: dates, dosages, identifiers #
Semantic memory works well for meaning. It is unsafe for exact values. Compress “the next infusion is on 2026–05–15” into an embedding and a clinician later asking “when is the next infusion?” may receive “next month” or, worse, the wrong date. The compliance-extractor path pulls regex-recognised facts (ISO/DE/US dates, EUR/USD/kg/% numerics, UUID/IBAN/DE-VAT identifiers) out of free text before the input is folded into the EMA vector. Each fact lands in the literal cache where it survives compression byte-for-byte:
text = "Carlos starts therapy on 2026-05-15. Reimbursement IBAN DE89 3704 0044 0532 0130 00." facts = semvec.extract_facts(text)# [{"kind": "date", "raw": "2026-05-15", "value": "2026-05-15T00:00:00+00:00", ...},# {"kind": "identifier", "raw": "DE89 3704 0044 0532 0130 00",# "value": "DE89370400440532013000", "id_type": "iban", ...}] semvec.store_facts_as_entities(session_id, text)# {"stored": 2} # entries land in the session's literal cache
Two practical caveats. The upstream unit whitelist is small (built for finance and operations), so medical units like mg/m² are not auto-detected. For those, prefer inject_memory with the full sentence. And the literal cache stores facts under the kind constant because the upstream EntityKind enum is closed; the original kind (numeric / date / identifier) is preserved in the entity context so downstream queries can still tell them apart.
Part two. Multi-agent memory with Semvec Cortex #
Cortex methods sit on the same SemvecClient. There is no additional service to deploy. Cluster, region, observer, network, consensus engine, and chat proxy are all bundled in the semvec package and reachable through one facade.
Layer 2. Clusters: shared memory across agents #
A cluster is a shared semantic state that multiple agent sessions read from and write to. Three specialists examining David Park (essential hypertension + COPD):
cluster = semvec.create_cluster(name="ward_round_david_park", aggregation_mode="weighted_average")cid = cluster["cluster_id"]semvec.add_cluster_member(cid, chen_session)for question, answer in chens_baseline_qa_pairs: semvec.cluster_store(cid, message=question, response=answer) semvec.add_cluster_member(cid, volkov_session)result = semvec.cluster_run( cluster_id=cid, message="What's the patient's current oxygen saturation trend?", short_circuit_threshold=0.52, # calibrated for all-mpnet-base-v2 (768-d))
The Neo4j side stores cluster membership and INVESTIGATED edges from each agent to the entities they touched. A single Cypher query then answers “who has looked at David Park, and what did each one find?”
Layer 3. Regions: consensus drift across clusters #
A region groups multiple clusters and watches for correlated drift events. If both cardiology at Memorial General and emergency at Riverside Medical drift toward the same off-domain topic within a configurable time window, the region emits a consensus drift event:
region = semvec.create_region(name="hospital_network_north", consensus_threshold=0.5, vote_window_seconds=60.0)rid = region["region_id"]semvec.add_region_cluster(rid, cardiology_cluster_id)semvec.add_region_cluster(rid, emergency_cluster_id) events = semvec.get_region_events(rid, limit=20)
What this catches in practice: a hospital-network IT outage shows up as multiple clusters simultaneously drifting from clinical to “I cannot access the chart” queries, long before any single ticket reaches the help desk.
Layer 4. Global Observer: cross-region anomaly detection #
The observer sits one level above regions and detects anomalies no individual region would flag: a cluster silent for too long, a region whose consensus events are accelerating, or an unusual coupling between two regions.
semvec.create_observer(sample_interval_seconds=30.0, region_ids=[north_region_id, south_region_id])semvec.observer_sample()for a in semvec.get_anomalies(limit=20): print(a["severity"], a["anomaly_type"], a["affected_cluster_ids"], a["description"])
Layer 5. Shift handover via export / import #
The night-shift physician’s session has built up deep context about a difficult patient. At 6 a.m. she exports it (with SHA-256 checksum), and the day-shift physician imports it. His first query immediately surfaces the relevant memories, with top_similarity = 0.578 instead of 0.000 starting cold:
exported = semvec.export_session(volkov_session_id) # {"state_dict": ..., "checksum": "<sha256>"} tanaka_session_id = semvec.create_session()["session_id"]semvec.import_session(tanaka_session_id, state_dict=exported["state_dict"]) # Optional: a partial semantic-delta transfer instead of a full import,# capped at max_weight to avoid swamping the receiver's existing context.semvec.transfer_delta(source_session_id=volkov_session_id, target_session_id=tanaka_session_id, max_weight=0.15)
Layer 6. Behavioural-consistency probe after import #
The Layer 5 checksum catches bit-level corruption on transfer. That is useful, but it cannot tell you whether the imported state actually behaves like the source. verify_consistency runs an embedding probe through both sessions and checks whether the cosine similarities match within a configurable tolerance:
exp = semvec.export_session(volkov_session_id)semvec.import_session(tanaka_session_id, exp["state_dict"]) probes = [embedder.get_embedding(t).astype(float).tolist() for t in ( "Morrison overnight glucose trend", "Metformin held for catheterization", "Cardiac biomarkers at 6h",)]passed = semvec.verify_consistency( tanaka_session_id, probes, reference_session_id=volkov_session_id, tolerance=1e-3,)print("consistency probe:", "PASSED" if passed else "FAILED")
The demo’s shift-handover scenario prints the verdict directly under the import line, so a corrupted hand-off is visible at the boundary, not three turns later when the wrong recommendation surfaces.
Layer 7. ConsensusEngine: explicit voting on top of region drift #
Region-level drift detection (Layer 3) flags correlated drift on its own. Sometimes the application wants a deliberate, named voting protocol on top of that signal. semvec.cortex.ConsensusEngine gives every consensus mode a first-class API: SIMPLE_MAJORITY, QUALIFIED_MAJORITY, UNANIMOUS, WEIGHTED_VOTE, ADAPTIVE_THRESHOLD. The wrapper threads it through:
eng = semvec.create_consensus_engine( local_id="hospital-orchestrator", network_id="hospital-network", level="qualified_majority",)for inst, weight in [("cardio", 1.0), ("emerg", 1.0), ("supervisor", 1.5)]: semvec.register_consensus_voter(eng["engine_id"], inst, weight=weight) prop = semvec.submit_consensus_proposal( eng["engine_id"], proposal_type="admin_pivot_alert", proposed_state=[0.0] * 8, rationale="Both clusters drifted to facility-admin within the vote window. Incident?",)semvec.vote_on_consensus(eng["engine_id"], prop["proposal_id"], True, voting_instance="cardio")semvec.vote_on_consensus(eng["engine_id"], prop["proposal_id"], False, voting_instance="emerg")semvec.vote_on_consensus(eng["engine_id"], prop["proposal_id"], True, voting_instance="supervisor") verdict = semvec.evaluate_consensus(eng["engine_id"], prop["proposal_id"])# {"accepted": True, "ratio": 0.71, "votes_for": 2, "votes_against": 1, "status": "accepted"}
The hospital-network scenario wires this directly behind the existing region-drift step: when both hospital clusters drift to admin topics, a qualified-majority vote (with the supervisor weighted at 1.5) decides whether to escalate to a network-wide incident.
The bridge: INVESTIGATED in Neo4j #
Every time an agent touches a domain entity (a patient, a medication, a diagnosis), an INVESTIGATED edge is written to Neo4j, carrying a rich audit-trail payload. Properties on every edge include step, phase, drift_score, started_at, ended_at, duration_ms, query_preview, response_preview, top_k_similarity, semvec_drift_phase, short_circuit, llm_call, agent_role, plus per-scenario extras like cluster_id, shift, imported_from, baseline_delta, paraphrase_of, and trigger_keyword.
A single Cypher query joins both sides. Which agents investigated James Morrison, in which cluster, did any of them drift, and what diagnoses did they end up touching?
MATCH (s:AgentSession)-[inv:INVESTIGATED]->(pat:Patient {name: 'James Morrison'})MATCH (pat)-[:DIAGNOSED_WITH]->(diag:Diagnosis)OPTIONAL MATCH (s)-[:CURRENT_STATE]->(:SemanticState)-[:TRIGGERED]->(d:DriftEvent)OPTIONAL MATCH (s)-[:MEMBER_OF]->(c:Cluster)RETURN s.agent_id, inv.step, inv.drift_score, inv.duration_ms, inv.semvec_drift_phase, diag.name AS diagnosis, d.severity AS drift_severity, c.name AS clusterORDER BY s.agent_id, inv.step;
A compliance reviewer’s query for every short-circuited turn in the last week:
MATCH (s:AgentSession)-[inv:INVESTIGATED]->(pat:Patient)WHERE inv.short_circuit = true AND inv.started_at >= datetime() - duration('P7D')RETURN s.agent_id, pat.name, inv.query_preview, inv.paraphrase_of, inv.top_k_similarity, inv.duration_msORDER BY inv.started_at DESC;
Agents, facts, and observers as first-class graph citizens #
Sessions, states, and drift events are only half of the conversational-state graph. The integration — a clean, installable Python package (src/semvec_neo4j/, MIT-licensed) on top of the proprietary semvec 0.7.0 engine — persists four further node types through dedicated stores, turning that half of the graph into a complete, queryable audit surface. Every node type you see in db.schema.visualization() corresponds to something the engine actually writes; there are no orphan labels.
Agents as a cross-session anchor #
An agent is more than a single conversation. A first-class (:Agent) node, linked to each of its runs via (:Agent)-[:RAN]->(:AgentSession), turns scattered sessions into a queryable roster — the natural home for per-agent trust and influence analytics over time. A bare agent_id property on each session would not let you ask, in a single hop, for everything a given clinician has ever run.
MATCH (a:Agent)-[:RAN]->(s:AgentSession)RETURN a.agent_id AS agent, count(s) AS sessionsORDER BY sessions DESC
Verbatim facts in the graph #
Part one showed extract_facts pulling dates, dosages, and identifiers into the literal cache byte-for-byte. Those same facts land in Neo4j as (:LiteralFact) nodes linked to their session via (:AgentSession)-[:EXTRACTED]->(:LiteralFact), with the (session_id, key) pair unique so re-extracting the same verbatim span is an idempotent upsert. A compliance reviewer can ask which exact values an agent pulled out of a patient’s record — and get the byte-exact answer, not a compressed paraphrase.
MATCH (s:AgentSession)-[:EXTRACTED]->(f:LiteralFact)RETURN s.agent_id, f.kind, f.key AS verbatim, f.value, f.unit, f.id_typeORDER BY s.agent_id, f.kind
In the oncology-safety run this captured the chemo start date 2026–05–15, two German-format dates, a 4.500,00 EUR reimbursement amount, and a validated IBAN — each stored exactly as written.
Observers and anomalies as audit nodes #
Layer 4’s Global Observer is persisted as a (:GlobalObserver) node, linked to the regions it watches via OBSERVES, and the cross-cluster anomalies it detects are written as (:AnomalyEvent) nodes via (:GlobalObserver)-[:DETECTED]->(:AnomalyEvent) — the network-level counterpart to the (:SemanticState)-[:TRIGGERED]->(:DriftEvent) edge that records per-session drift. The observer emits an anomaly only when its thresholds trip (≥2 regions converging, or majority systemic drift across clusters); the persistence path is unit-tested independently, so the audit trail is reliable the moment a real anomaly fires.
What a single full run produces #
Running the six demo scenarios once against a live Neo4j and the bundled healthcare graph produces the inventory below — every count is from a real run, not a mock-up:
Drift detection in practice #
A pure threshold on drift_score alone does not separate cleanly. Medical sub-topics (Lisinopril versus Atorvastatin versus catheterisation) produce drift scores in the same band as a genuine specialty switch. What works in practice is a combined rule:
def is_drift(result: dict) -> bool: if result.get("drift_detected", False): return True # Semvec's own verdict (>= 0.5) return result["drift_score"] >= 0.35 and result["top_similarity"] <= 0.45
The intuition: a high drift_score paired with a still-high top_similarity means the new query is semantically novel but the accumulated context already covers it (a sub-topic, not a new domain). A high drift_score paired with a low top_similarity means the query is novel and unrelated to anything in memory: a real topic switch.
The healthcare demo runs four phases through one agent: 17 turns of diabetes (James Morrison), 5 turns of psychiatry (Aisha Patel), 3 turns of cardiology (Maria Rodriguez), and 5 paraphrased Phase-1 queries. DRIFT fires only at the genuine specialty switches (Phase 1 → 2 and Phase 2 → 3), not within-specialty topic shifts.
Production patterns #
• **Pick one embedder, stick with it. **The default is all-mpnet-base-v2 (768-d), which has better recall than all-MiniLM-L6-v2 (384-d). The vector index dimension in Neo4j must match; switching mid-deployment requires re-embedding every persisted SemanticState.
• **Cluster lifecycle is short-lived by default. **A cluster maps to a unit of shared work: a ward round, an incident response, or a sales-call team. Create it when the work starts, delete it when it ends. Long-lived clusters drift; promote genuinely long-term shared knowledge explicitly into the domain graph instead.
• **consensus_threshold is your false-positive dial. **Set it too low (0.2) and you will get region events for any random correlation. Set it too high (0.9) and you will never see anything until it is already a major incident. Start at 0.5 with vote_window_seconds=60, then tune from observed event rates. When you need a named protocol on top of that signal (for example, “any escalation needs a qualified majority of clusters plus an explicit supervisor vote”), wire ConsensusEngine instead of folding it into a hand-rolled threshold.
• **Anchors plus QUARANTINE isolation prevent prompt injection at the semantic layer. **If a session is anchored to oncology medication safety and someone slips in a question about credit-card numbers, the isolation filter catches it before it reaches the LLM.
• **Always verify the export checksum on import, then probe behaviour on top. **export_session returns SHA-256; import_session refuses to load if the dict does not match. Then run verify_consistency with a handful of probe embeddings to confirm the imported state actually behaves like the source. Network transfers are exactly where state corruption silently happens.
• **Use Neo4j’s vector index, not a separate vector store. **With Neo4j 5.11+ vector indexes on SemanticState.vector, you keep one operational system. The Cypher round-trip joining memory + domain is worth far more than the millisecond saved by a dedicated vector DB.
• **Store dosages, dates, and identifiers verbatim. **extract_facts and store_facts_as_entities pull exact values out of free text and bypass embedding compression. For regulated data this is the difference between “the next infusion is in May” and “the next infusion is on 2026–05–15.”
Key takeaways #
• **Semvec gives a single agent persistent memory in three lines of Python. **SemvecClient(embedder=…), client.run(message), client.store(session_id, response). Constant token cost per turn, unlimited session length, free phase tracking and drift detection.
• **Cortex scales to multi-agent scenarios with no additional service. **create_cluster, cluster_run, create_region, create_observer, transfer_delta, create_consensus_engine, verify_consistency, all on the same SemvecClient instance.
• **Neo4j as a single source of truth for both domain and memory beats running two systems. **Vector indexes on SemanticState, constraints on AgentSession, and standard graph traversal against domain entities, all in one place, queried in one Cypher.
• **The INVESTIGATED relationship is the bridge. **14+ properties on every edge make conversational state queryable like structured data, and make structured data injectable as synthetic memory.
• **Drift detection is a first-class signal, not a debugging tool. **Wire it into your alerting. A cluster that drifts to admin topics during a clinical shift is telling you something about your operations, not your model.
• **Live token savings are measurable per turn. **create_chat_proxy returns the PSS-compressed prompt token count next to the full-history baseline for every call, so the headline ~8× token reduction reproduces against your own questions.
• **Verbatim facts survive compression. **extract_facts and store_facts_as_entities keep dates, dosages, and identifiers byte-exact in the literal cache.
• **No API key. No base URL. No network round-trip. No telemetry. **The semvec package runs entirely in-process. One fewer dependency to monitor and zero per-request egress cost.
Resources #
Install: pip install semvec.PyPI:pypi.org/project/semvec. Latest tested release: 0.7.0.** Reference implementation:https://github.com/VersinoPsiOmega/Semvec-neo4j-agent-integration. SemvecClient wrapper, healthcare demo (six interactive scenarios + Cypher explorer), 274-test pytest suite.pip install semvec alone does not include SemvecClient, the persistence stores, or the demo scenarios. Those live in this repository.Single-instance example:https://github.com/johnymontana/habitat-ai-test. Semvec with PydanticAI, two-file quickstart.Documentation:semvec-docs.pages.dev. Quickstart, concepts, every public class, REST endpoint catalogue, integration recipes for LangChain, DeepAgents, PostgreSQL, Neo4j, and Mem0.Pricing & licensing:semvec.io. Community / Pro / Enterprise tiers; offline-issued JWTs for air-gapped deployments. Neo4j AuraDB Free:neo4j.com/cloud/aura-free. Free hosted Neo4j 5.x with vector index support.** Healthcare demo data:uvx create-context-graph healthcare-test-data –domain healthcare –framework pydanticai –demo-data Support:[email protected]· Security disclosures:[email protected]· Founder:[email protected] non-provisional U.S. patent applications pending Nos. 19/269,195 and 19/550,466; European applications EP 25 188 105 · EP 26 160 795.**
Run the demo end-to-end #
git clone https://github.com/VersinoPsiOmega/Semvec-neo4j-agent-integration.gitpython3 -m venv .venv && source .venv/bin/activateuv pip install -e ".[dev]" # pulls semvec from PyPI + repo extrascp .env.example .env # fill in NEO4J_TEST_PASSWORD + OPENAI_*python scripts/seed_test_data.py # seed the healthcare graph (required once)python scripts/interactive_demo.py
semvec-docs.pages.dev* · semvec.io · *pypi.org/project/semvec
Constant-cost semantic memory for multi-agent systems was originally published in Neo4j Developer Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.