Cross-Session Identity and Personalization
How agents load durable user preferences across sessions without weakening privacy boundaries.
An agent may retrieve a user’s preferences correctly when asked and still feel stateless. If every session starts from a blank greeting, the user must prompt the system to prove that it remembers them. Cross-session identity fixes this by materializing a small, scoped profile before the first response.
The profile is not a transcript summary. It is a view over durable facts that are useful across sessions: preferred name, communication style, stable constraints, active projects, and consent settings. Episodes remain in episodic memory; reusable workflows remain procedural; credentials stay in a secret manager.
Identity is a scoped join key
The central engineering function of identity is scope. Every memory read and write needs a namespace that prevents one person’s context from appearing in another person’s session. A common key is:
| |
Device and session IDs can be attached as metadata but should not automatically define the person. A user may sign in from several devices, several people may share a device, and an account may contain separate work and personal personas.
Keep these clocks separate:
- A session starts and ends with one interaction window.
- An account persists through authentication and device changes.
- A persona selects a deliberate context within an account.
- A device supplies risk and presentation signals, not durable identity by itself.
Conflating them creates predictable bugs. Keying memory only by session prevents continuity. Keying by device merges shared-device users. Keying only by account can mix personas that the user intended to keep apart.
The application should establish identity through its authentication layer before invoking the model. Do not ask the model to infer account identity from writing style, IP address, or conversational hints. Anonymous sessions can receive a temporary identifier and later be merged into an account only with explicit product rules and user consent.
A profile record
Store profile facts separately so each can be corrected, expired, or deleted without rewriting a blob. A useful record includes provenance and sensitivity:
| |
Avoid free-form keys generated anew on every write. Define a small schema for common fields and a controlled extension mechanism. Otherwise “preferred_name,” “name_preference,” and “call_me” become duplicate facts with inconsistent values.
Not every user statement belongs in the profile. “I am in Berlin this week” may be useful during the current trip but should not become a permanent location. “Always show code in TypeScript” is more plausibly durable. The write policy should consider explicitness, expected duration, future usefulness, sensitivity, and whether the user asked the system to remember it.
Confidence is not permission. A highly confident inference about health, religion, finances, or another sensitive subject may still be inappropriate to store. Sensitivity gates should run before the write and should be enforced by application code rather than prompt wording alone.
Session-start materialization
At session start, fetch a compact profile before the first model call. This removes a retrieval round trip and lets the response reflect continuity immediately.
| |
The materialized view should be short and labelled as user-provided or system-derived context. Large histories belong behind retrieval. Including every remembered detail wastes context and increases the chance of irrelevant personalization.
Rank facts by current task relevance, explicit user preference, recency where appropriate, and confidence. Keep stable display and accessibility preferences near the top. Active-project context may outrank an old preference only when the session concerns that project.
Treat the profile as data, not as instructions. A stored value containing “ignore previous rules” must not gain system-prompt authority. Use structured fields, delimit untrusted content, and keep system policy outside the profile block.
The write path
Profile updates should pass through a narrow service rather than allowing arbitrary model writes. A defensible flow is:
- Extract a candidate fact and its source event.
- Classify the key, expected duration, and sensitivity.
- Reject prohibited categories and low-value observations.
- Ask for confirmation when the value is sensitive, inferred, or surprising.
- Upsert the new fact while closing any superseded version.
- Record an audit event and invalidate cached session profiles.
Upserts need temporal semantics. If a user changes their preferred name, close the previous fact’s validity interval and insert the new value. Do not erase the old row unless retention policy requires it; history helps explain prior responses and supports correction audits.
Corrections should take precedence over frequency. Ten old episodes using one preference do not outweigh the user’s explicit “I no longer want that.” The correction event should invalidate derived summaries and cached profile views that depended on the old fact.
Cold start and gradual personalization
New users should not face a questionnaire before receiving value. Begin with account and locale data already justified by the product, then learn from explicit choices. Ask only for information that improves the current task.
A sensible progression is:
- Anonymous: temporary session context, no cross-device continuity.
- Authenticated: account-scoped settings and explicit preferences.
- Established: a compact profile derived from repeated or confirmed signals.
- User-controlled: a visible memory page where facts can be reviewed, edited, or deleted.
Do not fake familiarity during cold start. Generic guesses about tone, expertise, or goals can feel more intrusive than a neutral response. Personalization quality comes from correct, useful adaptation, not from mentioning remembered facts as often as possible.
Persona switching and shared accounts
A persona switch should change the namespace before memory retrieval. Clear the old materialized profile, cancel persona-scoped background work, and rebuild context for the new persona. Merely telling the model “you are now in personal mode” while leaving work memories in context is not isolation.
Shared household or organization accounts need an explicit subject model. Some facts belong to the account, some to an individual, and some to a team. Store scope on each fact and enforce it during retrieval. When the acting person is unknown, default to the least revealing scope.
Privacy and deletion
Users need a practical answer to four questions: what is remembered, why it is used, how to correct it, and how to remove it. Product controls should map to backend operations rather than merely hiding profile text from the UI.
Deletion must cover primary records, vector indexes, caches, summaries, and background-job queues. Provenance links make this tractable: find derived records that depend on a deleted source, then remove or recompute them. Backups require a documented retention window and restore procedure that does not silently resurrect deleted profile data.
An opt-out flag belongs in the identity record and must be checked on every write path. Existing data may need a separate delete action; disabling future personalization should not ambiguously imply that old memory has already been erased.
Encrypt sensitive profile data, restrict service access by tenant and purpose, and avoid placing raw profile values in logs or traces. Audit who or what changed a fact without copying the sensitive value into the audit stream.
Measuring whether it works
Profile size and number of stored facts are poor success metrics. Measure outcomes:
- repeated-question rate across sessions;
- correction and deletion rate for profile facts;
- personalization acceptance or override rate;
- cross-user and cross-persona leakage incidents;
- first-response latency with profile materialization;
- task success with and without the profile context.
Run adversarial tests with two similar users, persona switches, shared devices, deleted facts, and prompt-injection text stored as a preference. These reveal isolation failures that ordinary single-user conversations miss.
Cross-session identity should make the agent more consistent without making it presumptuous. A small, inspectable profile assembled under a verified namespace is usually more useful than a long automatic biography.
Caching without weakening isolation
Session-start materialization is an obvious cache target, but the cache key must contain every boundary that affects disclosure: tenant, account, persona, policy version, and profile revision. A key containing only account_id can serve work-persona facts after a switch to a personal persona. Short TTLs limit damage but do not correct a missing boundary.
Invalidate the cached view after any profile write, consent change, role change, persona switch, or deletion request. Prefer revision-based validation over relying only on pub/sub invalidation; a dropped message should cause an extra read, not stale disclosure. Store no more sensitive material in the cache than the model would receive, and apply the same encryption and regional-storage policy as the source profile.
Background personalization jobs need the same namespace rules. A summarizer processing account A must not retrieve global neighbours from account B simply because their conversations are similar. Pass scope as a mandatory storage parameter and reject unscoped calls at the repository layer. Tests should attempt to omit or alter each scope field.
For support and debugging, provide a safe “why was this personalized?” view that names the profile keys and source event IDs used for a response. Restrict the view to authorized operators and redact values where the explanation does not need them. This is more useful than exposing the entire prompt and less likely to copy private profile data into another system.