Files
pv-agent/agent/README.md
T
fegger b6a6f5b788 Ollama-URL korrigiert (100.103.83.12:11435) und M1/M2 gegen Echt-System validiert
Der Host betreibt zwei Ollama-Instanzen: 11434 (fast leer, 0.16.2) und
11435 (Ziel-Instanz, 0.32.13, Modell-Zoo) — Port nicht mehr auf 11434
'korrigieren' (Dokumentation + Skill + Config-Default angepasst).

Validierung gegen das echte System: bge-m3 per API gepullt, Hybrid-Index
(3005 Chunks, 100 % eingebettet, 144 s), M1-Akzeptanz erreicht (Recall@8
0,952 > 0,9; Hit-Rate 0,968). Antwortmodus-Eval mit qwen3.8:27b (Thinking
verifiziert aus): Zitier-Präzision 100 % (4 Regenerierungen), Verweigerung
korrekt 94,3 %, Latenz mean 32 s. ATZ-Konfliktfall korrekt beide Werte
mit Warnung. generate.py: sources enthaelt jetzt nur zitierte Quellen;
Systemprompt ohne redundante Quellenzeile.

Bake-off-Feld (M3) bereits installiert: qwen3.6:27B, gemma4:26b,
mistral-small3.1:24b, gemma4:12B.
2026-09-14 22:09:09 +02:00

4.9 KiB
Raw Blame History

PV RAG Agent

Lokaler RAG-Agent für österreichische Personalverrechnung: beantwortet Fragen ausschließlich aus der kuratierten Wissensbasis (Layer 2, wissensbasis/, 601 Einträge) — mit ID- und Stand-Beleg, ohne Trainingswissen, ohne Web-Zugriff. Verbindliche Regeln: .agents/skills/pv-rag-agent/SKILL.md, Plan: planung.md.

Architektur (Kurzfassung)

wissensbasis/dokumente/*.md ──ingest──▶ data/index.db
                                        ├─ chunks (FTS5, BM25, Umlaut-Folding)
                                        ├─ vectors (bge-m3, Content-Hash-Cache)
                                        └─ Metadaten (stand, topic, tags, …)
Frage ──retrieve──▶ Hybrid BM25+Dense (RRF) + cross_ref-Erweiterung
      ──generate──▶ Ollama (Systemprompt, Zitierpflicht)
      ──validate──▶ zitierte IDs ⊆ Retrieved-Set? sonst 1× regenerieren, dann verweigern
  • Nur Layer 2 als Korpus (kuratiert, lizenzkonform). Layer-1-Volltexte (.lexis360/, .wiku/) bleiben außen vor (offener Lizenzpunkt).
  • Kein Ausweg nach außen: keine Tools, kein Browsing — der einzige HTTP-Client spricht mit Ollama.
  • Verweigerungspflicht: leeres/schwaches Retrieval → deterministische Antwort „Dazu enthält die Wissensbasis keine Aussage." (kein LLM-Call).

Schnellstart

pip install -r requirements.txt

# 1) Index bauen (mit Embeddings, wenn Ollama erreichbar)
python -m agent.cli ingest            # --no-embed erzwingt BM25-only

# 2) Frage im Terminal
python -m agent.cli ask "Wie hoch ist die AMS-Ersatzquote bei geblockter Altersteilzeit?"

# 3) Goldset-Evaluation (offline: Retrieval-Metriken)
python -m agent.cli eval
#    inkl. Antworten + Verweigerungsfälle (benötigt Ollama):
python -m agent.cli eval --answers --json-out data/eval-report.json

# 4) HTTP-API + Test-Chat
python -m agent.cli serve             # http://127.0.0.1:8080 (/ask, /health, /reindex)

Konfiguration (Umgebungsvariablen)

Variable Default Bedeutung
OLLAMA_URL http://100.103.83.12:11435 Ollama-Server (Ziel-Instanz; der Host betreibt zusätzlich eine fast leere Instanz auf 11434 — nicht "korrigieren")
PV_ANSWER_MODEL qwen3.8:27b Antwortmodell (provisorisch bis Bake-off M3)
PV_EMBED_MODEL bge-m3 Embedding-Modell
PV_DB_PATH data/index.db SQLite-Index
PV_KB_DIR wissensbasis Wissensbasis-Verzeichnis
PV_THINK false Thinking per Request (qwen3.8: default an)
PV_EMBED_OFF false true = BM25-only
PV_CONTEXT_BLOCKS 8 Kontextblöcke im Prompt
PV_PORT 8080 API-Port

Deployment auf dem Host (Ollama-Maschine)

# Ollama-Ziel-Instanz prüfen (Custom-Port! Achtung: auf dem Host läuft
# zusätzlich eine fast leere Instanz auf 11434 — nicht verwexseln)
curl http://100.103.83.12:11435/api/tags   # qwen3.8:27b, bge-m3, Bake-off-Feld installiert

# Vollständiger Index (BM25 + Dense)
python -m agent.cli ingest
python -m agent.cli eval
python -m agent.cli eval --answers --json-out data/eval-report.json   # Zitier-Präzision, Verweigerungen, Latenz

Baseline (2026-09-14, Hybrid BM25 + bge-m3, Ollama :11435)

Retrieval (Goldset, 31 Fragen): Hit-Rate 0,968 · Recall@8 0,952 · MRR 0,690 — M1-Ziel >0,9 erreicht (BM25-only war 0,855; die vier BM25-Fehltreffer behebt die Dense-Suche alle).

Antworten (qwen3.8:27b, Thinking aus, Temperatur 0,1): Zitier-Präzision 100 % (4 Zitierverletzungen wurden von der Post-Validierung abgefangen und regeneriert) · Verweigerung korrekt 94,3 % · erwartete Quelle zitiert 80,6 % · Latenz mean 32 s / p95 53 s.

Bekannte Fehlverweigerungen: q-008 (Abfertigung Verfügungsmöglichkeiten), q-031 (Mindestlohngesetz) — breite Fragen, Retrieval erfolgreich, Modell verweigert trotzdem (sicheres Versagensmuster; M3-Prompt-Tuning-Kandidat). Latenz-Hebel für M3: weniger Kontextblöcke, schnellere Kandidaten (gemma4:12B, MoE).

Dateien

agent/
  config.py         Env-Konfiguration
  kb.py             Layer-2-Parsing + kb.json-Gate
  normalize.py      Umlaut-Folding, FTS-Query-Bau
  ingest.py         Index-Bau (chunks + FTS5 + Vektoren-Cache)
  retrieve.py       Hybrid-Retrieval (BM25 + Dense, RRF, cross_refs)
  ollama_client.py  Ollama-HTTP (embed + chat, think-Fallback)
  generate.py       Systemprompt, Post-Validierung, Verweigerung
  api.py            FastAPI (/ask, /health, /reindex)
  cli.py            ingest | ask | eval | serve
  eval/             goldset.yaml + evaluate.py
web/index.html      Minimaler Test-Chat
tests/              41 Tests (offline, Fake-Ollama)
data/               index.db (gitignored)

Tests

python -m pytest -q     # 41 Tests, alle offline

Lizenz-Disziplin

.lexis360/, .wiku/, .firecrawl/, .ris/ sind lokal und unversioniert (.gitignore). Der Index enthält ausschließlich Layer-2-Kuratierung; Layer-1-Prompts wären ein Lizenzverstoß und sind im Code nicht vorgesehen.