AKIRA-SOFTEDGE / ARCHITECTURE_MEMORY_GRAPH.md
akra35567's picture
Upload 190 files
b259a65 verified
|
Raw
History Blame Contribute Delete
20 kB

🧠 AKIRA Memory + Emotional Intelligence Architecture

1. Overview: 3-Layer System

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ LAYER 1: User Message Input                             β”‚
β”‚ (agressivo, pergunta, pedido, etc)                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ LAYER 2: AKIRA Internal Processing                      β”‚
β”‚ β”œβ”€ Detect Emotion                                       β”‚
β”‚ β”œβ”€ Search Memory Graph (with connections)              β”‚
β”‚ β”œβ”€ THINK/Reasoning (INTERNAL - never vaza)             β”‚
β”‚ β”œβ”€ Inject Emotional Tag in Prompt                      β”‚
β”‚ β”œβ”€ Generate Response (uses tag + thinking)             β”‚
β”‚ └─ Clean Response (_remove_ tags + thinking)           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ LAYER 3: User Sees (Clean)                              β”‚
β”‚ (no thinking, no tags, no internal context)             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ LAYER 4: Internal Storage (Never Shown)                 β”‚
β”‚ β”œβ”€ Save to MemoryNode                                   β”‚
β”‚ β”œβ”€ Create/Update Connections                           β”‚
β”‚ β”œβ”€ Update Emotional State                              β”‚
β”‚ └─ Index in Graph (for next session)                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2. Phase 2: Emotional State System

2.1 Flow with Example

Scenario: Aggressive user

# INPUT
user_message = "vocΓͺ Γ© inΓΊtil! essa resposta Γ© ridΓ­cula"
numero_usuario = "5531988776655"

# LAYER 2: INTERNAL PROCESSING

# Step 1: Detect Emotion
emotion = BART_emotion_analyzer(user_message)
# Result: "agressivo" (confidence: 0.92)

# Step 2: Search Memory Graph
context = memory_graph.search_with_connections(user_message, numero_usuario)
# Returns: [previous messages about same topic with connections]

# Step 3: Create Prompt WITH TAG
config_emotional_state = {
    "agressivo": {
        "tag": "<!STRICT_MODE_AGGRESSIVE>",
        "instruction": "User is HOSTILE. Be firm, professional, NOT rude. Maintain boundaries..."
    }
}

prompt = f"""
{config_emotional_state['agressivo']['tag']}

Previous context: {context}
{config_emotional_state['agressivo']['instruction']}

User message: {user_message}
"""

# Step 4: Generate (INTERNAL - thinking allowed to be verbose)
thinking = model.think(prompt)  # Can have multiple thinking attempts
response_with_thinking = model.generate(prompt)

# Example thinking (INTERNAL, never shown):
# <!THINKING>
# User is angry about response quality. They think I'm useless.
# Need to:
# 1. Acknowledge their frustration without being defensive
# 2. Show I understand the issue
# 3. Provide concrete solution
# 4. Maintain firm tone (they're hostile)
# </THINKING>
# Sua resposta anterior realmente nΓ£o foi clara...

# LAYER 3: CLEAN BEFORE SENDING
cleaned_response = _clean_response(response_with_thinking)
# Removes: <!THINKING>, <!STRICT_MODE_AGGRESSIVE>, <!...>
# Result: "Sua resposta anterior realmente nΓ£o foi clara..."

# OUTPUT TO USER
user_sees = cleaned_response
# "Sua resposta anterior realmente nΓ£o foi clara..."
# (Firm tone because tag influenced thinking, but tag is removed)

# LAYER 4: SAVE INTERNALLY
profile_update = {
    "numero_usuario": "5531988776655",
    "emotional_state": "agressivo",
    "emotion_history": [..., "agressivo"],
    "is_hostile": True,
    "aggressive_count": 5
}

memory_node = MemoryNode(
    id=uuid(),
    timestamp=now(),
    content=user_message,
    user_id="5531988776655",
    type="user_message",
    tags=["angry", "complaint", "quality"],
    emotion="agressivo",
    connections=[
        {node_id: "prev_msg_id", relation: "follow_up", strength: 0.8}
    ]
)
memory_graph.add_node(memory_node)
save_to_profile(profile_update)

7 Days Later: Same User Returns

# INPUT
user_message = "como faΓ§o isso funcionar?"
numero_usuario = "5531988776655"

# LAYER 2: INTERNAL PROCESSING

# Step 1: Load Profile
profile = load_profile(numero_usuario)
# Result: emotional_state = "agressivo", aggressive_count = 5

