Skip to content

martin0ne/docs-qa

Repository files navigation

docs-qa — hybrid RAG nad folderem dokumentów

Zadaj pytanie naturalnym językiem nad folderem dokumentów → dostań odpowiedź z twardymi cytatami plik:linie. Retrieval hybrydowy: BM25 (leksykalny) + embeddingi (semantyczny, lokalny nomic przez LM Studio) łączone przez reciprocal rank fusion (RRF). Pure Python stdlib, zero zależności, 0 PLN (LM Studio lokalnie).

Dlaczego hybryda (teza: robustness)

Żadna pojedyncza metoda nie wystarcza. Na korpusie demo (realne embeddingi, top-5):

Zapytanie BM25 embeddingi RRF
"WIS stawka podatku..." (rzadki token) 1 5 2
"pismo zmieniające wartość transakcji..." (parafraza) poza top-5 3 4

BM25 sam gubi parafrazy bez wspólnych słów; embeddingi same gubią rzadkie tokeny w gęstym klastrze. RRF utrzymuje target w top-k w obu przypadkach (9/9 zapytań korpusu). To odporność, nie magia — RRF bywa neutralny lub lekko gorszy tam, gdzie jedna metoda była ostro lepsza (np. WIS: RRF=2 vs BM25=1). Pełna tabela: reports/hybryda-vs-pojedyncza.md.

Pipeline

folder → chunking → BM25 + embeddingi (pula 4·k) → RRF → top-k → odpowiedź z cytatami
                                                                  │
                                          tryb asystent (NL)  ────┴──── tryb --extractive
                                          (walidacja cytatów w kodzie)   (zero LLM)

Użycie

# Tryb asystent (wymaga LM Studio: Local Server + model czatowy + embeddingowy)
python3 docs_qa.py "do kiedy składam JPK" --docs corpus

# Tryb extractive (deterministyczny, bez LLM)
EMBED_MOCK=1 python3 docs_qa.py "faktura korygująca" --docs corpus --extractive

Przykładowy output: examples/sample-answer.md.

Honest RAG

  • Każdy inline cytat (plik:linie) jest walidowany w kodzie względem zwróconych fragmentów (nie tylko proszony w prompcie). Odpowiedź bez ważnego cytatu → „Nie znalazłem odpowiedzi w dokumentach".
  • Cytaty inline per-twierdzenie (nie zbiorcza sekcja na końcu).
  • Embeddingi padną → degradacja do BM25 z adnotacją (nie crash). LLM padnie → tryb extractive z adnotacją.

Jak to się ma do moich innych repo

  • agent-flow — wieloetapowy agent ReAct, retrieval leksykalny.
  • rag-lexical-vs-semantic — ławka: porównanie BM25 vs embeddingi (jedna metoda na raz).
  • docs-qa (to repo) — produkcyjna synteza: hybryda (BM25+embeddingi via RRF) + warstwa odpowiedzi.

Testy

EMBED_MOCK=1 python3 -m unittest discover -s tests -v   # offline, mechanika (CI)
python3 -m unittest tests.test_e2e_live -v              # teza robustness (wymaga LM Studio)

Ograniczenia (v0)

  • Honest-RAG — zakres gwarancji: walidacja potwierdza, że cytat WSKAZUJE fragment podany do LLM; NIE gwarantuje, że zdanie wokół cytatu jest wierne treści fragmentu. Pełna weryfikacja semantyczna = v1.
  • Brak progu odcięcia: embeddingi zawsze zwracają pulę kandydatów, więc każde pytanie daje k wyników (nawet bez sensownego dopasowania). Deterministyczne „nie wiem" opiera się na braku ważnego cytatu w trybie NL; próg cosinusa = v1.
  • Tryb mock (EMBED_MOCK=1) NIE jest semantyczny — testuje mechanikę, nie tezę. Co więcej, mock zawsze echuje cytat top-1, więc na pytanie spoza korpusu udaje trafienie; realny LLM dostaje instrukcję „jeśli fragmenty nie zawierają odpowiedzi, napisz 'Nie znalazłem...'". Dowód tezy = test_e2e_live.
  • rrf_k=60 kalibrowane na duże korpusy; na małym spłaszcza ranking (param --rrf-k).
  • Chunking dzieli na nagłówkach md; pliki bez nagłówków → grubsze cytaty. Brak scalania mikro-chunków.
  • Fallback łapie ConnectionError; inne wyjątki adaptera LLM nieobsłużone = v1.
  • W tabelach reports/ "poza top-5" / brak rangi = dokument poza pulą retrievera, nie zero wyników.

MIT License.

About

Hybrydowy RAG (BM25 + embeddingi, fuzja RRF) z cytatami plik:linie i walidacją cytatów w kodzie — demo nad syntetycznym korpusem polskich przepisów (JPK/KSeF/VAT/ZUS/CIT).

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages