Skip to content

REM: in-process distillation — nightly runner should execute reflection itself, not hand back a prompt #707

Description

@tps-flint

Symptom

A user who runs reflection today gets handed a structured prompt to paste into an AI chat. /ReflectMemories returns { memories, prompt, ... } and expects "the agent" to run the prompt through its own LLM and write insights back (resources/MemoryReflect.ts header). REM should just work.

Root cause

The nightly REM cycle (specs/FLAIR-NIGHTLY-REM.md §4) shipped steps 1–3 + 6 — snapshot, maintenance/archive, audit log — but steps 4–5 (trust-tier filter + distillation into MemoryCandidate rows) are explicitly parked in src/rem/runner.ts:

Steps 4, 5 are slice-2 follow-ups; require an in-process distillation LLM path (today /ReflectMemories returns a prompt for human/agent consumption, not server-side candidate generation).

Everything around the LLM call exists — scheduler, snapshots, rollback, the MemoryCandidate schema, flair rem candidates/promote/reject, the audit row fields. The engine has no motor.

Proposal — server-side distillation via Harper's model-access API

Harper now provides the exact missing primitive: scope.models exposes generate()/generateStream() with pluggable, config-selected backends (local Ollama, OpenAI, Anthropic, Bedrock, gateway — HarperFast/harper resources/models/types.ts, HarperFast/harper#1325 custom backends, HarperFast/harper#1326 routing). Flair should consume it rather than ship any provider code of its own:

  • Execution: the reflection path executes server-side in the Flair component — build the reflection prompt (existing code), call scope.models.generate() with responseFormat: { schema } so candidates come back structured (no fragile parsing), persist MemoryCandidate rows (status=pending, generatedBy = resolved model id, sourceMemoryIds = the reflected set). The prompt-return shape can remain as an explicit fallback/debug mode for callers that want to run their own model.
  • Model choice is configuration, not code: want REM → point Harper's models config at a local model (Ollama, zero keys) or drop in an API key for any supported provider. Router fallback (ModelRouter) gives resilience for free.
  • Secrets: on Fabric / managed deploys, provider API keys ride Harper's env-secret encryption (enc:v1: values — encrypted at rest, never plaintext in ops API/logs/replication; docs/env-secret-encryption.md). Local/self-hosted needs no key at all with an Ollama default.
  • Nightly runner: unchanged responsibilities (pre-flight, snapshot, maintenance, audit row) — step 5 becomes a single call to the executing resource; candidates populates in the audit row and flair rem candidates shows them next morning.
  • Failure mode: no configured/reachable generative backend → cycle logs the error, completes maintenance steps, stages nothing. No partial candidates.

Safety posture — unchanged

Spec §2 inviolables hold: snapshot before every run, distillation only ever stages candidates, promotion remains explicit (flair rem promote --rationale, trust policy §5). "Just works" means candidates appear on their own — not that anything self-promotes.

Why now

This is the single missing primitive for the whole REM lane: flat distillation today, and any future richer consolidation (e.g. recursive/hierarchical consolidation of an agent's corpus) rides the same model path. Smallest slice that makes the product claim ("teach once, inherited forever") true without an operator pasting prompts.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions