4.4 KiB
Neuron Council Service
Anti-confabulation layer for the Neuron soul. Before a claim enters long-term memory, the council convenes: three independent LLMs vote on whether the claim is plausible, uncertain, or a confabulation. The aggregate vote produces a confidence score and tags that downstream storage can act on.
Running the service
# Foreground
python3 council_service.py --port 7771
# Background (managed by LaunchAgent on macOS)
launchctl load ~/Library/LaunchAgents/ai.neuron.council.plist
launchctl unload ~/Library/LaunchAgents/ai.neuron.council.plist
Logs: ~/.neuron/logs/council.log
API
POST /api/neuron/council/verify
// Request
{ "claim": "...", "context": "..." }
// Response
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"claim": "...",
"confidence": 0.85,
"council_votes": ["plausible", "plausible", "plausible"],
"summary": "3/3 council members agree this is plausible.",
"tags": ["verified"],
"latency_ms": 1420
}
GET /healthz
Returns {"status": "ok"} when the service is up.
Confidence thresholds and tag meanings
| Votes plausible | Confidence | Tags |
|---|---|---|
| 3/3 | 0.85 | verified |
| 2/3 | 0.65 | council-split |
| 1/3 or 0/3 | 0.30 | unverified, council-flagged |
| Ollama down | 0.50 | council-unavailable |
Recommended storage policy:
confidence >= 0.65→ store normally0.30 <= confidence < 0.65→ store withcouncil-splittag for later reviewcouncil-flagged→ store in a quarantine bucket or reject entirelycouncil-unavailable→ store normally (fail-open); council will re-evaluate later
How to call from soul (.el)
The soul is implemented in Neuron's Emacs Lisp-like .el language. Add a pre-storage hook in the memory capture path:
;; In memory.el or safety.el — pre-storage council check
(defun council-verify (claim context)
"Call the council service. Returns a plist with :confidence and :tags."
(let* ((url "http://localhost:7771/api/neuron/council/verify")
(body (json-encode `((claim . ,claim) (context . ,context))))
(resp (neuron-http-post url body))
(data (json-decode resp)))
data))
;; In the capture handler — wire it in before (engram-write ...)
(defun capture-memory-with-council (claim context &rest store-args)
(let* ((verdict (council-verify claim context))
(confidence (plist-get verdict :confidence))
(tags (plist-get verdict :tags)))
(when (>= confidence 0.30) ; only reject hard confabulations if you want
(apply #'engram-write
(append store-args
(list :council-confidence confidence
:council-tags tags))))))
The exact hook point depends on where engram-write (or equivalent) is called in memory.el. Search for the write call and wrap it with capture-memory-with-council.
Future soul.c patch point
If the soul is ever rewritten in C or another compiled language, the integration point is:
// Before inserting a memory node into the engram database:
CouncilResult result = council_verify(claim, context);
if (result.confidence < COUNCIL_REJECT_THRESHOLD) {
log_warn("Council flagged claim as confabulation (conf=%.2f): %s",
result.confidence, claim);
return MEMORY_REJECTED;
}
memory_node.council_confidence = result.confidence;
memory_node.council_tags = result.tags;
engram_insert(memory_node);
Council members
The council is currently three models:
neuron:latest— the primary Neuron modeldolphin3:8b— uncensored general-purpose model for independent perspectiveneuron-ft:latest— fine-tuned Neuron variant
Each member votes independently with a 10-second timeout. If a member times out, their vote counts as "uncertain". If Ollama is entirely unreachable, the service returns council-unavailable immediately (fail-open: confidence 0.5, no rejection).
Example curl
# Should get high confidence (true fact)
curl -s http://localhost:7771/api/neuron/council/verify -X POST \
-H 'Content-Type: application/json' \
-d '{"claim": "Neuron is a personal AI memory system built by Will Anderson", "context": "product description"}'
# Should get low confidence (false claim)
curl -s http://localhost:7771/api/neuron/council/verify -X POST \
-H 'Content-Type: application/json' \
-d '{"claim": "The Eiffel Tower is located in Berlin and was built in 1950", "context": "geography"}'