124 lines
4.4 KiB
Markdown
124 lines
4.4 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
# 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`
|
|
|
|
```json
|
|
// 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 normally
|
|
- `0.30 <= confidence < 0.65` → store with `council-split` tag for later review
|
|
- `council-flagged` → store in a quarantine bucket or reject entirely
|
|
- `council-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:
|
|
|
|
```elisp
|
|
;; 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:
|
|
|
|
```c
|
|
// 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 model
|
|
- `dolphin3:8b` — uncensored general-purpose model for independent perspective
|
|
- `neuron-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
|
|
|
|
```bash
|
|
# 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"}'
|
|
```
|