feat: update templates for executor tools, situational awareness and listener execution logic

This commit is contained in:
Rui
2026-05-17 03:01:54 +02:00
parent 5ec50d512a
commit d92f06fd7f
2 changed files with 128 additions and 79 deletions
+116 -72
View File
@@ -1,107 +1,151 @@
import os import os, json, redis, subprocess, base64
import json from urllib import request, parse
import redis
import argparse
import subprocess
from anthropic import Anthropic from anthropic import Anthropic
from key_manager import KeyManager
# Configurações de Caminho
BASE_DIR = "/home/deploy/projetos"
INSTRUCOES_DIR = "/home/deploy/instrucoes"
class SwarmAgent: class SwarmAgent:
def __init__(self, task_data): def __init__(self, task_data):
self.data = task_data self.data = task_data
self.repo_name = task_data.get('repository') self.repo_name = task_data.get('repository')
self.repo_url = f"http://10.99.0.3:3000/{self.repo_name}.git" self.repo_url = f"http://10.99.0.3:3000/{self.repo_name}.git"
self.local_path = os.path.join(BASE_DIR, self.repo_name.split('/')[-1]) self.local_path = os.path.join("/home/deploy/projetos", self.repo_name.split('/')[-1])
self.km = KeyManager()
# Conexão Redis para pegar chaves
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):
pool = json.loads(self.r.get('swarm:keys:pool'))
return random.choice([k['key'] for k in pool['anthropic'] if k['status'] == 'active'])
def setup_workspace(self): def setup_workspace(self):
"""Garante que o código e as instruções estão atualizados""" os.makedirs("/home/deploy/projetos", exist_ok=True)
print(f" [>] Sincronizando repositório: {self.repo_name}")
os.makedirs(BASE_DIR, exist_ok=True)
if not os.path.exists(self.local_path): if not os.path.exists(self.local_path):
subprocess.run(["git", "clone", self.repo_url, self.local_path]) subprocess.run(["git", "clone", self.repo_url, self.local_path])
else: else:
subprocess.run(["git", "-C", self.local_path, "pull"]) subprocess.run(["git", "-C", self.local_path, "pull"])
# Sincroniza instruções globais
subprocess.run(["git", "-C", INSTRUCOES_DIR, "pull"])
def get_system_prompt(self): def run_bash(self, command):
"""Carrega a doutrina e as instruções de cada VPS""" """Ferramenta para a IA rodar comandos na VPS 4"""
with open(f"{INSTRUCOES_DIR}/global/doutrina_agentes.md", "r") as f: result = subprocess.run(command, shell=True, capture_output=True, text=True, cwd=self.local_path)
doutrina = f.read() return f"STDOUT: {result.stdout}\nSTDERR: {result.stderr}"
with open(f"{INSTRUCOES_DIR}/global/claude.md", "r") as f:
especifico = f.read()
ip_vps = subprocess.getoutput("hostname -I").split()[0]
return f"""
{doutrina}
{especifico}
CONTEXTO ATUAL:
- Você está na VPS com IP: {ip_vps}
- Projeto: {self.repo_name}
- Tarefa (Issue): {json.dumps(self.data.get('data', {}).get('issue', {}))}
"""
def post_comment(self, message): def post_comment(self, message):
"""Posta um comentário de feedback na Issue do Gitea usando urllib (zero-dependency)"""
issue_id = self.data.get('data', {}).get('issue', {}).get('number') issue_id = self.data.get('data', {}).get('issue', {}).get('number')
if not issue_id: if not issue_id: return
return
url = f"http://10.99.0.3:3000/api/v1/repos/{self.repo_name}/issues/{issue_id}/comments" url = f"http://10.99.0.3:3000/api/v1/repos/{self.repo_name}/issues/{issue_id}/comments"
import base64
from urllib import request, parse
# Auth Basic
auth_str = 'ruicesar:h$tg@g5aga$ra1E3$C-yHW$-BA@DF2@Grfa!3#' auth_str = 'ruicesar:h$tg@g5aga$ra1E3$C-yHW$-BA@DF2@Grfa!3#'
auth_bytes = auth_str.encode('ascii') base64_auth = base64.b64encode(auth_str.encode('ascii')).decode('ascii')
base64_auth = base64.b64encode(auth_bytes).decode('ascii') req = request.Request(url, data=json.dumps({"body": message}).encode('utf-8'), method='POST')
data = json.dumps({"body": message}).encode('utf-8')
req = request.Request(url, data=data, method='POST')
req.add_header('Content-Type', 'application/json') req.add_header('Content-Type', 'application/json')
req.add_header('Authorization', f'Basic {base64_auth}') req.add_header('Authorization', f'Basic {base64_auth}')
try: try:
with request.urlopen(req) as response: with request.urlopen(req) as res: pass
if response.status == 201: except: pass
print(f" [✓] Comentário postado na Issue #{issue_id}")
else: def run_command(self, command):
print(f" [X] Erro ao postar comentário: {response.status}") """Executa comandos shell na VPS 4"""
except Exception as e: print(f" [Exec] Rodando: {command}")
print(f" [E] Falha na comunicação com Gitea via urllib: {e}") 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"""
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"""
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 "Erro: Arquivo não encontrado."
def execute(self): def execute(self):
self.setup_workspace() self.setup_workspace()
api_key = self.km.get_key('anthropic') # Identificação de Localização Automática
if not api_key: vps_ip = subprocess.getoutput("hostname -I").split()[0]
return vps_name = subprocess.getoutput("hostname")
with open("/home/deploy/instrucoes/global/doutrina_agentes.md", "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"
api_key = self.get_api_key()
client = Anthropic(api_key=api_key) client = Anthropic(api_key=api_key)
prompt = self.get_system_prompt()
print(f" [!] Agente iniciando na {vps_name} ({vps_ip})...")
task = self.data.get('data', {}).get('issue', {}).get('body', 'Sem descrição')
# O Agente agora tem um loop de pensamento e ferramentas
# Para este MVP, vamos permitir que ele faça uma análise e execute uma rodada de ferramentas
tools = [
{
"name": "read_file",
"description": "Lê o conteúdo de um arquivo do projeto",
"input_schema": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"]
}
},
{
"name": "write_file",
"description": "Escreve ou edita um arquivo no projeto",
"input_schema": {
"type": "object",
"properties": {
"path": {"type": "string"},
"content": {"type": "string"}
},
"required": ["path", "content"]
}
},
{
"name": "run_command",
"description": "Executa um comando shell (ex: docker compose up)",
"input_schema": {
"type": "object",
"properties": {"command": {"type": "string"}},
"required": ["command"]
}
}
]
print(" [!] IA Pensando na solução...") response = client.messages.create(
model="claude-3-5-sonnet-20240620",
max_tokens=4000,
system=doutrina + situational_awareness,
tools=tools,
messages=[{"role": "user", "content": f"Tarefa: {task}\nAnalise o diretório {self.local_path} e resolva o problema."}]
)
# Processamento simples de ferramentas (para este estágio)
final_feedback = f"### 🤖 [VPS 4 - EXECUTOR]:\n\n{response.content[0].text}\n\n"
# Simulação de resposta da IA para o teste for content in response.content:
feedback = "### 🤖 [VPS 4 - LABORATÓRIO]:\n* **Status**: Tarefa recebida e ambiente preparado.\n* **Ação**: Sincronização de arquivos concluída.\n* **Próximo**: Iniciando correção do erro de regressão de versão." if content.type == "tool_use":
if content.name == "read_file":
res = self.read_file(content.input["path"])
final_feedback += f"* **Lendo**: {content.input['path']}\n"
elif content.name == "write_file":
res = self.write_file(content.input["path"], content.input["content"])
final_feedback += f"* **Escrevendo**: {content.input['path']}\n"
elif content.name == "run_command":
res = self.run_command(content.input["command"])
final_feedback += f"* **Rodando**: `{content.input['command']}`\n"
self.post_comment(feedback) self.post_comment(final_feedback)
if __name__ == "__main__": if __name__ == "__main__":
import argparse, random
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--data", help="Dados do Webhook JSON") parser.add_argument("--data")
args = parser.parse_args() args = parser.parse_args()
if args.data: if args.data:
agent = SwarmAgent(json.loads(args.data)) SwarmAgent(json.loads(args.data)).execute()
agent.execute()
+12 -7
View File
@@ -13,7 +13,7 @@ r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASS, db=0)
pubsub = r.pubsub() pubsub = r.pubsub()
pubsub.subscribe(*CHANNELS) pubsub.subscribe(*CHANNELS)
print(f" [*] Aguardando eventos nos canais {CHANNELS}...") print(f" [*] Swarm Listener Ativo - Aguardando eventos nos canais {CHANNELS}...")
for message in pubsub.listen(): for message in pubsub.listen():
if message['type'] == 'message': if message['type'] == 'message':
@@ -21,13 +21,18 @@ for message in pubsub.listen():
payload = json.loads(message['data']) payload = json.loads(message['data'])
event = payload.get('event') event = payload.get('event')
repo = payload.get('repository') repo = payload.get('repository')
channel = message['channel'].decode('utf-8') if isinstance(message['channel'], bytes) else message['channel']
print(f" [!] Novo evento recebido: {event} no repo {repo}") print(f" [!] Novo evento recebido no canal {channel}: {event} no repo {repo}")
# Aqui entrará a lógica de disparar o agente de IA específico
# Por enquanto, apenas registramos o recebimento.
# Exemplo de comando que seria rodado:
# subprocess.run(["python3", "agent_executor.py", "--event", event, "--data", json.dumps(payload)])
# Se for uma tarefa de desenvolvimento (tasks), dispara o executor inteligente
if channel == 'swarm:tasks':
executor_path = "/home/deploy/instrucoes/templates/agent_executor.py"
if os.path.exists(executor_path):
print(f" [>] Disparando executor inteligente: {executor_path}")
subprocess.run(["python3", executor_path, "--data", json.dumps(payload)])
else:
print(f" [E] Executor não encontrado em {executor_path}")
except Exception as e: except Exception as e:
print(f" [E] Erro ao processar mensagem: {e}") print(f" [E] Erro ao processar mensagem: {e}")