Prechádzať zdrojové kódy

Updated documentation

Efren Yevale Varela 1 mesiac pred
rodič
commit
824704ce84
3 zmenil súbory, kde vykonal 27 pridanie a 22 odobranie
  1. 6 3
      AGENTS.md
  2. 18 16
      docs/ASSISTANT.md
  3. 3 3
      docs/DEVELOPMENT.md

+ 6 - 3
AGENTS.md

@@ -65,9 +65,12 @@ publish-only.
 - `[triage]` is the display-only signal panel, off by default (`enabled = false`). Its
   `questions_file` (`questions.json` in the cwd) is private user state and is gitignored, like
   `config.toml`. `dump_io = true` writes one JSON per triage under `triage/` (also gitignored);
-  a coalesced brief update writes two files. The brief engine itself is driven by
-  `[llm].triage_system_prompt` (plain text, no JSON) and gated by the fail-open
-  `substantive_update` signal.
+  a coalesced brief update writes two files. The brief engine is driven by
+  `[llm].triage_system_prompt` and returns two plain-text sections (`NEW FACTS:` + `BRIEF:`);
+  code owns the append-only `FactLedger` (dedupe/eviction/render, `ledger_max_facts`), so the
+  model never rewrites recorded facts. The optional fail-open `substantive_update` gate is
+  `brief_gate_enabled` (default `true`); `periodic_s` re-runs triage every N seconds while
+  speech accumulates without a settled turn.
 - `[speaker]` is opt-in per-utterance speaker identification, off by default, backed by the
   `SpeakerConfig` dataclass (12th known section; 14 keys: `enabled`, `model_path`, `provider`,
   `threshold_id`, `threshold_new`, `min_enroll_s`, `rms_floor`, `margin`, `mint_cooldown_s`,

+ 18 - 16
docs/ASSISTANT.md

@@ -138,11 +138,11 @@ the note is UI-only and never sent to the model.
 A reply cut off by the output limit (`finish_reason: "length"`) surfaces as
 `error{code:"truncated"}` instead of disappearing silently; an empty truncated turn is not stored.
 
-Each engine keeps its own history and compacts independently: the chat engine and the brief
-engine each inject their own rolling summary as a second `system` message. The brief engine
-uses `[llm].brief_compaction_system_prompt` as its summarizer. A compaction shifts that
-engine's prompt prefix, so the server's prefix cache is invalidated once for it, and the
-summarizer call can hold the shared LLM lane for up to `compaction_timeout_s`.
+The chat engine keeps its own history and compacts it. The brief engine does **not**: it is
+stateless per call (a static system prompt plus one user turn carrying the code-owned fact
+ledger, the volatile narrative, and the new segment), so it has no history to compact, its
+prompt stays flat, and `[llm].brief_compaction_system_prompt` does not apply to it. Code owns
+the durable facts (see [TRIAGE.md](TRIAGE.md)).
 
 ## Inference queue
 
@@ -173,17 +173,19 @@ the lane classifies the last `window` transcribed utterances from the STT stream
 history). Results flow through a `signals -> content` two-stage exchange: the signals stage always
 arrives first (per-category confidences), and, when `extract_enabled` is true and the fail-open
 gate on `substantive_update` passes, the assistant queues one `brief-update` job on the existing LLM
-lane. That job runs a dedicated brief conversation (`[llm].triage_system_prompt`) that folds the
-pending speaker-labeled deltas plus the current signal words under a `Focus:` header into a
-plain-text brief, which arrives as the content stage and is rendered below the signals; on a Laya
-error it still runs with the deltas only. The job is token-guarded and single-outstanding: while one
-is in flight further turns are refused and their deltas stay buffered (bounded by
-`brief_pending_char_budget`), and a refusal, empty reply, or exception re-queues those deltas and
-clears the flag so the next turn retries. A successful update coalesces a flush when deltas remain.
-The brief engine's own history compacts independently with
-`[llm].brief_compaction_system_prompt`; a compaction resets its prompt prefix and can hold the shared
-LLM lane up to `compaction_timeout_s`. The deployment thresholds (`escalate_threshold`,
-`choice_min_confidence`) only drive the per-item `⚠` marker, not the gate and not the brief.
+lane. That job runs a stateless brief engine (`[llm].triage_system_prompt`) that sends one user turn
+carrying the code-owned fact ledger, the volatile narrative, and the pending speaker-labeled deltas
+plus the current signal words under a `Focus:` header; the model returns `NEW FACTS:` (only
+genuinely-new durable facts) and `BRIEF:` (the narrative), code appends facts to the ledger with
+conservative dedupe and renders the final brief, which arrives as the content stage and is rendered
+below the signals. On a Laya error it still runs with the deltas only. The job is token-guarded and
+single-outstanding: while one is in flight further turns are refused and their deltas stay buffered
+(bounded by `brief_pending_char_budget`), and a refusal, empty reply, or exception re-queues those
+deltas and clears the flag so the next turn retries. A successful update coalesces a flush when
+deltas remain. Code owns the durable facts, so a recorded fact can only disappear via a logged cap
+eviction (`ledger_max_facts`), never because the model dropped it. The deployment thresholds
+(`escalate_threshold`, `choice_min_confidence`) only drive the per-item `⚠` marker, not the gate
+and not the brief.
 The panel renders the last-triage clock (transcript time format/color) and both stages; it is
 display-only and triggers no modes, commands, or tools. Leaving chat mode does not clear the panel or
 its buffer; an explicit `clear` does (it also truncates the durable brief file). With

+ 3 - 3
docs/DEVELOPMENT.md

@@ -20,7 +20,7 @@ Tests cover:
 - **Provisioning**: `setup`/`ensure`/`teardown`, orphan-name cleanup, and `clear` idempotency
 - **Event bus**: envelope codec, ring replay, subscriber fan-out
 - **Assistant**: mode machine, command readiness/timeouts, tool schema validation, LLM client/history trimming, audit log
-- **Triage & brief**: Laya lane, questions-file validation, signal normalization, the fail-open `substantive_update` gate, single-outstanding `brief-update` coalescing, and the durable brief file
+- **Triage & brief**: Laya lane, questions-file validation, signal normalization, the fail-open `substantive_update` gate, the periodic `periodic_s` accumulating-speech trigger, single-outstanding `brief-update` coalescing, the code-owned append-only `FactLedger` (dedupe/eviction/render), the stateless brief engine, and the durable brief file
 - **Speakers**: prototype matching, mint/confirm/adapt guards, daemon wiring, assistant roster and naming, offline pre-enrollment (`tests/test_speaker_enroll.py`)
 - **Debug**: `DebugSink` JSONL lines, disabled-means-None, emit-throw safety, per-day byte cap, concurrent-emit integrity (`tests/test_debug.py`); assistant lock-free emits via `debug_sink_factory` (`tests/test_assistant_service.py`)
 - **TUI**: pure view-state reducer and control-socket client
@@ -57,9 +57,9 @@ src/stt_daemon/
 │   ├── control.py       - Control socket (viewers, commands, broadcast)
 │   ├── executor.py      - NullExecutor / LlmExecutor (tool loop)
 │   ├── tools.py         - Tool registry + schema validation + get_datetime
-│   ├── llm.py           - llama.cpp client + chat session (block trimming)
+│   ├── llm.py           - llama.cpp client, chat session (block trimming), stateless brief engine
 │   ├── inference.py     - Single-lane LLM queue (chat/command/brief-update)
-│   ├── triage.py        - Triage analyzer (normalize, flag, brief prompt/parse/gate)
+│   ├── triage.py        - Triage analyzer (normalize, flag, ledger prompt/parse) + code-owned FactLedger
 │   ├── laya_lane.py     - In-process Laya lane (loader + single-flight worker)
 │   ├── triage_dump.py   - Opt-in per-triage I/O dump (`dump_io`)
 │   ├── brief_file.py    - Opt-in durable brief snapshot writer (`[brief]`)