akira / modules /INTEGRATION_GUIDE.md
akra35567's picture
Upload 55 files
bbbdd24 verified
|
Raw
History Blame Contribute Delete
19.4 kB

""" ═══════════════════════════════════════════════════════════════════════ INTEGRATION GUIDE β€” COMO INTEGRAR NO api.py EXISTENTE ═══════════════════════════════════════════════════════════════════════ Este arquivo mostra:

  1. Como modificar o endpoint POST /akira existente
  2. Como usar ContextManagerV2
  3. Como usar ListenStreamProcessor
  4. Exemplos prΓ‘ticos de fluxo

IMPORTANTE: Isso SUBSTITUI o sistema antigo de context, mantendo compatibilidade com code existente. ═══════════════════════════════════════════════════════════════════════ """

═══════════════════════════════════════════════════════════════════════

πŸ“¦ IMPORTS NECESSÁRIOS

═══════════════════════════════════════════════════════════════════════

from modules.context_manager_v2 import ( ContextManagerV2, get_context_manager, MessageType ) from modules.listen_stream_processor import ( ListenStreamProcessor, get_listen_processor ) from typing import Dict, Any import logging

logger = logging.getLogger(name)

ctx_manager = get_context_manager() listen_processor = get_listen_processor()

═══════════════════════════════════════════════════════════════════════

πŸ”„ NOVO FLUXO DO ENDPOINT /akira

═══════════════════════════════════════════════════════════════════════

""" ANTES (buggy): ──────────────────────────────────────────────────────────────── POST /akira { "usuario": "Isaac", "numero": "202391978787009", "texto": "@AKIRA qual Γ© a capital?" }

β†’ api.py chama obter_historico() β†’ PROBLEMA: Retorna ALL mensagens, misturando grupos β†’ AKIRA responde sem isolaΓ§Γ£o

DEPOIS (robusto): ──────────────────────────────────────────────────────────────── POST /akira { "usuario": "Isaac", "numero": "202391978787009", "texto": "@AKIRA qual Γ© a capital?", "tipo_conversa": "grupo" | "pv", "grupo_id": "g120363392399993499" (opcional), "referenced_message_author": "AKIRA", (opcional) }

β†’ listen_processor.processar_mensagem_chegando() β†’ Classifica como DIRECT ou CONTEXTUAL β†’ Adiciona Γ  context_manager isolado por conversation_id

β†’ Se DIRECT (true): β†’ ctx_manager.obter_historico_direto() β†’ APENAS mensagens direcionadas a AKIRA β†’ Isolado por conversation_id β†’ AKIRA responde

→ Se CONTEXTUAL (false): → Registra mas NÃO processa → AKIRA entende fluxo via obter_contexto_grupo_amplificado() """

═══════════════════════════════════════════════════════════════════════

πŸ”§ MODIFICAÇÃO NO ENDPOINT /akira

═══════════════════════════════════════════════════════════════════════

def novo_akira_endpoint_handler(request_data: Dict[str, Any]) -> Dict[str, Any]: """ Novo handler para POST /akira com isolaΓ§Γ£o de contexto.

Substitui a lΓ³gica antiga em api.py:akira_endpoint()
"""

# ─────────────────────────────────────────────────────────────
# PASSO 1: EXTRAI DADOS CHEGANDO
# ─────────────────────────────────────────────────────────────
usuario = request_data.get('usuario', 'desconhecido')
numero = request_data.get('numero', '')
texto = request_data.get('texto', '')
tipo_conversa = request_data.get('tipo_conversa', 'pv')
grupo_id = request_data.get('grupo_id')

# ReferΓͺncia (se Γ© resposta a outra mensagem)
referenced_author = request_data.get('referenced_message_author')
referenced_texto = request_data.get('referenced_message_texto')
referenced_id = request_data.get('referenced_message_id')

logger.info(
    f"πŸ“¨ Mensagem chegando: {usuario} ({numero}) em {tipo_conversa} | "
    f"Texto: {texto[:50]}..."
)

# ─────────────────────────────────────────────────────────────
# PASSO 2: PROCESSA VIA LISTEN STREAM
# ─────────────────────────────────────────────────────────────
evento = {
    'usuario': usuario,
    'numero': numero,
    'texto': texto,
    'tipo_conversa': tipo_conversa,
    'grupo_id': grupo_id,
    'referenced_message_author': referenced_author,
    'referenced_message_texto': referenced_texto,
    'referenced_message_id': referenced_id,
}

resultado_processamento = listen_processor.processar_mensagem_chegando(evento)

logger.info(
    f"πŸ” ClassificaΓ§Γ£o: {resultado_processamento['tipo_message']} | "
    f"Deve processar: {resultado_processamento['deve_processar']}"
)

# ─────────────────────────────────────────────────────────────
# PASSO 3: DECIDE SE AKIRA RESPONDE
# ─────────────────────────────────────────────────────────────

if not resultado_processamento['deve_processar']:
    # Mensagem CONTEXTUAL β†’ apenas registra, nΓ£o responde
    logger.info(f"πŸ“š Mensagem contextual (escuta). ConversΓ£o_id: {resultado_processamento['conversation_id']}")
    return {
        'status': 'context_registered',
        'tipo': 'contextual',
        'message': f"Entendi. {usuario} falou sobre isso no grupo.",
        'deve_responder': False,
        'conversation_id': resultado_processamento['conversation_id'],
    }

# ─────────────────────────────────────────────────────────────
# PASSO 4: AKIRA RESPONDE (mensagem Γ© DIRETA)
# ─────────────────────────────────────────────────────────────

conversation_id = resultado_processamento['conversation_id']

# ObtΓ©m contexto ISOLADO
contexto = listen_processor.obter_contexto_para_resposta(
    numero=numero,
    tipo_conversa=tipo_conversa,
    grupo_id=grupo_id,
    include_contextual=True
)

logger.info(
    f"πŸ“– Contexto obtido: {contexto['total_direct']} mensagens diretas | "
    f"Participantes: {contexto.get('participants', [])}"
)

# ─────────────────────────────────────────────────────────────
# PASSO 5: ENRIQUECER COM CONTEXTO DE GRUPO
# ─────────────────────────────────────────────────────────────

contexto_grupo_info = contexto.get('grupo_flow', {})
participants = contexto.get('participants', [])

# Monta prompt para AKIRA
prompt_system = f"""

VocΓͺ Γ© AKIRA, assistente IA em um grupo. Contexto:

  • Participantes do grupo: {', '.join(participants) if participants else 'Desconhecido'}
  • TΓ³picos recentes: {', '.join(contexto.get('topics', [])) if contexto.get('topics') else 'VΓ‘rios'}
  • Sua conversa com {usuario}: {len(contexto['direct_messages'])} mensagens anteriores

IMPORTANTE: Responda APENAS sobre o que foi perguntado a vocΓͺ. NΓƒO comente sobre outras conversas do grupo (escuta sem falar). """

# HistΓ³rico ISOLADO
mensagens_anteriores = [
    {
        'usuario': m['usuario'],
        'texto': m['texto'],
        'timestamp': m['timestamp']
    }
    for m in contexto['direct_messages']
]

# ─────────────────────────────────────────────────────────────
# PASSO 6: CHAMA LLM (seu cΓ³digo existente)
# ─────────────────────────────────────────────────────────────

# Isso seria integrado com seu chain LLM existente:
# resposta = gerar_resposta_llm(
#     sistema=prompt_system,
#     historico=mensagens_anteriores,
#     mensagem_atual=texto,
#     usuario=usuario
# )

resposta = f"[RESPOSTA DE AKIRA] VocΓͺ perguntou: {texto[:100]}"  # Placeholder

# ─────────────────────────────────────────────────────────────
# PASSO 7: RETORNA
# ─────────────────────────────────────────────────────────────

return {
    'status': 'success',
    'tipo': 'direct',
    'resposta': resposta,
    'deve_responder': True,
    'conversation_id': conversation_id,
    'usuario': usuario,
    'contexto_info': {
        'num_direct_messages': contexto['total_direct'],
        'participants': participants,
        'topics': contexto.get('topics', []),
    }
}

═══════════════════════════════════════════════════════════════════════

πŸ“ ALTERAÇÕES NECESSÁRIAS NO api.py

═══════════════════════════════════════════════════════════════════════

""" LOCAIS ESPECÍFICOS A MODIFICAR EM api.py:

1️⃣ IMPORT SECTION (top do arquivo) ──────────────────────────────────────────────────────────────── Adicionar:

from modules.context_manager_v2 import get_context_manager
from modules.listen_stream_processor import get_listen_processor

ctx_manager = get_context_manager()
listen_processor = get_listen_processor()

2️⃣ FUNÇÃO akira_endpoint() - SUBSTITUIR INTEIRA ──────────────────────────────────────────────────────────────── LocalizaΓ§Γ£o: api.py::akira_endpoint() (aproximadamente linha 950)

AΓ§Γ£o: SUBSTITUIR todo o corpo da funΓ§Γ£o pelo code do
novo_akira_endpoint_handler() acima.

O que muda:
βœ… Remove: data = contexto.obter_historico()  (BUGGY - sem filtro)
βœ… Adiciona: resultado_processamento = listen_processor.processar_mensagem_chegando()
βœ… Adiciona: if not resultado_processamento['deve_processar']: return (skip contextual)
βœ… Adiciona: contexto = listen_processor.obter_contexto_para_resposta() (ISOLADO)

3️⃣ FUNÇÃO _get_user_context() - MODIFICAR ──────────────────────────────────────────────────────────────── LocalizaΓ§Γ£o: api.py::_get_user_context() (aproximadamente linha 1000)

ANTES:
def _get_user_context(numero, usuario):
    historico = contexto.obter_historico()  # ❌ SEM FILTRO
    return {
        'historico': historico,
        'usuario': usuario
    }

DEPOIS:
def _get_user_context(numero, usuario, tipo_conversa='pv', grupo_id=None):
    # Usa novo context manager ISOLADO
    historico = ctx_manager.obter_historico_direto(
        numero=numero,
        tipo_conversa=tipo_conversa,
        grupo_id=grupo_id,
        limit=50
    )
    contexto_grupo = ctx_manager.obter_contexto_grupo_amplificado(
        numero=numero,
        tipo_conversa=tipo_conversa,
        grupo_id=grupo_id
    ) if tipo_conversa == "grupo" else None
    
    return {
        'historico': [m.to_dict() for m in historico],
        'usuario': usuario,
        'contexto_grupo': contexto_grupo,
        'conversation_id': historico[0].conversation_id if historico else None
    }

4️⃣ FUNÇÃO obter_historico() - DEPRECAR OU SUBSTITUIR ──────────────────────────────────────────────────────────────── LocalizaΓ§Γ£o: database.py ou modules/database.py

ANTES:
def obter_historico():
    return db.query(Mensagem).all()  # ❌ RETORNA TUDO

DEPOIS:
def obter_historico(conversation_id: str, msg_type: str = 'all'):
    # msg_type: 'direct', 'contextual', 'all'
    query = db.query(Mensagem).filter(
        Mensagem.conversation_id == conversation_id
    )
    if msg_type == 'direct':
        query = query.filter(Mensagem.tipo == 'direct')
    elif msg_type == 'contextual':
        query = query.filter(Mensagem.tipo == 'contextual')
    return query.order_by(Mensagem.timestamp.desc()).limit(100).all()

5️⃣ SUPORTE AO PAYLOAD NOVO ──────────────────────────────────────────────────────────────── O endpoint POST /akira AGORA ACEITA:

{
    "usuario": "Isaac",
    "numero": "202391978787009",
    "texto": "@AKIRA qual Γ© a capital?",
    "tipo_conversa": "grupo",  ← NOVO
    "grupo_id": "g120363392399993499",  ← NOVO (opcional)
    "referenced_message_author": "StefΓ’nio",  ← NOVO (opcional)
    "referenced_message_texto": "Oi",  ← NOVO (opcional)
    "referenced_message_id": "msg_123"  ← NOVO (opcional)
}

Os campos 'tipo_conversa', 'grupo_id', 'referenced_*' sΓ£o opcionais
e vΓͺm do discord-ts quando disponΓ­veis. Se nΓ£o vierem, valor default
Γ© 'pv' (conversa privada).

"""

