Files
instrucoes_gerais/templates/agent_executor.py
T

268 lines
11 KiB
Python

import os
import json
import redis
import subprocess
import base64
import random
import urllib.request
import urllib.error
import time
class SwarmAgent:
def __init__(self, task_data):
self.data = task_data
self.repo_name = task_data.get('repository')
self.repo_url = f"http://10.99.0.3:3000/{self.repo_name}.git"
self.local_path = os.path.join("/home/deploy/projetos", self.repo_name.split('/')[-1])
# Conexão Redis para pegar chaves do pool
self.r = redis.Redis(host='10.99.0.3', port=6379, password='clube67_dragonfly_pass_9903', db=0, decode_responses=True)
def get_api_key(self, provider):
pool = json.loads(self.r.get('swarm:keys:pool'))
active_keys = [k['key'] for k in pool.get(provider, []) if k.get('status') == 'active']
if not active_keys:
raise Exception(f"Nenhuma chave ativa encontrada para o provedor: {provider}")
return random.choice(active_keys)
def setup_workspace(self):
os.makedirs("/home/deploy/projetos", exist_ok=True)
if not os.path.exists(self.local_path):
print(f" [>] Clonando repositório {self.repo_url} em {self.local_path}...")
subprocess.run(["git", "clone", self.repo_url, self.local_path])
else:
print(f" [>] Atualizando repositório em {self.local_path}...")
subprocess.run(["git", "-C", self.local_path, "pull"])
def run_command(self, command):
"""Executa comandos shell na VPS 4"""
print(f" [Exec] Rodando comando: {command}")
result = subprocess.run(command, shell=True, capture_output=True, text=True, cwd=self.local_path)
return {"stdout": result.stdout, "stderr": result.stderr}
def write_file(self, path, content):
"""Escreve/Edita arquivos no projeto"""
print(f" [Exec] Escrevendo arquivo: {path}")
full_path = os.path.join(self.local_path, path)
os.makedirs(os.path.dirname(full_path), exist_ok=True)
with open(full_path, "w") as f:
f.write(content)
return f"Arquivo {path} escrito com sucesso."
def read_file(self, path):
"""Lê arquivos do projeto"""
print(f" [Exec] Lendo arquivo: {path}")
full_path = os.path.join(self.local_path, path)
if os.path.exists(full_path):
with open(full_path, "r") as f:
return f.read()
return f"Erro: Arquivo {path} não encontrado."
def post_comment(self, message):
"""Posta um comentário de feedback na Issue do Gitea"""
issue_id = self.data.get('data', {}).get('issue', {}).get('number')
if not issue_id:
return
url = f"http://10.99.0.3:3000/api/v1/repos/{self.repo_name}/issues/{issue_id}/comments"
# Auth Basic
auth_str = 'ruicesar:h$tg@g5aga$ra1E3$C-yHW$-BA@DF2@Grfa!3#'
base64_auth = base64.b64encode(auth_str.encode('ascii')).decode('ascii')
data = json.dumps({"body": message}).encode('utf-8')
req = urllib.request.Request(url, data=data, method='POST')
req.add_header('Content-Type', 'application/json')
req.add_header('Authorization', f'Basic {base64_auth}')
try:
with urllib.request.urlopen(req) as response:
if response.status == 201:
print(f" [✓] Comentário postado na Issue #{issue_id}")
except Exception as e:
print(f" [E] Falha ao postar comentário no Gitea: {e}")
def call_openai(self, messages, system_instruction, api_key):
"""Chama a API do OpenAI com ferramentas em formato puramente nativo"""
url = 'https://api.openai.com/v1/chat/completions'
headers = {
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
}
# OpenAI estruturado com o systemInstruction no topo do array
payload_messages = [{"role": "system", "content": system_instruction}] + messages
tools = [
{
"type": "function",
"function": {
"name": "read_file",
"description": "Lê o conteúdo de um arquivo do projeto para análise técnica.",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "Caminho relativo do arquivo no repositório."}
},
"required": ["path"]
}
}
},
{
"type": "function",
"function": {
"name": "write_file",
"description": "Cria ou edita um arquivo com o novo conteúdo fornecido.",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "Caminho relativo do arquivo no repositório."},
"content": {"type": "string", "description": "Conteúdo completo a ser gravado no arquivo."}
},
"required": ["path", "content"]
}
}
},
{
"type": "function",
"function": {
"name": "run_command",
"description": "Executa um comando de terminal (como NPM, Docker, Git, etc) no diretório do projeto.",
"parameters": {
"type": "object",
"properties": {
"command": {"type": "string", "description": "Comando shell a ser executado."}
},
"required": ["command"]
}
}
}
]
payload = {
"model": "gpt-4o-mini",
"messages": payload_messages,
"tools": tools,
"tool_choice": "auto"
}
req = urllib.request.Request(url, data=json.dumps(payload).encode('utf-8'), headers=headers, method='POST')
with urllib.request.urlopen(req) as res:
return json.loads(res.read().decode('utf-8'))
def execute(self):
self.setup_workspace()
# Identificação de Localização Automática
vps_ip = subprocess.getoutput("hostname -I").split()[0]
vps_name = subprocess.getoutput("hostname")
# Carrega a Doutrina de Agentes
doutrina_path = "/home/deploy/instrucoes/global/doutrina_agentes.md"
doutrina = ""
if os.path.exists(doutrina_path):
with open(doutrina_path, "r") as f:
doutrina = f.read()
# Consciência Situacional Injetada
situational_awareness = f"\n\n[CONSCIÊNCIA SITUACIONAL]: Você está rodando na VPS: {vps_name} (IP: {vps_ip})\n"
system_instruction = doutrina + situational_awareness
task = self.data.get('data', {}).get('issue', {}).get('body', 'Corrigir projeto')
print(f" [!] Agente iniciando na {vps_name} ({vps_ip}) para resolver a tarefa.")
# Histórico inicial
messages = [
{
"role": "user",
"content": f"Tarefa: {task}. Diretório do projeto: {self.local_path}. Analise o código e corrija a regressão aplicando as mudanças necessárias."
}
]
api_key = self.get_api_key('openai')
feedback_steps = []
final_text = ""
# Loop de multi-turn de ferramentas (máximo 15 iterações)
for turn in range(15):
print(f" [>] Iniciando turno {turn + 1} com o cérebro OpenAI (GPT-4o-mini)...")
try:
response = self.call_openai(messages, system_instruction, api_key)
except Exception as e:
print(f" [E] Erro ao chamar a API do OpenAI: {e}")
feedback_steps.append(f"* **Erro na API**: {e}")
break
choices = response.get('choices', [])
if not choices:
print(" [E] Nenhuma resposta retornada do OpenAI.")
break
assistant_message = choices[0].get('message', {})
content = assistant_message.get('content')
if content:
final_text += content + "\n"
# Mantém no histórico a resposta do modelo
messages.append(assistant_message)
tool_calls = assistant_message.get('tool_calls', [])
if not tool_calls:
print(" [🏁] Ciclo finalizado pelo modelo de IA.")
break
print(f" [!] OpenAI solicitou {len(tool_calls)} chamadas de ferramentas.")
# Para o OpenAI, processamos cada chamada sequencialmente e adicionamos o resultado ao histórico
for tool_call in tool_calls:
func_name = tool_call.get('function', {}).get('name')
func_args_str = tool_call.get('function', {}).get('arguments', '{}')
try:
args = json.loads(func_args_str)
except Exception as e_args:
args = {}
print(f" [!] Executando ferramenta: {func_name} (args: {args})")
result_data = None
try:
if func_name == "read_file":
path = args.get('path')
result_data = self.read_file(path)
feedback_steps.append(f"* **Lendo**: `{path}`")
elif func_name == "write_file":
path = args.get('path')
content_to_write = args.get('content')
result_data = self.write_file(path, content_to_write)
feedback_steps.append(f"* **Escrevendo**: `{path}`")
elif func_name == "run_command":
cmd = args.get('command')
result_data = self.run_command(cmd)
feedback_steps.append(f"* **Rodando**: `{cmd}`")
except Exception as e_func:
print(f" [E] Erro ao rodar ferramenta {func_name}: {e_func}")
result_data = f"Erro de execução da ferramenta: {e_func}"
# Prepara o feedback do tool e anexa ao histórico
messages.append({
"role": "tool",
"tool_call_id": tool_call.get('id'),
"name": func_name,
"content": json.dumps(result_data) if isinstance(result_data, dict) else str(result_data)
})
# Formata o feedback e posta de volta no Gitea
steps_str = "\n".join(feedback_steps)
final_comment = (
f"### 🤖 [VPS 4 - EXECUTOR SWARM GPT-4o-mini]:\n\n"
f"{final_text}\n\n"
f"**⚙️ Ações e Diagnóstico de Infraestrutura:**\n{steps_str}"
)
self.post_comment(final_comment)
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--data")
args = parser.parse_args()
if args.data:
SwarmAgent(json.loads(args.data)).execute()