Files
html/api/wevia-agent-loop.php
2026-04-16 02:28:32 +02:00

323 lines
14 KiB
PHP

<?php
/**
* ╔══════════════════════════════════════════════════════════════════╗
* ║ WEVIA AGENT LOOP v1.0 — Autonomous Agent via Master Router ║
* ║ Plan → RAG → Execute → Observe → Decide → Loop ║
* ║ Uses Master Router for ALL LLM calls (0€ sovereign cascade) ║
* ╚══════════════════════════════════════════════════════════════════╝
*
* ENDPOINTS:
* /api/wevia-agent-loop.php?agent=devops&goal=check+disk+space
* /api/wevia-agent-loop.php?agent=ethica&goal=count+HCP+by+specialty
* /api/wevia-agent-loop.php?agent=security&goal=check+compromised+accounts
* /api/wevia-agent-loop.php?list (list available agents)
*/
header("Content-Type: application/json; charset=utf-8");
header("Access-Control-Allow-Origin: *");
// Load master router (includes RAG)
require_once "/opt/wevia-brain/wevia-master-router.php";
define('AGENT_MAX_STEPS', 10);
define('AGENT_LOG_DIR', '/tmp/wevia-agents');
@mkdir(AGENT_LOG_DIR, 0777, true);
// ═══════════════════════════════════════════════════════════════
// AGENT DEFINITIONS
// ═══════════════════════════════════════════════════════════════
$AGENTS = [
'mirofish' => [
'name' => 'MiroFish Swarm Intelligence',
'system' => "Tu es l'agent MiroFish, moteur d'intelligence en essaim. Tu utilises la simulation multi-agents pour faire des prédictions.
API MiroFish: http://127.0.0.1:5001
- GET /health: statut du service
- GET /api/report/list: liste des rapports
- POST /api/report/generate: generer un rapport (body: {topic, language})
- POST /api/report/chat: discuter avec un rapport (body: {message, report_id})
- GET /api/report/<id>/progress: progression
Bridge: http://127.0.0.1/api/mirofish-bridge.php?action=ceo (donnees CEO)
Reponds en JSON:
{\"action\":\"api\",\"url\":\"http://127.0.0.1:5001/api/report/list\"} pour appeler l'API MiroFish
{\"action\":\"generate\",\"topic\":\"...\"} pour lancer une simulation
{\"action\":\"done\",\"summary\":\"...\",\"data\":{}} quand tu as termine",
'tools' => ['api_call', 'exec_local'],
'max_steps' => 6,
],
'devops' => [
'name' => 'WEVIA DevOps Agent',
'system' => "Tu es un agent DevOps autonome pour WEVAL Consulting. Tu surveilles l'infrastructure:
- S204 (204.168.152.13): PRIMARY, Ollama, Qdrant, 22 Docker, nginx, PostgreSQL
- S95 (95.216.167.89): WEVADS, Arsenal:5890, iResponse:5821, PMTA, PostgreSQL
- S151 (151.80.235.110): OVH tracking, Ollama, nginx
Tu peux exécuter des commandes shell pour diagnostiquer. Réponds en JSON:
{\"action\":\"exec\",\"cmd\":\"df -h /\"} pour exécuter une commande
{\"action\":\"analyze\",\"findings\":\"...\"} pour donner ton analyse
{\"action\":\"done\",\"summary\":\"...\",\"status\":\"ok|warning|critical\"} quand tu as terminé",
'tools' => ['exec_local'],
'max_steps' => 8,
],
'ethica' => [
'name' => 'WEVIA Ethica Agent',
'system' => "Tu es un agent Ethica spécialisé dans la gestion des HCP (Healthcare Professionals) pour le Maroc, Tunisie, Algérie.
DB: PostgreSQL schema ethica dans adx_system sur S95.
Table: medecins_real (135K+ HCPs).
Scrapers: DabaDoc, Google Maps, RichScraper, ethica-enrich-v4.py.
Tu peux exécuter des requêtes SQL et des scripts.
Réponds en JSON:
{\"action\":\"sql\",\"query\":\"SELECT COUNT(*) FROM ethica.medecins_real\"} pour une requête SQL
{\"action\":\"exec\",\"cmd\":\"...\"} pour un script
{\"action\":\"done\",\"summary\":\"...\",\"data\":{}} quand tu as terminé",
'tools' => ['sql_s95', 'exec_s95'],
'max_steps' => 6,
],
'security' => [
'name' => 'WEVIA Security Agent',
'system' => "Tu es un agent de sécurité pour WEVAL Consulting. Tu audites:
- 5 comptes O365 compromis: rodolftripp, sfgb518, phyleciaamato, kamrynnbonilla, jolineweatherly
- PMTA/MTA status sur S95
- Spamhaus blacklist check
- SSL certificates expiry
- Firewall ports ouverts
Réponds en JSON:
{\"action\":\"exec\",\"cmd\":\"...\"} pour vérifier
{\"action\":\"done\",\"summary\":\"...\",\"alerts\":[]} quand tu as terminé",
'tools' => ['exec_local', 'exec_s95'],
'max_steps' => 8,
],
'monitor' => [
'name' => 'WEVIA Monitor Agent',
'system' => "Tu es un agent de monitoring. Tu vérifies:
- Disk usage (<85%)
- Docker containers (tous UP?)
- Ollama (port 11434 répond?)
- Qdrant (collections saines?)
- L99 tests (derniers résultats?)
- Crons (tous actifs?)
Réponds en JSON:
{\"action\":\"exec\",\"cmd\":\"...\"} pour vérifier
{\"action\":\"done\",\"summary\":\"...\",\"metrics\":{}} quand tu as terminé",
'tools' => ['exec_local'],
'max_steps' => 6,
],
];
// ═══════════════════════════════════════════════════════════════
// TOOL EXECUTORS
// ═══════════════════════════════════════════════════════════════
function agent_exec_local($cmd, $timeout = 10) {
// $cmd = escapeshellcmd($cmd); // Removed: corrupts pipes and flags
$result = shell_exec("timeout $timeout bash -c " . escapeshellarg($cmd) . " 2>&1");
$output = $result ? mb_substr($result, 0, 3000) : "";
return ['output' => $output, 'exit_code' => 0];
}
function agent_exec_s95($cmd, $timeout = 15) {
$escaped = str_replace("'", "'\\''", $cmd);
$sshCmd = "ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 -p49222 root@10.1.0.3 '$escaped'";
$output = [];
$return = 0;
exec("timeout $timeout $sshCmd 2>&1", $output, $return);
return ['output' => implode("\n", array_slice($output, 0, 50)), 'exit_code' => $return];
}
function agent_sql_s95($query, $timeout = 10) {
$escaped = str_replace("'", "'\\''", $query);
$cmd = "ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 -p49222 root@10.1.0.3 'PGPASSWORD=admin123 psql -U admin -d adx_system -t -A -c \"$escaped\"'";
$output = [];
$return = 0;
exec("timeout $timeout $cmd 2>&1", $output, $return);
return ['output' => implode("\n", array_slice($output, 0, 30)), 'exit_code' => $return];
}
// ═══════════════════════════════════════════════════════════════
// AGENT LOOP ENGINE
// ═══════════════════════════════════════════════════════════════
function agent_run($agentId, $goal, $maxSteps = null) {
global $AGENTS;
if (!isset($AGENTS[$agentId])) return ['error' => "Unknown agent: $agentId"];
$agent = $AGENTS[$agentId];
$maxSteps = $maxSteps ?? $agent['max_steps'] ?? AGENT_MAX_STEPS;
$systemPrompt = $agent['system'];
$history = [];
$steps = [];
$startTime = microtime(true);
// Initial message: the goal
$history[] = ['role' => 'user', 'content' => "OBJECTIF: $goal\n\nCommence par analyser ce qui est nécessaire, puis exécute étape par étape. Réponds UNIQUEMENT en JSON valide."];
for ($step = 1; $step <= $maxSteps; $step++) {
// Call Master Router with RAG
$result = mr_route(
end($history)['content'],
$systemPrompt,
array_slice($history, 0, -1),
['max_tier' => 2, 'force_tier' => 1] // Skip Ollama (too slow on CPU), go Cerebras direct
);
$response = $result['content'] ?? '';
$history[] = ['role' => 'assistant', 'content' => $response];
// Parse JSON action from response
$action = null;
if (preg_match('/\{[^{}]*"action"\s*:\s*"[^"]+"/s', $response, $matches)) {
$jsonStr = $matches[0];
// Try to find the complete JSON object
$braceCount = 0;
$jsonComplete = '';
for ($i = strpos($response, $jsonStr); $i < strlen($response); $i++) {
$char = $response[$i];
if ($char === '{') $braceCount++;
if ($char === '}') $braceCount--;
$jsonComplete .= $char;
if ($braceCount === 0) break;
}
$action = json_decode($jsonComplete, true);
}
$stepData = [
'step' => $step,
'model' => $result['model'] ?? 'unknown',
'provider' => $result['provider'] ?? 'unknown',
'latency_ms' => $result['latency_ms'] ?? 0,
'action' => $action,
];
if (!$action) {
$stepData['raw_response'] = mb_substr($response, 0, 500);
$stepData['error'] = 'no_json_action';
$steps[] = $stepData;
// Add context for retry
$history[] = ['role' => 'user', 'content' => "ERREUR: Ta réponse n'est pas en JSON valide. Réponds UNIQUEMENT avec un objet JSON contenant \"action\"."];
continue;
}
// Execute action
switch ($action['action'] ?? '') {
case 'exec':
$cmd = $action['cmd'] ?? '';
if (empty($cmd)) {
$stepData['result'] = 'empty command';
break;
}
// Determine if local or S95
if (in_array('exec_s95', $agent['tools']) && (
strpos($cmd, 'S95') !== false ||
strpos($cmd, 's95') !== false ||
strpos($cmd, '10.1.0.3') !== false ||
strpos($cmd, 'ethica') !== false ||
strpos($cmd, 'pmta') !== false
)) {
$execResult = agent_exec_s95($cmd);
} else {
$execResult = agent_exec_local($cmd);
}
$stepData['result'] = $execResult;
$history[] = ['role' => 'user', 'content' => "RÉSULTAT COMMANDE:\n```\n{$execResult['output']}\n```\nExit code: {$execResult['exit_code']}\n\nAnalyse ce résultat et continue. Réponds en JSON."];
break;
case 'sql':
$query = $action['query'] ?? '';
if (empty($query)) {
$stepData['result'] = 'empty query';
break;
}
$sqlResult = agent_sql_s95($query);
$stepData['result'] = $sqlResult;
$history[] = ['role' => 'user', 'content' => "RÉSULTAT SQL:\n```\n{$sqlResult['output']}\n```\n\nAnalyse et continue. Réponds en JSON."];
break;
case 'analyze':
$stepData['findings'] = $action['findings'] ?? '';
$history[] = ['role' => 'user', 'content' => "OK, analyse notée. Continue avec la prochaine vérification ou termine avec {\"action\":\"done\"}. Réponds en JSON."];
break;
case 'done':
$stepData['summary'] = $action['summary'] ?? '';
$stepData['status'] = $action['status'] ?? 'unknown';
$steps[] = $stepData;
$totalTime = round((microtime(true) - $startTime) * 1000);
$totalCost = array_sum(array_column($steps, 'cost'));
$report = [
'agent' => $agent['name'],
'goal' => $goal,
'steps_count' => count($steps),
'total_time_ms' => $totalTime,
'total_cost' => $totalCost,
'summary' => $action['summary'] ?? '',
'status' => $action['status'] ?? 'unknown',
'data' => $action['data'] ?? $action['metrics'] ?? $action['alerts'] ?? null,
'steps' => $steps,
];
// Log
$logFile = AGENT_LOG_DIR . "/$agentId-" . date('Y-m-d-His') . ".json";
@file_put_contents($logFile, json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
return $report;
default:
$stepData['error'] = "Unknown action: " . ($action['action'] ?? 'null');
$history[] = ['role' => 'user', 'content' => "Action inconnue. Utilise: exec, sql, analyze, ou done. Réponds en JSON."];
}
$steps[] = $stepData;
}
// Max steps reached
return [
'agent' => $agent['name'],
'goal' => $goal,
'steps_count' => count($steps),
'total_time_ms' => round((microtime(true) - $startTime) * 1000),
'status' => 'max_steps_reached',
'steps' => $steps,
];
}
// ═══════════════════════════════════════════════════════════════
// API HANDLER
// ═══════════════════════════════════════════════════════════════
// List agents
if (isset($_GET['list'])) {
global $AGENTS;
$list = [];
foreach ($AGENTS as $id => $a) {
$list[$id] = ['name' => $a['name'], 'tools' => $a['tools'], 'max_steps' => $a['max_steps']];
}
echo json_encode($list, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
exit;
}
$agentId = $_GET['agent'] ?? $_POST['agent'] ?? '';
$goal = $_GET['goal'] ?? $_POST['goal'] ?? '';
if (empty($agentId) || empty($goal)) {
echo json_encode([
'error' => 'Usage: ?agent=devops|ethica|security|monitor&goal=your+task',
'agents' => array_keys($AGENTS),
]);
exit;
}
// Run agent
$report = agent_run($agentId, $goal);
echo json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);