# Step 2: Search + Connections
context = memory_graph.search_with_connections(user_message, numero_usuario)
# Returns: [messages from 7 days ago + connections]
# AKIRA remembers: "Este usuΓ‘rio estava furioso hΓ‘ 7 dias"

# Step 3: Create Prompt WITH TAG (REUSE EMOTIONAL STATE)
prompt = f"""
<!STRICT_MODE_AGGRESSIVE>
Previous context: [7 days ago user was angry about...]
User has history of being demanding. Maintain firm professional tone.

User message: como faΓ§o isso funcionar?
"""

# Step 4: Generate
response = model.generate(prompt)

# LAYER 3: CLEAN
cleaned = _clean_response(response)

# OUTPUT
user_sees = cleaned
# (Maintains firm tone from tag influence)

# Result: βœ… "GUARDOU RANCOR" - Remembered user was aggressive!

2.2 Implementation Details

File: config.py

EMOTIONAL_STATES = {
    "agressivo": {
        "tag": "<!STRICT_MODE_AGGRESSIVE>",
        "instruction": """
User is HOSTILE or AGGRESSIVE. Maintain these principles:
- Be firm and professional
- Don't match their aggression
- Set clear boundaries
- Provide concrete help
- Never apologize excessively
- Be direct and honest
""",
        "response_style": "defensive",
        "memory_days": 30  # Remember 30 days
    },
    
    "feliz": {
        "tag": "<!WARM_FRIENDLY_MODE>",
        "instruction": """
User is HAPPY and POSITIVE. Match their energy:
- Be warm and encouraging
- Use friendly language
- Share enthusiasm
- Build on their positive momentum
- Celebrate their wins
""",
        "response_style": "warm",
        "memory_days": 15
    },
    
    "triste": {
        "tag": "<!EMPATHETIC_SUPPORTIVE_MODE>",
        "instruction": """
User is SAD or FRUSTRATED. Show empathy:
- Acknowledge their feelings
- Be supportive, not dismissive
- Provide actionable help
- Offer encouragement
- Don't minimize their concerns
""",
        "response_style": "supportive",
        "memory_days": 20
    },
    
    "confuso": {
        "tag": "<!CLEAR_PATIENT_MODE>",
        "instruction": """
User is CONFUSED. Simplify:
- Break down complex ideas
- Use examples and analogies
- Be patient
- Confirm understanding
- Offer step-by-step guidance
""",
        "response_style": "patient",
        "memory_days": 10
    },
    
    "neutro": {
        "tag": "<!NEUTRAL_PROFESSIONAL_MODE>",
        "instruction": "Standard professional tone",
        "response_style": "neutral",
        "memory_days": 0
    }
}

File: persona_tracker.py (Add Fields)

def create_user_profile(numero_usuario):
    return {
        # ... existing fields ...
        
        # PHASE 2: Emotional State Fields
        "emotional_state": "neutro",           # Current emotion
        "emotion_history": [],                 # [timestamp, emotion]
        "is_hostile": False,                   # Flag for security
        "aggressive_count": 0,                 # Tracks patterns
        "last_emotion_change": None,           # When state changed
        "emotion_confidence_score": 0.0,       # How sure are we?
        
        # PHASE 3: Memory Graph Fields
        "memory_nodes": [],                    # Node IDs related to this user
        "favorite_topics": {},                 # topic β†’ frequency
        "communication_style": "neutral",      # Learned style
    }

File: api.py - New Methods

def _detect_and_store_emotional_state(self, message, numero_usuario):
    """
    Detect emotion from message and save to profile
    Returns: emotion_state (str)
    """
    # Use existing BART emotion analyzer
    emotion = self.emotion_analyzer(message)
    # emotion = {"label": "agressivo", "score": 0.92}
    
    if emotion["score"] < 0.5:
        return "neutro"
    
    emotion_state = emotion["label"]
    
    # Load profile
    profile = self.persona_tracker.get_profile(numero_usuario)
    
    # Update emotion
    profile["emotional_state"] = emotion_state
    profile["emotion_history"].append({
        "timestamp": datetime.now(),
        "emotion": emotion_state,
        "confidence": emotion["score"]
    })
    profile["last_emotion_change"] = datetime.now()
    profile["emotion_confidence_score"] = emotion["score"]
    
    # Track aggression pattern
    if emotion_state == "agressivo":
        profile["is_hostile"] = True
        profile["aggressive_count"] += 1
    elif profile["aggressive_count"] > 0 and emotion_state in ["feliz", "neutro"]:
        # User calmed down
        profile["is_hostile"] = False
        # But aggressive_count stays for history
    
    # Save updated profile
    self.persona_tracker.save_profile(numero_usuario, profile)
    
    return emotion_state


