feat: switch executor to high-performance, unlimited GPT-4o-mini engine

This commit is contained in:
Rui
2026-05-17 03:20:39 +02:00
parent 6a60d1cd2a
commit 630c1f4b60
+95 -102
View File
@@ -81,71 +81,73 @@ class SwarmAgent:
except Exception as e: except Exception as e:
print(f" [E] Falha ao postar comentário no Gitea: {e}") print(f" [E] Falha ao postar comentário no Gitea: {e}")
def call_gemini(self, contents, system_instruction, api_key): def call_openai(self, messages, system_instruction, api_key):
"""Chama a API do Gemini com ferramentas ativas em formato puramente nativo""" """Chama a API do OpenAI com ferramentas em formato puramente nativo"""
url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-latest:generateContent?key={api_key}" url = 'https://api.openai.com/v1/chat/completions'
headers = {'content-type': 'application/json'} headers = {
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
}
tools = [{ # OpenAI estruturado com o systemInstruction no topo do array
"functionDeclarations": [ payload_messages = [{"role": "system", "content": system_instruction}] + messages
{
tools = [
{
"type": "function",
"function": {
"name": "read_file", "name": "read_file",
"description": "Lê o conteúdo de um arquivo do projeto para análise técnica.", "description": "Lê o conteúdo de um arquivo do projeto para análise técnica.",
"parameters": { "parameters": {
"type": "OBJECT", "type": "object",
"properties": { "properties": {
"path": {"type": "STRING", "description": "Caminho relativo do arquivo no repositório."} "path": {"type": "string", "description": "Caminho relativo do arquivo no repositório."}
}, },
"required": ["path"] "required": ["path"]
} }
}, }
{ },
{
"type": "function",
"function": {
"name": "write_file", "name": "write_file",
"description": "Cria ou edita um arquivo com o novo conteúdo fornecido.", "description": "Cria ou edita um arquivo com o novo conteúdo fornecido.",
"parameters": { "parameters": {
"type": "OBJECT", "type": "object",
"properties": { "properties": {
"path": {"type": "STRING", "description": "Caminho relativo do arquivo no repositório."}, "path": {"type": "string", "description": "Caminho relativo do arquivo no repositório."},
"content": {"type": "STRING", "description": "Conteúdo completo a ser gravado no arquivo."} "content": {"type": "string", "description": "Conteúdo completo a ser gravado no arquivo."}
}, },
"required": ["path", "content"] "required": ["path", "content"]
} }
}, }
{ },
{
"type": "function",
"function": {
"name": "run_command", "name": "run_command",
"description": "Executa um comando de terminal (como NPM, Docker, Git, etc) no diretório do projeto.", "description": "Executa um comando de terminal (como NPM, Docker, Git, etc) no diretório do projeto.",
"parameters": { "parameters": {
"type": "OBJECT", "type": "object",
"properties": { "properties": {
"command": {"type": "STRING", "description": "Comando shell a ser executado."} "command": {"type": "string", "description": "Comando shell a ser executado."}
}, },
"required": ["command"] "required": ["command"]
} }
} }
] }
}] ]
payload = { payload = {
"contents": contents, "model": "gpt-4o-mini",
"systemInstruction": { "messages": payload_messages,
"parts": [{"text": system_instruction}] "tools": tools,
}, "tool_choice": "auto"
"tools": tools
} }
for attempt in range(5): req = urllib.request.Request(url, data=json.dumps(payload).encode('utf-8'), headers=headers, method='POST')
try: with urllib.request.urlopen(req) as res:
req = urllib.request.Request(url, data=json.dumps(payload).encode('utf-8'), headers=headers, method='POST') return json.loads(res.read().decode('utf-8'))
with urllib.request.urlopen(req) as res:
return json.loads(res.read().decode('utf-8'))
except urllib.error.HTTPError as e:
if e.code == 429:
wait_time = (attempt + 1) * 8
print(f" [W] Rate Limit (429) detectado. Aguardando {wait_time} segundos antes de tentar novamente (tentativa {attempt + 1}/5)...")
time.sleep(wait_time)
else:
raise e
raise Exception("Falha após 5 tentativas devido a limite de requisições (429).")
def execute(self): def execute(self):
self.setup_workspace() self.setup_workspace()
@@ -169,97 +171,88 @@ class SwarmAgent:
print(f" [!] Agente iniciando na {vps_name} ({vps_ip}) para resolver a tarefa.") print(f" [!] Agente iniciando na {vps_name} ({vps_ip}) para resolver a tarefa.")
# Histórico inicial # Histórico inicial
contents = [ messages = [
{ {
"role": "user", "role": "user",
"parts": [{"text": 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."}] "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('google') api_key = self.get_api_key('openai')
feedback_steps = [] feedback_steps = []
final_text = "" final_text = ""
# Loop de multi-turn de ferramentas (máximo 15 iterações) # Loop de multi-turn de ferramentas (máximo 15 iterações)
for turn in range(15): for turn in range(15):
print(f" [>] Iniciando turno {turn + 1} com o cérebro Gemini...") print(f" [>] Iniciando turno {turn + 1} com o cérebro OpenAI (GPT-4o-mini)...")
if turn > 0:
print(" [>] Resfriando a API por 3 segundos para evitar rate limit (429)...")
time.sleep(3.0)
try: try:
response = self.call_gemini(contents, system_instruction, api_key) response = self.call_openai(messages, system_instruction, api_key)
except Exception as e: except Exception as e:
print(f" [E] Erro ao chamar a API do Gemini: {e}") print(f" [E] Erro ao chamar a API do OpenAI: {e}")
feedback_steps.append(f"* **Erro na API**: {e}") feedback_steps.append(f"* **Erro na API**: {e}")
break break
candidates = response.get('candidates', []) choices = response.get('choices', [])
if not candidates: if not choices:
print(" [E] Nenhuma resposta retornada.") print(" [E] Nenhuma resposta retornada do OpenAI.")
break break
content = candidates[0].get('content', {}) assistant_message = choices[0].get('message', {})
parts = content.get('parts', []) content = assistant_message.get('content')
if content:
final_text += content + "\n"
# Mantém no histórico a resposta do modelo # Mantém no histórico a resposta do modelo
contents.append(content) messages.append(assistant_message)
has_tool_call = False tool_calls = assistant_message.get('tool_calls', [])
function_responses = [] if not tool_calls:
for part in parts:
if 'text' in part:
final_text += part['text'] + "\n"
if 'functionCall' in part:
has_tool_call = True
func_call = part['functionCall']
func_name = func_call.get('name')
args = func_call.get('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}"
function_responses.append({
"functionResponse": {
"name": func_name,
"response": {
"output": result_data
}
}
})
if not has_tool_call:
print(" [🏁] Ciclo finalizado pelo modelo de IA.") print(" [🏁] Ciclo finalizado pelo modelo de IA.")
break break
else:
contents.append({ print(f" [!] OpenAI solicitou {len(tool_calls)} chamadas de ferramentas.")
"role": "function",
"parts": function_responses # 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 # Formata o feedback e posta de volta no Gitea
steps_str = "\n".join(feedback_steps) steps_str = "\n".join(feedback_steps)
final_comment = ( final_comment = (
f"### 🤖 [VPS 4 - EXECUTOR SWARM]:\n\n" f"### 🤖 [VPS 4 - EXECUTOR SWARM GPT-4o-mini]:\n\n"
f"{final_text}\n\n" f"{final_text}\n\n"
f"**⚙️ Ações e Diagnóstico de Infraestrutura:**\n{steps_str}" f"**⚙️ Ações e Diagnóstico de Infraestrutura:**\n{steps_str}"
) )