Spaces:
Running
Running
π§ 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