A multi-perspective AI response system built on Ollama. Five specialized models running across the home lab respond to the same prompt — then a larger consensus model synthesizes their answers into a single, cross-validated response. Streamed end-to-end, lives entirely on the tailnet, no cloud round-trips required.
Any one LLM can hallucinate, miss perspectives, or get stuck in a reasoning rut. The fix isn't a bigger model — it's a council of smaller ones, each with a distinct role, queried in parallel, then synthesized. Multiple training backgrounds plus role-specific system prompts produces answers that are cross-validated by construction, not by hope.
Gemma3:27b reads all five responses and writes a unified answer that reconciles agreements, surfaces tensions, and notes where the council disagrees.adi-cortex runs the larger models, a secondary host carries the lighter ones. Nothing leaves the tailnet.Each member is a distinct Ollama model with a role-specific system prompt. The role isn't just flavor — it shapes the prompt the model sees and the kind of answer the consensus engine has to weigh.
adi-cortex:11434.adi-cortex:11434.A PHP proxy sits between the browser and Ollama. The browser doesn't know about model endpoints — it sends a prompt, the proxy fans out to all five members, streams every response back, then calls the consensus endpoint when the council is done.
Two modes selectable from a dropdown. Sequential reads like a conversation; parallel reads like a brief. Same models, same prompts, different feel.
Promise.all(). Each response streams independently into its own panel; total time drops to the slowest model. Use when you want answers, not theatre.Each member receives a role-specific system prompt before the user's question. The prompt is what makes the historian historical and the strategist strategic — the model itself is unaware of the council.
const rolePrompts = {
adi: `You are Gemma3, the primary AI assistant. Provide
comprehensive, well-structured responses with clear
explanations and examples when helpful.`,
qwen: `You are Qwen3, focused on quick, concise insights.
Give clear, direct answers that capture the key
points efficiently.`,
wizardlm: `You are WizardLM, the critical analyst. Question
assumptions, present alternative viewpoints, and
provide constructive criticism.`,
historian: `You are Phi3, specializing in historical context.
Relate topics to historical precedents and trends.`,
strategos: `You are GPT-OSS STRATEGOS, the Council's staff
planner. Turn problems into clear, executable plans
with assumptions, numbered steps, verification
checks, rollback paths, and risks. Prefer commands,
exact paths, and short rationale.`
};
PHP procedural, no frameworks. Streaming over Server-Sent Events — curl on the server side, EventSource on the browser side. Tailscale for access control. Apache binds only to the tailnet interface.
index.php for the UI, ollama_proxy.php for streaming, consensus.php for synthesis. No frameworks, no ORM.adi-cortex; a secondary host carries the smaller ones. Models swap by editing the COUNCIL_MEMBERS array.curl with WRITEFUNCTION + ob_flush() to push tokens as they arrive. Browser reads with EventSource. Stream ends with a sentinel [STREAM_END].All council traffic goes through one PHP file. The browser never talks to Ollama directly — that keeps endpoints, model names, and the tailnet topology server-side.
<?php
// ollama_proxy.php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
$input = json_decode(file_get_contents('php://input'), true);
$endpoint = $input['endpoint'] ?? 'adi-cortex';
$model = $input['model'] ?? 'gemma3:12b';
$prompt = $input['prompt'] ?? '';
$payload = json_encode([
'model' => $model,
'prompt' => $prompt,
'stream' => true
]);
$ch = curl_init("http://{$endpoint}:11434/api/generate");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($ch, $data) {
echo $data;
ob_flush();
flush();
return strlen($data);
});
curl_exec($ch);
echo '[STREAM_END]';
curl_close($ch);
Three files plus a config block. Drop on a LAMP stack with Ollama reachable over the network, edit the COUNCIL_MEMBERS array to match your hosts, and it runs.
PHP proxy, consensus engine, and browser UI — the same code currently running on the lab's tailnet against adi-cortex.