EgoZDocs

Learning

Per-end-user memory — how EgoZ learns durable facts about each of your users, under owner review, and personalizes future /ask answers.

Status: shipped. Per-end-user learning (internally exURM) is built and verified end-to-end — backend, @ego-z/contracts, and the Console screen (Phases A–C). One optional item remains (roster/facts pagination for very large tenants). See Build status at the bottom.

Today, the externalUserId you pass to /ask is opaque — EgoZ uses it to own threads and attribute audit records, and nothing more. Two sessions from the same person are two unrelated conversations.

Learning turns externalUserId into a per-end-user memory scope. When a project owner opts in, the agent proposes durable facts about each end-user; the owner reviews and approves them; approved facts are injected into future /ask answers for that user — across sessions.

The idea in one example

Session 1  extUser=ABC:  "do you have these pants in my size, 38?"
             → agent proposes a fact:  { size: 38 }   (status: pending)
             → owner approves it in the Console Learning screen

Session 2  extUser=ABC:  "what jerseys would you recommend?"
             → approved fact is injected into the prompt
             → "Assuming size 38 from your last visit, these three jerseys…"

Nothing personalizes an answer until a human has approved it. That is the core guarantee.

How it works

Learning adds no new step to the /ask lifecycle. It hooks the two existing points where the agent reads context and where it calls tools.

Read path — inject approved facts

After the project's personality block is assembled, EgoZ loads what it knows about (tenant, externalUserId) and appends a ## Known facts about this user section to the system prompt:

  • the approved profile (structured attributes, e.g. size = 38) — always injected;
  • the top-K approved memories (free-text notes) most similar to the current question, retrieved by vector similarity.

Write path — the remember_fact tool

The agent gets a built-in remember_fact tool alongside your endpoints and knowledge base — it works exactly like the built-in knowledge search. When the model decides something is worth remembering, it calls remember_fact; EgoZ writes a pending record and returns a neutral "saved for review" to the model. The proposed fact does not affect the current answer, and nothing is stored as approved.

The gate

The read and write paths activate only when all of the following hold:

  • the project has learningEnabled turned on (default off — opt-in), and
  • the request carries an externalUserId, and
  • the request is not in no-store mode (metadata-only tenants never learn).

Memory model

Learning uses one hybrid store keyed on (tenant_id, external_user_id) — the same isolation boundary as threads. Each record is one of two kinds:

KindWhat it isRetrieval
profileA structured attribute (attrKey → value), e.g. size = 38Always injected when approved
memoryA free-text note, embedded as a vectorTop-K by similarity to the current question

Every record carries a status (pending · approved · rejected), its source, the originating thread/message, an optional confidence, and review metadata. Only approved records ever reach a prompt.

The Console "Learning" screen

Learning is switched on from the project's Agent / LLM Settings screen, alongside model and prompt configuration — the same screen carries the retention-window selector (learningRetentionDays). Once enabled, a Learning area appears in the Console sidebar — it stays hidden from the menu while learning is off:

  • List page — the roster of end-users with their approved / pending counts. A badge surfaces the total pending review queue.
  • Detail page (per end-user) — the review queue (approve / reject / edit each pending fact), the approved profile and memories, and a Forget this user action that erases everything EgoZ has learned about them. Each fact deep-links back to the source conversation (via its originating thread/message) so you can see the context it came from before approving.

Trust & governance

Learning is built to the same trust rules as the rest of EgoZ:

  • Opt-in. Off by default; a project owner turns it on deliberately.
  • Review-before-use. Proposed facts are pending; only owner-approved facts are ever injected. The agent proposing a fact never changes the answer that proposed it.
  • No-store no-op. Metadata-only (no-store) tenants never learn — the gate is closed regardless of the flag.
  • Tenant isolation. Every query is scoped by tenant_id; one project can never read another's user memory.
  • Right to be forgotten. The per-user Forget action deletes all learned records for that externalUserId.
  • Retention. Each project sets a retention window (learningRetentionDays — Off / 30 / 60 / 90 days). A daily job purges pending and rejected facts past that window; approved facts are never auto-purged. Off (the default) keeps everything until a human acts on it.
  • Operator-visible. /ask metadata reports whether user memory was injected (memoryInjected) or proposed (memoryProposed) on a turn, and (planned) the debug trace surfaces learning nodes. A Presidio PII pass screens each proposal at write time and surfaces the flagged entity types (piiEntities, never the matched text) in the review queue, so the owner sees possible PII before approving.

Build status

Rollout is phased. Current state:

PhaseScopeStatus
A — Backend coremigrations, tenant-config flag, @ego-z/contracts shapes, user-memory module, read-path injection, remember_fact tool, admin API✅ Complete — migrations, learningEnabled flag, @ego-z/contracts v0.6.0, the user-memory module, orchestrator read-path injection, the remember_fact write tool, and the 7 admin routes are all landed; backend typecheck clean, tests green
B — Console screenSDK module, screen registration, list + detail pages✅ Complete — SDK, screen registration, and the list + review-queue pages built on @ego-z/contracts@0.6.0; the enable toggle lives in Agent/LLM Settings and the Learning nav item is hidden while disabled. Live E2E green: learn → pending → approve → inject → forget verified end-to-end on the real routes
C — PolishPresidio PII pass, debug-trace observability, per-tenant analytics, retention & auto-approve, roster/facts paginationIn progress — observability ✅ (memoryInjected / memoryProposed on /ask), the Presidio PII pass ✅ (proposals screened at write time; flagged entity types on the fact, never the matched text), per-tenant analytics ✅ (GET …/learning/analyticsLearningAnalyticsResponseData), and retention ✅ (per-tenant learningRetentionDays — Off/30/60/90 days — with a daily Worker purge of pending/rejected facts; approved is never purged) all landed; auto-approve was declined to keep review-before-use mandatory. Backend, contracts, and the Console screen are shipped and verified; only optional roster/facts pagination (for very large tenants) remains

The wire contract (UserMemoryWire and the /egoz/tenants/:tenantId/learning/* routes) is locked in @ego-z/contracts. If you're integrating against it, build to that shape.

On this page