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',
]