Files
Nexus.Reader/.agent/skills/km-rag-methodology/artifacts/deep-research-report-rag.md
T

588 lines
41 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Mapa Wiedzy i kontrola w RAG: jak wdrożyć „nowe podejście” w sposób inżynieryjny
## Executive summary
Autor posta (entity["people","Vladimir Alekseichenko","dataworkshop ceo"], entity["organization","DataWorkshop","ml/ai training poland"]) kontrastuje „klasyczny” RAG oparty o mechaniczne chunkowanie i wektoryzację z podejściem, w którym buduje się **Mapę Wiedzy**: „graf z metadanymi, powiązaniami i odniesieniami do źródeł” (w kontekście praktyki na danych z entity["organization","Giełda Papierów Wartościowych w Warszawie","warsaw stock exchange"]). citeturn2view0turn2view1
W tym raporcie formalizuję tę ideę jako **KnowledgeMap RAG (KMRAG)**: RAG, w którym warstwa „R” nie jest tylko wyszukiwaniem semantycznym po losowych fragmentach, ale **kontrolowanym wyborem jednostek wiedzy** (sekcja, tabela, rekord, definicja, reguła) powiązanych grafowo, z pełną **proweniencją (skąd to jest), politykami dostępu, wersjonowaniem i testowalnością**. To jest spójne z tezą autora, że „R w RAG” to przede wszystkim **ryzyko**: jeśli retrieval jest błędny, model będzie „pewnie” odpowiadał na podstawie złego kontekstu. citeturn2view0turn6view0
Ponieważ nie podałeś ograniczeń (skala, budżet, SLA/latencja), przyjmuję **brak specyficznych constraintów** i podaję warianty: od małych wdrożeń (Postgres/pgvector) po architektury wielotenancy (Qdrant/Pinecone/Weaviate) oraz hybrydy graf + wektory. citeturn12search2turn14search1turn14search16turn14search0turn14search2
Najważniejsze rekomendacje wdrożeniowe:
Po pierwsze, zastąp „losowe chunki” **jednostkami sensu**: segmentacją strukturalną (nagłówki/sekcje/tabele) i/lub semantyczną, z metadanymi i relacjami (poprzedni/następny, należy do sekcji, cytuje, definiuje). citeturn6view0turn11search1turn11search29
Po drugie, zbuduj **Mapę Wiedzy jako graf** (property graph) + indeksy (wektorowy i leksykalny/hybrydowy). Praktycznie: graf przechowuje relacje i proweniencję, a wektory dają tani „candidate generation”; dopiero potem używasz grafu do „dociągnięcia” brakujących kontekstów i do audytu. To jest zgodne z rodziną podejść GraphRAG (np. publikacja entity["company","Microsoft","tech company"] o GraphRAG: graf encji + „community summaries” dla lepszych odpowiedzi na pytania globalne). citeturn0search1turn3search4turn3search20
Po trzecie, „kontrola zamiast nadziei” oznacza: (a) **mierniki retrieval i generation**, (b) automatyczne testy regresji i audyt ścieżki źródeł, (c) monitoring i alerty driftu oraz incydentów bezpieczeństwa (prompt injection, data leakage). W praktyce: RAGAS/TruLens + OWASP LLM Top 10 jako checklisty, plus logowanie „trace” (kontekst → odpowiedź → cytowania). citeturn4search1turn4search2turn4search6turn4search13turn4search7
## Definicja podejścia „Mapa Wiedzy zamiast losowych chunków”
W poście autor opisuje Mapę Wiedzy jako artefakt, który budujesz **w 3 dni**: „graf z metadanymi, powiązaniami i odniesieniami do źródeł” (wspomina też kontekst narzędziowy: repozytorium na entity["company","GitHub","code hosting platform"] i notatki w entity["company","Obsidian","note-taking app company"]). citeturn2view1
Jednocześnie w dłuższym materiale autor rozwija intuicję, dlaczego „chunking + vector DB” bywa drogą donikąd: mechaniczne cięcie rozrywa jednostki sensu (akapit, tabela), a model językowy zwykle **nie weryfikuje kontekstu** odpowiada w oparciu o to, co mu dostarczysz, nawet jeśli kontekst jest sprzeczny (stąd losowość i halucynacje). citeturn6view0turn7view1
### Precyzyjna definicja operacyjna (KMRAG)
**KnowledgeMap RAG (KMRAG)** to architektura RAG, w której warstwa „R” jest realizowana przez:
**Reprezentację wiedzy**: dokumenty są przekształcane do zbioru **jednostek wiedzy** (Knowledge Units) o stabilnej proweniencji (ID, wersja, lokalizacja w źródle) i spójnej semantyce (sekcja definicji, tabela, rozdział, procedura), a nie losowych wycinków znaków. citeturn6view0turn11search9turn16search0
**Mapę (graf) zależności**: jednostki są węzłami grafu (np. DOCUMENT → SECTION → UNIT; ENTITY ↔ UNIT; UNIT ↔ UNIT przez „refers_to/next/derives_from”), a krawędzie niosą informację ułatwiającą retrieval i audyt (np. „to jest definicja terminu X”, „to jest wyjątek od reguły”). citeturn2view1turn10search3turn3search4
**Polityki retrieval**: zapytanie jest mapowane na intencję i encje, a retrieval wykonuje plan: generuje kandydatów (wektory/keyword/hybrid), następnie rozszerza kontekst grafowo (np. sekcja nadrzędna, definicje encji, powiązane tabele), na końcu dokonuje selekcji (rerank/pruning) i buduje kontekst z cytowaniami. citeturn12search3turn12search11turn10search6turn10search31
**Kontrolę i audytowalność**: system jest projektowany tak, aby można było odpowiedzieć na pytania: „Dlaczego ten fragment?”, „Czy użytkownik miał uprawnienia?”, „Jaka wersja źródła?”, „Czy odpowiedź jest ugruntowana (grounded) w kontekście?”. Autor wprost wiąże „mapę wiedzy” z uszczelnianiem rozwiązań, wymaganiami prawnymi/bezpieczeństwa oraz audytowalnością. citeturn7view1turn14search2
### Dlaczego „losowe chunki” są słabą abstrakcją inżynieryjną
Mechaniczne chunkowanie jest często liczone w znakach/tokenach; nawet z overlapem rozrywa strukturę i wymusza „magiczne” heurystyki (większy chunk_size, więcej chunków w kontekście), które łatwo psują wcześniej działające przypadki i utrudniają stabilną ewaluację. citeturn6view0
Z perspektywy governance kluczowy problem jest też bezpieczeństwo: w jednym dokumencie mogą być fragmenty o różnych poziomach dostępu, więc „wrzucanie wszystkiego do jednego kontekstu” łamie zasady separacji i komplikuje zgodność (ten motyw pojawia się u autora wprost). citeturn7view1turn14search2
## Architektura referencyjna i komponenty
Poniżej przedstawiam architekturę komponentową KMRAG, obejmującą: ingestion, mapę wiedzy, strategie segmentacji, embeddingi i wektory, retrievery i rerankery, prompt engineering i grounding, oraz kontrolę halucynacji i ewaluację.
### Diagram architektury
```mermaid
flowchart LR
subgraph Ingestion
A[Źródła: PDF/HTML/DOCX/DB] --> B[Parsing + normalizacja]
B --> C[Jednostki wiedzy: sekcje, tabele, rekordy]
C --> D[Metadane: źródło, wersja, ACL, lokalizacja]
C --> E[Ekstrakcja encji/relacji]
E --> G[(Graf / Mapa Wiedzy)]
C --> F[Embedding + indeks]
F --> V[(Vector DB)]
end
subgraph QueryTime
Q[Zapytanie użytkownika] --> R[Routing/intencja/encje]
R --> V1[Candidate gen: vector/keyword/hybrid]
V1 --> V
V --> K[Top-K kandydatów]
K --> G1[Graph expansion\n(definicje, zależności, sekcje)]
G1 --> G
G --> S[Context assembly + dedup + cytowania]
S --> L[LLM generacja\n(z zasadą "answer from sources")]
L --> O[Odpowiedź + cytowania + confidence]
end
subgraph Control
O --> M[Logi/trace]
M --> EV[Ewaluacja offline/online]
M --> MON[Monitoring KPI + alerty]
end
```
Model ten jest kompatybilny zarówno z „klasycznym RAG” w sensie pracy na wektorach (RAG w ujęciu Lewis et al. zakłada połączenie pamięci parametrycznej i nieparametrycznej poprzez retrieval z indeksu wektorowego), jak i z odmianami grafowymi (GraphRAG: budowa grafu encji i „community summaries” jako warstwa indeksu). citeturn0search2turn0search5turn0search1turn3search4
image_group{"layout":"carousel","aspect_ratio":"16:9","query":["GraphRAG architecture diagram","knowledge graph retrieval augmented generation diagram","vector database similarity search diagram","Neo4j graph visualization example"],"num_per_query":1}
### Ingestion: parsowanie, normalizacja i jednostki wiedzy
W KMRAG ingestion nie kończy się na „wyciągnij tekst z PDF”. Kluczowe jest zachowanie/rekonstrukcja struktury: tytuły, listy, tabele, numer stron, sekcje. Biblioteka entity["company","Unstructured","document processing company"] wprost opisuje „partitioning” jako ekstrakcję ustrukturyzowanych elementów (Title/NarrativeText/ListItem itd.), aby móc decydować, co zachować. citeturn16search0turn16search8turn16search4
Jeśli pracujesz na bardzo różnych formatach lub potrzebujesz także metadanych i obsługi np. zaszyfrowanych PDF, narzędzia z ekosystemu entity["organization","Apache Software Foundation","open source foundation"] (Apache Tika) podkreślają możliwość parsowania PDF, w tym obsługi dokumentów szyfrowanych przy podaniu hasła. citeturn16search1turn16search30
Wniosek projektowy: „Jednostka wiedzy” w KMRAG to obiekt typu np.:
- `unit_type`: `section`, `definition`, `table`, `row`, `procedure_step`, `policy_rule`
- `canonical_text` (tekst do embeddingu i rerankingu)
- `rendered_context` (tekst/fragment do wklejenia do prompta)
- `provenance`: `source_id`, `page`, `section_path`, `span_offsets`
- `governance`: `acl_tags`, `pii_class`, `retention_class`
- `links`: `prev/next`, `references`, `same_topic`
Taki model danych bezpośrednio adresuje problem autora: model nie „weźmie odpowiedzialności” za konfliktujący kontekst, więc to system ma pilnować jakości kontekstu i jego zaufania. citeturn6view0turn7view1
### Strategie segmentacji: od „chunków” do „węzłów” (Nodes)
Jeżeli musisz działać na tekście, i tak będziesz coś „dzielił” różnica polega na tym, czy są to losowe fragmenty znaków, czy **węzły semantyczne**.
- W ekosystemie entity["company","LangChain","llm app framework company"] często proponuje się `RecursiveCharacterTextSplitter` jako „solidny default” dla wielu przypadków, ale to nadal jest heurystyka bazująca na znakach i separatorach. citeturn11search8turn11search0
- entity["company","LlamaIndex","llm data framework company"] oferuje semantyczne parsowanie węzłów: `SemanticSplitterNodeParser` dzieli tekst na grupy zdań powiązane semantycznie (z użyciem embeddingów), a dokumentacja podkreśla, że to alternatywa dla stałego rozmiaru chunków. citeturn11search1turn11search9turn11search29
KMRAG traktuje segmentację jako element modelowania danych: węzły mają typ, hierarchię i relacje.
### Embeddingi i Vector DB: candidate generation + filtrowanie po metadanych
Embeddingi są nadal bardzo użyteczne, ale w KMRAG pełnią rolę „szybkiego generatora kandydatów”, a nie „wyroczni”.
Otwartoźródłowo, entity["company","Hugging Face","ml model hub company"] utrzymuje Sentence Transformers, które dostarcza zarówno modele embeddingowe (bi-encoders), jak i rerankery (cross-encoders). citeturn12search38turn12search3
Warstwa metadanych jest w KMRAG krytyczna: np. do ograniczania domeny, wersji dokumentu, języka, daty wejścia w życie, uprawnień.
- entity["company","Qdrant","vector database company"] opisuje payload/metadata i filtrowanie oraz zaleca indeksowanie pól payload dla efektywności filtrowania. citeturn11search2turn11search6turn11search37
- entity["company","Pinecone","vector database company"] opisuje filtrowanie po metadanych oraz pokazuje wzorzec multitenancy przez namespaces. citeturn11search7turn14search16turn14search12
- entity["company","Weaviate","vector database company"] opisuje hybrydę BM25F + wektory (fuzja wyników i wagi są konfigurowalne) oraz posiada natywną wielodzierżawność (tenant per request). citeturn12search0turn14search0
- entity["company","Milvus","vector database project"] dokumentuje hybrydę sparse+dense i wskazuje scenariusze, w których połączenie poprawia wyniki (semantyka + dopasowanie słów kluczowych). citeturn12search1turn12search5
W KMRAG niemal zawsze warto rozważyć hybrid retrieval (dense + sparse), bo ogranicza „semantic drift” i poprawia precyzję przy terminach domenowych (np. numery, nazwy własne). Jest to wspólny wątek w dokumentacji Weaviate i Pinecone, opisującej fuzję wyników i podejścia do hybrydy. citeturn12search0turn11search3turn11search19
### Retrievery, rerankery i kontrola halucynacji
KMRAG rozdziela retrieval na etapy:
**Candidate generation (tani):** dense retriever (np. dual-encoder) i/lub sparse (BM25). Klasyczna praca o dense retrieval (DPR) pokazuje dual-encoder jako praktyczny mechanizm retrieval i porównuje go do BM25 w QA. citeturn8search0turn8search4
**Reranking (droższy):** cross-encoder reranker znacząco poprawia ranking, ale jest kosztowny, bo ocenia pary (query, doc) wspólnie w modelu. Sentence Transformers opisuje retrieve&rerank pipeline oraz rolę CrossEncodera. citeturn12search11turn12search19
**Graph expansion (precyzja i kompletność):** graf dostarcza „brakujących mostów” (definicje, zależności, wyjątki, kontekst sekcji) oraz daje audyt to jest sedno „Mapy Wiedzy”. W wariantach GraphRAG (Microsoft) graf jest budowany z encji i relacji, a następnie grupowany w społeczności i streszczany, co poprawia odpowiedzi na pytania „globalne” (np. „jakie są główne tematy w korpusie?”), gdzie naiwny RAG zawodzi. citeturn0search1turn0search13turn3search4turn3search20
**Halucynacje i „kontrola”:** literatura proponuje pętle weryfikacji (np. ChainofVerification: draft → pytania weryfikacyjne → niezależne odpowiedzi → final) i mechanizmy samorefleksji (SelfRAG) oraz korekty retrieval (CRAG). Są to techniki „kontroli” na poziomie architektury, a nie tylko promptu. citeturn8search3turn9search1turn9search2
## Opcje projektowe i tradeoffy
### Porównanie: klasyczny RAG vs KMRAG
| Wymiar | Klasyczny „chunk + vector DB” | KMRAG (Mapa Wiedzy) | Konsekwencja praktyczna |
|---|---|---|---|
| Jednostka indeksowania | fragment znaków/tokenów | jednostka sensu: sekcja/tabela/rekord + typ | mniej „urwanych” kontekstów, mniej przypadkowości |
| Reprezentacja | embedding + (czasem) metadata | embedding + metadata + graf relacji + proweniencja | lepsza ścieżka audytu i „dlaczego to” |
| Retrieval | topk similarity | plan retrieval: hybrid + graf expansion + rerank | wyższa precyzja i odporność na trudne pytania |
| Zmiany w danych | częsty reindex, ryzyko regresji | wersjonowanie, testy regresji per typ jednostki | stabilniejsze wdrożenia i migracje |
| Bezpieczeństwo/ACL | łatwo mieszać fragmenty o różnych uprawnieniach | ACL na poziomie jednostki i ścieżki grafu | mniejsze ryzyko wycieku kontekstu |
| Debuggowanie | „dlaczego takie chunki?” | „jaki węzeł, z jakiego źródła, jaka relacja?” | szybsze RCA i audyt |
Uzasadnienie co do problemów chunkingu i „model ufa kontekstowi” pochodzi z materiału autora; definicja Mapy Wiedzy jako grafu z metadanymi i odniesieniami jest wprost w poście. citeturn6view0turn2view1turn7view1
### Wybory technologiczne: wektory, graf, hybryda
Poniżej pokazuję typowe opcje i kompromisy (bez narzuconych constraintów dobór zależy od QPS, wolumenu i wymagań bezpieczeństwa).
**Vector store**
- Qdrant: mocne filtrowanie payload + mechanizmy multitenancy (w tym „tiered multitenancy”). citeturn11search6turn14search1turn14search18
- Pinecone: proste multitenancy przez namespaces; dobrze opisane podejścia do hybrid search (single hybrid index vs osobne indeksy, z plusami i minusami). citeturn14search16turn11search3
- Weaviate: wbudowany hybrid BM25F + wektor, oraz multitenancy z tenantem w operacjach. citeturn12search0turn14search0
- Milvus: rozbudowane podejścia do sparse+dense i multivector, z dokumentacją dla hybrydy. citeturn12search1turn12search5turn12search33
- pgvector: dobre, gdy chcesz „mniej systemów” i akceptujesz kompromisy wydajności; repo dokumentuje różnice IVFFlat vs HNSW (build time/memory vs speedrecall). citeturn12search2turn12search14
- Elasticsearch: istotny, gdy potrzebujesz „enterprise security” (RBAC, field/documentlevel security) i hybrydowego wyszukiwania w jednej platformie. citeturn14search2turn14search15
**Graph / Knowledge Map store**
- Neo4j: bogate wzorce GraphRAG (graph traversal, fulltext, vector, text2cypher). Neo4j publikuje GraphRAG field guide i pakiet GraphRAG dla Pythona. citeturn10search18turn10search14turn10search31turn10search2
- Microsoft GraphRAG: gotowy pipeline budowy grafowego indeksu (encje → społeczności → streszczenia), opensource na GitHubie + dokumentacja „Getting started”. citeturn3search0turn3search31turn3search20turn0search1
- LlamaIndex KnowledgeGraphIndex: praktyczna automatyzacja budowy grafu z tekstu i query po encjach. citeturn10search3turn10search11
**Kompromisy**
- Skalowalność: graf może zmniejszać liczbę „strzałów” w LLM (np. przez prestreszczenia społeczności w GraphRAG) kosztem cięższego ingestion i większej złożoności danych. citeturn0search1turn3search4
- Latencja: rerankery crossencoder podnoszą jakość, ale zwiększają czas (N par do oceny); dlatego standardem jest retrieval → rerank topN, nie rerank całego korpusu. citeturn12search11turn12search19
- Koszt: hybryda i graf często zwiększają koszt ingest (LLM do ekstrakcji encji/relacji), ale zmniejszają koszt „ratowania” jakości w runtime przez kolejne heurystyki. To jest w duchu argumentu autora o „dokładaniu miniklocków” versus poprawa fundamentu. citeturn6view0turn7view1
- Maintainability: mniej „magicznych” parametrów chunk_size; więcej jawnych typów jednostek i testów per typ. citeturn7view1turn13search3
- Security/data governance: najlepiej wspierać **permissionaware retrieval** już w retrieverze (prefilter), bo wtedy model nie ma czego „wyciec”. Dokumentacja Elastic i wektor DB pokazuje mechanizmy RBAC/DLS, namespaces/tenants i filtrowanie po metadanych. citeturn14search2turn14search16turn14search0turn11search6
## Migracja z klasycznego RAG do KMRAG
Migracja jest łatwiejsza, jeśli potraktujesz ją jak refactoring warstwy danych i retrieval, a nie „przepisanie wszystkiego od zera”.
### Ścieżka migracji krok po kroku
**Krok pierwszy: ustal bazową prawdę (baseline) i testy.**
Bez ewaluacji będziesz „liczyć na cud” wprost przeciwieństwo postulatu „kontrola zamiast nadziei”. Zacznij od małego zestawu pytań i oczekiwań (golden set) oraz logowania kontekstu i odpowiedzi. W praktyce możesz użyć RAGAS (metryki retrieval i faithfulness bez konieczności pełnych anotacji) oraz TruLens (RAG triad: context relevance, groundedness, answer relevance). citeturn4search1turn4search2turn4search6
**Krok drugi: dołóż metadane i proweniencję zanim dołożysz graf.**
W klasycznym RAG często brakuje stabilnych ID i lokalizacji w źródle; tymczasem autor wiąże mapę wiedzy z odniesieniami do źródeł. Minimalny zestaw to: `source_id`, `version`, `page/section`, `timestamp`, `acl_tags`. Mechanizmy filtrowania po metadanych są standardem m.in. w Pinecone i Qdrant. citeturn2view1turn11search7turn11search6
**Krok trzeci: zamień chunki na węzły o typach i relacjach.**
Zamiast „1000 znaków”, twórz: `SectionNode`, `TableNode`, `DefinitionNode`, `PolicyNode`. Jeśli nie możesz od razu, przejdź etapowo przez semantyczne node parsers (LlamaIndex) lub segmentację po strukturze dokumentu (partitioning). citeturn11search9turn16search0turn11search1
**Krok czwarty: zbuduj Mapę Wiedzy (graf) i zacznij od najtańszego użycia w runtime.**
Nie musisz od razu robić pełnego „GraphRAG global”. Najpierw używaj grafu do: (a) definicji i wyjątków, (b) dołączania kontekstu „nadrzędna sekcja” / „poprzedninastępny”, (c) audytu ścieżki cytowań. Dopiero potem dokładaj stricte grafowe retrievery. citeturn10search6turn10search31turn3search4
**Krok piąty: wprowadź gating i rollout.**
Zgodnie z najlepszymi praktykami ewaluacji: iteruj, porównuj wersje, ustaw continuous evaluation i progi akceptacji. citeturn13search3turn13search35
### Proponowana sekwencja wdrożenia
| Faza | Co dostarczasz | Typowy czas (brak constraintów) | Kryterium „done” |
|---|---|---:|---|
| Audit RAG | logi + golden set + baseline metryk | 12 tyg. | masz mierzalne recall/faithfulness + top failure modes |
| Metadata-first | proweniencja + filtry + ACL | 12 tyg. | brak „orphan” chunków bez źródła; prefiltrowanie działa |
| Nodes & map | węzły typowane + relacje | 24 tyg. | stable IDs, relacje prev/next/contains/refers_to |
| Hybrid + rerank | dense+sparse + rerank topN | 13 tyg. | poprawa metryk retrieval bez wzrostu halucynacji |
| Graph expansion | dołączanie kontekstu grafem | 24 tyg. | poprawa trudnych pytań „łączących fakty” |
| Produkcja | monitoring KPI + procedury incydentów | ciągłe | CE + alerty + playbook audytu |
Metryki i praktykę continuous evaluation wspiera dokumentacja OpenAI (zalecenia dot. progów context recall/precision i pipelineu ewaluacji), co jest spójne z „kontrolą” jako procesem, nie jednorazową konfiguracją. citeturn13search3turn13search27
## Implementacje przykładowe
Poniższe implementacje są „szkieletami” (reference implementations). W obu wariantach zakładam brak narzuconych wymagań co do skali, więc pokazuję rozwiązania, które da się skalować horyzontalnie (wektor DB) i/lub uprościć (pgvector zamiast osobnej bazy).
### Stack A: opensource embeddings + opensource Vector DB (Sentence Transformers + Qdrant) + graf w Neo4j
**Kiedy wybrać:** gdy chcesz uniezależnić embeddingi od dostawcy, mieć pełną kontrolę nad danymi i implementować multitenancy/filtry wprost w wektor DB. Payload/filtry i multitenancy są natywnie wspierane w Qdrant. citeturn11search6turn14search1turn14search7
**Zależności (przykład):** `sentence-transformers`, `qdrant-client`, `neo4j`, parser dokumentów (`unstructured` lub Tika).
```python
# --- Ingestion: parse -> units -> embeddings -> Qdrant + graph ---
from dataclasses import dataclass
from typing import Iterable, Optional
import hashlib
import time
from sentence_transformers import SentenceTransformer, CrossEncoder
from qdrant_client import QdrantClient, models as qmodels
from neo4j import GraphDatabase
@dataclass
class KnowledgeUnit:
unit_id: str
source_id: str
version: str
unit_type: str # e.g. "section", "definition", "table"
text: str # canonical text for embedding
page: Optional[int] = None
section_path: Optional[str] = None
acl: str = "public" # e.g. role/tenant tag
def stable_id(source_id: str, version: str, unit_type: str, page: str, text: str) -> str:
raw = f"{source_id}|{version}|{unit_type}|{page}|{text}".encode("utf-8")
return hashlib.sha256(raw).hexdigest()[:24]
# 1) Embeddings (bi-encoder) + reranker (cross-encoder)
embed_model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2") # example
reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2") # example
# 2) Vector DB: Qdrant
qdrant = QdrantClient(url="http://localhost:6333", timeout=30)
COLLECTION = "kmrag_units"
DIM = embed_model.get_sentence_embedding_dimension()
qdrant.recreate_collection(
collection_name=COLLECTION,
vectors_config=qmodels.VectorParams(size=DIM, distance=qmodels.Distance.COSINE),
)
# Index payload fields frequently used in filters (performance)
qdrant.create_payload_index(
collection_name=COLLECTION,
field_name="acl",
field_schema=qmodels.PayloadSchemaType.KEYWORD,
)
qdrant.create_payload_index(
collection_name=COLLECTION,
field_name="source_id",
field_schema=qmodels.PayloadSchemaType.KEYWORD,
)
# 3) Graph DB: Neo4j (property graph)
neo4j_driver = GraphDatabase.driver(
"neo4j://localhost:7687", auth=("neo4j", "password")
)
def upsert_units(units: Iterable[KnowledgeUnit]) -> None:
batch = list(units)
# embeddings
vectors = embed_model.encode([u.text for u in batch], normalize_embeddings=True)
# upsert into Qdrant with payload metadata (provenance + ACL)
qdrant.upsert(
collection_name=COLLECTION,
points=[
qmodels.PointStruct(
id=u.unit_id,
vector=vectors[i].tolist(),
payload={
"source_id": u.source_id,
"version": u.version,
"unit_type": u.unit_type,
"page": u.page,
"section_path": u.section_path,
"acl": u.acl,
"ingested_at": int(time.time()),
},
)
for i, u in enumerate(batch)
],
)
# build/update graph nodes + relationships
cypher = """
UNWIND $rows AS r
MERGE (d:Document {source_id: r.source_id, version: r.version})
MERGE (u:Unit {unit_id: r.unit_id})
SET u.unit_type = r.unit_type,
u.page = r.page,
u.section_path = r.section_path
MERGE (d)-[:HAS_UNIT]->(u)
"""
with neo4j_driver.session() as s:
s.run(cypher, rows=[u.__dict__ for u in batch])
# --- Query-time retrieval: vector -> graph expansion -> rerank -> context ---
def retrieve(query: str, acl: str, top_k: int = 30, rerank_k: int = 8):
qvec = embed_model.encode([query], normalize_embeddings=True)[0].tolist()
# 1) Candidate generation with metadata filter (permission-aware)
hits = qdrant.search(
collection_name=COLLECTION,
query_vector=qvec,
limit=top_k,
query_filter=qmodels.Filter(
must=[qmodels.FieldCondition(key="acl", match=qmodels.MatchValue(value=acl))]
),
)
candidate_ids = [h.id for h in hits]
# 2) Graph expansion: pull neighbor units from same document/section (simple example)
expand_cypher = """
MATCH (u:Unit) WHERE u.unit_id IN $ids
OPTIONAL MATCH (d:Document)-[:HAS_UNIT]->(u)
OPTIONAL MATCH (d)-[:HAS_UNIT]->(u2:Unit)
WHERE u2.section_path = u.section_path
RETURN DISTINCT u2.unit_id AS unit_id
LIMIT 200
"""
with neo4j_driver.session() as s:
rows = s.run(expand_cypher, ids=candidate_ids).data()
expanded_ids = list({r["unit_id"] for r in rows}) or candidate_ids
# 3) Fetch texts for reranking (here: from Qdrant payload 'text' not stored; you'd load from your storage)
# In production: keep canonical text in your doc store; Qdrant payload keeps provenance only.
# For demo: assume we can map id->text elsewhere:
id_to_text = load_texts(expanded_ids) # implement in your system
pairs = [(query, id_to_text[i]) for i in expanded_ids]
scores = reranker.predict(pairs)
ranked = sorted(zip(expanded_ids, scores), key=lambda x: x[1], reverse=True)[:rerank_k]
return ranked # list of (unit_id, score) + you can also return provenance from payload
def load_texts(unit_ids):
# Placeholder: pull canonical text from your document store / data lake
raise NotImplementedError
```
Co w tym szkielecie jest „Mapą Wiedzy”: Neo4j przechowuje relacje (Document→Unit, a dalej możesz dodać Entity↔Unit, REFERENCES, NEXT), a Qdrant przechowuje wektory + payload do filtrowania; filtrowanie i indeksowanie payload jest sformalizowane w dokumentacji Qdrant. citeturn11search6turn11search2turn14search7
Rerank to klasyczny krok „retrievethenrerank” opisywany przez Sentence Transformers, gdzie CrossEncoder podnosi jakość finalnych wyników kosztem obliczeń. citeturn12search11turn12search19
### Stack B: managed LLM + Vector DB (OpenAI + Pinecone) + graf (Neo4j GraphRAG / Text2Cypher)
**Kiedy wybrać:** gdy zależy Ci na szybkości iteracji, jakości modeli oraz gotowych mechanizmach „structured output”, a retrieval chcesz oprzeć o managed vector DB z namespaces i hybrid search. citeturn13search1turn14search16turn11search3
W wariancie managed sensownie jest też wykorzystać Structured Outputs do wymuszenia formatu odpowiedzi (np. `answer` + `citations[]`), co jest elementem „kontroli” i audytu. OpenAI opisuje Structured Outputs jako mechanizm gwarantujący zgodność odpowiedzi z JSON Schema. citeturn13search1turn13search8
```python
# --- Managed stack: OpenAI embeddings + Pinecone + structured outputs + graph retrieval ---
from openai import OpenAI
from pinecone import Pinecone
from neo4j_graphrag import GraphRAG # example usage; adjust to actual package API
OPENAI_MODEL_EMB = "text-embedding-3-large"
OPENAI_MODEL_GEN = "gpt-5.4-mini" # example; choose by latency/cost needs
client = OpenAI()
pc = Pinecone(api_key="PINECONE_API_KEY")
index = pc.Index("kmrag")
def embed(texts):
resp = client.embeddings.create(model=OPENAI_MODEL_EMB, input=texts)
return [d.embedding for d in resp.data]
def upsert_to_pinecone(units, namespace):
vecs = embed([u["text"] for u in units])
index.upsert(
vectors=[
(u["unit_id"], vecs[i], {
"source_id": u["source_id"],
"version": u["version"],
"unit_type": u["unit_type"],
"page": u.get("page"),
"section_path": u.get("section_path"),
"acl": u.get("acl"),
})
for i, u in enumerate(units)
],
namespace=namespace, # multitenancy / workspace isolation
)
def retrieve_candidates(query, namespace, acl, top_k=30):
qvec = embed([query])[0]
res = index.query(
vector=qvec,
top_k=top_k,
include_metadata=True,
namespace=namespace,
filter={"acl": {"$eq": acl}},
)
return res["matches"]
# Optional: graph retrieval pattern via Text2Cypher (Neo4j GraphRAG package)
# The idea: use graph schema + question -> generated Cypher -> execute -> return records as extra grounded context.
gr = GraphRAG(neo4j_uri="neo4j+s://...", user="neo4j", password="...")
ANSWER_SCHEMA = {
"name": "kmrag_answer",
"schema": {
"type": "object",
"properties": {
"answer": {"type": "string"},
"citations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"unit_id": {"type": "string"},
"source_id": {"type": "string"},
"quote": {"type": "string"}
},
"required": ["unit_id", "source_id"]
}
},
"confidence": {"type": "number", "minimum": 0, "maximum": 1}
},
"required": ["answer", "citations", "confidence"]
}
}
def answer(query, namespace, acl):
hits = retrieve_candidates(query, namespace, acl)
text_context = "\n\n".join(
f"[{m['id']}] ({m['metadata'].get('source_id')}) {load_unit_text(m['id'])}"
for m in hits[:8]
)
graph_context = gr.text2cypher_retrieve(query) # e.g. definitions, relationships
system = (
"Odpowiadasz wyłącznie na podstawie kontekstu i grafu.\n"
"Jeśli brakuje danych, powiedz wprost, czego nie wiesz.\n"
"Zwróć cytowania do unit_id/source_id."
)
resp = client.responses.create(
model=OPENAI_MODEL_GEN,
input=[
{"role": "system", "content": system},
{"role": "user", "content": f"Pytanie: {query}\n\nKontekst:\n{text_context}\n\nGraf:\n{graph_context}"}
],
text={
"format": {
"type": "json_schema",
"json_schema": {**ANSWER_SCHEMA, "strict": True}
}
}
)
return resp.output_text
def load_unit_text(unit_id):
# fetch canonical unit text from your storage
raise NotImplementedError
```
Źródła dla tego stosu: OpenAI opisuje nowe modele embeddingowe (`text-embedding-3-small/large`) i guide embeddings, a także Structured Outputs i evaluation best practices. citeturn13search2turn13search1turn13search3turn13search9
Pinecone opisuje hybrydę oraz filtrowanie po metadanych i multitenancy przez namespaces. citeturn11search3turn11search7turn14search16
Wzorzec Text2Cypher tłumaczenie pytania + schematu grafu na Cypher i wykonanie query jest opisany w materiałach Neo4j. citeturn10search2turn10search6turn10search10
## Kontrola jakości, audyt i monitoring
„Kontrola zamiast nadziei” warto potraktować jako trzy warstwy: (A) kontrola danych i retrieval, (B) kontrola generacji, (C) kontrola procesu (ewaluacja i monitoring).
### Metryki i ewaluacja
**Ewaluacja retrieval** (czyt. „czy przynosimy właściwy kontekst”)
- Recall@K / Precision@K / MRR / NDCG: standardowe metryki IR; w pracach o retrieval z grafami i/lub KG są one explicite używane do oceny retrieval (np. praca o RAG+KG dla customer service raportuje MRR/Recall@K/NDCG@K). citeturn10search1turn10search5
- Offline test set buduj iteracyjnie na podstawie prawdziwych porażek (failure traces) to jest zgodne z podejściem „evaluation flywheel” i continuous evaluation. citeturn13search35turn13search3
**Ewaluacja generation** (czyt. „czy odpowiedź jest ugruntowana w źródłach”)
- RAGAS: framework do „referencefree evaluation” RAG, mierzący różne wymiary retrieval i generation. citeturn4search1turn4search5
- TruLens: RAG triad context relevance, groundedness, answer relevance jako praktyczny zestaw ocen dla halucynacji. citeturn4search2turn4search6
**Progi jakości (przykład)**
OpenAI w evaluation best practices podaje przykładowe targety (np. context recall ≥ 0.85, context precision > 0.7) jako część praktyk ewaluacji i porównywania wersji. Traktuj to jako punkt startowy, nie prawo natury. citeturn13search3
### Checklist audytu KMRAG
**Dane i ingestion**
- Czy parser zachowuje strukturę (sekcje/tabele/numery stron) i czy masz testy parsera na „trudnych dokumentach” (tabele, wielokolumnowe layouty)? citeturn16search0turn16search10turn6view0
- Czy każda jednostka wiedzy ma stabilne `source_id`, `version`, lokalizację i politykę retencji/PII? citeturn7view1turn14search2
**Mapa Wiedzy**
- Czy graf ma jasno zdefiniowane typy węzłów i relacje (HAS_UNIT, DEFINES, EXCEPTION_OF, REFERENCES, NEXT), oraz czy masz reguły walidacji (np. brak cykli w „NEXT”, spójność sekcji)? citeturn2view1turn10search31
- Czy ekstrakcja encji/relacji jest mierzalna (precision/recall) i odporna na duplikaty/rozbieżności nazw? (w praktyce: canonicalization + entity resolution). Koncepcja grafu encji jako indeksu jest centralna w GraphRAG. citeturn0search1turn0search13
**Retrieval**
- Czy stosujesz prefilter po ACL/tenant (permission-aware retrieval), zanim cokolwiek trafi do prompta? (mechanizmy namespaces/tenants i DLS/RBAC istnieją w narzędziach retrieval). citeturn14search16turn14search0turn14search2
- Czy masz hybrydę dense+sparse tam, gdzie słowa kluczowe są krytyczne (regulacje, numery, tickery)? Pinecone i Weaviate opisują hybrydę jako fuzję wyników. citeturn11search3turn12search0
- Czy reranking działa na topN, a nie na setkach wyników (koszt/latencja), i czy jest mierzony? citeturn12search11turn12search19
**Generacja i grounding**
- Czy model ma jasną instrukcję „answer from sources” oraz czy odpowiedź wymusza strukturę (JSON schema) i cytowania? Structured Outputs jest mechanizmem wspierającym niezawodność formatu. citeturn13search1turn13search8
- Czy masz mechanizm „I dont know / insufficient evidence” zamiast konfabulacji (np. minimalny próg evidence coverage)? Podejścia typu CoVe/SelfRAG/CRAG pokazują, że pętle weryfikacji i korekty podnoszą factuality. citeturn8search3turn9search1turn9search2
**Bezpieczeństwo**
- Czy testujesz prompt injection na poziomie aplikacji, nie tylko promptu? OWASP opisuje prompt injection jako manipulację zachowaniem modelu przez wejście, a cheat sheet sugeruje praktyki obrony. citeturn4search3turn4search7turn4search13
- Czy masz kontrolę kosztu (rate limits, timeouts, budżety tokenów) to też „kontrola”, bo DoS na LLM to realny wektor ryzyka (OWASP LLM Top 10 zawiera kategorie dot. DoS i supply chain). citeturn4search13turn13search12
### KPI i monitoring w produkcji
Rekomendowany zestaw KPI (z podziałem na warstwy):
**Retrieval KPI**
- Context Recall@K / Context Precision@K (offline i online na próbie logów). citeturn13search3turn4search1
- % zapytań, w których retrieval zwraca „pustkę” lub tylko niskie score (sugeruje routing lub brak danych).
**Generation KPI**
- Faithfulness/groundedness (TruLens/RAGAS). citeturn4search1turn4search6
- Citation coverage: % zdań mających przypisane źródło, oraz „citation accuracy” (czy cytat faktycznie zawiera wspierający fragment). SelfRAG raportuje poprawę citation accuracy w długich generacjach jako jeden z efektów frameworku. citeturn9search1turn9search9
**Ops KPI**
- Latencja p95/p99 per etap (retrieval, rerank, LLM).
- Koszt per zapytanie (tokeny, liczba wywołań modeli) + alerty „unbounded consumption”. OpenAI publikuje production best practices i evaluation tooling jako część przejścia prototyp → produkcja. citeturn13search12turn13search3
**Narzędzia do obserwowalności**
- RAGAS opisuje łączenie ewaluacji z tracingiem/analizą (np. Phoenix). citeturn4search34
- TruLens ma integracje i dokumentację quickstart dla trace + feedback. citeturn4search2turn4search27
- Jeśli używasz OpenAI, masz też guidance dot. ewaluacji i ciągłego monitorowania regresji. citeturn13search3turn13search6
### Typowe failure modes KMRAG i mitigacje
**„Graf rośnie w chaos” (sprawl, duplikaty encji, zła kanonikalizacja).**
Mitigacja: wprowadź entity resolution, reguły normalizacji nazw, walidację schematu grafu i testy na podzbiorze; zacznij od grafu dokumentsekcjaunit, dopiero potem dodawaj encje/relacje automatyczne. GraphRAG wprost zaczyna od grafu encji jako indeksu, ale też pipelineu budowy i transformacji danych, co sugeruje konieczność procesu, nie jednorazowego prompta. citeturn3search0turn0search1
**„Retrieval jest poprawny semantycznie, ale zły merytorycznie” (conflicts).**
Mitigacja: hybryda dense+sparse + rerank + kontrola jakości źródeł + mechanizmy korekty (CRAG: evaluator jakości retrieval i akcje naprawcze). citeturn9search2turn11search3turn12search0
**„Źródła przenoszą instrukcje (prompt injection z dokumentów)”**
Mitigacja: separacja „instructions vs data”, sanitation, polityki „nie wykonuj instrukcji z kontekstu”, oraz przede wszystkim permission-aware retrieval (prefilter). OWASP opisuje prompt injection i praktyki obrony. citeturn4search3turn4search7turn14search2
**„Latency/cost eksploduje przez reranking i graf”**
Mitigacja: ogranicz N rerankowanych kandydatów; cache embeddingów; cache wyników graf expansion; prestreszczenia (GraphRAG community summaries) dla klas pytań globalnych. citeturn12search11turn0search1turn3search4
**„Zgodność i audyt”**
Mitigacja: loguj trace: query → (filtry ACL) → dokumenty → fragmenty → odpowiedź; uzupełnij o standardy zarządzania ryzykiem i bezpieczeństwem (entity["organization","NIST","us standards institute"] AI RMF; entity["organization","ISO","international standards body"]/IEC 27001; entity["organization","OWASP","security foundation"] LLM Top 10). Zapewnia to język kontroli dla audytu, nawet jeśli implementacje są różne. citeturn15search1turn15search2turn4search13turn15search3
### Źródła priorytetowe do dalszej pracy
Najbardziej „nośne” (loadbearing) źródła do wdrożenia KMRAG, w kolejności praktycznej użyteczności:
Źródła autora: definicja Mapy Wiedzy (graf + metadane + odniesienia) oraz argument o „R jako ryzyku” i potrzebie kontroli retrieval. citeturn2view1turn7view1
Podstawy RAG: praca Lewis et al. (RAG jako retrieval + generacja z nieparametrycznej pamięci) jako fundament terminologiczny. citeturn0search2turn0search5
GraphRAG: publikacja i repozytorium Microsoft (graf encji, społeczności, streszczenia) jako referencyjny wariant Mapy Wiedzy w postaci pipelineu. citeturn0search1turn3search0turn3search4turn3search20
KGRAG / hybrydy: prace o łączeniu KG i RAG (np. HybridRAG; RAG+KG w customer service) pokazują, że graf zmniejsza skutki segmentacji i poprawia retrieval. citeturn10search0turn10search1
Ewaluacja i kontrola jakości: RAGAS + TruLens + best practices ewaluacji jako praktyczny „system kontroli”. citeturn4search1turn4search2turn13search3
Bezpieczeństwo: OWASP prompt injection i LLM Top 10 jako checklisty dla warstwy „R” i integracji z danymi. citeturn4search3turn4search13turn4search7