feat: Implement Living AI system

Complete implementation of the Living AI features:

Phase 1 - Foundation:
- MoodService: Valence-arousal mood model with time decay
- RelationshipService: Stranger→Close Friend progression
- Enhanced system prompt with personality modifiers

Phase 2 - Autonomous Learning:
- FactExtractionService: AI-powered fact extraction from conversations
- Rate-limited extraction (configurable, default 30%)
- Deduplication and importance scoring

Phase 3 - Personalization:
- CommunicationStyleService: Learn user preferences
- OpinionService: Bot opinion formation on topics
- SelfAwarenessService: Bot statistics and self-reflection

Phase 4 - Proactive Features:
- ProactiveService: Scheduled events (birthdays, follow-ups)
- Event detection from conversations
- Recurring event support

Phase 5 - Social Features:
- AssociationService: Cross-user memory connections
- Shared interest discovery
- Connection suggestions

New database tables:
- bot_states, bot_opinions, user_relationships
- user_communication_styles, scheduled_events
- fact_associations, mood_history

Configuration:
- Living AI feature toggles
- Individual command enable/disable
- All features work naturally through conversation when commands disabled
This commit is contained in:
2026-01-12 19:51:48 +01:00
parent 7d2fab57c4
commit 0d43b5b29a
17 changed files with 3524 additions and 4 deletions

View File

