Skip to content

feat(user-memory): target-aware profile extraction + anti-bloat durable-abstraction rule (en+zh)#4

Open
LoveS0ph1e wants to merge 1 commit into
EverMind-AI:mainfrom
LoveS0ph1e:feat/user-memory-target-aware-and-anti-bloat
Open

feat(user-memory): target-aware profile extraction + anti-bloat durable-abstraction rule (en+zh)#4
LoveS0ph1e wants to merge 1 commit into
EverMind-AI:mainfrom
LoveS0ph1e:feat/user-memory-target-aware-and-anti-bloat

Conversation

@LoveS0ph1e

Copy link
Copy Markdown

Summary

This PR makes ProfileExtractor target-aware in multi-speaker conversations and adds a durable-abstraction (anti-bloat) rule to the profile prompts, applied symmetrically to both English and Chinese. It consolidates and supersedes three earlier, overlapping PRs (#1, #2, #3) into one clean, minimal, reviewable change.

Motivation

Two independent quality problems surfaced when running ProfileExtractor on real group chats:

  1. Cross-speaker contamination. In a multi-person transcript, facts stated by another participant — or by the AI assistant itself — could be absorbed into the wrong user's profile (including the assistant's own persona leaking in as a "user trait").
  2. Profile bloat. Over many UPDATE cycles, description fields accumulated dated, event-log-style entries ("On 2026-06-29 …; on 2026-07-01 …") instead of stable generalizations, diluting the profile and hurting readability/retrieval.

What changed

  • Target-aware extraction (profile.py, prompts/{en,zh}/profile.py): the sender_id already passed to aextract is now threaded into the INIT and UPDATE prompts as target_user. Each conversation line is tagged (user_id:...); the prompts instruct the model to attribute every fact to its stated speaker and to treat all other participants — and the assistant — as context, never the profile's subject. Attribution keys on user_id, not the display name.
  • Anti-bloat "durable abstraction" HARD RULE (INIT + UPDATE, en + zh): each description must be a timeless generalization — no date/weekday/clock time (coarse time belongs in evidence); repeated instances of a pattern must be folded into one existing item via update rather than appended as new dated clauses. Includes WRONG/RIGHT examples and a pre-output self-check.
  • Timestamp coarsening (_render_conversation): per-message timestamps are rendered at date granularity (YYYY-MM-DD). Profile extraction needs no intraday precision; this removes second-level noise, avoids UTC/local intraday misreads, and reinforces the no-clock-time rule.
  • Test: a multi-speaker test asserts the target user_id is threaded into the prompt and that _render_conversation tags each speaker with their own user_id (so attribution keys on id, not name).
  • CHANGELOG updated under [Unreleased].

Backward-compatible: render_prompt performs key-wise substitution and tolerates extra fields, so a caller-supplied prompt= override that omits {target_user} remains a no-op. No public signatures change.

Relationship to prior PRs (this supersedes #1, #2, #3)

These changes previously existed as three entangled PRs against the same two files; consolidating them removes mutual conflicts and cleans up the issues found in an audit:

Validation

  • Prompt modules load; {target_user} and the anti-bloat rule are present in INIT and UPDATE for both en and zh.
  • render_prompt substitutes target_user, unescapes the literal JSON braces in the examples, and leaves no stray placeholders.
  • The added multi-speaker test asserts attribution keys on user_id.
  • The anti-bloat rule was validated in downstream production use (dated-description rate dropped substantially on a real bloated profile). Note: the repo's uv workspace test harness was not run in the authoring environment; CI should exercise the full suite.

…le-abstraction rule (en+zh)

Thread the existing sender_id into the INIT and UPDATE profile prompts as
target_user so extraction attributes each fact to the speaker who stated it
(lines are tagged (user_id:...)), keeping other participants and the AI
assistant as context only. Prevents cross-speaker contamination in group
conversations. render_prompt tolerates the extra field, so caller prompt=
overrides that omit {target_user} remain no-op-safe.

Add a durable-abstraction HARD RULE to both INIT and UPDATE prompts: each
description must be a timeless generalization (no date/weekday/clock time;
coarse time only in evidence), and repeated instances of a pattern must be
folded into one item via update rather than appended as dated clauses.

Coarsen _render_conversation per-message timestamps to date granularity.

Applied symmetrically to en and zh prompts. Adds a multi-speaker test
asserting attribution keys on user_id (not display name). CHANGELOG updated.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant