8.0 KiB
PV RAG Agent — HTTP API v1
Zweck und Grenze
Die API ist der eigenständige Zugriffspunkt für CLI-, Web- und spätere Odoo-Clients. Version 1 ist ein zustandsloser Wissensdienst:
- verarbeitet ausschließlich
question, optionale Retrieval-Tiefetop_kund den festen Modusknowledge; - antwortet ausschließlich aus der Layer-2-Wissensbasis;
- nimmt keine Mandanten-, Mitarbeiter- oder Abrechnungsobjekte entgegen;
- speichert keinen Gesprächsverlauf;
- nutzt keine Websuche und keine externen Tools.
Personenbezogene Lohndaten dürfen nicht in den Fragetext eingebettet werden. Die spätere Odoo-Lohndatenintegration bekommt einen getrennten Vertrag mit Mandantenbindung, Rollenprüfung, Datenminimierung, Auditierung und definierter Aufbewahrung. Ein allgemeiner API-Key allein reicht dafür nicht aus.
Endpunkte
| Methode | Pfad | Auth | Zweck |
|---|---|---|---|
GET |
/ |
Eingabe im UI | Test-Frontend |
POST |
/v1/ask |
Service-Key | belegte Wissensantwort |
POST |
/v1/ratings |
Service-Key | Antwort bewerten |
GET |
/v1/health |
öffentlich | Readiness ohne interne Hostdetails |
POST |
/v1/reindex |
Admin-Key | Index nach KB-Änderung neu aufbauen |
Die bisherigen Pfade /ask, /health und /reindex bleiben vorläufig als
deprecated Kompatibilitätsrouten erhalten und liefern denselben Vertrag.
Test-Frontend
Das dependency-freie Frontend wird vom API-Prozess same-origin unter /
ausgeliefert. Dadurch sind keine CORS-Freigaben nötig. Es zeigt Dienststatus,
Antwort, zitierte Quellen, Quellenkonflikte, Rückfragen und technische
Grounding-Metadaten. Modellantworten werden nur als Text gerendert; HTML aus
einer Antwort wird nicht ausgeführt.
Für den vorgesehenen Tailscale-Host:
export PV_API_KEY='service-key-aus-secret-store'
python -m agent.cli serve --host 100.103.83.12
Danach ist die Oberfläche unter http://100.103.83.12:8080/ erreichbar. Der
Service-Key wird vom Benutzer im Frontend eingegeben und niemals serverseitig
in HTML oder JavaScript eingebettet. Optional speichert ihn die Oberfläche nur
im sessionStorage des aktuellen Browser-Tabs; Chatverlauf und Fragen werden
nicht im Browser gespeichert. Außerhalb eines verschlüsselten Tailnets ist vor
den Dienst ein TLS-Reverse-Proxy zu setzen.
Authentisierung
export PV_API_KEY='service-key-aus-secret-store'
export PV_ADMIN_API_KEY='separater-admin-key-aus-secret-store'
python -m agent.cli serve --host 127.0.0.1
Requests senden den Key als Bearer-Token:
Authorization: Bearer <key>
- Ist
PV_ADMIN_API_KEYleer, verwendet/v1/reindexden normalenPV_API_KEY. - Sind beide Variablen leer, läuft der Dienst zur lokalen Entwicklung ohne
Authentisierung. Der CLI-Start lehnt in diesem Zustand nicht-lokale Bind-
Adressen wie
0.0.0.0ab. Ein exponiertes Deployment muss mindestensPV_API_KEYsetzen. - Keys gehören in einen Secret Store bzw. eine nicht versionierte Umgebungsdatei; sie dürfen nicht in Odoo-Quellcode oder Git liegen.
- TLS wird am Reverse Proxy bzw. Service Mesh terminiert. Bearer-Tokens dürfen nicht unverschlüsselt über fremde Netze übertragen werden.
POST /v1/ask
Request:
{
"question": "Wie hoch ist der steuerfreie Tagesgeldsatz?",
"top_k": 8,
"mode": "knowledge"
}
Unbekannte Felder werden mit HTTP 422 abgewiesen. Das ist insbesondere die
technische Vertragsgrenze gegen ad-hoc-Felder wie employee_data oder
payroll_context.
Response (gekürzt):
{
"api_version": "v1",
"request_id": "odoo-request-123",
"status": "answered",
"question": "Wie hoch ist der steuerfreie Tagesgeldsatz?",
"answer": "... [lb-rei-09] ...",
"refused": false,
"verified": true,
"citations": ["lb-rei-09"],
"sources": [
{
"id": "lb-rei-09",
"title": "...",
"section": "Kernwerte & Fristen",
"stand": "2026-01",
"work": "..."
}
],
"conflicts": [],
"assumptions": [],
"clarification_question": null,
"alternatives": [],
"answer_type": "specific",
"planned": false,
"planned_queries": [],
"grounding": {
"data_scope": "knowledge_base_only",
"citations_verified": true,
"context_count": 8,
"regenerations": 0
},
"n_context": 8,
"model": "qwen3.8:27b",
"latency_ms": 38800,
"regenerations": 0
}
Semantik
status=answered: beantwortet und zitierseitig verifiziert.status=refused: Wissensbasis deckt die Frage nicht; sichere Verweigerung.status=uncertain: Regenerierung konnte die Grounding-Regeln nicht erfüllen.sourcesenthält nur tatsächlich zitierte Quellen, nicht sämtliche Retrieval-Treffer.conflictsenthält mit⚠markierte, bereits in der belegten Antwort vorkommende Konfliktpassagen und deren Quellen-IDs.clarification_questionwird aus einer abschließenden Rückfrage übernommen.assumptionsundalternativessind bereits stabile Vertragsfelder, bleiben in v1 aber leer. Der Dienst errät diese Strukturen nicht aus Freitext; ihre spätere Befüllung benötigt einen eigenen belegbaren Generierungsvertrag.grounding.data_scope=knowledge_base_onlyist in v1 unveränderlich.ratings_enabledzeigt, ob diese Antwort über/v1/ratingsbewertet werden kann.
Der Client darf verified=false nicht als normale Fachantwort darstellen.
Empfohlen ist ein sichtbarer Warnzustand ohne automatische Folgeverarbeitung.
POST /v1/ratings
Eine zuvor protokollierte Antwort kann über ihre request_id bewertet werden:
{
"request_id": "odoo-request-123",
"rating": "up",
"feedback": "Die Quellen beantworten die Frage nachvollziehbar."
}
rating ist up oder down, feedback optional und auf 1.000 Zeichen
begrenzt. Pro Antwort wird eine Bewertung gespeichert; ein weiterer Request
aktualisiert sie. Unbekannte Request-IDs liefern 404, deaktiviertes Audit
503. Das Frontend blendet die Bewertungsfunktion nur bei
ratings_enabled=true ein.
Audit-Protokoll
Bei PV_AUDIT_ENABLED=true werden Interaktionen und Bewertungen in der über
PV_AUDIT_DB_PATH festgelegten SQLite-Datei gespeichert. Mit
PV_AUDIT_STDOUT=true werden strukturierte JSON-Ereignisse zusätzlich mit dem
Präfix AUDIT nach stdout geschrieben. PV_AUDIT_LOG_CONTENT=false entfernt
Freitext einschließlich Frage, Antwort, Quellenbeschreibungen, Konflikten,
Suchplan und Bewertungskommentar; technische Metriken und KB-IDs bleiben.
API-Key und Authorization-Header werden nie protokolliert.
Request-Korrelation
Ein Client kann einen technisch neutralen Header mitsenden:
X-Request-ID: odoo-request-123
Erlaubt sind 1–128 Zeichen aus A-Z, a-z, 0-9, ., _, :, -.
Ungültige oder fehlende IDs werden durch eine zufällige ID ersetzt. Die ID
wird im Response-Header und Response-Body zurückgegeben. Keine Namen,
Personalnummern oder sonstigen personenbezogenen Daten als Request-ID nutzen.
Fehlerverhalten
401: fehlender oder falscher Bearer-Key;422: ungültiger Request bzw. nicht erlaubte Felder;503: Index, Modellserver oder Reindex vorübergehend nicht verfügbar.
Öffentliche Fehlerantworten enthalten keine internen Hosts, Dateipfade oder
Exception-Texte. Der Server loggt technische Fehler mit request_id.
Odoo-Anbindung der Wissens-API
Odoo soll /v1/ask serverseitig aufrufen, nicht direkt aus dem Browser:
- Service-URL und Key verschlüsselt bzw. als Deployment-Secret verwalten;
- Benutzerzugriff in Odoo über eine eigene Gruppe steuern;
- nur die Frage und eine nicht personenbezogene
X-Request-IDsenden; answer,sources,conflictsundclarification_questionrendern;statusundverifiedzwingend auswerten;- keine eigene Retrieval- oder Grounding-Logik in Odoo duplizieren.
Die Payroll-Datenintegration wird nicht durch zusätzliche freie JSON-Felder an
/v1/ask umgesetzt. Sie benötigt mindestens: Odoo-seitige Datensatzregeln,
service-seitige Tenant-Bindung, erlaubte Datenprojektionen statt Rohobjekten,
Zweckbindung, Audit-Events, kurze Aufbewahrung und Tests gegen Cross-Tenant-
Datenabfluss.