livrare lot 2
This commit is contained in:
commit
8ecc78e729
763 changed files with 164593 additions and 0 deletions
629
backend/services/orchestration-layer/agent-v3/BRAIN_V2_DESIGN.md
Normal file
629
backend/services/orchestration-layer/agent-v3/BRAIN_V2_DESIGN.md
Normal file
|
|
@ -0,0 +1,629 @@
|
|||
# Brain v2 Extension — Design Doc
|
||||
|
||||
Status: DRAFT 2026-04-30
|
||||
Owner: tehnic@finesynergy.eu
|
||||
Companion doc: HIL_MODERATION_DESIGN.md (queue + UI — separat)
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
Extindem didi-brain să cache-uiască rezultate de inferență pentru **techniques** și **ai_tampered** (la fel ca face deja pentru claims), cu 3 tier-uri de încredere (gold/silver/bronze) și invalidare la schimbare prompt/framework. Atomi gold provin din corecții moderator (vezi HIL doc); silver din LLM cache regular; bronze NU se servește.
|
||||
|
||||
**Out of scope**: workflow moderare, UI, queue management — vezi HIL_MODERATION_DESIGN.md.
|
||||
|
||||
---
|
||||
|
||||
## Decizii agreate
|
||||
|
||||
| # | Decizie |
|
||||
|---|---|
|
||||
| 1 | 1 atom per (componentă × content_hash) — `techniques` separat de `ai_tampered` separat de `claims` |
|
||||
| 2 | Embedding model = ce folosește brain deja (NU adăugăm infra nouă). Brain are deja semantic search + cross-encoder reranking |
|
||||
| 3 | 3 tier-uri: `gold` (human_validated), `silver` (LLM cache neverificat), `bronze` (pending review, nu se servește) |
|
||||
| 4 | Write trigger: silver pe LLM run (fire-and-forget); gold pe PATCH cu human_validated=true; bronze pe LLM run cu confidence < 60 |
|
||||
| 5 | Confidence threshold pe write — sub 60, NU se scrie silver (lasă miss → re-try cu poate model premium) |
|
||||
| 6 | Prompt versioning: gold supraviețuiește la schimbare prompt; silver e invalidat |
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ agent-v3 (techniques/ai-tampered/claims executors) │
|
||||
│ │
|
||||
│ 1. Pre-LLM: │
|
||||
│ POST http://10.11.10.13:8090/v1/analysis_atom/lookup │
|
||||
│ body: { content_hash, content?, component, tier, │
|
||||
│ prompt_hash, framework_version } │
|
||||
│ │
|
||||
│ 2. Brain răspunde: │
|
||||
│ { │
|
||||
│ hit: true | false, │
|
||||
│ atom: { tier, result_processed, ... } | null, │
|
||||
│ staleness: 'fresh' | 'stale_prompt' | 'stale_framework', │
|
||||
│ hit_count_incremented: true │
|
||||
│ } │
|
||||
│ │
|
||||
│ 3. Branch logic: │
|
||||
│ - tier=gold + fresh → return cached, skip LLM │
|
||||
│ - tier=silver + fresh → return cached, skip LLM │
|
||||
│ - tier=silver + stale_prompt → run LLM (prompt changed) │
|
||||
│ - miss / bronze → run LLM normal │
|
||||
│ │
|
||||
│ 4. Post-LLM (miss path, fire-and-forget): │
|
||||
│ POST /v1/analysis_atom │
|
||||
│ body: { ..., tier: 'silver' | 'bronze' (if conf<60) } │
|
||||
│ │
|
||||
│ 5. Post-moderation (HIL flow): │
|
||||
│ PATCH /v1/analysis_atom/:atom_id │
|
||||
│ body: { tier: 'gold', human_validated: true, │
|
||||
│ human_corrections: {...}, validator_user_id } │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Model (în PG-ul brain, NU în DIDI cluster)
|
||||
|
||||
### Tabel nou `analysis_atom`
|
||||
|
||||
```sql
|
||||
-- Migration brain-side: analysis_atom_v1.sql
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS vector;
|
||||
|
||||
CREATE TABLE analysis_atom (
|
||||
atom_id BIGSERIAL PRIMARY KEY,
|
||||
content_hash TEXT NOT NULL,
|
||||
content_embedding vector(1024), -- ajustat la dimensiunea modelului brain (poate fi 768/1024/3072)
|
||||
content_preview TEXT, -- first 200 chars pentru debug, NU full text
|
||||
component TEXT NOT NULL CHECK (component IN ('techniques', 'ai_tampered', 'claims')),
|
||||
tier TEXT NOT NULL CHECK (tier IN ('free', 'premium')),
|
||||
prompt_hash TEXT NOT NULL,
|
||||
framework_version TEXT NOT NULL,
|
||||
model_used TEXT, -- ex: 'qwen35:Qwen3.5-397B-A17B' sau 'openrouter:google/gemini-3-flash-preview'
|
||||
result_processed JSONB NOT NULL, -- mapped la canonical types din agent-v3 (TechniquesResult, AITamperedResult etc.)
|
||||
result_raw JSONB, -- raw LLM output (pentru re-mapping ulterior dacă schimba schema)
|
||||
cache_tier TEXT NOT NULL DEFAULT 'silver'
|
||||
CHECK (cache_tier IN ('gold', 'silver', 'bronze')),
|
||||
human_validated BOOLEAN NOT NULL DEFAULT false,
|
||||
human_corrections JSONB, -- diff din moderation (vezi HIL doc)
|
||||
validator_user_id TEXT,
|
||||
validated_at TIMESTAMP,
|
||||
hit_count INTEGER NOT NULL DEFAULT 0,
|
||||
last_hit_at TIMESTAMP,
|
||||
llm_confidence NUMERIC, -- 0-100, folosit pentru bronze decision
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
expires_at TIMESTAMP, -- TTL 90 zile pentru silver/bronze, NULL pentru gold
|
||||
|
||||
UNIQUE (content_hash, component, tier, prompt_hash)
|
||||
);
|
||||
|
||||
-- Indexuri
|
||||
CREATE INDEX idx_analysis_atom_lookup
|
||||
ON analysis_atom(content_hash, component, tier);
|
||||
|
||||
CREATE INDEX idx_analysis_atom_embedding
|
||||
ON analysis_atom USING ivfflat (content_embedding vector_cosine_ops)
|
||||
WITH (lists = 100);
|
||||
|
||||
CREATE INDEX idx_analysis_atom_gold
|
||||
ON analysis_atom(component, cache_tier)
|
||||
WHERE cache_tier = 'gold';
|
||||
|
||||
CREATE INDEX idx_analysis_atom_expires
|
||||
ON analysis_atom(expires_at)
|
||||
WHERE expires_at IS NOT NULL;
|
||||
|
||||
-- Trigger pentru updated_at
|
||||
CREATE TRIGGER trigger_analysis_atom_updated_at
|
||||
BEFORE UPDATE ON analysis_atom
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- TTL job (pg_cron sau external cleanup zilnic)
|
||||
-- DELETE FROM analysis_atom WHERE expires_at < NOW() AND cache_tier != 'gold';
|
||||
```
|
||||
|
||||
### `result_processed` shapes — exemple per component
|
||||
|
||||
**Pentru `component='techniques'`** (schema TechniquesResult din agent-v3):
|
||||
```json
|
||||
{
|
||||
"manipulation_score": 65,
|
||||
"total_severity": 23.4,
|
||||
"dimensions_affected": ["emotional_appeal", "loaded_language"],
|
||||
"techniques_count": 4,
|
||||
"techniques_detected": [
|
||||
{ "code": "loaded_language", "intensity": 3, "evidence": "...", "severity": 7.2 }
|
||||
],
|
||||
"coupling_context": null,
|
||||
"screening_duration_ms": 1200,
|
||||
"deep_analysis_duration_ms": 3400
|
||||
}
|
||||
```
|
||||
|
||||
**Pentru `component='ai_tampered'`**:
|
||||
```json
|
||||
{
|
||||
"ai_probability": 78,
|
||||
"verdict": "LIKELY_AI",
|
||||
"risk_score": 65,
|
||||
"categories_affected": ["text_patterns"],
|
||||
"indicators_count": 6,
|
||||
"disclosure_detected": false,
|
||||
"indicators_detected": [...]
|
||||
}
|
||||
```
|
||||
|
||||
**Pentru `component='claims'`** (păstrăm compatibilitatea cu schema existentă verification_cache):
|
||||
- Minimal: 1 row în `analysis_atom` cu component='claims' acoperă **TOATE claim-urile** din analiza acelui content (rezultatul agregat)
|
||||
- SAU: continuăm pe schema veche `verification_cache` per claim individual + un `analysis_atom` agregator
|
||||
- **Recomandare**: păstrăm `verification_cache` per-claim cum e (atomar, granular) + adăugăm `analysis_atom` ca agregator opțional pentru "claims output" (rezultat final post-LLM-extraction)
|
||||
|
||||
---
|
||||
|
||||
## Cache Tier Logic
|
||||
|
||||
### Write rules
|
||||
|
||||
```
|
||||
Trigger: post LLM run în executor
|
||||
Input: { result, component, tier, content, prompt_hash, framework_version, llm_confidence }
|
||||
|
||||
if (component === 'claims') {
|
||||
// Claims continuă să folosească verification_cache existent (nu schimbăm)
|
||||
// Plus adăugăm un atom agregator pentru "claims output as a whole"
|
||||
}
|
||||
|
||||
confidence = result.confidence ?? estimateConfidence(result);
|
||||
|
||||
if (confidence < 60) {
|
||||
cache_tier = 'bronze'; // se scrie pentru audit/debug, NU se servește
|
||||
} else {
|
||||
cache_tier = 'silver'; // se scrie și se servește
|
||||
}
|
||||
|
||||
POST /v1/analysis_atom { ..., cache_tier, human_validated: false }
|
||||
```
|
||||
|
||||
### Lookup rules (priority order)
|
||||
|
||||
```
|
||||
1. Match exact pe (content_hash, component, tier, prompt_hash)
|
||||
→ if found: check tier
|
||||
- gold + fresh → SERVE
|
||||
- silver + fresh → SERVE
|
||||
- bronze → IGNORE (treat as miss)
|
||||
- silver + stale (prompt_hash diferă față de current) → IGNORE pe stale_prompt
|
||||
|
||||
2. Match semantic pe content_embedding cu cosine_distance < 0.08
|
||||
AND component=X AND tier=Y AND cache_tier='gold'
|
||||
AND human_validated=true
|
||||
→ if found: SERVE (gold supraviețuiește prompt change pentru că răspunsul corect nu depinde de prompt)
|
||||
|
||||
3. Match semantic pe embedding < 0.08 AND cache_tier='silver' AND prompt_hash matches
|
||||
→ if found: SERVE (silver semantic, dar doar pe prompt actual)
|
||||
|
||||
4. Miss → return { hit: false }
|
||||
```
|
||||
|
||||
### Promotion rules
|
||||
|
||||
```
|
||||
silver → gold: când moderator face PATCH cu human_validated=true
|
||||
+ human_corrections=null (mod a aprobat fără modificări)
|
||||
SAU human_corrections!=null (mod a corectat → result_processed actualizat)
|
||||
|
||||
bronze → silver: dacă același content e re-analizat și produce confidence ≥ 60
|
||||
→ atom-ul vechi bronze e șters, se scrie silver nou
|
||||
|
||||
gold → expires never: gold rămâne forever (sau până prompt schimbă fundamental schema, atunci marcaj manual)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints (brain side)
|
||||
|
||||
### `POST /v1/analysis_atom/lookup`
|
||||
|
||||
Request:
|
||||
```json
|
||||
{
|
||||
"content": "text full optional pentru semantic search",
|
||||
"content_hash": "sha256:abc123...",
|
||||
"component": "techniques",
|
||||
"tier": "free",
|
||||
"prompt_hash": "p_v3_2026_03_15",
|
||||
"framework_version": "fw_v1_2026_03",
|
||||
"allow_semantic_match": true,
|
||||
"semantic_threshold": 0.08
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"hit": true,
|
||||
"atom": {
|
||||
"atom_id": 4242,
|
||||
"cache_tier": "gold",
|
||||
"human_validated": true,
|
||||
"result_processed": { ... },
|
||||
"model_used": "qwen35:Qwen3.5-397B-A17B",
|
||||
"validator_user_id": "admin-123",
|
||||
"validated_at": "2026-04-15T12:34:56Z",
|
||||
"hit_count": 17
|
||||
},
|
||||
"staleness": "fresh",
|
||||
"match_type": "exact" // sau "semantic"
|
||||
}
|
||||
```
|
||||
|
||||
Sau pe miss:
|
||||
```json
|
||||
{ "hit": false, "atom": null, "staleness": null }
|
||||
```
|
||||
|
||||
### `POST /v1/analysis_atom`
|
||||
|
||||
Request:
|
||||
```json
|
||||
{
|
||||
"content": "full text pentru embedding compute",
|
||||
"content_hash": "sha256:abc123...",
|
||||
"content_preview": "first 200 chars...",
|
||||
"component": "techniques",
|
||||
"tier": "free",
|
||||
"prompt_hash": "p_v3_2026_03_15",
|
||||
"framework_version": "fw_v1_2026_03",
|
||||
"model_used": "qwen35:Qwen3.5-397B-A17B",
|
||||
"result_processed": { ... },
|
||||
"result_raw": { ... },
|
||||
"llm_confidence": 78,
|
||||
"cache_tier": "silver" // calculat client-side din confidence
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{ "success": true, "atom_id": 4243, "cache_tier": "silver" }
|
||||
```
|
||||
|
||||
Idempotent: dacă există atom pentru același (content_hash, component, tier, prompt_hash), face UPDATE (sau ignoră dacă cache_tier=gold — nu suprascriu gold cu silver).
|
||||
|
||||
### `PATCH /v1/analysis_atom/:atom_id`
|
||||
|
||||
Request:
|
||||
```json
|
||||
{
|
||||
"human_validated": true,
|
||||
"human_corrections": { "verdict": {...}, "techniques": {...} },
|
||||
"validator_user_id": "admin-123",
|
||||
"result_processed": { ... corrected version ... },
|
||||
"cache_tier": "gold"
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{ "success": true, "atom_id": 4242, "cache_tier": "gold", "expires_at": null }
|
||||
```
|
||||
|
||||
### `GET /v1/analysis_atom/stats`
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"total_atoms": 12345,
|
||||
"by_tier": { "gold": 234, "silver": 11000, "bronze": 1111 },
|
||||
"by_component": { "techniques": 4500, "ai_tampered": 4200, "claims": 3645 },
|
||||
"hit_rate_24h": 0.42,
|
||||
"writes_24h": 850,
|
||||
"promotions_24h": { "silver_to_gold": 12, "bronze_to_silver": 3 }
|
||||
}
|
||||
```
|
||||
|
||||
Folosit de admin dashboard pentru tracking.
|
||||
|
||||
---
|
||||
|
||||
## Modificări în agent-v3
|
||||
|
||||
### 1. `shared/brain/client.ts` (extindere)
|
||||
|
||||
```typescript
|
||||
// Pseudo-cod, nu pentru implementare directă
|
||||
|
||||
// Existent: gatherFromBrain, writeVerificationCacheAsync (pentru claims)
|
||||
// NOU:
|
||||
|
||||
export interface AnalysisAtomLookupResult {
|
||||
hit: boolean;
|
||||
atom: {
|
||||
atom_id: number;
|
||||
cache_tier: 'gold' | 'silver' | 'bronze';
|
||||
human_validated: boolean;
|
||||
result_processed: any;
|
||||
hit_count: number;
|
||||
} | null;
|
||||
staleness: 'fresh' | 'stale_prompt' | 'stale_framework' | null;
|
||||
}
|
||||
|
||||
export async function lookupAnalysisAtom(params: {
|
||||
content: string;
|
||||
contentHash: string;
|
||||
component: 'techniques' | 'ai_tampered' | 'claims';
|
||||
tier: 'free' | 'premium';
|
||||
promptHash: string;
|
||||
frameworkVersion: string;
|
||||
}): Promise<AnalysisAtomLookupResult | null> {
|
||||
if (!process.env.DIDI_BRAIN_URL) return null;
|
||||
try {
|
||||
const res = await fetch(`${process.env.DIDI_BRAIN_URL}/v1/analysis_atom/lookup`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({...}),
|
||||
signal: AbortSignal.timeout(2000), // brain MUST respond fast
|
||||
});
|
||||
return await res.json();
|
||||
} catch {
|
||||
return null; // fail open — fallback la LLM
|
||||
}
|
||||
}
|
||||
|
||||
export function writeAnalysisAtomAsync(params: {
|
||||
content: string;
|
||||
contentHash: string;
|
||||
component: 'techniques' | 'ai_tampered' | 'claims';
|
||||
tier: 'free' | 'premium';
|
||||
promptHash: string;
|
||||
frameworkVersion: string;
|
||||
modelUsed: string;
|
||||
resultProcessed: any;
|
||||
resultRaw: any;
|
||||
llmConfidence: number;
|
||||
}) {
|
||||
// Fire-and-forget, nu așteaptă răspuns
|
||||
const cacheTier = params.llmConfidence < 60 ? 'bronze' : 'silver';
|
||||
fetch(`${process.env.DIDI_BRAIN_URL}/v1/analysis_atom`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ ...params, cacheTier }),
|
||||
signal: AbortSignal.timeout(5000),
|
||||
}).catch(() => {}); // log warn, ignore
|
||||
}
|
||||
|
||||
export async function patchAnalysisAtomGold(params: {
|
||||
atomId: number;
|
||||
validatorUserId: string;
|
||||
humanCorrections: any | null;
|
||||
resultProcessed: any;
|
||||
}) {
|
||||
// Apelat din moderation/queue-manager.ts după resolve
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### 2. `components/techniques/executor.ts` (modificare)
|
||||
|
||||
```typescript
|
||||
// La începutul execute():
|
||||
const promptHash = computePromptHash(systemPrompt, userTemplate);
|
||||
const frameworkVersion = computeFrameworkVersion();
|
||||
const contentHash = sha256(input.text).slice(0, 16);
|
||||
|
||||
const lookup = await lookupAnalysisAtom({
|
||||
content: input.text,
|
||||
contentHash,
|
||||
component: 'techniques',
|
||||
tier: searchTier,
|
||||
promptHash,
|
||||
frameworkVersion,
|
||||
});
|
||||
|
||||
if (lookup?.hit && lookup.atom) {
|
||||
if (lookup.atom.cache_tier === 'gold' || (lookup.atom.cache_tier === 'silver' && lookup.staleness === 'fresh')) {
|
||||
// Return cached, skip LLM
|
||||
return {
|
||||
...lookup.atom.result_processed,
|
||||
_cache_hit: true,
|
||||
_cache_tier: lookup.atom.cache_tier,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Miss path — run LLM normal:
|
||||
const result = await runLLMScreeningAndDeepAnalysis(...);
|
||||
|
||||
// Fire-and-forget write back
|
||||
writeAnalysisAtomAsync({
|
||||
content: input.text,
|
||||
contentHash,
|
||||
component: 'techniques',
|
||||
tier: searchTier,
|
||||
promptHash,
|
||||
frameworkVersion,
|
||||
modelUsed: result.llm_screening,
|
||||
resultProcessed: result,
|
||||
resultRaw: result._raw,
|
||||
llmConfidence: estimateConfidence(result),
|
||||
});
|
||||
|
||||
return result;
|
||||
```
|
||||
|
||||
### 3. `components/ai-tampered/executor.ts` (modificare)
|
||||
|
||||
Identic cu techniques. Schimbă `component: 'ai_tampered'`.
|
||||
|
||||
### 4. `components/claims/executor.ts` (NU modifica)
|
||||
|
||||
Claims continuă pe `verification_cache` existent (granular per-claim). Eventual adăugăm `analysis_atom` agregator în Faza 2 doar dacă măsurăm și hit rate scăzut pe claims output ca whole.
|
||||
|
||||
### 5. `api/moderation-routes.ts` resolve handler (din Doc A, dar dependent de brain)
|
||||
|
||||
```typescript
|
||||
// În handler-ul PUT /api/v3/moderation/queue/:queueId/resolve:
|
||||
|
||||
if (action === 'corrected') {
|
||||
// ... salvăm corecții în analysis_session ...
|
||||
|
||||
// Pentru fiecare componentă afectată în human_corrections, găsim atom-ul corespunzător și PATCH gold
|
||||
for (const component of ['techniques', 'ai_tampered', 'claims']) {
|
||||
if (corrections[component]) {
|
||||
const atomLookup = await lookupAnalysisAtom({...});
|
||||
if (atomLookup?.atom) {
|
||||
await patchAnalysisAtomGold({
|
||||
atomId: atomLookup.atom.atom_id,
|
||||
validatorUserId: userId,
|
||||
humanCorrections: corrections[component],
|
||||
resultProcessed: applyCorrections(atomLookup.atom.result_processed, corrections[component]),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (action === 'approved') {
|
||||
// Userul a aprobat fără modificări → ridicăm silver la gold
|
||||
// PATCH cu human_corrections=null
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bootstrap Strategy
|
||||
|
||||
Brain pornește gol pe atomi. Pentru a accelera hit rate:
|
||||
|
||||
### Opțiunea A — re-rulare istoric (recomandată)
|
||||
|
||||
Avem ~thousands de sesiuni completate în `bos_analysis.analysis_session`. Putem face un script one-time:
|
||||
|
||||
```
|
||||
1. SELECT session_id, input_text, techniques (JSONB), ai_tampered (JSONB)
|
||||
FROM analysis_session WHERE status='completed' AND created_at > '2026-01-01'
|
||||
2. Pentru fiecare row, calculează content_hash + apel POST /v1/analysis_atom
|
||||
cu cache_tier='silver', human_validated=false
|
||||
3. Skip dacă confidence < 60 (sau marcaj bronze)
|
||||
```
|
||||
|
||||
Hit rate jump: 0% → ~25-35% peste noapte.
|
||||
|
||||
### Opțiunea B — start gol, learn organic
|
||||
|
||||
Hit rate creste lent (5% săpt 1 → 25% săpt 4). Mai puțin risc de a propaga erori vechi.
|
||||
|
||||
**Recomandare**: Opțiunea A doar pentru sesiuni completate ≥ 90 zile (probabilitate mare că nu au erori), iar pentru ultimele 90 zile — start gol și lasă organic.
|
||||
|
||||
---
|
||||
|
||||
## Embedding Model
|
||||
|
||||
Brain folosește deja embedding pentru semantic search (vezi `/v1/search` cu cross-encoder reranking). NU schimbăm modelul. Dimensiunea vector field în PG (`vector(1024)` în schema) trebuie aliniată cu modelul actual al brain.
|
||||
|
||||
**Action item**: Înainte de migration, query brain `SELECT vector_dims(embedding) FROM atom LIMIT 1` (sau echivalent) ca să confirmăm dimensiunea exactă.
|
||||
|
||||
---
|
||||
|
||||
## Cost & Latency Impact
|
||||
|
||||
### Cost
|
||||
|
||||
Cu hit rate 50% după 3 luni, cost LLM scade ~40-50%. Pentru analiză text simplă premium:
|
||||
- Înainte: $0.05/analiză (techniques + ai + claims + verdict)
|
||||
- După (50% hits): $0.025-0.030/analiză amortizat
|
||||
|
||||
Brain operating cost neglijabil — postgres + pgvector pe mașina existentă (10.11.10.13). Singura cost suplimentar: storage atomi (~ 5KB per atom, 20K atomi/lună = 100MB).
|
||||
|
||||
### Latency
|
||||
|
||||
| Scenariu | Înainte | După |
|
||||
|---|---|---|
|
||||
| Cache HIT exact | n/a | ~80-150ms (brain lookup + serialize) |
|
||||
| Cache HIT semantic | n/a | ~120-250ms (vector search adaugat) |
|
||||
| Cache MISS | 5-15s | 5-15s + 100ms (lookup overhead pe miss) |
|
||||
|
||||
Lookup overhead pe miss path = ~100ms cost net. Acceptabil dacă hit rate ≥ 30%.
|
||||
|
||||
---
|
||||
|
||||
## Rollout Plan
|
||||
|
||||
### Faza 1 — Brain schema + endpoints (2 zile)
|
||||
- [ ] Migration brain-side (`analysis_atom` tabel + indexuri)
|
||||
- [ ] Endpoint-uri `/v1/analysis_atom/lookup`, POST, PATCH, GET stats
|
||||
- [ ] Test cu Postman: write silver, read back, write gold, lookup match exact + semantic
|
||||
- [ ] Deploy brain v2 pe staging-ul brain (separate environment dacă există)
|
||||
|
||||
### Faza 2 — agent-v3 client integration (2 zile)
|
||||
- [ ] Extindere `shared/brain/client.ts` cu `lookupAnalysisAtom`, `writeAnalysisAtomAsync`, `patchAnalysisAtomGold`
|
||||
- [ ] Modificare `techniques/executor.ts` cu lookup + write back
|
||||
- [ ] Modificare `ai-tampered/executor.ts` la fel
|
||||
- [ ] Feature flag: `BRAIN_ATOMS_ENABLED=false` default. Activate per env var pentru testing
|
||||
- [ ] Test E2E: rulează 2x aceeași analiză → a doua vine din cache silver
|
||||
|
||||
### Faza 3 — HIL integration (depinde de Doc A)
|
||||
- [ ] Modificare `moderation-routes.ts` resolve handler să cheme `patchAnalysisAtomGold`
|
||||
- [ ] UI badge "Verified by analyst" în extensie + dashboard pentru sesiuni cu atom gold
|
||||
|
||||
### Faza 4 — Bootstrap (1 zi)
|
||||
- [ ] Script `scripts/bootstrap-brain-from-history.ts` pentru re-import sesiuni vechi
|
||||
- [ ] Run pe sesiuni completate înainte de `now() - 90 days`
|
||||
- [ ] Monitor hit rate creste
|
||||
|
||||
### Faza 5 — Monitoring + tuning (continuous)
|
||||
- [ ] Dashboard Grafana: hit rate per component, gold/silver ratio, drift detection
|
||||
- [ ] Alertă: hit rate scade brusc → cineva a schimbat prompt
|
||||
- [ ] Alertă: bronze ratio crește → modelul produce confidence scăzut, problemă de calitate
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **Repo brain location**: unde e fizic codul brain? Pe ce mașină rulează? Cum se face deploy? (necesar pentru Faza 1)
|
||||
2. **Brain stack**: e FastAPI + Python? Folosește SQLAlchemy/asyncpg? Pgvector deja instalat? (necesar pentru migration)
|
||||
3. **Embedding model exact**: Qwen embed local sau alt model? Dimensiune exactă vector? (necesar pentru `vector(N)` în schema)
|
||||
4. **Cross-encoder reranking**: brain face deja reranking pe `/v1/search`. Aplicăm același reranking și pe `/v1/analysis_atom/lookup` semantic match? Sau e overkill (lookup-ul trebuie să fie fast)?
|
||||
5. **Atom expiration**: TTL 90 zile pentru silver/bronze. Gold = forever. Acceptabil sau prea generos? Putem pune 30 zile silver pentru a forța regenerare cu modele mai noi.
|
||||
|
||||
---
|
||||
|
||||
## Risk Register
|
||||
|
||||
| Risc | Probabilitate | Impact | Mitigare |
|
||||
|---|---|---|---|
|
||||
| Brain endpoint slow (>500ms) → analize globale lente | Medie | Mare | AbortSignal.timeout(2000) pe lookup. Dacă brain nu răspunde în 2s, fail open la LLM |
|
||||
| Embedding drift (model schimbat în brain) | Mică | Mare | Versionare strict embedding_model_id în atom; la schimbare → invalidate tot |
|
||||
| Storage atomi explodează | Mică | Mediu | TTL 90 zile + cleanup zilnic; gold păstrat forever (volum mic) |
|
||||
| Race condition write atom (2 agenți scriu același content_hash) | Medie | Mic | UPSERT pe UNIQUE constraint (content_hash, component, tier, prompt_hash) |
|
||||
| Mock human_corrections invalid (mod scrie diff care nu se aplică) | Mică | Mare | Validare schema diff în resolve handler; dacă invalid, aprobă fără gold write |
|
||||
| Brain v2 endpoint break compat cu brain v1 (claims) | Mică | Mare | NU modificăm `verification_cache` table sau `/v1/gather` endpoint. analysis_atom e separat |
|
||||
|
||||
---
|
||||
|
||||
## Metrice cheie
|
||||
|
||||
- `brain_lookup_total{component, tier, hit}` — counter
|
||||
- `brain_lookup_latency_ms{component, hit}` — histogram
|
||||
- `brain_atoms_count{component, tier, cache_tier}` — gauge (din /v1/analysis_atom/stats)
|
||||
- `brain_writes_total{component, cache_tier}` — counter
|
||||
- `brain_promotions_total{from, to}` — counter
|
||||
- `analyses_with_cache_hit_ratio` — derived: hit_total / lookup_total
|
||||
- `analyses_with_gold_ratio` — derived: gold_hits / hit_total
|
||||
|
||||
Toate scrise via /metrics endpoint pe agent-v3 + brain.
|
||||
|
||||
---
|
||||
|
||||
## Dependencies cu Doc A
|
||||
|
||||
Brain v2 funcționează STANDALONE pe path-ul silver (LLM cache) FĂRĂ HIL. Beneficiile inițiale (cost reduction 30%) vin de aici, nu din gold.
|
||||
|
||||
Gold path (HIL → brain) e benefic doar după ce moderatorii produc atomi gold (~200/lună la 1-2 mod). Beneficiul gold = quality lift + badge "Verified", nu cost reduction.
|
||||
|
||||
**Concluzie**: putem deploy Brain v2 înainte de a avea HIL complet. HIL doar adaugă layer gold deasupra.
|
||||
Loading…
Add table
Add a link
Reference in a new issue