Integrate pgmem into your agent
Wire pgmem into your agent's turn loop.
Wiring pgmem into an agent is one rule applied to your turn loop: read memory to
build the prompt, write memory after you reply. Only the read
(compile_context) is on the response path; everything else runs after the user
has their answer.
Prerequisites
- pgmem installed and the schema applied.
- An embedder and an LLM client if you want the semantic graph (Tier 4) and compaction. The turn/memory/context surface works without them, but a real agent usually wants both.
- You've skimmed How a turn flows.
The llm you pass to PgMem powers pgmem's internal work (compaction
summaries, graph extraction). Generating the user-facing reply is your own
call against context.messages — the same provider or a different one. pgmem
hands you messages; how you call your model is up to you.
Steps
Construct the client once
Build PgMem at startup and reuse it across turns and sessions. It holds the
connection pool.
from pgmem import PgMem
mem = await PgMem.create(
dsn="postgresql://user:pass@host:5432/db", # your database
embedder=embedder, # your Embedder implementation
llm=llm, # your LLMClient implementation
)Resume or create a session per conversation
Create a fresh session for a new conversation; resume only to pick one up that
was interrupted mid-flight (e.g. a crashed worker). resume is group-scoped for
tenant isolation.
if session_id:
session = await mem.session.resume(session_id, group_id)
else:
session = await mem.session.create(group_id=group_id, user_id=user_id)
session_id = session.uuidOn each turn: record input, compile, reply
async def on_turn(user_text: str) -> str:
await session.add_turn(role="user", content=user_text)
context = await session.compile_context(query=user_text)
reply = await respond(context.messages) # your own model call
await session.add_turn(role="assistant", content=reply)
return replycompile_context is pure indexed reads under a token budget — no LLM — so it fits
the latency budget. Pass token_budget= and strategy= to tune what the model
sees (see Tune retrieval).
Compact off the turn
After replying, fold the turn buffer down without blocking the next turn. This returns immediately; summarisation and validation run in the background.
await session.compact_async()End the session when the conversation is over
Sealing preserves state for audit and queues promotion of eligible memory into the graph.
await session.end()Verify
Restart your process mid-conversation and call mem.session.resume(session_id, group_id) — the active execution state and recent turns come back, so the agent
continues where it left off. A new conversation gets a fresh session and retrieves
only the durable continuity it needs.
Gotchas
Keep memory writes and compaction off the response path. add_turn is a cheap
write, but compact_async, add_episode, and session.end (promotion) do real
work — never await them inline before producing the reply.
- Create a fresh session per conversation; don't
resumean old one to "continue" past context — let retrieval bring back the saga and facts instead. - Without an embedder, the semantic tier (Tier 4) is skipped; the rest still works.
Related
- How a turn flows — the on-turn vs off-turn split
- Decide what to remember — form memory from tool calls
- Use live system-of-record state — inject current state
- LiveKit / voice agents — the stateless-worker pattern