323 lines
14 KiB
PHP
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);
|