Add human-led conversation learning loop
This commit is contained in:
1 parent
bc42c55555
commit
687abab82b
52 files changed
+2112
-53
No files matched your search
@@ -15,6 +15,7 @@ The most important operational fact is that local and production configuration m
|
||||
| [Product goals and safety](product-and-safety.md) | You need the purpose, product boundaries, eligibility policy, or definition of success. |
|
||||
| [Architecture](architecture.md) | You need the system shape, process lifecycle, dependency boundaries, or repository map. |
|
||||
| [Runtime flows](data-flow.md) | You are changing discovery, screening, candidate delivery, replies, chat sync, media, or realtime updates. |
|
||||
| [Human-led conversation learning](conversation-learning.md) | You are changing attribution, credit, review, retrieval, evaluation, rollout, or framework integration. |
|
||||
| [Service map](services.md) | You need to find the module that owns a responsibility or identify extension seams. |
|
||||
| [Data model](database.md) | You are changing persistence, migrations, account scoping, idempotency, or analytics. |
|
||||
| [Configuration](configuration.md) | You are adding a setting, changing precedence, or diagnosing startup validation. |
|
||||
|
||||
@@ -63,6 +63,8 @@ an administrator credential.
|
||||
| `POST` | `/api/v1/messages` | Send a message to a Reddit user with automatic account selection | admin | yes |
|
||||
| `GET` | `/api/stats` | Get bot statistics | read | no |
|
||||
| `GET` | `/api/analytics/candidate-funnel` | Get durable candidate funnel counts | read | no |
|
||||
| `GET` | `/api/learning` | Inspect attributed learning trajectories and credit | read | no |
|
||||
| `PATCH` | `/api/learning/trajectories/{id}` | Include or exclude a learning trajectory | admin | yes |
|
||||
| `GET` | `/api/activity` | Get recent activity | read | no |
|
||||
| `GET` | `/api/posts/by-author/{username}` | Get processed posts by author | read | no |
|
||||
| `GET` | `/api/messages` | Get recent sent messages | read | no |
|
||||
|
||||
@@ -144,3 +144,6 @@ docs/ Maintainer and agent documentation
|
||||
## Known design pressure
|
||||
|
||||
`src/bootstrap.ts` remains a large composition root and the legacy single-runtime path duplicates parts of the multi-account delivery flow. Changes should reduce duplication through small factories/policies without hiding operational state or merging account-owned dependencies into process-global singletons.
|
||||
### Learning is derived and revocable
|
||||
|
||||
Raw Matrix history remains authoritative. Event attribution, trajectories, decisions, retrieval selections, and evaluation runs are separate projections so attribution fixes and later safety outcomes can revoke examples without rewriting chat history. The retrieval provider is a local interface rather than a dependency on a hosted observability framework; external tools can consume the audit/evaluation seam later without becoming runtime infrastructure.
|
||||
@@ -135,3 +135,15 @@ When adding a runtime-editable setting, update all of:
|
||||
## Local safe setup
|
||||
|
||||
Install dependencies and run builds/tests without starting the application. If a local backend is explicitly needed, use a dry-run configuration and verify the database path/port before starting. Importing a real session or calling the start control is a separate live-capable action.
|
||||
### `learning`
|
||||
|
||||
- `captureEnabled` controls attribution and extraction; it does not enable generation.
|
||||
- `generationEnabled` permits retrieved examples to affect generation only when `shadowMode` is also false.
|
||||
- `shadowMode` records which examples would have been selected while leaving prompts unchanged.
|
||||
- `maturityHours` delays eligibility so later negative signals can revoke apparent success.
|
||||
- `maxContextExamples`, `humanExampleSlots`, and `autonomousExampleSlots` bound and separate retrieval cohorts; slot totals may not exceed the context maximum.
|
||||
- `maxTurnsPerExample` bounds durable redacted context.
|
||||
- `minimumAttributionConfidence` accepts `exact`, `fingerprint`, or `inferred`.
|
||||
- `policyVersion` versions retrieval/evaluation audit records.
|
||||
|
||||
The safe default is capture enabled, generation disabled, and shadow mode enabled. Enabling injected use is a configuration change and still does not deploy or start the bot.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Human-led conversation learning
|
||||
|
||||
## Purpose
|
||||
|
||||
The loop learns from the decision that caused a useful outcome, not merely from the last AI message before it. Manual interventions are first-class evidence because an operator may repair tone, timing, boundaries, or a media invitation before a recipient shares an image.
|
||||
|
||||
## Pipeline and gates
|
||||
|
||||
```text
|
||||
authoritative Matrix timeline
|
||||
-> event attribution (exact > fingerprint > inferred)
|
||||
-> outcome trajectory and causal credit
|
||||
-> identifier-redacted decision examples
|
||||
-> maturity / review / invalidation gates
|
||||
-> account-scoped, quota-controlled retrieval
|
||||
-> shadow audit or injected prompt context
|
||||
-> offline evaluation record
|
||||
```
|
||||
|
||||
Capture and use are independent. The default configuration captures and audits retrieval in shadow mode, but cannot affect generated replies. Injection requires both `generationEnabled: true` and `shadowMode: false`.
|
||||
|
||||
## Attribution and credit
|
||||
|
||||
Authoritative Matrix event IDs link openers, automated claims, dashboard manual sends, and exchange-library sends. Fingerprints support legacy backfill only. An unmatched outbound event is human-owned for the automation safety boundary, but inferred learning is held back when exact attribution is required.
|
||||
|
||||
Credit classes are:
|
||||
|
||||
- `human-created-success`: the human introduced the successful decision.
|
||||
- `human-improved-success`: AI attempted the step and the human replaced or improved it.
|
||||
- `human-completed-ai-trajectory`: the human intervention helped complete an AI-started exchange without directly making the media ask.
|
||||
- `autonomous-success`: the bounded automated policy made the attributed ask.
|
||||
- `recipient-initiated-media`: media arrived without an attributed ask.
|
||||
- `uncertain`: provenance is insufficient for causal use.
|
||||
|
||||
Image receipt does not override a decline, boundary, or upsell outcome. Those outcomes invalidate the trajectory and all derived decisions. Operator exclusion is sticky across re-sync; explicit inclusion can reverse review/exclusion, but cannot resurrect an invalidated safety outcome.
|
||||
|
||||
## Retrieval and evaluation
|
||||
|
||||
Retrieval stays within the owning account, excludes the current room, filters confidence, prefers the same policy/stage/community, and fills separate human and autonomous quotas. Stored context is bounded and redacted. The generation ledger stores selected decision IDs, mode, policy version, and an optional prompt hash—not prompt text.
|
||||
|
||||
`npm run learning:eval -- --db <path> --policy <version>` versions the current eligible dataset and enforces hard gates for leaked identifiers, invalid decision states, and empty outputs. Evaluation is deliberately local and deterministic. More subjective pairwise or model-graded evaluation can be added behind the same saved-run contract.
|
||||
|
||||
## Infrastructure and framework boundary
|
||||
|
||||
No new Dokploy service is required. SQLite, the existing backend, Matrix synchronization, OpenRouter client, and React dashboard are sufficient. Deployment needs only the normal application image plus a database backup before the migration; it does not require a vector database, queue, object store, or hosted tracing system.
|
||||
|
||||
The repository/provider/evaluation interfaces are the framework seams. A hosted platform such as Langfuse or an experiment tool can later receive generation and evaluation records, and DSPy-style optimization or model fine-tuning can consume exported reviewed examples. None is placed in the online reply path until volume, evaluation quality, and operational need justify the extra infrastructure.
|
||||
|
||||
## Rollout
|
||||
|
||||
1. Deploy with capture enabled, generation disabled, and shadow mode enabled.
|
||||
2. Run a full chat sync to backfill authoritative histories.
|
||||
3. Review inferred and fingerprint-attributed trajectories in `/learning`.
|
||||
4. Let examples mature and run the offline evaluation against a backup/non-production copy.
|
||||
5. Compare human, autonomous, and recipient-initiated cohorts; do not blend them into the opener strategy metric.
|
||||
6. Enable generation while retaining shadow mode to exercise selection/auditing.
|
||||
7. Only after review and evaluation, turn shadow mode off for a small account/policy cohort; rollback is setting it true again.
|
||||
|
||||
Starting, restarting, deploying, or enabling the live bot remains a separate explicit operational action.
|
||||
@@ -137,3 +137,17 @@ Use three categories consistently:
|
||||
- Human-required: policy or safety cannot continue autonomously; stop automation and surface an attention item.
|
||||
|
||||
Avoid converting errors into generic skips. Operational diagnosis depends on preserving which stage failed and why.
|
||||
## Human learning loop
|
||||
|
||||
After a room has an authoritative Matrix timeline, `ChatService` records the canonical response outcome and invokes the account-scoped learning observer. The observer:
|
||||
|
||||
1. reconciles opener, automation-claim, exchange-library, manual-dashboard, recipient, and legacy events;
|
||||
2. stores text-minimising event provenance with an attribution confidence;
|
||||
3. finds received-image outcomes and the manual decisions that preceded them;
|
||||
4. assigns human-created, human-improved, human-completed, autonomous, recipient-initiated, or uncertain credit;
|
||||
5. stores bounded, identifier-redacted decision context; and
|
||||
6. leaves new trajectories provisional until the maturity window passes.
|
||||
|
||||
Full chat synchronization naturally backfills older rooms. Upserts are keyed by account, room, and outcome event, so the pass is repeatable. A later negative canonical outcome invalidates both the trajectory and every derived decision.
|
||||
|
||||
Retrieval is account-scoped and excludes the current conversation. It uses explicit human and autonomous quotas, same-policy/stage preference, confidence filtering, and a generation audit ledger. With the default configuration it runs in shadow mode: selected IDs are recorded but examples are not put into the prompt.
|
||||
@@ -120,3 +120,12 @@ Do not edit a live SQLite file by hand. Back it up, stop writers when required,
|
||||
## Backup and recovery
|
||||
|
||||
For a running WAL database, use SQLite's online backup mechanism or stop the container before copying the database plus `-wal`/`-shm` state. A full production backup should include the entire persistent volume because sessions and media live beside the database. Validate that the backup can be opened and contains expected tables before any destructive reset.
|
||||
### Conversation learning
|
||||
|
||||
- `conversation_events` stores durable Matrix provenance and only a SHA-256 digest of event text.
|
||||
- `conversation_learning_trajectories` stores revocable outcome attribution, maturity, confidence, and credit class.
|
||||
- `conversation_learning_decisions` stores bounded redacted contexts and the operator decision that led toward an outcome.
|
||||
- `learning_generation_context` audits shadow or injected retrieval without storing the generated prompt.
|
||||
- `learning_evaluation_runs` versions offline hard-gate results.
|
||||
|
||||
`sent_messages.matrix_event_id` and `conversation_automation_claims.matrix_event_id` provide exact opener/reply attribution. `media_library_sends.initiated_by` distinguishes operator library shares from automated exchanges. `sent_messages.image_outcome_credit` is a projection for analytics; the trajectory tables remain authoritative.
|
||||
@@ -122,3 +122,5 @@ Start with the durable state transition and idempotency rule. Test rejection/ret
|
||||
### Add dashboard state
|
||||
|
||||
Define the shared schema first, add a typed query/mutation, keep route/filter state in the URL where useful, invalidate snapshots from relevant SSE events, and verify loading/error/stale/accessibility states.
|
||||
- `npm run learning:eval -- --db <sqlite-path> --policy <version>` runs the offline learning hard gates against eligible redacted decisions and persists a versioned evaluation result. Use a database backup or non-production copy when evaluating production-derived data.
|
||||
AI request logs contain prompt length and SHA-256 only. Do not restore full prompt logging: conversation prompts can contain private messages and retrieved learning context.
|
||||
@@ -120,3 +120,5 @@ Run focused `npx vitest run tests/dashboard/<file>`, then `npm run dashboard:typ
|
||||
5. Map relevant realtime events to cache updates/invalidations.
|
||||
6. Add component/realtime/contract coverage.
|
||||
7. Update `api.md` when the operation catalog changed and update this or the relevant product/flow document.
|
||||
- `/learning` shows human/autonomous credit totals, confidence, maturity and review state. Operators can include or exclude non-invalidated trajectories; raw private messages and media are intentionally absent.
|
||||
Learning uses `GET /api/learning` and `PATCH /api/learning/trajectories/:id`. Shared dashboard schemas live in `packages/api-contracts/src/learning.ts`; the server catalogue mirrors the boundary schemas for generated OpenAPI documentation.
|
||||
@@ -115,3 +115,10 @@ Read-only inspection, builds, tests, and documentation work do not grant any of
|
||||
Treat these as sensitive: `.env`, Reddit cookies, exported session JSON, Matrix access tokens, dashboard/agent tokens, the SQLite database, browser profiles, conversation text, usernames, received private media, operator exchange media, and logs that may contain request metadata.
|
||||
|
||||
Never commit or reproduce them in documentation, fixtures, logs, issue text, or tool output. Prefer redacted structural evidence. Production backups contain account and private conversation data and need the same protection as the live volume.
|
||||
### Human-led conversation learning
|
||||
|
||||
A received image is an outcome, not proof that the preceding AI action caused it. The learning loop therefore attributes every Matrix event before extracting an example and gives manual interventions their own credit classes: human-created, human-improved, and human-completed AI trajectories. Autonomous and recipient-initiated outcomes remain separate comparison cohorts.
|
||||
|
||||
Only privacy-redacted decision context is retained as learning material. Media bytes, private attachment paths, raw prompts, usernames, URLs, email addresses, and phone numbers are not copied into examples. Exact Matrix event IDs are preferred; fingerprint or inferred histories are held for review according to `learning.minimumAttributionConfidence`.
|
||||
|
||||
Examples mature only after the configured delay. Later declines, boundaries, or upsell classifications revoke the trajectory. An operator can explicitly include or exclude a trajectory in **Learning review**. Generation use is independently gated and defaults to capture-only shadow mode, so collecting evidence cannot silently change live replies.
|
||||
@@ -118,3 +118,4 @@ Authorization is semantic, not just authentication: read and control agent token
|
||||
- `utils/json-safe.ts`: safe serialization for log/event data.
|
||||
|
||||
When a module starts owning two independent reasons to change, extract a narrow collaborator and inject it. Do not solve size alone by moving methods into a generic utility file.
|
||||
Learning responsibilities are deliberately split: `conversation-attribution.ts` reconciles authorship, `conversation-learning-extractor.ts` derives credit and decisions, `conversation-learning-redactor.ts` enforces data minimisation, `conversation-learning-provider.ts` retrieves bounded examples, and `conversation-learning-evaluator.ts` runs offline hard gates. SQLite ownership stays in the event and learning repositories. `ChatService` only calls the narrow observer interface after authoritative synchronization.
|
||||
Reference in new issue
Block a user