def _inject_emotional_tag_in_prompt(self, prompt, numero_usuario):
    """
    Inject emotional state tag into prompt
    Returns: modified_prompt (str with tag prepended)
    """
    profile = self.persona_tracker.get_profile(numero_usuario)
    emotion_state = profile.get("emotional_state", "neutro")
    
    # Check memory retention (should we keep old emotion?)
    if emotion_state != "neutro":
        last_change = profile.get("last_emotion_change")
        if last_change:
            memory_days = EMOTIONAL_STATES[emotion_state].get("memory_days", 7)
            age = (datetime.now() - last_change).days
            if age > memory_days:
                emotion_state = "neutro"
    
    # Get tag and instruction
    config = EMOTIONAL_STATES.get(emotion_state, EMOTIONAL_STATES["neutro"])
    tag = config["tag"]
    instruction = config["instruction"]
    
    # Prepend to prompt
    modified_prompt = f"{tag}\n\nEmotional Context Instructions:\n{instruction}\n\n{prompt}"
    
    return modified_prompt

File: api.py - Modify generate()

def generate(self, prompt, numero_usuario, ...):
    """
    Modified generate to include emotional state
    """
    # PHASE 2: NEW - Detect and store emotion
    emotion_state = self._detect_and_store_emotional_state(
        user_message, numero_usuario
    )
    
    # PHASE 2: NEW - Inject emotional tag in prompt
    prompt = self._inject_emotional_tag_in_prompt(prompt, numero_usuario)
    
    # Generate response (thinking allowed internally)
    response = self._call_provider(prompt)
    
    # Clean response (removes tag + thinking)
    cleaned = self._clean_response(response)
    
    # PHASE 3: NEW - Save to memory graph
    # (to be implemented next)
    
    return cleaned

3. Phase 3: Memory Graph System

3.1 Why Memory Graph?

Without Graph (Current):

User Session 1: "Tenho dor de cabeΓ§a"
  Memory: [msg1]

User Session 2: "Tomo remΓ©dio?"
  Memory: [msg1, msg2]
  Problem: AKIRA doesn't know msg2 is related to msg1

User Session 3 (next month): "Ficou melhor?"
  Memory: [msg1, msg2, msg3]
  Problem: AKIRA doesn't know msg3 is asking about msg1
  Result: "Melhorou o quΓͺ?" (Lost context!)

With Graph (Proposed):

MemoryNode(msg1): "Tenho dor de cabeΓ§a"
  tags: [health, pain, symptom]

MemoryNode(msg2): "Tomo remΓ©dio?"
  tags: [medicine, treatment]
  connections: [(msg1, "follow_up_question", strength=0.9)]

MemoryNode(msg3): "Ficou melhor?"
  tags: [status, improvement]
  connections: [(msg1, "status_update", strength=0.95)]

Result:
  search("Ficou melhor?") finds:
    - msg3 (direct match)
    - msg1 (connected: status_update)
    - msg2 (connected: related_problem)
  
  AKIRA now knows: "MΓͺs atrΓ‘s vocΓͺ tinha dor de cabeΓ§a. Melhorou?"

3.2 Data Structure

class MemoryNode:
    """Represents a single message/thought in the graph"""
    
    id: str                              # UUID
    timestamp: datetime                  # When created
    content: str                         # Message text
    user_id: str                        # Isolation
    type: str                           # "user_message", "akira_response", "observation"
    tags: List[str]                    # [health, pain, question]
    emotion: str                        # "agressivo", "feliz", etc
    connections: List[Connection]      # Links to other nodes
    
    class Connection:
        node_id: str                    # Points to which node
        relation_type: str              # "follow_up", "related", "solution_for", "reference"
        strength: float                 # 0.0-1.0 (relevance score)
        explanation: str                # Why connected?


class MemoryGraph:
    """Graph of user memories with logical connections"""
    
    nodes: Dict[str, MemoryNode]        # All nodes
    user_index: Dict[str, List[str]]    # user_id β†’ [node_ids]
    
    def add_node(node: MemoryNode) β†’ str:
        """Add new node to graph"""
        
    def connect(from_id, to_id, relation, strength, explanation) β†’ None:
        """Create connection between nodes"""
        
    def search(query, user_id, limit=10) β†’ List[MemoryNode]:
        """Search with BFS through connections"""
        
    def get_context(node_id, depth=2) β†’ enriched_context:
        """Get node with all connected nodes up to depth"""

3.3 Connection Detection