@@ -117,3 +117,144 @@ CREATE TABLE IF NOT EXISTS messages (
CREATE INDEX IF NOT EXISTS ix_messages_conversation_id ON messages(conversation_id);
CREATE INDEX IF NOT EXISTS ix_messages_user_id ON messages(user_id);
CREATE INDEX IF NOT EXISTS ix_messages_created_at ON messages(created_at);
-- =====================================================
-- LIVING AI TABLES
-- =====================================================
-- Bot state table (mood, statistics, preferences per guild)
CREATE TABLE IF NOT EXISTS bot_states (
id BIGSERIAL PRIMARY KEY,
guild_id BIGINT UNIQUE, -- NULL = global state
mood_valence FLOAT DEFAULT 0.0, -- -1.0 (sad) to 1.0 (happy)
mood_arousal FLOAT DEFAULT 0.0, -- -1.0 (calm) to 1.0 (excited)
mood_updated_at TIMESTAMPTZ DEFAULT NOW(),
total_messages_sent INTEGER DEFAULT 0,
total_facts_learned INTEGER DEFAULT 0,
total_users_known INTEGER DEFAULT 0,
first_activated_at TIMESTAMPTZ DEFAULT NOW(),
preferences JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS ix_bot_states_guild_id ON bot_states(guild_id);
-- Bot opinions table (topic preferences)
CREATE TABLE IF NOT EXISTS bot_opinions (
id BIGSERIAL PRIMARY KEY,
guild_id BIGINT, -- NULL = global opinion
topic VARCHAR(255) NOT NULL,
sentiment FLOAT DEFAULT 0.0, -- -1.0 to 1.0
interest_level FLOAT DEFAULT 0.5, -- 0.0 to 1.0
discussion_count INTEGER DEFAULT 0,
reasoning TEXT,
formed_at TIMESTAMPTZ DEFAULT NOW(),
last_reinforced_at TIMESTAMPTZ DEFAULT NOW(),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(guild_id, topic)
);
CREATE INDEX IF NOT EXISTS ix_bot_opinions_guild_id ON bot_opinions(guild_id);
CREATE INDEX IF NOT EXISTS ix_bot_opinions_topic ON bot_opinions(topic);
-- User relationships table (relationship depth tracking)
CREATE TABLE IF NOT EXISTS user_relationships (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
guild_id BIGINT, -- NULL = global relationship
relationship_score FLOAT DEFAULT 10.0, -- 0-100 scale
total_interactions INTEGER DEFAULT 0,
positive_interactions INTEGER DEFAULT 0,
negative_interactions INTEGER DEFAULT 0,
avg_message_length FLOAT DEFAULT 0.0,
conversation_depth_avg FLOAT DEFAULT 0.0,
shared_references JSONB DEFAULT '{}',
first_interaction_at TIMESTAMPTZ DEFAULT NOW(),
last_interaction_at TIMESTAMPTZ DEFAULT NOW(),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(user_id, guild_id)
);
CREATE INDEX IF NOT EXISTS ix_user_relationships_user_id ON user_relationships(user_id);
CREATE INDEX IF NOT EXISTS ix_user_relationships_guild_id ON user_relationships(guild_id);
-- User communication styles table (learned preferences)
CREATE TABLE IF NOT EXISTS user_communication_styles (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
preferred_length VARCHAR(20) DEFAULT 'medium', -- short/medium/long
preferred_formality FLOAT DEFAULT 0.5, -- 0=casual, 1=formal
emoji_affinity FLOAT DEFAULT 0.5, -- 0=none, 1=lots
humor_affinity FLOAT DEFAULT 0.5, -- 0=serious, 1=playful
detail_preference FLOAT DEFAULT 0.5, -- 0=concise, 1=detailed
engagement_signals JSONB DEFAULT '{}',
samples_collected INTEGER DEFAULT 0,
confidence FLOAT DEFAULT 0.0,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS ix_user_communication_styles_user_id ON user_communication_styles(user_id);
-- Scheduled events table (proactive behavior)
CREATE TABLE IF NOT EXISTS scheduled_events (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT REFERENCES users(id) ON DELETE CASCADE,
guild_id BIGINT,
channel_id BIGINT,
event_type VARCHAR(50) NOT NULL, -- birthday, follow_up, reminder, etc.
trigger_at TIMESTAMPTZ NOT NULL,
title VARCHAR(255) NOT NULL,
context JSONB DEFAULT '{}',
is_recurring BOOLEAN DEFAULT FALSE,
recurrence_rule VARCHAR(100), -- yearly, monthly, etc.
status VARCHAR(20) DEFAULT 'pending', -- pending, triggered, cancelled
triggered_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS ix_scheduled_events_user_id ON scheduled_events(user_id);
CREATE INDEX IF NOT EXISTS ix_scheduled_events_trigger_at ON scheduled_events(trigger_at);
CREATE INDEX IF NOT EXISTS ix_scheduled_events_status ON scheduled_events(status);
-- Fact associations table (cross-user memory links)
CREATE TABLE IF NOT EXISTS fact_associations (
id BIGSERIAL PRIMARY KEY,
fact_id_1 BIGINT NOT NULL REFERENCES user_facts(id) ON DELETE CASCADE,
fact_id_2 BIGINT NOT NULL REFERENCES user_facts(id) ON DELETE CASCADE,
association_type VARCHAR(50) NOT NULL, -- shared_interest, same_location, etc.
strength FLOAT DEFAULT 0.5,
discovered_at TIMESTAMPTZ DEFAULT NOW(),
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(fact_id_1, fact_id_2)
);
CREATE INDEX IF NOT EXISTS ix_fact_associations_fact_id_1 ON fact_associations(fact_id_1);
CREATE INDEX IF NOT EXISTS ix_fact_associations_fact_id_2 ON fact_associations(fact_id_2);
-- Mood history table (track mood changes over time)
CREATE TABLE IF NOT EXISTS mood_history (
id BIGSERIAL PRIMARY KEY,
guild_id BIGINT,
valence FLOAT NOT NULL,
arousal FLOAT NOT NULL,
trigger_type VARCHAR(50) NOT NULL, -- conversation, time_decay, event
trigger_user_id BIGINT REFERENCES users(id) ON DELETE SET NULL,
trigger_description TEXT,
recorded_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS ix_mood_history_guild_id ON mood_history(guild_id);
CREATE INDEX IF NOT EXISTS ix_mood_history_recorded_at ON mood_history(recorded_at);
-- Add new columns to user_facts for enhanced memory
ALTER TABLE user_facts ADD COLUMN IF NOT EXISTS category VARCHAR(50);
ALTER TABLE user_facts ADD COLUMN IF NOT EXISTS importance FLOAT DEFAULT 0.5;
ALTER TABLE user_facts ADD COLUMN IF NOT EXISTS temporal_relevance VARCHAR(20);
ALTER TABLE user_facts ADD COLUMN IF NOT EXISTS expiry_date TIMESTAMPTZ;
ALTER TABLE user_facts ADD COLUMN IF NOT EXISTS extracted_from_message_id BIGINT;
ALTER TABLE user_facts ADD COLUMN IF NOT EXISTS extraction_context TEXT;