"goal required"]); exit; } $t0 = microtime(true); // PLAN with richer agent diversity $plan_sys = "Tu es un planificateur multi-agent WEVAL. Pour l'objectif donné, génère UN JSON STRICT :\n" . "{\n" . ' "objective": "",' . "\n" . ' "agents": [' . "\n" . ' {"role":"", "task":"", "tool":"", "model_hint":""},' . "\n" . " ...\n" . " ]\n" . "}\n" . "Tools disponibles:\n" . "- llm_fast (Cerebras/Groq rapide)\n" . "- llm_think (Cerebras think raisonnement profond)\n" . "- llm_ollama (local qwen3/llama3.2 souverain)\n" . "- pdf_premium (génère PDF 5 pages)\n" . "- mermaid (diagramme SVG)\n" . "- web_search (recherche web)\n" . "- kb_search (mémoire partagée)\n" . "- calc (calcul)\n" . "Max $max_agents agents · roles variés · tools diversifiés pour parallélisme optimal."; $plan_raw = @file_get_contents("http://127.0.0.1:4000/v1/chat/completions", false, stream_context_create([ "http" => ["method"=>"POST","header"=>"Content-Type: application/json\r\n", "content"=>json_encode(["model"=>"fast","messages"=>[["role"=>"system","content"=>$plan_sys],["role"=>"user","content"=>"Objectif: $goal"]],"max_tokens"=>1000,"temperature"=>0.4]), "timeout"=>25], ])); $pd = @json_decode($plan_raw, true); $pt = $pd["choices"][0]["message"]["content"] ?? ""; $pt = preg_replace('/^```(?:json)?\s*/m', '', $pt); $pt = preg_replace('/\s*```\s*$/m', '', trim($pt)); $plan = @json_decode($pt, true); if (!$plan || !isset($plan["agents"])) { echo json_encode(["error"=>"plan invalid", "raw"=>substr($pt, 0, 400)]); exit; } $plan_ms = round((microtime(true)-$t0)*1000); // EXECUTE $agents = array_slice($plan["agents"], 0, $max_agents); $mh = curl_multi_init(); $handles = []; foreach ($agents as $i => $agent) { $tool = strtolower($agent["tool"] ?? "llm_fast"); $task = $agent["task"] ?? ""; $ch = curl_init(); if ($tool === "llm_ollama") { // Ollama LOCAL sovereign (qwen3:4b fastest) curl_setopt_array($ch, [ CURLOPT_URL => "http://127.0.0.1:11434/api/generate", CURLOPT_POST => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode(["model"=>"qwen3:4b","prompt"=>$task,"stream"=>false,"options"=>["num_predict"=>400]]), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 60, CURLOPT_CONNECTTIMEOUT => 3, ]); } elseif ($tool === "llm_think") { curl_setopt_array($ch, [ CURLOPT_URL => "http://127.0.0.1:4000/v1/chat/completions", CURLOPT_POST => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode(["model"=>"think","messages"=>[["role"=>"user","content"=>$task]],"max_tokens"=>500]), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 45, CURLOPT_CONNECTTIMEOUT => 3, ]); } elseif ($tool === "pdf_premium") { curl_setopt_array($ch, [ CURLOPT_URL => "http://127.0.0.1/api/ambre-tool-pdf-premium.php", CURLOPT_POST => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode(["topic"=>$task]), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 50, CURLOPT_CONNECTTIMEOUT => 3, ]); } elseif ($tool === "mermaid") { curl_setopt_array($ch, [ CURLOPT_URL => "http://127.0.0.1/api/ambre-tool-mermaid.php", CURLOPT_POST => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode(["topic"=>$task]), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 20, CURLOPT_CONNECTTIMEOUT => 3, ]); } elseif ($tool === "web_search") { curl_setopt_array($ch, [ CURLOPT_URL => "http://127.0.0.1/api/ambre-tool-web-search.php", CURLOPT_POST => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode(["query"=>$task]), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_CONNECTTIMEOUT => 3, ]); } elseif ($tool === "kb_search") { curl_setopt_array($ch, [ CURLOPT_URL => "http://127.0.0.1/api/ambre-mermaid-learn.php", CURLOPT_POST => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode(["action"=>"search","query"=>$task]), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, CURLOPT_CONNECTTIMEOUT => 3, ]); } elseif ($tool === "calc") { curl_setopt_array($ch, [ CURLOPT_URL => "http://127.0.0.1/api/ambre-tool-calc.php", CURLOPT_POST => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode(["expression"=>$task]), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, CURLOPT_CONNECTTIMEOUT => 3, ]); } else { // default = llm_fast (Cerebras) curl_setopt_array($ch, [ CURLOPT_URL => "http://127.0.0.1:4000/v1/chat/completions", CURLOPT_POST => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode(["model"=>"fast","messages"=>[["role"=>"user","content"=>$task]],"max_tokens"=>400]), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_CONNECTTIMEOUT => 3, ]); $tool = "llm_fast"; } curl_multi_add_handle($mh, $ch); $handles[$i] = ["ch"=>$ch, "agent"=>$agent, "tool_used"=>$tool, "t0"=>microtime(true)]; } $exec_t0 = microtime(true); $running = null; do { curl_multi_exec($mh, $running); curl_multi_select($mh, 0.5); } while ($running > 0); $results = []; foreach ($handles as $i => $h) { $output = curl_multi_getcontent($h["ch"]); $http = curl_getinfo($h["ch"], CURLINFO_HTTP_CODE); $ems = round((microtime(true)-$h["t0"])*1000); curl_multi_remove_handle($mh, $h["ch"]); curl_close($h["ch"]); $d = @json_decode($output, true); $summary = ""; if ($d) { if (isset($d["mermaid_code"])) $summary = "📊 Mermaid " . strlen($d["mermaid_code"]) . "B · source=" . ($d["source"] ?? "?"); elseif (isset($d["url"])) $summary = "📄 " . $d["url"] . " · " . ($d["size_kb"] ?? "?") . "KB"; elseif (isset($d["choices"][0]["message"]["content"])) $summary = substr($d["choices"][0]["message"]["content"], 0, 350); elseif (isset($d["response"])) $summary = substr($d["response"], 0, 350); elseif (isset($d["answer"])) $summary = substr($d["answer"], 0, 350); elseif (isset($d["result"])) $summary = (string)$d["result"]; else $summary = substr($output, 0, 200); } else $summary = substr($output ?: "empty", 0, 200); $results[] = [ "agent" => $h["agent"]["role"] ?? "agent_$i", "task" => $h["agent"]["task"] ?? "", "tool" => $h["tool_used"], "http" => $http, "elapsed_ms" => $ems, "summary" => $summary, ]; } curl_multi_close($mh); $exec_ms = round((microtime(true)-$exec_t0)*1000); // RECONCILE via Groq (souverain non-Cerebras pour diversité) $synth_input = "Objectif: $goal\n\nRésultats " . count($results) . " agents :\n"; foreach ($results as $r) { $synth_input .= "• " . $r["agent"] . " (tool=" . $r["tool"] . ", " . $r["elapsed_ms"] . "ms): " . substr($r["summary"], 0, 250) . "\n"; } $synth_input .= "\nProduis une synthèse finale en français, structurée, actionnable. Max 300 mots."; $synth_raw = @file_get_contents("http://127.0.0.1:4000/v1/chat/completions", false, stream_context_create([ "http"=>["method"=>"POST","header"=>"Content-Type: application/json\r\n", "content"=>json_encode(["model"=>"fast","messages"=>[["role"=>"user","content"=>$synth_input]],"max_tokens"=>600]), "timeout"=>20], ])); $sd = @json_decode($synth_raw, true); $reconciled = $sd["choices"][0]["message"]["content"] ?? "synthèse échouée"; // Register in learning pool $shared_kb_file = "/opt/wevads/internal-memory/_shared-learning.json"; $shared_kb = @json_decode(@file_get_contents($shared_kb_file), true) ?: []; $shared_kb[] = [ "ts" => time(), "source" => "multiagent-v2", "topic" => $plan["objective"] ?? $goal, "synthesis_preview" => substr($reconciled, 0, 300), "agents" => count($results), ]; if (count($shared_kb) > 500) $shared_kb = array_slice($shared_kb, -500); @file_put_contents($shared_kb_file, json_encode($shared_kb, JSON_UNESCAPED_UNICODE)); echo json_encode([ "ok" => true, "goal" => $goal, "plan" => $plan, "plan_ms" => $plan_ms, "results" => $results, "exec_ms" => $exec_ms, "parallel_speedup" => round(array_sum(array_column($results, "elapsed_ms")) / max($exec_ms, 1), 2), "reconciled" => trim($reconciled), "total_ms" => round((microtime(true)-$t0)*1000), "agents_count" => count($results), "tools_diversity" => count(array_unique(array_column($results, "tool"))), "provider" => "WEVIA Multi-Agent V2 · external IA dispatch · wave-260", "shared_kb_size" => count($shared_kb), ], JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE);