diff --git a/ai_agent.py b/ai_agent.py index 790e126..111cc4a 100644 --- a/ai_agent.py +++ b/ai_agent.py @@ -5,11 +5,11 @@ import json from tools import AVAILABLE_TOOLS from config import get_config -def get_llm_response(prompt: str, provider: str, cfg: dict, temp: float = 0.7, ctx: int = 4096) -> str: +def get_llm_response(prompt: str, provider: str, cfg: dict) -> str: """Invoca o provedor de LLM configurado.""" if provider == "gemini": api_key = cfg.get("gemini_api_key") or os.getenv("GEMINI_API_KEY") - url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={api_key}" + url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={api_key}" payload = {"contents": [{"parts": [{"text": prompt}]}]} res = requests.post(url, json=payload) if res.status_code == 200: @@ -21,11 +21,7 @@ def get_llm_response(prompt: str, provider: str, cfg: dict, temp: float = 0.7, c res = requests.post(f"{ollama_host}/api/generate", json={ "model": os.getenv("OLLAMA_MODEL", "qwen2.5-coder:1.5b"), "prompt": prompt, - "stream": False, - "options": { - "temperature": temp, - "num_ctx": ctx - } + "stream": False }) if res.status_code == 200: return res.json().get("response", "") @@ -34,13 +30,17 @@ def get_llm_response(prompt: str, provider: str, cfg: dict, temp: float = 0.7, c return "Provedor desconhecido." def query_agent(prompt: str, override_provider: str = None, chat_history: list = None) -> str: - # 1. Ajuste de Provedor e Configuração + """ + Motor Agente em Loop (ReAct): Pensamento -> Ação -> Observação -> Resposta Final. + """ cfg = get_config() provider = override_provider or cfg.get("active_provider", "gemini") + + # Contexto de Ferramentas para a IA tools_desc = "\n".join([f"- {k}: {v['description']}" for k,v in AVAILABLE_TOOLS.items()]) - - # 2. Prompt especializado (O manual de elite) - system_prompt_base = """Você é o [Antigravity VPS Agent], SysAdmin de elite do Marcos. + + # Prompt especializado reformulado para evitar alucinações + system_prompt_base = """Você é o [Antigravity VPS Agent], o SysAdmin de elite do Marcos. Você tem acesso root completo à VPS e deve agir de forma profissional e precisa. ### REGRAS DE OURO: @@ -48,59 +48,84 @@ Você tem acesso root completo à VPS e deve agir de forma profissional e precis 2. Se o usuário pedir o status da VPS, SEMPRE use a ferramenta 'get_system_health'. 3. Se o usuário pedir algo sobre containers, use 'get_docker_stats'. 4. Antes de decidir que um arquivo não existe, use 'run_bash_command' com 'ls' para verificar o diretório. -5. NUNCA invente links fictícios. Se localizar um arquivo, use `read_vps_file`. -6. O disco da VPS está focado em `/host_root`. Use este caminho para buscar arquivos. -7. Use [TOOL:nome] arg [/TOOL] para ações. Rode UMA por vez. +5. NUCA invente que buscou por arquivos (como syslog.conf) se o usuário não pediu especificamente por eles. +6. A seção deve conter apenas as informações solicitadas. Se não houver imagem relevante, não inclua tags de imagem. +7. O disco da VPS está montado em `/host_root`. Os arquivos do Marcos ficam principalmente em `/host_root/root/VPS_Sync`. Use este caminho como ponto de partida se o `find` na raiz falhar ou demorar demais. + +### FORMATO DE AÇÃO: +Use: [TOOL:nome_da_ferramenta] argumento [/TOOL] +Rode UMA ferramenta por vez. Aguarde a saída do SISTEMA antes de concluir. ### RESPOSTA FINAL: -- Deve ter um resumo técnico e uma seção com Markdown. -- Se localizou imagem, use ![alt](/host_root/caminho/foto.jpg) dentro do . +Sua resposta terminada deve ter: +- Um resumo técnico. +- Uma seção ... com Markdown limpo. +- **DICA**: Só use imagens em se o usuário pediu para ver um arquivo de imagem específico que você localizou. Use o caminho absoluto encontrado. -### FERRAMENTAS: -{TOOLS_LIST}""" +### FERRAMENTAS DISPONÍVEIS: +{TOOLS_LIST} + +### EXEMPLO DE SUCESSO: +Usuário: qual o uso de ram agora? +Agente: [TOOL:get_system_health] [/TOOL] +SISTEMA: CPU: 5% | RAM Usada: 20% | Disco Usado: 40% +Resposta: A memória RAM está operando com 20% de uso. + +### 📊 Memória e CPU +- **RAM Utilizada**: 20% +- **CPU**: 5% + +""" system_prompt = system_prompt_base.replace("{TOOLS_LIST}", tools_desc) + + # Constrói o histórico da conversa (memória de curto prazo) + history_str = "" + if chat_history: + for msg in chat_history[-5:]: # Pega as últimas 5 interações + history_str += f"\nUsuário: {msg['user']}\nAgente: {msg['bot']}\n" - # 3. Contexto e Memória Otimizada - num_context = 2048 if provider == "ollama" else 4096 - hist_list = chat_history[-4:] if chat_history else [] - hist_txt = "\n".join([f"U: {m['user']}\nA: {m['bot']}" for m in hist_list]) - - execution_context = f"\n{hist_txt}\nU: {prompt}\nA:" - current_reasoning = "" + history_str += f"\nUsuário: {prompt}\n" + current_iteration_history = history_str max_loops = 12 + print(f"--- INICIANDO AGENTE ({provider}) ---") for i in range(max_loops): print(f"\n[LOOP {i+1}/{max_loops}]") + full_prompt = system_prompt + current_iteration_history + response = get_llm_response(full_prompt, provider, cfg) - full_prompt = f"{system_prompt}\n{execution_context}\n{current_reasoning}\n" - - # Injeta o num_ctx dinâmico no payload - if provider == "ollama": - response = get_llm_response(full_prompt, provider, cfg, temp=0.1, ctx=num_context) - else: - response = get_llm_response(full_prompt, provider, cfg) - print(f"PENSAMENTO:\n{response}") + # Procura por chamadas de ferramentas na resposta match = re.search(r"\[TOOL:(.*?)\](.*?)\[/TOOL\]", response, re.IGNORECASE | re.DOTALL) if match: - # ... (lógica de ferramenta existente) ... tool_name = match.group(1).strip() arg = match.group(2).strip() - print(f"EXECUTANDO: {tool_name}") + + print(f"EXECUTANDO: {tool_name} | ARGS: {arg}") if tool_name in AVAILABLE_TOOLS: func = AVAILABLE_TOOLS[tool_name]["func"] - observation = func() if tool_name in ["get_system_health", "get_docker_stats"] else func(arg) + # Caso a ferramenta não aceite argumentos (ex: get_system_health) + if tool_name in ["get_system_health", "get_docker_stats"]: + observation = func() + else: + observation = func(arg) + + print(f"OBSERVAÇÃO (suprimida): {str(observation)[:200]}...") else: observation = f"Erro: Ferramenta '{tool_name}' não encontrada." + print(f"ERRO: {observation}") - current_reasoning += f"\nAção: {response}\nSISTEMA: {observation}\n" + # Adiciona ao histórico do loop atual + current_iteration_history += f"\nAgente (Ação): {response}\nSISTEMA (Saída de {tool_name}): {observation}\n" else: + # Se não tem comando, é a resposta final print("--- RESPOSTA FINAL ENCONTRADA ---") return response - return "O agente atingiu o limite de tentativas." + print("!!! ERRO: LIMITE DE TENTATIVAS ATINGIDO !!!") + return "O agente atingiu o limite de tentativas para esta tarefa."