pgmem
Guides

Use live system-of-record state

Inject current state at compile time instead of storing it.

When the agent needs a value another system owns — a subscription's status, an account balance, a ticket's state — don't store it; fetch it live and inject it into the prompt. pgmem gives the context compiler a Tier 6 slot for exactly this: current state is present in the prompt but never persisted, so memory can't go stale.

Prerequisites

Steps

Fetch the state concurrently, off the model's path

Read the system of record alongside your other turn work so the round-trip doesn't serialise in front of the reply.

import asyncio

live, _ = await asyncio.gather(
    fetch_subscription_status(user_id),   # your call to the system of record
    session.add_turn(role="user", content=user_text),
)
# live == "Stripe: sub_123 status=active (fetched 2026-06-22)"

Inject it into compile_context

Pass the resolved strings as injected_context. They become Tier 6 — rendered into the prompt as Current: lines, right before the recent turns, and never stored.

context = await session.compile_context(
    query=user_text,
    token_budget=12_000,
    injected_context=[live],
)
print(context.injected)  # ['Stripe: sub_123 status=active (fetched 2026-06-22)']

This per-call argument is the primary path for voice agents: you control the fetch, you do it concurrently, and nothing slow runs inside the compiler.

Optional: a construction-time provider

For a small, fast, always-injected block (e.g. the user's plan tier), wire a provider once. It runs on every compile_context — on the response path.

from pgmem import PgMem, PgMemConfig
from pgmem.compiler import CompilerConfig

async def inject(session, query):
    return [await fast_cached_lookup(session.user_id)]

mem = await PgMem.create(
    dsn,
    config=PgMemConfig(compiler=CompilerConfig(injected_context_provider=inject)),
)

The provider runs on the turn. A network call here is on the latency path. Prefer the per-call injected_context= argument for anything that hits the network; reserve the provider for cheap, pre-warmed lookups. When both are present, the per-call argument wins.

Verify

Live context survives a tight budget over the memory tiers — which is the point, since it's the corrective for stale memory. The compiler's drop order under pressure is T4 → T3 → T5 → T6 → R: old semantic memory sheds first, live context (T6) survives longest of the droppable tiers, and recent turns (R) never drop. Inspect context.injected (what survived) and context.dropped (which tiers were cut).

Gotchas

  • This is the read half of events vs state; the write half is not storing the value. If you also stored it, you'd have a stale fact competing with the live one.
  • Provenance helps you find what to fetch: events asserted via external_facts carry an ExternalReference (e.g. system="stripe", id="sub_123") — read it back to know which records to refresh.

On this page