Files
html/api/wevia-v76-multi-agent-intent.php
2026-04-19 15:50:02 +02:00

195 lines
8.3 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* V76 — WEVIA Master Multi-Agent Orchestrator Intent
* Purpose: when user says "agis en multi-agents" / "mobilise N agents pour X"
* DO real parallel execution of multiple sub-intents, not simulate.
*
* Router chain: wevia-master-api.php → wevia-master-router.php
* Registered via include, detects trigger early, short-circuits fallback LLM.
*
* Auto-wire: if no matching sub-intent, creates stub in /wired-pending/.
* Zero hallucination: returns facts only, says "je n ai pas cet agent" if missing.
*/
if (!isset($msg) && !isset($_mam)) return;
$V76_MSG = $msg ?? $_mam ?? '';
if (empty($V76_MSG)) return;
/* Trigger detection: "agis en multi-agents" / "mobilise N agents" / "parallele" */
$V76_TRIGGER = false;
$V76_N = 3; // default
$V76_TASK = '';
if (preg_match('/\b(mobilise|sollicite|lance|agis?)\s+(\d+)?\s*agents?\s+(en\s+)?(parallele|parall[eè]le|multi[- ]?agents?)/iu', $V76_MSG, $mm)) {
$V76_TRIGGER = true;
$V76_N = !empty($mm[2]) ? min(10, max(2, intval($mm[2]))) : 3;
}
elseif (preg_match('/\bagis?\s+en\s+multi[- ]?agents?/iu', $V76_MSG)) {
$V76_TRIGGER = true;
}
elseif (preg_match('/\b(multi[- ]?agents?|multiagent)\b/iu', $V76_MSG) && preg_match('/\b(audit|verif|test|lance|check|scan|analys)/iu', $V76_MSG)) {
$V76_TRIGGER = true;
}
if (!$V76_TRIGGER) return;
/* Parse task */
$V76_TASK = preg_replace('/\b(mobilise|sollicite|lance|agis?)\s+\d*\s*agents?\s+(en\s+)?(parallele|parall[eè]le|multi[- ]?agents?)\s+(pour|afin|et)?\s*/iu', '', $V76_MSG);
$V76_TASK = trim(preg_replace('/\bagis?\s+en\s+multi[- ]?agents?\s*(pour|afin|et)?\s*/iu', '', $V76_TASK));
if (empty($V76_TASK)) $V76_TASK = $V76_MSG;
/* Available sub-intents catalog (self-describing) */
$V76_AGENTS = [
'avatar_audit' => [
'match' => '/(avatar|persona|portrait|image|icone)/iu',
'cmd' => 'bash -c "echo \'AVATAR AUDIT\' && for p in enterprise-model.html agents-archi.html wevia-meeting-rooms.html sales-hub.html weval-technology-platform.html; do echo \"=== $p ===\"; n=$(curl -sk --max-time 3 http://127.0.0.1:5890/$p -H \"Host: weval-consulting.com\" 2>/dev/null | grep -oE \"(dicebear|robohash|agent-avatar-svg)\" | sort -u | tr \"\\n\" \",\"); echo \"sources: $n\"; done"',
'label' => '🖼️ Agent Audit Avatars'
],
'playwright_test' => [
'match' => '/(playwright|test.?visu|e2e|visual)/iu',
'cmd' => 'bash -c "echo PLAYWRIGHT_TEST && ls -la /var/www/html/api/playwright-results/ 2>/dev/null | tail -5"',
'label' => '🎭 Agent Playwright'
],
'selenium_test' => [
'match' => '/(selenium|chrome|browser)/iu',
'cmd' => 'bash -c "echo SELENIUM_CHECK && which chromedriver 2>&1 && which selenium 2>&1 && pgrep -f chrome | head -3"',
'label' => '🧪 Agent Selenium'
],
'six_sigma' => [
'match' => '/(sigma|dpmo|dmaic|qualite)/iu',
'cmd' => 'curl -sk --max-time 5 http://127.0.0.1:5890/api/wevia-v74-sixsigma-api.php -H "Host: weval-consulting.com" 2>/dev/null | head -c 800',
'label' => '📊 Agent Six Sigma'
],
'nonreg' => [
'match' => '/(nonreg|regression|qualite|health)/iu',
'cmd' => 'cat /var/www/html/api/nonreg-latest.json 2>/dev/null | head -c 500',
'label' => '✅ Agent NonReg'
],
'git_status' => [
'match' => '/(git|commit|repo|push|sync)/iu',
'cmd' => 'cd /var/www/html && git log --oneline -5 2>&1 | head -5',
'label' => '📁 Agent Git'
],
'vault_audit' => [
'match' => '/(vault|backup|gold)/iu',
'cmd' => 'ls -la /opt/wevads/vault/ 2>/dev/null | wc -l && echo "GOLD backups total"',
'label' => '🔒 Agent Vault'
],
'docker_health' => [
'match' => '/(docker|container|service)/iu',
'cmd' => 'docker ps --format "{{.Names}}: {{.Status}}" 2>&1 | head -10',
'label' => '🐳 Agent Docker'
],
'coverage_check' => [
'match' => '/(coverage|couverture|100|complet)/iu',
'cmd' => 'bash /var/www/html/api/v74-coverage-check.sh 2>&1 | head -20',
'label' => '🎯 Agent Coverage'
],
'registry_inspect' => [
'match' => '/(registry|agents.?liste|catalog|inventair)/iu',
'cmd' => 'python3 -c "import json;d=json.load(open(\"/var/www/html/api/agent-avatars.json\"));v=json.load(open(\"/var/www/html/api/agent-avatars-v75.json\"));print(f\"V1 string URL: {len(d)} agents\");print(f\"V75 emoji: {len(v)} agents\")" 2>&1',
'label' => '📇 Agent Registry'
]
];
/* Select agents: match task against each agent's match pattern, limit N */
$V76_SELECTED = [];
$V76_UNMATCHED_REASON = '';
foreach ($V76_AGENTS as $agent_id => $spec) {
if (preg_match($spec['match'], $V76_TASK)) {
$V76_SELECTED[$agent_id] = $spec;
}
if (count($V76_SELECTED) >= $V76_N) break;
}
/* If nothing matched, pick diverse defaults (not just 1 random) */
if (empty($V76_SELECTED)) {
$defaults = ['six_sigma', 'nonreg', 'git_status', 'coverage_check', 'registry_inspect'];
foreach (array_slice($defaults, 0, $V76_N) as $agent_id) {
$V76_SELECTED[$agent_id] = $V76_AGENTS[$agent_id];
}
$V76_UNMATCHED_REASON = 'Aucun agent matché "'.$V76_TASK.'" — défaut santé globale.';
}
/* Execute sub-intents in parallel (background bash with timeout 8s each, aggregate by files) */
$V76_TS = date('YmdHis');
$V76_WORKDIR = "/tmp/v76_multi_{$V76_TS}";
@mkdir($V76_WORKDIR, 0755, true);
foreach ($V76_SELECTED as $agent_id => $spec) {
$outfile = "{$V76_WORKDIR}/{$agent_id}.out";
$cmd = $spec['cmd'] . " > {$outfile} 2>&1 &";
@shell_exec($cmd);
}
/* Wait up to 10s for all results */
$V76_DEADLINE = time() + 10;
$V76_DONE = [];
while (time() < $V76_DEADLINE && count($V76_DONE) < count($V76_SELECTED)) {
foreach ($V76_SELECTED as $agent_id => $spec) {
if (isset($V76_DONE[$agent_id])) continue;
$outfile = "{$V76_WORKDIR}/{$agent_id}.out";
if (file_exists($outfile)) {
// Check if writing finished: file age > 200ms or no writers via lsof too slow
clearstatcache(true, $outfile);
if (time() - filemtime($outfile) >= 1) {
$V76_DONE[$agent_id] = @file_get_contents($outfile) ?: '(empty)';
}
}
}
usleep(500000); // 500ms
}
/* Aggregate report */
$V76_REPORT = [];
$V76_REPORT[] = "🎭 MULTI-AGENT EXECUTION · {$V76_N} agents parallèles";
$V76_REPORT[] = "Tâche: " . substr($V76_TASK, 0, 120);
if ($V76_UNMATCHED_REASON) $V76_REPORT[] = " " . $V76_UNMATCHED_REASON;
$V76_REPORT[] = str_repeat('─', 50);
foreach ($V76_SELECTED as $agent_id => $spec) {
$out = $V76_DONE[$agent_id] ?? '(timeout après 10s)';
$V76_REPORT[] = "";
$V76_REPORT[] = $spec['label'] . " [{$agent_id}]";
$V76_REPORT[] = substr(trim($out), 0, 600);
}
/* Auto-wire: detect tasks without matching agent → create pending stub */
if (!empty($V76_UNMATCHED_REASON)) {
$stub_name = 'multi_' . preg_replace('/[^a-z0-9_]/', '_', mb_strtolower(substr($V76_TASK, 0, 40)));
$stub_path = "/var/www/html/api/wired-pending/intent-v76-{$stub_name}.php";
@mkdir(dirname($stub_path), 0755, true);
if (!file_exists($stub_path)) {
$stub = [
'name' => $stub_name,
'task_original' => $V76_TASK,
'triggers' => [mb_strtolower(substr($V76_TASK, 0, 60))],
'created_at' => date('c'),
'source' => 'wevia-v76-multi-agent-autowire',
'status' => 'PENDING_APPROVAL'
];
@file_put_contents($stub_path, "<?php\nreturn " . var_export($stub, true) . ";\n");
$V76_REPORT[] = "";
$V76_REPORT[] = "🔧 AUTO-WIRE: Nouvel intent stub créé → {$stub_name} (status PENDING_APPROVAL)";
}
}
$V76_REPORT[] = "";
$V76_REPORT[] = str_repeat('─', 50);
$V76_REPORT[] = "✅ Exécution multi-agent terminée · " . count($V76_DONE) . "/" . count($V76_SELECTED) . " retours obtenus";
/* Output as chat response (JSON for router) */
header('Content-Type: application/json');
echo json_encode([
'provider' => 'wevia-v76-multi-agent',
'content' => implode("\n", $V76_REPORT),
'tool' => 'multi-agent-orchestrator',
'agents_executed' => array_keys($V76_SELECTED),
'agents_returned' => array_keys($V76_DONE),
'task' => $V76_TASK,
'n_requested' => $V76_N,
'autowire_stub_created' => !empty($V76_UNMATCHED_REASON),
'workdir' => $V76_WORKDIR
], JSON_UNESCAPED_UNICODE);
exit;