feat: switch executor to high-performance, unlimited GPT-4o-mini engine
This commit is contained in:
+96
-103
@@ -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:
|
||||||
# Mantém no histórico a resposta do modelo
|
final_text += content + "\n"
|
||||||
contents.append(content)
|
|
||||||
|
|
||||||
has_tool_call = False
|
|
||||||
function_responses = []
|
|
||||||
|
|
||||||
for part in parts:
|
|
||||||
if 'text' in part:
|
|
||||||
final_text += part['text'] + "\n"
|
|
||||||
|
|
||||||
if 'functionCall' in part:
|
# Mantém no histórico a resposta do modelo
|
||||||
has_tool_call = True
|
messages.append(assistant_message)
|
||||||
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:
|
tool_calls = assistant_message.get('tool_calls', [])
|
||||||
|
if not tool_calls:
|
||||||
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}"
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user