═══════════════════════════════════════════════════════════════════════

πŸ“Š EXEMPLO DE USO COMPLETO

═══════════════════════════════════════════════════════════════════════

""" CENÁRIO: Isaac e StefÒnio em grupo

1️⃣ ISAAC envia: "@AKIRA qual Γ© a capital de Portugal?" grupo: "g120363392399993499"

β†’ listen_processor classifica como DIRECT (menciona @AKIRA)
β†’ ctx_manager.adicionar_message_direta() com conversation_id ΓΊnico
β†’ AKIRA responde: "A capital Γ© Lisboa"

2️⃣ STEFΓ‚NIO responde (sem @AKIRA): "Bacano, eu nΓ£o sabia"

β†’ listen_processor classifica como CONTEXTUAL
β†’ ctx_manager.adicionar_message_contextual()
→ AKIRA NÃO responde (não foi direcionada a ela)
β†’ Mas AKIRA "entende" que StefΓ’nio achou legal

3️⃣ STEFΓ‚NIO envia: "Qual Γ© a capital da FranΓ§a?" (sem @AKIRA)

β†’ listen_processor classifica como CONTEXTUAL
β†’ AKIRA nΓ£o responde

4️⃣ ISAAC responde: "@AKIRA? JΓ‘ que perguntou, qual Γ©?"

β†’ listen_processor classifica como DIRECT (menciona @AKIRA)
β†’ ObtΓ©m contexto ISOLADO (sΓ³ conversas diretas com Isaac)
β†’ AKIRA NΓƒO vΓͺ a pergunta de StefΓ’nio (contextual)
β†’ AKIRA responde baseada sΓ³ no que Isaac perguntou

RESULTADO: βœ… AKIRA entende quem fala o quΓͺ βœ… NΓ£o mistura contextos βœ… Responde apenas quando direcionada βœ… Escuta o fluxo sem contaminar """

═══════════════════════════════════════════════════════════════════════

🎯 PRΓ“XIMOS PASSOS

═══════════════════════════════════════════════════════════════════════

""" TAREFAS PARA IMPLEMENTAR:

  1. βœ… Criar context_manager_v2.py (FEITO)
  2. βœ… Criar listen_stream_processor.py (FEITO)
  3. πŸ”„ Modificar api.py:
    • Importar novos mΓ³dulos
    • Reescrever akira_endpoint()
    • Atualizar _get_user_context()
  4. πŸ”„ Modificar database.py:
    • Adicionar suporte a conversation_id na query
    • Adicionar campo 'tipo' na tabela Mensagem
  5. πŸ”„ Testar com fluxo real:
    • Isaac + StefΓ’nio em grupo
    • Verificar isolamento
  6. πŸ”„ Integrar com discord-ts:
    • discord-ts deve enviar 'tipo_conversa' e 'grupo_id'
    • Enviar 'referenced_message_author' quando Γ© reply """

all = [ 'novo_akira_endpoint_handler', 'ctx_manager', 'listen_processor', ]