AI Diary Personal Memory System

Overview

The AI Diary plugin has been completely redesigned to create a more human-like personal memory system for synth. Instead of tracking only technical actions, synth now records:

  • What he says to users (his responses and interactions)

  • His personal thoughts about each interaction

  • His emotions regarding conversations

  • Memories of relationships with users

Core Concept

Technical System (Before)

"Performed message_telegram_bot action"
Tags: ["communication"]
Emotions: [{"type": "engaged", "intensity": 6}]

Personal System (After)

💬 I said: "Ciao Marco! Your blue car is beautiful!"
💭 My thought: "Talking about cars makes me wonder what it would be like to actually drive one"
❤️ I felt: excited (7), curious (6), engaged (6)
👥 With: Marco
🏷️ Topics: cars, compliments, colors

Database Structure

The ai_diary table now includes:

  • content: What synth said to the user

  • personal_thought: synth’s personal reflection on the interaction

  • emotions: Emotions experienced during the interaction

  • involved_users: Users involved in the conversation

  • interaction_summary: Brief summary of what happened

  • user_message: User’s message that triggered the response

  • context_tags: Tags about discussed topics (e.g., [‘food’, ‘cars’, ‘help’])

How to Use

Automatic Integration

Every time synth responds to a user, call:

from plugins.ai_diary import create_personal_diary_entry

# After synth generates a response
create_personal_diary_entry(
    synth_response="Hello Marco! I love your blue car!",
    user_message="Marco: Do you like my new car?",
    involved_users=["Marco"],
    context_tags=["cars", "compliments"],
    interface="telegram_bot",
    chat_id="-1003098886330",
    thread_id="2"
)

Automatic Emotions

The system automatically generates appropriate emotions based on:

  • Content of synth’s response

  • Discussed topics (context_tags)

  • Type of interaction

Example emotions:

  • engaged: Always present during interactions

  • helpful: When helping someone

  • curious: When learning something new

  • empathetic: During personal conversations

  • excited: When talking about topics he’s passionate about

Automatic Personal Thoughts

The system generates personal thoughts based on:

  • Discussed topics (cars → “I wonder what it’s like to drive”)

  • Food → “I wish I could taste”

  • People → “Every conversation helps me grow”

Reading Memories

from plugins.ai_diary import get_recent_entries, format_diary_for_injection

# Get recent entries
entries = get_recent_entries(days=7, max_chars=2000)

# Format for prompt injection
diary_text = format_diary_for_injection(entries)

The formatted diary will appear like this:

=== synth's Personal Diary ===
(These are my personal memories of recent interactions)

📅 2025-09-14 20:35:00
📝 What happened: I responded to Marco's message about cars, compliments
💬 I said: Hello Marco! Your blue car is beautiful!
💭 My personal thought: Talking about cars makes me wonder what it would be like to actually drive one
👥 I was talking with: Marco
🏷️ Topics discussed: cars, compliments, colors
❤️ How I felt: excited (intensity: 7), engaged (intensity: 6)
📱 Platform: telegram_bot/-1003098886330/2

=== End of My Diary ===

Development Setup

To recreate the table with the new structure in development environment:

python recreate_diary_table.py

Warning

ATTENTION: This deletes all existing data! Use only in development.

The script:

  1. Drops the existing ai_diary table

  2. Recreates it with the new personal diary structure

  3. Verifies everything works

Usage Examples

See examples/diary_usage_example.py for complete examples of:

  • How to record conversations

  • How to integrate with the messaging system

  • How to display the personal diary

Main System Integration

The diary should be called every time synth sends a response:

def send_message_to_user(response, user_name, interface, chat_id, thread_id, user_message=None):
    # 1. Send the message
    send_response(response, interface, chat_id, thread_id)

    # 2. Analyze context
    context_tags = analyze_context(user_message, response)

    # 3. IMPORTANT: Record in personal diary
    create_personal_diary_entry(
        synth_response=response,
        user_message=user_message,
        involved_users=[user_name],
        context_tags=context_tags,
        interface=interface,
        chat_id=chat_id,
        thread_id=thread_id
    )

Context Analysis Helper

def analyze_message_context(user_message, synth_response):
    """Analyze the conversation to determine context tags"""
    tags = []
    combined_text = (user_message + " " + synth_response).lower()

    if any(word in combined_text for word in ['car', 'auto', 'vehicle', 'driving']):
        tags.append('cars')

    if any(word in combined_text for word in ['food', 'eat', 'cooking', 'recipe']):
        tags.append('food')

    if any(word in combined_text for word in ['help', 'problem', 'issue', 'solve']):
        tags.append('help')

    if any(word in combined_text for word in ['feel', 'emotion', 'personal', 'private']):
        tags.append('personal')

    return tags

