Carlexxx
feat: ✨ aBINC 2.2
fb56537
raw
history blame
5.42 kB
# aduc_framework/engineers/prompt_engine.py
#
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
#
# Versão 3.0.0 (Provider-Aware Prompt Translator)
#
# O PromptEngine atua como o tradutor entre a lógica de tarefa agnóstica
# do Composer e o formato de prompt específico exigido por um LLM.
# Esta versão é ciente do provedor (Llama, Gemini, etc.) e aplica os
# templates de formatação apenas quando necessário (ex: para o Llama),
# usando um prompt genérico direto para outros (ex: Gemini).
import logging
from pathlib import Path
from typing import Dict
logger = logging.getLogger(__name__)
class PromptEngine:
"""
O PromptEngine traduz prompts genéricos para o formato específico do
modelo de linguagem alvo. Ele seleciona o template correto com base
no provedor de LLM ativo e na presença de imagens na requisição.
"""
def __init__(self):
"""
Inicializa o PromptEngine em um estado "em espera". O provedor
e os templates são carregados posteriormente pelo Composer.
"""
self.provider: str | None = None
self.model_map_name: str = "llama_3_2_vision" # Padrão para Llama
self.template_with_image: str | None = None
self.template_text_only: str | None = None
logger.info("PromptEngine inicializado (aguardando configuração do provedor pelo Composer).")
def _load_model_template(self, map_name: str, template_file: str) -> str:
"""
Carrega um arquivo de template de um mapa de modelo específico.
"""
map_path = Path(__file__).resolve().parent.parent / "prompts" / "model_maps" / map_name / template_file
if not map_path.is_file():
raise FileNotFoundError(f"Template de modelo '{template_file}' não encontrado no mapa '{map_name}' em: {map_path}")
with open(map_path, 'r', encoding='utf-8') as f:
return f.read()
def set_provider(self, provider: str):
"""
Configura o provedor de LLM ativo (ex: 'llama_multimodal', 'gemini')
e carrega os templates de formatação específicos, se necessário.
Este método é chamado pelo Composer durante sua inicialização.
"""
if self.provider == provider:
return # Evita recarregamentos desnecessários
self.provider = provider
logger.info(f"PromptEngine: Provedor de LLM definido como '{self.provider}'.")
# Carrega os templates específicos do Llama apenas se ele for o provedor.
# Para o Gemini, nenhum template é necessário, então os atributos permanecem None.
if self.provider == 'llama_multimodal':
logger.info(f"Carregando templates para o mapa de modelo '{self.model_map_name}'...")
self.template_with_image = self._load_model_template(self.model_map_name, "image_template.txt")
self.template_text_only = self._load_model_template(self.model_map_name, "text_template.txt")
else:
self.template_with_image = None
self.template_text_only = None
def translate(self, generic_prompt_content: str, has_image: bool) -> str:
"""
Envolve o conteúdo do prompt genérico com o template específico do modelo,
mas somente se o provedor ativo (Llama) exigir. Para provedores como o
Gemini, retorna o prompt genérico sem modificação.
Args:
generic_prompt_content (str): O conteúdo do prompt agnóstico gerado pelo Composer.
has_image (bool): Sinaliza se a tarefa atual inclui um contexto visual.
Returns:
str: O prompt final, formatado e pronto para ser enviado ao LLM.
"""
if self.provider is None:
raise RuntimeError("PromptEngine: O provedor não foi definido. Chame set_provider() antes de usar.")
# --- LÓGICA CONDICIONAL ---
# Se o provedor for Gemini, ele usa o "prompt padrão genérico" sem modificação.
if self.provider == 'gemini':
logger.debug("PROMPT ENGINE (Gemini): Retornando prompt genérico sem aplicar template.")
return generic_prompt_content
# A lógica original do Llama é executada para o provedor padrão ('llama_multimodal')
logger.debug(f"--- PROMPT ENGINE ({self.provider}): INICIANDO TRADUÇÃO (Imagem: {has_image}) ---")
template = self.template_with_image if has_image else self.template_text_only
if not template:
raise RuntimeError(f"PromptEngine: Template para o provedor '{self.provider}' não foi carregado corretamente.")
try:
# Insere o conteúdo genérico dentro do template específico do modelo
final_prompt = template.format(generic_prompt_content=generic_prompt_content)
logger.debug(f"--- PROMPT ENGINE ({self.provider}): TRADUÇÃO CONCLUÍDA ---")
return final_prompt
except KeyError as e:
logger.error(f"PROMPT ENGINE: Erro de chave durante a tradução! Chave não encontrada: {e}", exc_info=True)
logger.error("Verifique se o template do modelo contém apenas o placeholder '{generic_prompt_content}'.")
raise e
# --- Instância Singleton ---
# A instância é criada vazia e será configurada dinamicamente pelo Composer.
prompt_engine_singleton = PromptEngine()