mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
387 lines
13 KiB
Markdown
387 lines
13 KiB
Markdown
# Overseer v2 Detailed Architecture
|
|
|
|
## Purpose
|
|
This document is the durable architecture spec for Overseer v2 so design intent is not lost across chats, sessions, or contributors.
|
|
|
|
Overseer v2 is a **new, separate service** focused on controlling the room/experience, not just commenting on rover activity.
|
|
|
|
## Non-Goals
|
|
- Do not replace or delete `llmCommentaryService`.
|
|
- Do not run two LLM overseer systems simultaneously in normal operation.
|
|
- Do not rely on giant unbounded context windows.
|
|
|
|
## Legacy Service Position
|
|
- Keep `llmCommentaryService` as a preserved legacy service.
|
|
- Keep it disable-able via config.
|
|
- New v2 service should be the primary runtime when enabled.
|
|
- Legacy service remains available for fallback/testing.
|
|
|
|
## Core Product Direction
|
|
Overseer v2 is "control-first":
|
|
- It can act on room systems and chat.
|
|
- Chat is one capability, not the whole job.
|
|
- It should feel ominous/playful and intentional, not spammy.
|
|
|
|
High-level behavior goals:
|
|
- Not overbearing.
|
|
- Does things when it can make moments interesting.
|
|
- Always responds when directly addressed.
|
|
- Does not always obey.
|
|
- Does not ramble.
|
|
|
|
## Primary Inputs Overseer Must Understand
|
|
Overseer must see all three lanes every decision cycle:
|
|
|
|
1. People activity
|
|
- Who is active now.
|
|
- Who is driving what.
|
|
- Activity bursts vs quiet periods.
|
|
- Direct addresses toward Overseer.
|
|
|
|
2. Conversation context
|
|
- **Actual recent chat conversation** (human + bot messages), not only tags.
|
|
- Last N human messages (minimum 5).
|
|
- Last 1-2 bot messages for anti-repeat context.
|
|
|
|
3. World/control state
|
|
- Rover states/events.
|
|
- Lift state/cooldowns.
|
|
- Neato state/cooldowns.
|
|
- Home Assistant entity states/availability.
|
|
- Safety/lock policy state.
|
|
|
|
## Runtime Model Strategy (Hardware-Aware)
|
|
Given modest hardware and ~30s generation on `mistral-small:24b` today:
|
|
|
|
- Use one LLM system only.
|
|
- Use deterministic program logic gate before LLM calls.
|
|
- Keep LLM calls sparse and meaningful.
|
|
- Keep context bounded and compact.
|
|
|
|
### Why Not Tiny-LLM Gate
|
|
Default gate should be deterministic logic, not another LLM:
|
|
- less latency
|
|
- less complexity
|
|
- fewer failure modes
|
|
- lower cumulative compute
|
|
|
|
A tiny LLM gate can be reconsidered later only for borderline cases.
|
|
|
|
## Continuous Loop Design (Never Stops)
|
|
System runs continuously but in two stages:
|
|
|
|
1. Fast gate loop (1-3s)
|
|
- No LLM call.
|
|
- Evaluates trigger conditions.
|
|
- If no trigger: continue.
|
|
|
|
2. LLM decision loop (on trigger or heartbeat)
|
|
- Triggered by gate or max-interval heartbeat.
|
|
- Builds bounded conversational context + state update.
|
|
- Model decides and optionally calls tools.
|
|
|
|
### Trigger Examples
|
|
- Direct address to Overseer in chat.
|
|
- Chat burst / topic change / challenge language.
|
|
- High-signal world event (dock/undock/hazard/control transition).
|
|
- Heartbeat timeout reached (for ambient presence).
|
|
|
|
## Decision Modes
|
|
The orchestrator should normalize model behavior to one of:
|
|
- `SKIP`
|
|
- `CHAT`
|
|
- `ACTION`
|
|
- `ACTION+CHAT`
|
|
|
|
Meaning:
|
|
- `SKIP`: no output, no tool call.
|
|
- `CHAT`: post chat only.
|
|
- `ACTION`: run tool(s) only.
|
|
- `ACTION+CHAT`: run tool(s) and post chat.
|
|
|
|
## Tool Output vs Chat Output
|
|
These are separate channels by design:
|
|
- Tool calls are structured actions.
|
|
- Chat text is explicit messaging output.
|
|
- A tool call does not automatically produce chat.
|
|
|
|
This separation is required to avoid chat spam and keep control behavior deliberate.
|
|
|
|
## Conversational Context Format (Important)
|
|
Overseer should use:
|
|
- Stable `system` prompt with identity/rules/policy.
|
|
- Real rolling transcript (users + overseer).
|
|
- A compact `STATE_UPDATE` message each decision cycle.
|
|
|
|
### Bounded Window Rules
|
|
To protect latency:
|
|
- Keep rolling window bounded (for example 20-40 recent turns, or last 5-10 min).
|
|
- Always include last 5 human messages minimum.
|
|
- Include last 1-2 bot messages.
|
|
- Drop/summarize older content.
|
|
|
|
## Tool Availability Contract
|
|
Critical refinement: do not advertise unusable tools as available.
|
|
|
|
Each cycle split into:
|
|
1. `available_tools`
|
|
- callable now
|
|
|
|
2. `blocked_tools`
|
|
- not callable now with reason:
|
|
- cooldown
|
|
- busy
|
|
- unavailable/offline
|
|
- policy lock
|
|
- rate limit
|
|
|
|
Example:
|
|
- If Neato is cooling down, remove `neato_*` from `available_tools` and list under `blocked_tools` with remaining seconds.
|
|
|
|
This prevents wasted model turns and impossible tool attempts.
|
|
|
|
## Initial Tool Surface
|
|
Planned callable tools (subject to runtime availability):
|
|
- `chat_say(text)`
|
|
- `lift_up()` / `lift_down()`
|
|
- `neato_start()` / `neato_send_home()` / `neato_locate()` / `neato_clear_errors()`
|
|
- `ha_set_entity(entity_id, state)` for configured controllable entities
|
|
- memory tools:
|
|
- `memory_read()`
|
|
- `memory_write(slot, text)` where slot ∈ {1,2,3}
|
|
|
|
Optional later:
|
|
- controlled button-box count adjustment tool (strictly bounded and auditable)
|
|
|
|
## Persistent Memory (Tiny, Explicit)
|
|
Use tiny explicit memory store:
|
|
- exactly 3 slots
|
|
- string lines only
|
|
- explicit read/write via tools
|
|
- no autonomous unbounded memory growth
|
|
|
|
Model decides which slot to replace when writing.
|
|
|
|
|
|
|
|
## Mode and Policy Locks (Tool Disabling Rules)
|
|
Overseer tool availability must respect global site mode and room-light policy locks.
|
|
|
|
Hard requirements:
|
|
- When site mode is `admin` or `lockdown`, disable Overseer action tools for:
|
|
- Neato controls (`neato_*`)
|
|
- Lift controls (`lift_*`)
|
|
- Room controls (`ha_set_entity` and related room-light toggles)
|
|
- When room lights are locked on by policy, disable room control tools even if site mode is otherwise open.
|
|
|
|
How this must appear to the model:
|
|
- Disabled tools must be removed from `available_tools`.
|
|
- Disabled tools must be present in `blocked_tools` with clear reason codes.
|
|
|
|
Recommended reason codes:
|
|
- `policy_lock:site_mode_admin`
|
|
- `policy_lock:site_mode_lockdown`
|
|
- `policy_lock:lights_locked_on`
|
|
|
|
Execution-time enforcement:
|
|
- Executor must re-check these locks immediately before executing any tool call.
|
|
- If a lock changed after context build, block execution and record a blocked action event.
|
|
|
|
## Safety and Control Guardrails
|
|
Must-have guardrails before enabling actions:
|
|
|
|
1. Hard allowlist
|
|
- Only approved tool names callable.
|
|
|
|
2. Tool-specific cooldowns
|
|
- e.g., lift/neato/HA each with independent cooldown rules.
|
|
|
|
3. Global action budget
|
|
- max actions per minute (or window).
|
|
|
|
4. Policy lock checks
|
|
- block actions during admin lock/safety states.
|
|
|
|
5. Execution-time validation
|
|
- arguments validated before invoking any control service.
|
|
|
|
6. Logging + audit trail
|
|
- every attempted/blocked/executed tool call recorded.
|
|
|
|
## Admin Debug UI Requirement
|
|
This is a core requirement.
|
|
|
|
Need a dedicated v2 admin panel that shows:
|
|
- enabled/running status
|
|
- loop phase (`idle`, `gate_check`, `awaiting_model`, `tool_exec`, `posted`, `failed`)
|
|
- last trigger reason
|
|
- last state-update payload snapshot
|
|
- model input summary
|
|
- model raw output + normalized decision mode
|
|
- tool calls attempted/executed/blocked with reasons
|
|
- cooldowns and remaining times
|
|
- recent action history
|
|
- last error/failure
|
|
|
|
### UI Switching Behavior
|
|
- If v2 enabled: show v2 panel.
|
|
- If legacy enabled: show legacy panel.
|
|
- If neither enabled: show disabled state.
|
|
|
|
Define a separate v2 status schema (do not overload legacy status object).
|
|
|
|
## Chat System Plumbing Changes
|
|
Keep the existing chat/Discord integration path and extend it minimally.
|
|
|
|
Decisions:
|
|
- Keep current internal bus-based chat flow as the canonical path (`chat:message` / `chat:typing`).
|
|
- Do not introduce a second parallel bridge path for v2.
|
|
- Add `bot: true|false` on chat message payloads.
|
|
- Overseer v2 must publish chat output through the same existing chat pipeline so Discord relay continues to work automatically.
|
|
- Keep legacy compatibility while migrating.
|
|
|
|
Goal:
|
|
- Any service can emit chat messages through one standardized path, with bot identity represented by `bot: true`.
|
|
|
|
## Example Message Topology Per LLM Decision
|
|
1. `system`: stable Overseer identity/rules/style/tool policy
|
|
2. `user`: compact `STATE_UPDATE`
|
|
3. transcript turns: recent chat + overseer messages
|
|
4. tool availability block (`available_tools`, `blocked_tools`)
|
|
5. model output: decision/tool calls/chat
|
|
6. executor: validates, executes tools, posts chat if applicable
|
|
7. append tool results and continue loop
|
|
|
|
## Implementation Phases
|
|
|
|
### Phase 0: Scaffolding
|
|
- Create new service module (suggested name: `overseerControlService`).
|
|
- Add config section and runtime enable switch.
|
|
- Keep legacy untouched.
|
|
|
|
### Phase 1: Observe-Only
|
|
- Build gate loop and context builder.
|
|
- Run model decisions with no real actions (dry-run tools).
|
|
- Log what would have happened.
|
|
|
|
### Phase 2: Chat + Safe Tools
|
|
- Enable chat output and lowest-risk tools.
|
|
- Verify anti-spam and direct-address behavior.
|
|
|
|
### Phase 3: Full Control Surface
|
|
- Enable lift/neato/HA with strict cooldowns and budgets.
|
|
- Add memory tool operations.
|
|
|
|
### Phase 4: Tuning
|
|
- Tune gate thresholds, cadence, cooldowns.
|
|
- Tune prompt style and anti-repeat behavior.
|
|
- Evaluate model alternatives only after gating/cadence tuning.
|
|
|
|
## Metrics To Track
|
|
- LLM calls per minute
|
|
- average LLM latency
|
|
- skipped vs acted decision ratio
|
|
- stale response rate (acted too late)
|
|
- blocked tool call rate and reasons
|
|
- action frequency per tool
|
|
- chat message frequency per minute
|
|
- repeated-line rate
|
|
|
|
## Rollback Plan
|
|
- Single config flip disables v2.
|
|
- Legacy overseer can be re-enabled without code rollback.
|
|
- Keep migration steps isolated and reversible.
|
|
|
|
## Practical Defaults (Starting Point)
|
|
- fast gate loop: 2s
|
|
- heartbeat model run: 30s
|
|
- min human chat context: 5 latest messages
|
|
- include latest bot messages: 2
|
|
- max tool calls per decision: 1 (start conservative)
|
|
- global action budget: low (start strict)
|
|
|
|
## Open Questions (Track During Build)
|
|
- Exact per-tool cooldown values for lift/neato/HA.
|
|
- Whether button-box count adjustment is worth enabling at all.
|
|
- Which model gives best tool reliability per watt on your machine.
|
|
- Ideal transcript window (turn-count vs time-window).
|
|
- Whether to include lightweight topic tags in addition to raw messages.
|
|
|
|
## One-Line Summary
|
|
Overseer v2 should be a separate, always-running, control-first orchestrator that uses deterministic gating + bounded real conversation context + dynamically available tools, with strict guardrails and a first-class debug UI.
|
|
|
|
## Naming Configuration Requirement
|
|
Overseer name must be configurable from server config.
|
|
|
|
Requirements:
|
|
- Add a config field for Overseer display/invocation name (for example under the v2 service config).
|
|
- The configured name must propagate everywhere it matters:
|
|
- in-chat bot nickname
|
|
- system prompt identity text
|
|
- direct-address recognition rules
|
|
- Prompt file should remain editable by non-code changes, so use a placeholder token in prompt text (for example `<NAME>`) and replace it at runtime.
|
|
- The prompt should still support fallback behavior if name config is missing (default name).
|
|
|
|
Implementation intent notes (future):
|
|
- Keep a single source of truth for name resolution (config + default).
|
|
- Avoid scattering hardcoded names in service code.
|
|
- If legacy and v2 coexist in codebase, ensure each service can use configured naming without breaking compatibility.
|
|
|
|
## Canonical "What the Bot Sees" Example
|
|
Use this as the concrete reference for message formatting per decision cycle.
|
|
|
|
```txt
|
|
[system]
|
|
You are <NAME>, a control-first room AI.
|
|
- You may CHAT and/or call tools.
|
|
- Respect safety/cooldowns/allowlists.
|
|
- Prefer restraint; avoid spam.
|
|
- Respond when directly addressed.
|
|
- If nothing meaningful changed, SKIP.
|
|
|
|
[user]
|
|
STATE_UPDATE
|
|
time: 2026-05-02T22:14:10-04:00
|
|
trigger: chat_burst
|
|
cooldowns: lift=ready, neato=12s, ha=ready
|
|
rovers:
|
|
- rover1: driving, driver=alex, docked=false
|
|
- rover2: driving, driver=sam, docked=false
|
|
- rover3: docked=true, charging=true
|
|
lift: down, busy=false
|
|
neato: connected=true, state=idle, charging=true
|
|
lights:
|
|
- shelf=on, bench=off, corner=on, color=purple, aux1=off, aux2=on
|
|
|
|
[user] alex: overseer you watching this drift?
|
|
[user] sam: dont kill my lights again
|
|
[assistant] <NAME>: Concrete lane stays lit. Earn the darkness.
|
|
[user] alex: i dare you to make this harder
|
|
[user] sam: bot pick a side
|
|
[assistant] <NAME>: Lift is staying put. Chaos has standards.
|
|
[user] alex: sam is about to bonk the dock
|
|
|
|
[user]
|
|
available_tools:
|
|
- chat_say(text)
|
|
- lift_up()
|
|
- lift_down()
|
|
- ha_set_entity(entity_id, state)
|
|
- memory_read()
|
|
- memory_write(slot, text)
|
|
|
|
[user]
|
|
blocked_tools:
|
|
- neato_start() reason=cooldown remaining=12s
|
|
- neato_send_home() reason=cooldown remaining=12s
|
|
- neato_locate() reason=cooldown remaining=12s
|
|
- neato_clear_errors() reason=cooldown remaining=12s
|
|
```
|
|
|
|
Notes:
|
|
- The conversation transcript is real recent chat (humans + bot), bounded by window rules.
|
|
- `STATE_UPDATE` is compact current truth, regenerated each decision cycle.
|
|
- Tools must be split into `available_tools` and `blocked_tools`; unavailable tools are not advertised as callable.
|
|
- `<NAME>` is runtime-substituted from config.
|
|
|