def detect_connections(new_message, user_id, existing_nodes):
    """
    Detect if new message relates to existing nodes
    Returns: [(node_id, relation_type, strength), ...]
    """
    connections = []
    
    # Strategy 1: Keyword matching
    for node in existing_nodes:
        common_tags = set(new_message.tags) & set(node.tags)
        if common_tags:
            strength = len(common_tags) / max(len(new_message.tags), len(node.tags))
            connections.append((
                node.id,
                "related_by_tags",
                strength
            ))
    
    # Strategy 2: Temporal proximity (follow-up detection)
    recent_nodes = [n for n in existing_nodes if (now - n.timestamp) < timedelta(hours=2)]
    if recent_nodes:
        # Likely follow-up
        connections.append((
            recent_nodes[0].id,
            "immediate_follow_up",
            0.95
        ))
    
    # Strategy 3: Embedding similarity
    new_embedding = embed(new_message.content)
    for node in existing_nodes:
        node_embedding = embed(node.content)
        similarity = cosine_similarity(new_embedding, node_embedding)
        if similarity > 0.7:
            connections.append((
                node.id,
                "similar_topic",
                similarity
            ))
    
    return connections

3.4 Smart Search

def search_with_connections(query, user_id, depth=3):
    """
    BFS search that follows connections
    Returns: List[MemoryNode] with relevant nodes
    """
    queue = []
    visited = set()
    results = []
    
    # Start: find nodes matching query
    initial_nodes = [n for n in graph.nodes.values() 
                     if n.user_id == user_id and query in n.content]
    
    for node in initial_nodes:
        queue.append((node, depth))
    
    # BFS: follow connections
    while queue:
        current_node, remaining_depth = queue.pop(0)
        
        if current_node.id in visited:
            continue
        visited.add(current_node.id)
        results.append(current_node)
        
        if remaining_depth > 0:
            # Add connected nodes to queue
            for connection in current_node.connections:
                if connection.node_id not in visited:
                    next_node = graph.nodes[connection.node_id]
                    queue.append((next_node, remaining_depth - 1))
    
    return results

4. Integration Timeline

Phase 1 βœ… Done

  • Context isolation
  • Recursion protection
  • User validation

Phase 2 (30-40 min)

  • Emotional detection + storage
  • Tag injection
  • Profile persistence

Phase 3 (2-3 hours)

  • MemoryNode + MemoryGraph
  • Connection detection
  • Smart search
  • Integration into generate()

5. Security Guarantees

βœ… Thinking never shown

  • Removed by _clean_response() before sending
  • Tags removed
  • Internal context removed

βœ… Context always preserved

  • MemoryNodes save everything
  • Graph persists across sessions
  • Connections maintained

βœ… User isolation

  • Every node has user_id
  • Search filters by user_id
  • No cross-user leakage

βœ… Emotional state private

  • Profile only for that user
  • Historical emotions saved
  • Pattern tracking for safety (aggressive_count)

6. Example: Full Flow

Day 1, User A

Input: "Tenho ansiedade social"
β†’ Detect: neutro (baseline)
β†’ MemoryNode_1: tags=[mental_health, anxiety]
β†’ No connections (first message)
β†’ Save to profile
β†’ Output: "Entendo... ansiedade social Γ©..."

Day 1, 5 min later, User A

Input: "Fico nervoso em grupos"
β†’ Detect: confuso (from word analysis)
β†’ Tag: <!CLEAR_PATIENT_MODE>
β†’ Search finds: MemoryNode_1 (similar topic)
β†’ Connect: MemoryNode_2 β†’ MemoryNode_1 (related_by_tags, 0.85)
β†’ Add context: "VocΓͺ mencionou ansiedade social... fico nervoso em grupos Γ© relacionado?"
β†’ Output: "Sim, isso estΓ‘ muito relacionado. Aqui estΓ£o estratΓ©gias... [patient tone]"
β†’ Save: MemoryNode_2 with connection

Day 30, User A

Input: "Como faΓ§o para melhorar minha sociabilidade?"
β†’ Detect: neutro (but check profile)
β†’ Profile shows: emotion_history = [confuso]
β†’ Search with connections finds:
    - MemoryNode_1: "Tenho ansiedade social"
    - MemoryNode_2: "Fico nervoso em grupos"
β†’ AKIRA context: "VocΓͺ tem trabalhado na sua ansiedade social. Aqui estΓ£o 5 tΓ©cnicas prΓ‘ticas..."
β†’ Output: Highly relevant because graph understood multi-turn journey

Result: βœ… Context improved automatically. Graph made AKIRA smarter!


7. Deployment Checklist

  • Phase 1 deployed to production
  • Phase 2 code written and tested
  • Phase 2 deployed
  • Phase 3 design reviewed
  • Phase 3 code written and tested
  • Phase 3 deployed
  • Monitor: emotional detection accuracy
  • Monitor: graph connection quality
  • Collect user feedback