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

121 lines
4.9 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.
# 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
```bash
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)
```bash
# 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
```bash
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.