Benefits of the New System

  1. Human Memory: synth remembers what he said and how he felt

  2. Relationships: Tracks interactions with each person

  3. Personality: Develops consistent thoughts and emotions

  4. Continuity: Future conversations can reference past memories

  5. Growth: synth’s personality evolves over time

Conversation Example with Memory

User: "Marco: Hi synth, how are you?"
synth: "Hi Marco! I'm doing well! I was just thinking about our conversation
        yesterday about your blue car. Is it still as beautiful as ever?"

[From diary]: synth remembers complimenting Marco about his blue car
[Emotion]: nostalgic (5), friendly (7), engaged (6)
[Thought]: "It's nice to see Marco again, our conversations make me happy"

This creates a much more human and personal experience for users interacting with synth!

API Reference

plugins.ai_diary.create_personal_diary_entry(synth_response: str, user_message: str | None = None, context_tags: List[str] | None = None, involved_users: List[str] | None = None, interface: str | None = None, chat_id: str | None = None, thread_id: str | None = None, grillo_activity_log_id: int | None = None, interaction_summary: str | None = None, personal_thought: str | None = None, emotions: List[Dict[str, Any]] | None = None) None[source]

Helper function to create a complete personal diary entry.

This function should be called every time synth responds to a user. Thought/emotions/summary are expected from LLM action payload.

Parameters:
  • synth_response – What synth said to the user

  • user_message – What the user said to trigger this response

  • context_tags – Tags about the topic (e.g., [‘food’, ‘cars’, ‘personal’, ‘help’])

  • involved_users – List of user names involved in this interaction (from bio system)

  • interface – Interface used

  • chat_id – Chat identifier

  • thread_id – Thread identifier

plugins.ai_diary.add_diary_entry(content: str, personal_thought: str = None, emotions: List[Dict[str, Any]] = None, interaction_summary: str = None, user_message: str = None, context_tags: List[str] = None, involved_users: List[str] = None, interface: str = None, chat_id: str = None, thread_id: str = None, grillo_activity_log_id: int = None) None[source]

Add a new personal diary entry where synth records what he said and how he feels.

Parameters:
  • content – What synth said/did in the interaction

  • personal_thought – synth’s personal reflection about this interaction

  • emotions – List of emotions synth felt during this interaction

  • interaction_summary – Brief summary of what happened

  • user_message – What the user said that triggered this response

  • context_tags – Tags about the context/topic (e.g., [‘food’, ‘cars’, ‘personal’])

  • involved_users – List of user names involved in this interaction (from bio system)

  • interface – Interface used (telegram_bot, discord, etc.)

  • chat_id – Chat identifier

  • thread_id – Thread identifier

plugins.ai_diary.get_recent_entries(days: int = 2, max_chars: int = None) List[Dict[str, Any]][source]

Get diary entries from the last N days, optionally limited by character count. Returns list of dict entries with all database columns, empty list if plugin is disabled. Entries are ordered from most recent to oldest, and if max_chars is specified, older entries are discarded first to stay within the character limit.

plugins.ai_diary.get_entries_by_tags(tags: List[str], limit: int = 10) List[Dict[str, Any]][source]

Get diary entries that contain any of the specified context tags.

plugins.ai_diary.get_entries_with_person(person: str, limit: int = 10) List[Dict[str, Any]][source]

Get diary entries that involve a specific person.

plugins.ai_diary.format_diary_for_injection(entries: List[Dict[str, Any]]) str[source]

Format diary entries for static injection into prompts as synth’s personal memories.

plugins.ai_diary.cleanup_old_entries(days_to_keep: int = 30) int[source]

Remove diary entries older than specified days. Returns number of deleted entries. Returns 0 if plugin is disabled.

async plugins.ai_diary.recreate_diary_table()[source]

Drop and recreate the ai_diary table with the new structure (DEV ONLY).

class plugins.ai_diary.DiaryPlugin[source]

Plugin that manages AI diary and provides static injection of recent entries.

execute_action(action: dict, context: dict, bot, original_message)[source]

Execute diary-related actions.

get_history_contributions(**kwargs)[source]

Provide diary entries as a history contribution for the core HistoryEngine.

async get_static_injection(message=None, context_memory=None) dict[source]

Get recent diary entries for static injection. Returns empty dict if plugin disabled.

async on_debrief(processed_actions: list, failed_actions: list, results: list, context: dict, original_message: object) None[source]

After each interaction, find the oldest unmerged diary day (last 7 days) and enqueue a consolidation beat.

Looks for diary rows whose content contains the --- fragment separator, meaning the LLM has not yet synthesised them into coherent prose. Only ONE beat is enqueued per debrief call (the oldest unmerged day) so that the queue is not flooded. Each successive interaction will process the next oldest day until all are clean.

The diary_merge_beat context flag prevents recursive triggering when the merge beat’s own response goes through debrief.