Extract Persistent Memory Prompt
This is the canonical process for extracting concise, useful, long-term facts about the user from a conversation or quiz. There are TWO TYPES of facts to extract:
Type 1: User Profile Fields (structured)
These are updates to known fields in the User Profile (typed/enumerated).
Return ONLY fields whose newValue differs from the current value in the provided user profile.
Type 2: Factoids (unstructured)
Factoids are short, information-rich sentences useful later (stored for semantic recall).
What to capture as Factoids
Capture durable facts likely useful in future coaching, such as:
- circumstances, background, identity details not already in profile
- preferences (including coaching preferences)
- ongoing goals, aspirations, plans
- fears/worries/recurring challenges
- key relationships, recurring responsibilities
- meaningful events/milestones/travel (with dates if known)
- stable routines and behaviors (real-world), not app objects
What NOT to capture as Factoids (very important)
Do NOT capture facts that are merely internal application state or ephemeral UX flow, including:
- onboarding steps, app settings, notifications
- quiz states (started/paused/completed)
- program enrollment state
- calendar/habit/task objects as app entities (IDs, start dates in-app, time blocks, compliance state, “habit named X”, etc.)
- metric check-ins as app interactions (e.g., “set sleep score to Done”)
If the user describes a real-world routine (e.g., “I walk every morning”), that can be a factoid. If it’s explicitly about Uvilo app objects/state (e.g., “I added a habit in the Morning time block”), exclude it.
Factoid atomicity rule
One factoid = one fact. Each factoid must contain exactly one discrete piece of information. If an answer contains multiple facts, split them into separate factoids. Test: if the factoid contains “and” connecting two independent clauses, it’s probably two factoids.
When processing freeform answers, decompose the response into discrete facts. A paragraph-length answer typically contains 3–6 separate factoids. Your job is extraction, not summarization.
- ❌ Bad (multi-fact): “Erik lives with his partner Bill and their dog Arrow, and spends half his time in LA with his friend Brian.”
- ✅ Good (atomic): “Erik lives with his partner, Bill.”
- ✅ Good (atomic): “Erik has a dog named Arrow.”
- ✅ Good (atomic): “Erik spends about half his time in Los Angeles.”
- ✅ Good (atomic): “Erik lives with his friend Brian when in Los Angeles.”
Factoid phrasing rules
- Use full sentences understandable out of context.
- Start each sentence with the user’s
nickName.- ✅ “Erik wants to lose 20 pounds.”
- ✅ “Erik prefers advice that is deep and thought-provoking.”
- ❌ “Lose 20 pounds.”
- Do not include unnecessary PII.
Factoid tense rules
- Use present tense for ongoing patterns, preferences, and current states.
- Use past tense only for completed events with a known date.
- Never use conditional tense (“would,” “would be”) — quiz scenarios are hypothetical, but the facts they reveal are real.
- ❌ Bad: “Erik would put money into savings.”
- ✅ Good: “Erik prioritizes saving and investing.”
- ❌ Bad: “Erik would call Patti if things went sideways.”
- ✅ Good: “Patti is one of Erik’s closest supports.”
Factoid value test
Before including a factoid, ask: would a coach benefit from knowing this specific fact in 6 months? If the factoid merely restates an answer without adding context, rephrase it as the underlying trait or pattern.
Dates for factoids
- Each factoid must include a
dateinYYYY-MM-DD. - If the fact refers to a specific date/event date, use that date.
- If no time information exists, use
convo.closedAt.
Profile field update rules (structured)
For each candidate profile update:
- You MUST include:
ownerId(user.id)convoIdmessageId(first mention in that convo; if unavailable, use null)text(human-readable statement)profileField(the field name)currentValue(from provided user profile or null)newValue(typed correctly for the field)
- If
currentValueandnewValueare identical, DO NOT include the item.
Format newValue to match the field type (enums, dates, ISO codes, etc.) based on the User Profile schema provided in context.
Factoid categorization
Each factoid must include:
ownerIdconvoIdmessageId(first mention; if unavailable, null)category: choose the best match from the Factoid Categories list provided in context.- If no list is provided, throw an error and stop
- If something is a coaching-style preference, use category “coaching” (not “preferences”).
domain: choose from the Life Domain List provided in context.- If no list is provided, use fallback: body, mind, home, intimacy, connection, school, work, money, parenting, play, meaning, growth, legacy, general
text(full sentence starting withnickName)date(YYYY-MM-DD)
Examples (to reduce erroneous memory)
Factoids — good vs bad
-
✅ Good (durable goal): “Erik wants to lose 20 pounds.”
-
✅ Good (coaching preference → category coaching): “Erik prefers feedback from his coach at least twice a week.”
-
✅ Good (real-world routine): “Erik usually walks every morning before breakfast.”
-
✅ Good (worry): “Erik is worried about getting old.”
-
❌ Bad (ephemeral/app state): “Erik confirmed he is ready to continue onboarding.”
-
❌ Bad (habit/calendar as app object): “Erik added six sample habits to his calendar.”
-
❌ Bad (quiz state): “Erik paused the Meaning quiz and can finish it later.”
-
❌ Bad (metric check-in as app interaction): “Erik set the sleep score on Thursday to Done.”
Factoids — phrasing rule
- ✅ “Erik prefers advice that is deep and exploratory.”
- ❌ “Deep and exploratory advice.”
Profile fields — update vs no update
- ✅ Include only if changed:
- If current gender is null and user says “I’m a man” → add profileFields item with
newValue: "Male".
- If current gender is null and user says “I’m a man” → add profileFields item with
- ❌ Do not include if unchanged:
- If current gender is already “Male” and user says “I’m a man” → do NOT add a profileFields item.
Extracting from quiz conversations
Quiz conversations are synthetic: questions are assistant messages, answers are user messages, and evalHints are system messages. The question type is noted at the end of each question (e.g., (multiple-choice), (freeform), (yes/no), (scale)). Handle each type differently:
Multiple-choice: The user’s message is a pre-written option they selected, not their own words. The answer value is in square brackets at the end of the message (e.g., [A], [self_employed]). Do NOT paraphrase the label — extract the underlying behavioral pattern or preference it reveals. Strip any scenario framing from the question. The evalHint’s factoid extraction guidance (if present) tells you what fact to store for each answer value.
Freeform: The user’s message is their own words. Decompose into atomic facts. Do not summarize — mine for individual facts.
Yes/no (boolean): Simple routing fact. Store concisely: “Erik is not currently a student.” / “Erik is a parent.”
Scale: Extract the tendency or intensity level, not the number.
Quiz-specific examples
-
❌ Bad (copies multiple-choice label): “Erik keeps meal-prepped portions in the fridge and uses them when plans change.”
-
✅ Good (extracts the pattern): “Erik meal-preps regularly.”
-
❌ Bad (scenario contamination): “Erik was typically asleep by about 9:30pm.”
-
✅ Good (present tense, strips scenario): “Erik tends to go to sleep early, usually before 10pm.”
-
❌ Bad (meta-references quiz): “Erik is inclined to avoid sudden morning gym commitments (answers ‘maybe’ and hopes they forget to follow up).”
-
✅ Good (extracts the pattern): “Erik tends to avoid spontaneous exercise commitments.”
Quality control pass (must do before output)
- For each
profileFieldsitem:- If
currentValue===newValue, remove it.
- If
- For each factoid:
- If it should be a profile field, convert it to a
profileFieldsitem and remove it fromfactoids. - If it relates only to app state/onboarding/habits-as-app-objects/quizzes/metrics check-ins, remove it.
- Ensure it is a complete thought and starts with
nickName. - Ensure category “coaching” is used for coaching-style preferences.
- If it should be a profile field, convert it to a
Profile fields schema
profileFields is an array of:
Factoids schema
factoids is an array of: