bae705aa97
- Add NFC ePassport roadmap (ICAO 9303, eIDAS) - Add TensorFlow.js edge face detection (BlazeFace) - Add structured audit logger (GDPR-compliant) - Risk scoring support Part of KYC Apple Native UX v1.1.0
462 lines
17 KiB
Markdown
462 lines
17 KiB
Markdown
# ESLM-B: RAG/CANON Runtime-Fakta-Injektion — Design
|
||
**Datum:** 2026-06-06
|
||
**Status:** Design + stub (EJ deployad)
|
||
**Utfört av:** Bernt (subagent ESLM-B)
|
||
**Kod-stub:** `/home/bernt/.openclaw/workspace/build/eslm-rag-injection.mjs`
|
||
|
||
---
|
||
|
||
## 0. Verifierat vs Antagande
|
||
|
||
| Symbol | Betydelse |
|
||
|--------|-----------|
|
||
| ✅ VERIFIERAT | Läst ur faktisk körande kod/fil |
|
||
| ⚠️ ANTAGANDE | Logisk slutledning, ej källverifierad |
|
||
|
||
---
|
||
|
||
## 1. NULÄGE — Exakt karta (verifierad mot körande kod)
|
||
|
||
### 1.1 Produktionsserver — var den kör
|
||
|
||
✅ **Tjänst:** `amos-core.service` via systemd
|
||
✅ **Fil:** `/opt/amos/services/amos-core/server.mjs`
|
||
✅ **Port:** 3100 (bind 0.0.0.0)
|
||
✅ **AMOS_ROOT:** `/opt/amos/` (resolve av `services/amos-core/../..`)
|
||
✅ **Node.js:** via `ExecStart=/usr/bin/node --max-old-space-size=2048`
|
||
|
||
### 1.2 Hur systemprompten byggs — chat-flödet
|
||
|
||
Det finns **två chat-endpoints** som båda använder samma systemprompt-logik:
|
||
|
||
#### `/api/aamos/chat` (huvud-AAMOS-chat)
|
||
```
|
||
[request] POST /api/aamos/chat
|
||
↓
|
||
[routes.mjs] /opt/amos/api/aamos/routes.mjs:77
|
||
↓ import getEnrichedPrompt
|
||
[system-prompt.mjs] /opt/amos/api/dynasty-counsel/system-prompt.mjs
|
||
├── getFactsContext() ← HÅRDKODAD fil: wavult-facts.mjs
|
||
└── getToolsContextBlock() + buildPersonaBlock()
|
||
↓
|
||
systemPrompt = getEnrichedPrompt(baseContext) + aamosContext [rad 109]
|
||
↓
|
||
[model-router.mjs] selectModelCompliant() → Qwen3 via AWS Bedrock
|
||
↓
|
||
[qwenStream()] SSE-svar till klient
|
||
```
|
||
|
||
✅ **Verifierat:** `api/aamos/routes.mjs` importerar `getEnrichedPrompt` från `api/dynasty-counsel/system-prompt.mjs`
|
||
✅ **Verifierat:** `getEnrichedPrompt` anropar `getFactsContext()` som läser `wavult-facts.mjs`
|
||
|
||
#### `/api/dynasty-counsel/chat` (juridisk assistent)
|
||
```
|
||
[request] POST /api/dynasty-counsel/chat
|
||
↓
|
||
[routes.mjs] /opt/amos/api/dynasty-counsel/routes.mjs
|
||
↓ const systemPrompt = getEnrichedPrompt(enrichedContext) [rad ~240]
|
||
↓
|
||
[system-prompt.mjs] → getFactsContext() → wavult-facts.mjs
|
||
↓
|
||
[runToolLoop()] med tool-loop.mjs
|
||
↓
|
||
SSE-stream till klient
|
||
```
|
||
|
||
✅ **Verifierat:** Samma `getEnrichedPrompt` används
|
||
✅ **Verifierat:** `WAVULT_FACTS` importeras direkt i routes.mjs rad ~20
|
||
|
||
#### `/api/chat` (generisk Qwen3-endpoint)
|
||
✅ **Verifierat:** `server.mjs` rad 1389 — `app.post('/api/chat', ...)` använder **INTE** `getEnrichedPrompt`. Minimal hårdkodad prompt: `"Du är AAMOS — Wavult Groups AI-plattform (Qwen3 via AWS Bedrock). Svara precist."` — **ingen faktainjektion alls**.
|
||
|
||
### 1.3 Var injiceras getFactsContext()
|
||
|
||
```
|
||
getEnrichedPrompt(context) [system-prompt.mjs]
|
||
├─ BASE_SYSTEM_PROMPT (statisk, ~4KB)
|
||
├─ getFactsContext() ← HÅRDKODAD import: wavult-facts.mjs
|
||
│ └─ formaterar WAVULT_FACTS till ~1KB kontextblock
|
||
├─ getToolsContextBlock() (tool-katalog)
|
||
├─ buildPersonaBlock() (user-specifik persona)
|
||
└─ session context (mode, tenant, user_email...)
|
||
```
|
||
|
||
✅ **Verifierat:** `system-prompt.mjs` rad ~1: `import { getFactsContext } from './knowledge/wavult-facts.mjs';`
|
||
|
||
### 1.4 wavult-facts.mjs — nuläge
|
||
|
||
✅ **Fil:** `/opt/amos/api/dynasty-counsel/knowledge/wavult-facts.mjs`
|
||
✅ **Version:** 1.1.0, uppdaterad 2026-06-06
|
||
✅ **Innehåll:** Hardkodad JavaScript-modul med WAVULT_FACTS objekt
|
||
✅ **Storlek:** ~5KB av produkt/entitets-/teamfakta
|
||
✅ **Backup-filer:** `.bak-canon-20260606-161310`, `.bak-cleanup-20260606-163506`, `.bak-purge` — indikerar nyliga ändringar
|
||
|
||
**Obs:** `wavult-facts.mjs` innehåller AAMOS_CANON-data som har duplicerats manuellt hit. Den är *inte* automatgenererad från CANON.
|
||
|
||
### 1.5 rag.mjs — nuläge
|
||
|
||
✅ **Fil:** `/opt/amos/api/rag.mjs`
|
||
✅ **RAG-index:** `/opt/amos/data/rag_index.json` — byggt 2026-06-06T15:57:39Z
|
||
✅ **Embeddings:** `/opt/amos/data/rag_embeddings.json` — **94/94 chunks har faktiska embeddings**
|
||
✅ **Embedding-modell:** OpenAI `text-embedding-3-small`
|
||
✅ **Retrieval:** Primär semantisk (cosine similarity), fallback BM25
|
||
✅ **Export:** `retrieveAsync()`, `retrieve()`, `formatContext()`, `buildIndex()`
|
||
|
||
**AAMOS_CANON.md är tier-1 i rag.mjs:**
|
||
|
||
```javascript
|
||
// /opt/amos/api/rag.mjs — KNOWLEDGE_SOURCES
|
||
{
|
||
id: 'aamos_canon',
|
||
label: 'AAMOS_CANON.md',
|
||
tier: 1, // ENDA KANONISKA SANNINGSKÄLLAN — Erik-låst 2026-06-06
|
||
path: join(DATA_DIR, 'AAMOS_CANON.md'),
|
||
}
|
||
```
|
||
|
||
✅ **Verifierat:** AAMOS_CANON är tier-1 med 11 chunks i nuvarande index
|
||
|
||
**Kritisk observation:**
|
||
|
||
✅ **VERIFIERAT:** `rag.mjs` används **INTE** av `/api/aamos/chat` eller `/api/dynasty-counsel/chat`.
|
||
Varken `api/aamos/routes.mjs` eller `api/dynasty-counsel/routes.mjs` importerar `rag.mjs` eller anropar `retrieveAsync()`.
|
||
|
||
RAG-systemet existerar och är fullt byggt med embeddings, men är **kopplat bort** från huvud-chat-flödet.
|
||
|
||
### 1.6 Index/embeddings-status per källa
|
||
|
||
| Källa | Tier | Chunks | Embeddings |
|
||
|-------|------|--------|-----------|
|
||
| AAMOS_CANON.md | 1 | 11 | 11/11 ✅ |
|
||
| WAVULT_TRUTH.md | 1 | 17 | 17/17 ✅ |
|
||
| AMOS_KNOWLEDGE.md | 1 | 56 | 56/56 ✅ |
|
||
| AMOS_CONSTITUTION.md | 1 | 10 | 10/10 ✅ |
|
||
| **Totalt** | | **94** | **94/94** |
|
||
|
||
✅ **RAG-indexet är byggt och komplett.**
|
||
|
||
### 1.7 ESLM-modellen
|
||
|
||
⚠️ **ANTAGANDE:** ESLM (v5) = `aamos-eslm` modell på vLLM-endpoint `172.31.36.61:8000`.
|
||
✅ **Verifierat:** `eslm-judge.mjs` refererar till `ESLM_ENDPOINT = 'http://172.31.36.61:8000/v1'` och `ESLM_MODEL = 'aamos-eslm'`.
|
||
⚠️ **Okänt:** Om ESLM-endpointen är live (curl timeout). ESLM kan vara under driftsättning.
|
||
|
||
---
|
||
|
||
## 2. PROBLEMANALYS — Gap mellan nuläge och målarkitektur
|
||
|
||
```
|
||
NULÄGE:
|
||
/api/aamos/chat → getEnrichedPrompt() → wavult-facts.mjs (hårdkodad JS)
|
||
/api/dynasty-counsel/chat → samma
|
||
rag.mjs (med CANON tier-1) → ANVÄNDS INTE av chatten
|
||
ESLM-modell (172.31.36.61:8000) → ingen faktainjektion alls
|
||
|
||
PROBLEM:
|
||
1. wavult-facts.mjs är en kopia av CANON-data, ej härledd från CANON
|
||
2. RAG/CANON-systemet existerar men är frånkopplat
|
||
3. Generell ESLM saknar ALL faktainjektion
|
||
4. /api/chat (generisk) = ingen injektion alls
|
||
```
|
||
|
||
---
|
||
|
||
## 3. INJEKTIONSDESIGN
|
||
|
||
### 3.1 Principer (Claude×Siemens-robust)
|
||
|
||
1. **En källa (CANON):** AAMOS_CANON.md är enda auktoritativa källan. wavult-facts.mjs deprecas eller autogenereras från CANON.
|
||
2. **Intelligent routing:** Klassificera frågan → injicera bara vid behov → generella frågor = ren generell modell.
|
||
3. **Ingen single point of failure:** RAG → statisk CANON-fallback → minimal hardkodad fallback.
|
||
4. **Latency-gräns:** RAG-anrop max 2s (timeout) → chat-svar ska ej fördröjas märkbart.
|
||
5. **Sekretess:** CANON-data injiceras bara i system-rollen, aldrig tillbaka till klienten.
|
||
|
||
### 3.2 Flödesdiagram
|
||
|
||
```
|
||
[User query]
|
||
↓
|
||
classifyQuery(query)
|
||
├─ 'generic' → systemPrompt = basePrompt (ingen injektion)
|
||
└─ 'company'
|
||
↓
|
||
retrieveAsync(query, {topK:5, minScore:0.15})
|
||
├─ OK (chunks > 0) → formatContext() → RAG-block
|
||
└─ FAIL/timeout → getStaticCanonFallback()
|
||
↓
|
||
systemPrompt = CANON-block + '---' + basePrompt
|
||
↓
|
||
[LLM anrop: ESLM eller Qwen3]
|
||
↓
|
||
[Svar till klient]
|
||
```
|
||
|
||
### 3.3 Klassificering — COMPANY vs GENERIC
|
||
|
||
**Company-keywords (komplett lista i kod-stub):**
|
||
- Bolagsnamn: `landvex`, `quixzoom`, `aamos`, `wavult`, `ouroboros`, `vyra`
|
||
- Teamnamn: `erik`, `winston`, `dennis`, `johan`, `bernt`, `sven`, `kjell`, `rufus`
|
||
- Produktspecifikt: `zoomer`, `kontrollintelligens`, `mission`, `uppdrag`, `gecl`, `stammregister`
|
||
- Org-nr: `559141-7042`, `dmcc`
|
||
|
||
**Generic-mönster (ingen injektion):**
|
||
- Frågor om GDPR/MOMS/bokföring som inte rör Wavult specifikt
|
||
- Kodfrågor (JavaScript, Python, SQL)
|
||
- Allmänna "explain X"-frågor
|
||
|
||
**Heuristik:** Svenska frågor med "vi/vår/våra/bolaget" → company
|
||
|
||
### 3.4 buildContextForQuery() — Huvud-API
|
||
|
||
Se kod-stub för komplett implementation. Signatur:
|
||
|
||
```javascript
|
||
/**
|
||
* @param {string} query - Användarens fråga
|
||
* @param {object} opts
|
||
* @returns {Promise<{
|
||
* contextBlock: string, // Injicera i systemPrompt ('' om generic)
|
||
* source: 'rag'|'static'|'none',
|
||
* classification: 'company'|'generic'
|
||
* }>}
|
||
*/
|
||
async function buildContextForQuery(query, { timeoutMs=2000, topK=5, minScore=0.15 } = {})
|
||
```
|
||
|
||
### 3.5 Var hookar den in i chat-flödet
|
||
|
||
**Alternativ A — Express Middleware (rekommenderat):**
|
||
|
||
```javascript
|
||
// I api/aamos/routes.mjs:
|
||
import { ragInjectionMiddleware } from '../aamos/rag-injection.mjs';
|
||
|
||
router.post('/chat', requireAuth, ragInjectionMiddleware, async (req, res) => {
|
||
// req.ragContextBlock sätts av middleware
|
||
const systemPrompt = req.ragContextBlock
|
||
? req.ragContextBlock + '\n\n---\n\n' + getEnrichedPrompt(baseContext) + aamosContext
|
||
: getEnrichedPrompt(baseContext) + aamosContext;
|
||
// ...resten oförändrat...
|
||
});
|
||
```
|
||
|
||
**Alternativ B — Inline i chat-handler:**
|
||
|
||
```javascript
|
||
// I api/aamos/routes.mjs, inuti POST /chat:
|
||
const { buildSystemPromptWithRAG } = await import('../aamos/rag-injection.mjs');
|
||
const { systemPrompt } = await buildSystemPromptWithRAG(
|
||
getEnrichedPrompt(baseContext) + aamosContext,
|
||
lastUserMsg
|
||
);
|
||
```
|
||
|
||
**Alternativ C — I buildSystemPrompt (direkt ersätt getEnrichedPrompt):**
|
||
|
||
```javascript
|
||
// system-prompt.mjs — modifiering av getEnrichedPrompt:
|
||
export async function getEnrichedPromptWithRAG(context = {}, query = '') {
|
||
const { buildContextForQuery } = await import('./rag-injection.mjs');
|
||
const { contextBlock } = await buildContextForQuery(query);
|
||
let out = contextBlock ? contextBlock + '\n\n---\n\n' : '';
|
||
out += BASE_SYSTEM_PROMPT + '\n\n';
|
||
out += getFactsContext({ verbose: false }) + '\n\n'; // behåll som fallback tills CANON är enda källa
|
||
// ...resten...
|
||
return out;
|
||
}
|
||
```
|
||
|
||
**Rekommendation:** Alternativ A (middleware) ger minst kod-ändring och isolerar logiken.
|
||
|
||
### 3.6 ESLM-specifik injektion
|
||
|
||
ESLM-endpointen (`172.31.36.61:8000`) har ingen injektion idag. För ESLM gäller:
|
||
|
||
```javascript
|
||
// Vid anrop till ESLM-endpointen (vLLM OpenAI API):
|
||
const { contextBlock } = await buildContextForQuery(userQuery);
|
||
const systemMessage = contextBlock
|
||
? contextBlock + '\n\nDu är en AI-assistent för Wavult/AAMOS. Svara precist.'
|
||
: 'Du är en AI-assistent. Svara precist.';
|
||
|
||
const response = await fetch('http://172.31.36.61:8000/v1/chat/completions', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
model: 'aamos-eslm',
|
||
messages: [
|
||
{ role: 'system', content: systemMessage },
|
||
...userMessages
|
||
],
|
||
temperature: 0.1,
|
||
max_tokens: 2048,
|
||
}),
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## 4. RISKER + FALLBACK
|
||
|
||
### 4.1 RAG är nere (OpenAI-api, disk, process-crash)
|
||
|
||
**Fallback-kedja (3 nivåer):**
|
||
1. **RAG OK** → semantisk retrieval, CANON tier-1 chunks → bäst
|
||
2. **RAG timeout/fel** → `getStaticCanonFallback()` — läser CANON.md direkt och extraherar bolagsstruktur + produktsektioner + förbjudna fakta (~600 tokens)
|
||
3. **CANON.md oläsbar** → minimal hardkodad sträng i minnet (bolag/produkt/team/regler)
|
||
|
||
**Timeout:** 2000ms (konfigurerbart via `opts.timeoutMs`). RAG-fel är aldrig fatala — chatten fortsätter.
|
||
|
||
### 4.2 Fakta hamnar i fel kontext (säkerhet)
|
||
|
||
**Risk:** CANON-data läcker till ej autentiserade endpoints.
|
||
|
||
**Mitigation:**
|
||
- Injektionen sker alltid **server-side**, i system-rollen, aldrig i user/assistant-rollen
|
||
- Klassificeringen sker per fråga — generella frågor får ingen injektion
|
||
- CANON innehåller inga hemligheter (org-nr, namn, produktfakta är semi-publik affärssanning)
|
||
- `/api/chat` (generisk, publik) ska **INTE** injiceras — skapa separat endpoint för ESLM om nödvändigt
|
||
- Auth krävs redan på `/api/aamos/chat` och `/api/dynasty-counsel/chat`
|
||
|
||
**CANON innehåller ALDRIG:**
|
||
- API-nycklar, lösenord, tokens
|
||
- Personuppgifter (personnummer, bankuppgifter)
|
||
- Intern infrastruktur-IP/portar (WAVULT_TRUTH.md hanteras separat)
|
||
|
||
### 4.3 Injektion av felaktig data (hallucination via RAG)
|
||
|
||
**Risk:** RAG hämtar irrelevant chunk och "hittar på" koppling.
|
||
|
||
**Mitigation:**
|
||
- `minScore = 0.15` (semantisk) eller BM25 threshold — låga scores ignoreras
|
||
- CANON är tier-1 (E4 evidens) — data är verifierad av Erik
|
||
- `formatContext()` märker varje chunk med källa + radnummer → auditerbart
|
||
- CANON uppdateras via kontrollerad process (Erik-godkännande), inte automatiskt
|
||
|
||
### 4.4 Latency-påverkan
|
||
|
||
**Risk:** RAG-anrop (OpenAI embedding) tar 200-800ms och fördröjer chat-svar.
|
||
|
||
**Mitigation:**
|
||
- Timeout 2000ms — timeout → statisk fallback (0ms extra)
|
||
- Embeddings cachas i minnet (`_embeddingsCache`, 1h TTL)
|
||
- Index cachas i minnet (5 min TTL), disk-cache (1h TTL)
|
||
- Klassificering är synkron (0ms) — generella frågor skippar RAG helt
|
||
|
||
### 4.5 wavult-facts.mjs — dupliceringsrisk
|
||
|
||
**Risk:** wavult-facts.mjs och AAMOS_CANON.md divergerar (uppdatering i en men inte båda).
|
||
|
||
**Mitigation (kort sikt):** behåll båda under migrationsfasen — RAG-CANON ersätter gradvis.
|
||
|
||
**Mitigation (lång sikt):** generera wavult-facts.mjs automatiskt från CANON:
|
||
```bash
|
||
# CI-script: auto-generera wavult-facts.mjs från CANON
|
||
node scripts/canon-to-wavult-facts.mjs \
|
||
--input /opt/amos/data/AAMOS_CANON.md \
|
||
--output /opt/amos/api/dynasty-counsel/knowledge/wavult-facts.mjs
|
||
```
|
||
|
||
---
|
||
|
||
## 5. DEPLOY-PLAN (vilka filer ändras, i vilken ordning)
|
||
|
||
> **OBS:** Inget i denna plan muterar produktionskod idag. Alla steg kräver explicit godkännande.
|
||
|
||
### Steg 1 — Placera injektionsfilen (låg risk)
|
||
```
|
||
KÄLLA: /home/bernt/.openclaw/workspace/build/eslm-rag-injection.mjs
|
||
MÅL: /opt/amos/api/aamos/rag-injection.mjs
|
||
ÄNDRAR: Inget befintligt
|
||
RISK: Noll (ny fil)
|
||
```
|
||
|
||
### Steg 2 — Verifiera RAG-hälsa (ingen mutation)
|
||
```bash
|
||
node --input-type=module << 'EOF'
|
||
import rag from '/opt/amos/api/aamos/rag-injection.mjs';
|
||
console.log(await rag.ragHealthCheck());
|
||
// Förväntat: canonExists: true, ragModuleAccessible: true, indexLoaded: true
|
||
EOF
|
||
```
|
||
|
||
### Steg 3 — Integrera i /api/aamos/chat (middleware-approach)
|
||
|
||
**Fil att ändra:** `/opt/amos/api/aamos/routes.mjs`
|
||
|
||
```diff
|
||
import { getEnrichedPrompt } from '../dynasty-counsel/system-prompt.mjs';
|
||
+import { ragInjectionMiddleware } from './rag-injection.mjs';
|
||
|
||
-router.post('/chat', requireAuth, aamosRateLimit, express.json({ limit: '256kb' }), async (req, res) => {
|
||
+router.post('/chat', requireAuth, aamosRateLimit, express.json({ limit: '256kb' }), ragInjectionMiddleware, async (req, res) => {
|
||
|
||
// ... (existerande kod) ...
|
||
|
||
- const systemPrompt = getEnrichedPrompt(baseContext) + aamosContext;
|
||
+ const ragBlock = req.ragContextBlock || '';
|
||
+ const systemPrompt = ragBlock
|
||
+ ? ragBlock + '\n\n---\n\n' + getEnrichedPrompt(baseContext) + aamosContext
|
||
+ : getEnrichedPrompt(baseContext) + aamosContext;
|
||
```
|
||
|
||
### Steg 4 — Integrera i /api/dynasty-counsel/chat
|
||
|
||
**Fil att ändra:** `/opt/amos/api/dynasty-counsel/routes.mjs`
|
||
|
||
Samma pattern som steg 3 — lägg till `ragInjectionMiddleware` och modifiera `systemPrompt`-konstruktionen.
|
||
|
||
### Steg 5 — ESLM-endpoint (framtida)
|
||
|
||
**Fil att skapa/ändra:** `/opt/amos/api/aamos/eslm-proxy.mjs` (ny fil)
|
||
Wrappa ESLM-anropet med `buildContextForQuery()` innan forward till `172.31.36.61:8000`.
|
||
|
||
### Steg 6 — Rebuild RAG-index med CANON som primär
|
||
|
||
```bash
|
||
# Trigga rebuild av index (kör mot körande server)
|
||
curl -X POST http://localhost:3100/api/rag/rebuild 2>/dev/null || \
|
||
node -e "import('/opt/amos/api/rag.mjs').then(m => m.buildIndex(true))"
|
||
```
|
||
|
||
### Steg 7 — (Lång sikt) Deprecera wavult-facts.mjs
|
||
|
||
1. Skapa `scripts/canon-to-wavult-facts.mjs` som autogenererar filen från CANON
|
||
2. Kör scriptet i CI vid varje CANON-ändring
|
||
3. `wavult-facts.mjs` blir en genererad fil, ej manuellt underhållen
|
||
|
||
### Steg 8 — Monitoring
|
||
|
||
Logga per request:
|
||
```json
|
||
{
|
||
"ts": "2026-06-06T23:00:00Z",
|
||
"component": "rag-injection",
|
||
"query_classification": "company",
|
||
"rag_source": "rag", // "rag" | "static" | "none"
|
||
"rag_chunks": 4,
|
||
"rag_latency_ms": 312,
|
||
"rag_coverage": "E4"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. SAMMANFATTNING
|
||
|
||
| Aspekt | Nuläge | Målarkitektur |
|
||
|--------|--------|---------------|
|
||
| Faktakälla | `wavult-facts.mjs` (hårdkodad JS-fil) | `AAMOS_CANON.md` via RAG |
|
||
| Injektionspunkt | Alltid, alla frågor | Intelligent klassificering (company vs generic) |
|
||
| RAG-status | Byggt + embeddings komplett — men frånkopplat | Kopplat till chat-flödet |
|
||
| ESLM-injektion | Ingen | Via `buildContextForQuery()` |
|
||
| Fallback | Ingen (kraschar tyst) | 3-nivå: RAG → CANON statisk → hardkodad |
|
||
| Duplikering | CANON + wavult-facts divergerar | CANON = enda källa, wavult-facts autogenereras |
|
||
|
||
**Kod-stub:** `/home/bernt/.openclaw/workspace/build/eslm-rag-injection.mjs`
|
||
**Exporterar:** `classifyQuery`, `buildContextForQuery`, `buildSystemPromptWithRAG`, `ragInjectionMiddleware`, `ragHealthCheck`
|
||
|
||
---
|
||
|
||
*Producerat av agent Bernt (ESLM-B subagent) 2026-06-06. Mutera ingenting i prod utan Erik-godkännande.*
|