import os import re import requests from tools import run_bash_command, get_system_health from config import get_config def query_agent(prompt: str, override_provider: str = None) -> str: """ Função principal que roteia pro LLM desejado, detecta intents e aciona as Tools. """ # SYSTEM PROMPT AVANÇADO: O "Ensinamento" da Inteligência Artificial system_prompt = """Você é o [Antigravity VPS Agent], uma Inteligência Artificial autônoma de Dev/SysAdmin operando diretamente em uma máquina Ubuntu Linux do usuário (Marcos). Sua missão é ajudar o Marcos a gerenciar o servidor, criar aplicações, analisar código e orquestrar o Docker. VOCÊ DEVE RESPONDER SEMPRE EM PORTUGUÊS FLUENTE DO BRASIL. ### SEUS PODERES E ARQUITETURA 1. **Nível Root**: Você roda em um Bot Python empacotado via Docker. Este container mapeia o `/var/run/docker.sock`, o que te dá o poder DIVINO sobre tudo. Você pode listar, deletar, parar e recriar qualquer container na máquina hospedeira inteira. 2. **Sistema de Arquivos**: O disco principal do servidor está montado de forma segura, o que permite que você leia logs nativos. 3. **Mecanismo de Ação (A Interface de Ferramentas)**: Você **não executa os comandos diretamente pela web**. Você possui uma interface conectada ao bot no Telegram e Web. Para interagir com o servidor, sempre que decidir que algo precisa ser lido (ex: `ls -la`, `docker network ls`, `cat arquivo.txt`) ou modificado, responda com a notação: [CMD] o_comando_aqui [/CMD]. 4. O bot Python varrerá sua resposta via Regex, extrairá o comando que você colocou entre os blocos, abrirá um shell Bash real, e te devolverá na conversa o resultado do seu comando. ### SEUS LIMITES E REGRAS - **Sem Comandos Interativos**: Jamais use comandos que exijam resposta humana travando o terminal (ex: `nano`, `top -d 1`, `apt-get install` sem `-y`). Se usar, o bash vai dar timeout (60s limit). - **Nunca use systemctl**: Você roda dentro de um container Docker. Logo, o comando `systemctl` NÃO FUNCIONA. Para ver serviços, use invariavelmente o comando `docker ps -a`. - **Aja, Não Explique Demais**: Se o usuário te der uma tarefa como "Veja meus containers", não explique o que é Docker. Apenas devolva [CMD] docker ps -a [/CMD] e diga "Estou olhando agora mesmo." - **Erros**: Se o comando retornar um código 127, 1 ou falha de acesso, aceite o erro e sugira tentar um comando de diagnóstico em seguida. Acorde, a VPS é sua para cuidar!""" if "logs do nginx" in prompt.lower() or "nginx" in prompt.lower(): output = run_bash_command("systemctl status nginx || echo 'Nginx não parece ser gerenciado por systemctl aqui'") return f"Executei a checagem no Nginx. Olha o resultado: \n\n{output}" if any(palavra in prompt.lower() for palavra in ["status", "cpu", "saude", "saúde", "sauda", "memória", "disco", "hd"]): health = get_system_health() return f"A saúde atual pontual da sua máquina direta do Python está assim:\n{health}" cfg = get_config() provider = override_provider or cfg.get("active_provider", "ollama") if provider == "gemini": gemini_api_key = cfg.get("gemini_api_key", "") if not gemini_api_key: return "Chave API do Gemini não configurada." try: url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={gemini_api_key}" payload = { "contents": [{"parts": [{"text": system_prompt + "\n\nUsuário: " + prompt}]}] } res = requests.post(url, json=payload) if res.status_code == 200: raw_response = res.json()["candidates"][0]["content"]["parts"][0]["text"] else: return f"Erro na API do Gemini: {res.text}" # Regex Universal para processar Comandos match = re.search(r"\[.*?CMD\](.*?)\[/.*?CMD\]", raw_response, re.IGNORECASE | re.DOTALL) if match: comando_bash = match.group(1).strip() resultado = run_bash_command(comando_bash) # Tradução via Gemini traducao_prompt = f"O comando `{comando_bash}` retornou o seguinte dado: {resultado[:1500]}\nTraduza gentilmente para um formato leigo sem códigos de erro difíceis." t_payload = {"contents": [{"parts": [{"text": system_prompt + "\n\n" + traducao_prompt}]}]} t_res = requests.post(url, json=t_payload) if t_res.status_code == 200: texto_leigo = t_res.json()["candidates"][0]["content"]["parts"][0]["text"] else: texto_leigo = "Falha ao gerar resumo no Gemini." return f"🤖 **Comando Técnico (Gemini):** `{comando_bash}`\n\n**🖥️ Log Nativo:**\n```\n{resultado[:1000]}\n```\n\n🧑‍🏫 **Tradução:**\n{texto_leigo}" return raw_response except Exception as e: return f"Falha de Conexão com Gemini Pro: {e}" elif provider == "ollama": try: ollama_host = os.getenv("OLLAMA_HOST", "http://ollama-lw4s8g4gc8gss4gkc4gg0wk4:11434") res = requests.post(f"{ollama_host}/api/generate", json={ "model": os.getenv("OLLAMA_MODEL", "qwen2.5-coder:1.5b"), "prompt": system_prompt + "\nUsuário: " + prompt, "stream": False }) if res.status_code == 200: raw_response = res.json().get("response", "Erro vazio do Ollama") # Motor de Tool Calling Tolerante: Detecta [CMD] ou [VCMD] ou variações que LLMs minúsculas inventam match = re.search(r"\[.*?CMD\](.*?)\[/.*?CMD\]", raw_response, re.IGNORECASE | re.DOTALL) if match: comando_bash = match.group(1).strip() resultado = run_bash_command(comando_bash) # ---------------------------------------------------- # NOVA LÓGICA: Tradução Leiga (Segundo Prompt para a IA) # ---------------------------------------------------- traducao_prompt = f"O comando `{comando_bash}` retornou a seguinte saída do servidor:\n\n{resultado[:1500]}\n\nTraduza GENTILMENTE essa saída técnica explicando de forma amigável, gentil e em português muito simples (para um não-técnico) o que isso indica. SE a saída for um 'ERRO', acalme o usuário, resuma que um comando técnico falhou e sugira verbalmente o que de forma segura investigar a seguir (não mande blocos confusos, apenas explique em português fluente)." try: res_traducao = requests.post(f"{ollama_host}/api/generate", json={ "model": os.getenv("OLLAMA_MODEL", "qwen2.5-coder:1.5b"), "prompt": system_prompt + "\n\n" + traducao_prompt, "stream": False }) if res_traducao.status_code == 200: texto_leigo = res_traducao.json().get("response", "Erro ao processar resumo.") else: texto_leigo = "Falha ao gerar resumo na LLM." except Exception as e_traducao: texto_leigo = "Ocorreu uma falha ao tentar traduzir o log." # Retorna na tela a versão técnica seguida da versão explicada return f"🤖 **Comando Técnico:** `{comando_bash}`\n\n**🖥️ Log Nativo (Terminal):**\n```\n{resultado[:1000]}\n```\n\n🧑‍🏫 **Tradução:**\n{texto_leigo}" return raw_response except Exception as e: return f"Falha ao conectar no Ollama local: {e}" return "Ação não reconhecida pelo Agente no momento."