pgmem
Guides

Ingest & search the graph

Ingest graph knowledge into Tier 4 and query it with hybrid search.

Build the knowledge graph two ways — LLM extraction from text, or asserting facts verbatim — then query it with hybrid search. This is the Tier 4 surface you use for cross-session recall outside a live session (evaluation, batch ingestion, direct lookups).

Prerequisites

Steps

Ingest by extraction

add_episode runs the LLM over content, extracts entities and edges, dedupes them against the graph, and resolves contradictions bi-temporally:

result = await mem.add_episode(
    content="Acme upgraded to Enterprise on a 2-year term, closed by Dana.",
    group_id="acme",
)
print(result.nodes, result.edges)   # what was created or matched

Assert facts verbatim (no extraction)

For facts you already trust — a webhook, a validated tool result — skip the LLM and write them directly with provenance. This never mints state edges by accident:

from pgmem import Authority, ExternalFact, ExternalReference

await mem.add_episode(
    content=raw_payload,      # kept for provenance; not re-extracted
    group_id="acme",
    external_facts=[
        ExternalFact(
            content="Acme's plan changed to Enterprise",
            external_refs=[ExternalReference(system="billing", id="acme")],
            source=Authority.application,
            confidence=1.0,
        ),
    ],
)

Search the graph

mem.search is hybrid — vector similarity, full-text, and graph traversal, fused into one ranked result. Always scope by group_ids:

results = await mem.search("what plan is Acme on?", group_ids=["acme"])
for node in results.nodes:
    print(node.name, node.summary)
for edge in results.edges:
    print(edge.fact)          # the relationship, with validity bounds

Pass config=SearchConfig(...) to force a recipe (the constants COMBINED_HYBRID_RRF / COMBINED_HYBRID_MMR / COMBINED_HYBRID_CROSS_ENCODER cover the common ones), or center_node_uuid= to bias toward a known entity.

(Optional) build communities

Cluster related entities into summarized communities for higher-level recall. It's a wipe-and-rebuild over the named groups, so run it periodically, not per-episode:

await mem.build_communities(group_ids=["acme"])

Verify

await mem.add_episode(content="Acme signed a 2-year Enterprise contract.", group_id="acme")
results = await mem.search("enterprise contract", group_ids=["acme"])
assert results.nodes, "expected at least one matching entity"

Gotchas

add_episode(content=...) is ungoverned extraction — feed it "the plan is Enterprise" (current state) and it may create an edge that goes stale. Assert events through external_facts, and keep current-state text out of content. See Events vs state.

  • Extraction is heavy (LLM + embeddings) — run it off the response path. For many episodes at once use add_episode_bulk (needs pool.max_size >= 2).
  • search is raw graph retrieval. To build a turn's prompt use compile_context, which folds the graph in with the other tiers under a budget.

On this page