agentsclimarketplace

Graphiti incremental update

Skill AnthonyAlcaraz/agentic-graph-rag-skills/skills/memory/graphiti-incremental-update

Companion repo for Agentic Graph RAG (O'Reilly, Anthony Alcaraz & Sam Julien) — 50 runnable skills + 8 pedagogical notebooks covering all eight chapters, on one moto-mocked AWS DevOps scenario

Install
npx -y skills add AnthonyAlcaraz/agentic-graph-rag-skills --skill graphiti-incremental-update

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Graphiti (Zep) incremental-update pattern (Ch4). When new content arrives, process only the new content — never re-process the entire graph. Pipeline: (1) extract entities from new episode, (2) entity-resolve against existing graph (dedupe by canonical name + alias + fuzzy match), (3) incremental- update — touch only the affected neighborhood, leave the rest of the graph unchanged. Keeps update latency O(new content) instead of O(full graph), enabling sub-second latency at millions-of-nodes scale. Use when memory graph grows continuously and full-graph re-embedding is impossibly expensive. NOT for one-shot batch ingestion (just process it once), NOT for static knowledge bases (no updates means no incremental anything).

SKILL.md

9.1 KB, as published. Nobody here has run it

Graphiti Incremental-Update

Overview

A naive add-to-graph implementation re-extracts entities across the whole corpus, re-computes embeddings, and re-clusters everything every time new content arrives. At millions-of-nodes scale this is fatal: update latency grows with graph size, and the agent ends up waiting for re-indexing instead of reasoning.

Graphiti (Zep) ships a different pattern (Ch4 Example 4-8):

  1. Extract entities and relationships only from the new episode. No backfill, no full-corpus re-pass.
  2. Entity-resolve: match each extracted entity against existing graph nodes by canonical name, then alias, then fuzzy/embedding similarity. If matched, reuse the existing node id. If not, create new.
  3. Incremental-update: modify only the touched neighborhood. New edges, possibly updated node metadata, no global re-clustering.

The chapter quote: "incremental_update modifies only the impacted neighborhood: the few nodes and edges touched by the new entities and relationships. The rest of the graph stays untouched, which keeps write operations predictably fast."

The retrieval side mirrors this: instead of one big query over everything, run parallel partial queries (vector / graph walk / keyword) targeting recent / specific / textual subsets, then merge. Production-grade Reciprocal Rank Fusion lives in a sibling skill; this skill is the ingestion side.

When to Use

  • Memory graph that grows continuously (every interaction adds nodes)
  • DevOps incident streams: new alerts, new deployments, new comments arriving every few seconds
  • Customer-support agents where each ticket is an "episode" — entities (customer, product, issue type) resolve to existing nodes, new edges are added
  • Multi-agent systems where each agent contributes new content to a shared graph and no agent should block on full-graph re-processing

Phrases: "incremental update", "don't re-process the whole graph", "entity resolution", "deduplicate against existing nodes", "Graphiti pattern", "add an episode".

When NOT to Use

  • One-shot batch ingestion. Process the corpus once, build the graph, done. Incremental pattern is overhead.
  • Static knowledge base. No updates means no incremental anything.
  • Adversarial entity resolution (bot accounts, evasion). Default fuzzy-match heuristic is too permissive; use a hardened resolver with domain rules.
  • Very small graph (< 1000 nodes). Full re-process is cheap; the incremental dispatch overhead exceeds the savings.

Process

StepInputActionOutputVerification
1new episode (text or structured)lib.extract_entities(episode)list of ExtractedEntity (name, type, aliases, metadata)each extracted entity has non-empty name and type
2extracted entities + existing graphlib.entity_resolution(extracted, graph)list of ResolvedEntity (existing_node_id or None) + resolution-method logresolution-method ∈ {canonical, alias, fuzzy, new}; method distribution should be heavily {canonical, alias} for mature graphs
3resolved entities + episode metadatalib.incremental_update(resolved, graph, episode_id)updated graph (in-place); touched-nodes listtouched_nodes count == size of impacted neighborhood (not full graph); rest of graph unchanged (checksum)
4full pipelinelib.add_episode(episode, graph)episode_id + touched_nodes + resolution_logend-to-end latency scales with episode size, not graph size
5graph + before/after snapshotslib.verify_locality(before, after)"X nodes changed of Y total — Z%"Z% should be small (< 5%) for typical mature-graph incremental adds

Rationalizations

Agent rationalizationDocumented rebuttal
"I'll just re-extract everything every time — disk is cheap."Disk is cheap; latency is not. The Ch4 anchor explicitly notes Graphiti maintains "sub-second response times even as the number of nodes and edges climbs into the millions." Re-extracting at that scale defeats the architecture.
"Entity resolution is too brittle — I'll create a new node every time."Then the graph bloats: 50 different ids for "Sarah" across 50 interactions. The Ch4 quote: "this step ensures they resolve to the same entity rather than creating duplicates." Without resolution, manages edges scatter across duplicate Sarah-nodes and the agent can't aggregate.
"Fuzzy match is good enough — I'll skip canonical + alias."Order matters. Canonical match is O(1) dictionary lookup; alias is O(1); fuzzy is O(n) or worse. Resolution should try cheap-and-exact first, fall back to expensive-and-approximate. Skipping the cheap path is a perf regression hiding as a correctness "improvement."
"Incremental update sounds like premature optimization — start with full re-extract."At small scale it doesn't matter. At any production scale (Zep target: millions of nodes), full re-extract is impossible. Building the architecture incremental-from-day-one means migration day never comes.
"Touched-nodes verification is overkill — trust the algorithm."The locality invariant is the property everything else rests on. A bug that silently touches every node looks identical to a correct incremental update at the call site. The verify_locality step is the falsifier — without it, regressions land silently.

Red Flags

  • touched_nodes count grows with graph size. The incremental property is broken; something is doing a full scan. Inspect entity_resolution and incremental_update for accidental full-graph passes.
  • Resolution method distribution is > 80% "new" on a mature graph. Either the graph is bootstrapping (early days, expected) or canonical/ alias resolution is broken (mature, anomalous).
  • Fuzzy match rate > 30% on a mature graph. Aliases are missing. Add the canonical aliases as exact matches; reserve fuzzy for genuinely unknown variants.
  • Entity-resolved-to-existing but the existing node's type doesn't match. Resolution is confusing person-named-Apple with company-named-Apple. Add type-aware resolution; never collapse across types.

Non-Negotiable Verification

  1. Run the benchmark battery. python cli.py benchmark must report:
    • touched_nodes count is independent of graph size (10-node graph and 1000-node graph produce the same touched_nodes for the same episode)
    • canonical / alias / fuzzy / new resolution methods all exercised
    • locality invariant: % nodes changed < 10% for the test workload
    • round-trip serialize/deserialize preserves graph state
  2. Run the DevOps scenario. python cli.py scenario incident-stream must process 20 incident-update episodes, with touched_nodes per episode << total graph size.
  3. Verify CLI help. python cli.py --help exits 0 and prints SKILL.md.

Security Posture

  • Prompt injection. Episodes are untrusted input (chat turns, incident feeds). Extraction and resolution treat episode text as data - nothing is executed - but an adversarial episode can plant false entities or alias itself onto an existing node to corrupt what the graph "knows". Type-aware resolution and the resolution-method log are the audit surface; review high-impact merges.
  • Data exfiltration. Episode content is persisted into the durable graph - sensitive facts mentioned once stay retrievable indefinitely. No network calls or file writes in the skill itself; apply retention policy at the graph store.
  • Privilege escalation. No shell invocation, no eval. The escalation path is graph poisoning: a crafted episode that resolves onto a trusted node inherits that node's standing in downstream reasoning. Keep the locality check (touched_nodes) as a blast-radius bound and gate bulk merges.

Source Attribution

Distilled from Agentic GraphRAG (O'Reilly, by Anthony Alcaraz and Sam Julien), Chapter 4 — Graphiti Pattern section + Example 4-8. Production anchor: Zep Graphiti.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.