Szkielet monitora halucynacji i zmęczenia (fatigue) modeli LLM w pętli produkcyjnej: co mierzymy, jakim zestawem regresyjnym, jakimi frameworkami evaluacyjnymi (Ragas / DeepEval) i gdzie w procesie CI stoi brama „nie wdrażaj, jeśli wskaźnik pogorszył się względem baseline". To specyfikacja PoC/ROADMAP, nie działający produkt — wykonawcza część mechanizmu opisanego w Playbooku I.
Monitor ma jeden cel: zamienić „model czasem zmyśla" w policzalny, powtarzalny sygnał — hallucination rate, faithfulness (wierność źródłu), answer relevancy — mierzony na stałym zestawie regresyjnym przy każdej zmianie promptu, modelu lub RAG-pipeline'u. To wsparcie decyzji (decision-support) dla zespołu, nie automatyczne orzeczenie o jakości modelu.
Reguły dowodowe (fakt/hipoteza/symulacja/GAP) obowiązujące jako metodyka. Opisane w Playbooku I.
Wersjonowany zestaw pytań/odpowiedzi referencyjnych (syntetyczny, bez danych produkcyjnych) do porównań przed/po zmianie.
Automatyczne uruchamianie zestawu metryk (faithfulness, context precision/recall, hallucination, G-Eval) przy każdym uruchomieniu.
Blokada wdrożenia, jeśli metryka spadnie poniżej progu ustalonego względem baseline — nie „automatyczna zgoda", tylko sygnał do przeglądu.
Wizualizacja trendu w czasie + wskaźnik degradacji jakości przy długich sesjach/kontekstach (fatigue).
Wynik evalu jako obiekt dowodowy (hash, znacznik czasu) w Evidence Layer, nie luźny log.
W tym kontekście fatigue (zmęczenie) oznacza mierzalną degradację jakości odpowiedzi wraz z długością sesji, rozmiarem kontekstu lub liczbą kolejnych wywołań w tej samej pętli agentowej — np. rosnący odsetek odpowiedzi bez oparcia w źródle, malejącą precyzję cytowania, narastające sprzeczności wewnętrzne. To nie jest twierdzenie o „zmęczeniu" modelu w sensie biologicznym ani dowód na konkretną przyczynę architektoniczną — to obserwowalny wzorzec w metrykach, wymagający dalszej analizy przyczyn, nie automatycznego wniosku.
| Metryka | Definicja | Narzędzie referencyjne | Status |
|---|---|---|---|
| Faithfulness | Czy odpowiedź jest oparta wyłącznie na dostarczonym kontekście/źródle, bez dodanych faktów spoza niego. | Ragas.faithfulness |
P1 |
| Hallucination rate | Odsetek odpowiedzi zawierających twierdzenie bez pokrycia w źródle (GAP wg Playbooku I). | DeepEval.HallucinationMetric |
P0 |
| Answer relevancy | Czy odpowiedź faktycznie adresuje zadane pytanie, niezależnie od poprawności faktograficznej. | Ragas.answer_relevancy |
P1 |
| Context precision / recall | Czy retrieval (RAG) dostarczył trafny i kompletny kontekst przed generacją odpowiedzi. | Ragas.context_precision/recall |
P1 |
| G-Eval (custom rubric) | Ocena LLM-jako-sędzia wg zdefiniowanej rubryki (np. zgodność z playbookiem, ton, kompletność sekcji „czego nie wiemy"). | DeepEval.GEval |
P2 |
| Drift regresyjny | Zmiana każdej z powyższych metryk względem ostatniego zatwierdzonego baseline na golden dataset. | porównanie run-to-run (harness ROADMAP) | P0 |
Cel modułu: wykrywać w odpowiedzi agenta twierdzenia bez pokrycia w (1) kontekście wejściowym, (2) wynikach narzędzi, (3) bazie wiedzy, (4) jawnie oznaczonej pamięci. Monitor nie „udowadnia prawdy absolutnej" — może wyłącznie porównać output z dostępnymi źródłami i wykryć brak wsparcia albo sprzeczność. To jest specyfikacja architektury (referencyjny szkielet), nie działający komponent — kod poniżej jest przykładem formatu.
agent output
│
▼
Claim Extractor
│
▼
Evidence Retriever
│
▼
Claim Verifier
│
▼
Risk Scorer
│
├── allow
├── warn
└── block / ask-for-citation
Referencyjne typy (TypeScript) dla obiektów wymienianych między etapami pipeline'u — przykład formatu, nie zaimplementowany interfejs.
type Claim = {
id: string
text: string
type: "fact" | "number" | "quote" | "causal" | "code" | "plan"
severity: "low" | "medium" | "high"
}
type Evidence = {
source_id: string
text: string
trust: number // 0..1
}
type Verdict = {
claim_id: string
status: "supported" | "contradicted" | "not_found" | "unclear"
confidence: number // 0..1
evidence: Evidence[]
}
type MonitorResult = {
risk: number // 0..1
action: "allow" | "warn" | "block"
verdicts: Verdict[]
}
Sprzeczność (contradicted) waży więcej niż brak źródła (not_found) — uzasadnienie: brak
źródła może oznaczać niepełny kontekst, natomiast sprzeczność wskazuje aktywny konflikt z dostępnymi dowodami.
Poniższy pseudokod to specyfikacja wagi/progów referencyjnych, do dostrojenia per domena przed jakimkolwiek
wdrożeniem.
def risk_score(verdicts):
score = 0.0
for v in verdicts:
if v["status"] == "contradicted":
score += 0.45 * v["confidence"]
elif v["status"] == "not_found":
score += 0.25 * v["confidence"]
elif v["status"] == "unclear":
score += 0.15 * v["confidence"]
return min(score, 1.0)
def action_for_risk(risk):
if risk >= 0.70:
return "block"
if risk >= 0.35:
return "warn"
return "allow"
| Próg risk | Akcja | Znaczenie |
|---|---|---|
| ≥ 0.70 | block | Odpowiedź wstrzymana do czasu weryfikacji źródeł — nie automatyczne odrzucenie na stałe, sygnał do przeglądu. |
| 0.35 – 0.69 | warn | Odpowiedź zwrócona z adnotacją „część twierdzeń wymaga weryfikacji". |
| < 0.35 | allow | Brak flagi — nie oznacza potwierdzonej poprawności, tylko brak wykrytej sprzeczności/braku w tym przebiegu. |
Poniżej baseline oparty na lexical overlap (nakładanie się słów kluczowych) — to nie jest finalny model NLI. Uzasadnienie: dopasowanie leksykalne nie wykrywa negacji, parafrazy ani sprzeczności semantycznych; służy wyłącznie jako punkt startowy specyfikacji i przykład kontraktu funkcji.
from dataclasses import dataclass
from typing import Literal
Status = Literal["supported", "contradicted", "not_found", "unclear"]
@dataclass
class Evidence:
source_id: str
text: str
trust: float
@dataclass
class Verdict:
claim_id: str
status: Status
confidence: float
evidence: list[Evidence]
def lexical_support(claim: str, evidence: str) -> float:
claim_terms = set(claim.lower().split())
ev_terms = set(evidence.lower().split())
if not claim_terms:
return 0.0
return len(claim_terms & ev_terms) / len(claim_terms)
def verify_claim(claim_id: str, claim: str, evidences: list[Evidence]) -> Verdict:
if not evidences:
return Verdict(claim_id, "not_found", 0.8, [])
ranked = sorted(
evidences,
key=lambda e: lexical_support(claim, e.text) * e.trust,
reverse=True,
)
best = ranked[0]
support = lexical_support(claim, best.text) * best.trust
if support >= 0.65:
return Verdict(claim_id, "supported", support, [best])
if support <= 0.15:
return Verdict(claim_id, "not_found", 1.0 - support, ranked[:3])
return Verdict(claim_id, "unclear", 0.5, ranked[:3])
Kierunek rozwoju ponad baseline lexical-overlap — czterostopniowy pipeline retrieval → rerank → weryfikacja → kalibracja progu. Żaden z tych czterech elementów nie jest dziś wdrożony; to specyfikacja docelowa.
1. retrieval: BM25 + embedding search
2. rerank: cross-encoder
3. verify: NLI model / LLM judge z cytatami
4. calibrate: próg zależny od typu claimu
Przykładowy szablon promptu dla „LLM jako sędzia" (weryfikacja twierdzenia wyłącznie wobec podanych dowodów — defensywne, ocena zgodności, zero instrukcji obejścia zabezpieczeń):
Sprawdź twierdzenie tylko wobec podanych dowodów.
Twierdzenie:
{claim}
Dowody:
{evidence_chunks}
Zwróć JSON:
{
"status": "supported|contradicted|not_found|unclear",
"confidence": 0.0-1.0,
"rationale": "krótkie uzasadnienie",
"source_ids": []
}
Nie używaj wiedzy spoza dowodów.
Przykładowy wzorzec owinięcia wywołania agenta monitorem — do decyzji, kiedy i jak blokować/oznaczać odpowiedź. Nie zastępuje przeglądu człowieka dla decyzji P0/P1.
def guarded_agent_call(agent, monitor, user_input, context):
output = agent.run(user_input, context=context)
result = monitor.check(
user_input=user_input,
context=context,
output=output,
)
if result["action"] == "block":
return {
"content": "Nie mogę zwrócić tej odpowiedzi bez weryfikacji źródeł.",
"monitor": result,
}
if result["action"] == "warn":
return {
"content": output + "\n\n[Uwaga: część twierdzeń wymaga weryfikacji.]",
"monitor": result,
}
return {
"content": output,
"monitor": result,
}
Szkic kontraktu endpointu — nie wdrożony, nie zamontowany w server.js, przykład formatu żądania/odpowiedzi.
POST /monitor/check
{
"agent_id": "agent-x",
"input": "user question",
"output": "agent answer",
"context": [],
"tool_logs": [],
"memory_refs": []
}
Odpowiedź (przykład):
{
"risk": 0.42,
"action": "warn",
"verdicts": [
{
"claim_id": "c1",
"status": "not_found",
"confidence": 0.81,
"evidence": []
}
]
}
Agent nie może podnosić confidence powyżej poziomu dostępnego evidence.
Czyli: claim ≤ proof — ta sama doktryna co w Playbooku I,
tu przełożona na kontrakt danych i logikę decyzyjną specyfikacji pipeline'u.
Doktryna claim ≤ proof, status GAP, 7 kroków reakcji. → Playbook I
Adwersaryjne testowanie AI/agentów w RoE, defensywnie. → AI Red-Team
Pełny rejestr tego, czego orchestrator jeszcze nie robi. → Known limitations
7 sprintów z dowodami LIVE/ROADMAP. → Roadmap dev
Kanon testów i dowodów: /roadmap-dev · kanon statusów wszystkich elementów: /status-matrix.