Files
odoo-at-payroll/pv-agent/docs/API.md
T

241 lines
8.6 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 — 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-Tiefe `top_k`
und den festen Modus `knowledge`;
- 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 |
| `POST` | `/v1/comments` | Service-Key | Kommentar zu einer Antwort protokollieren |
| `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:
```bash
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
```bash
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:
```http
Authorization: Bearer <key>
```
- Ist `PV_ADMIN_API_KEY` leer, verwendet `/v1/reindex` den normalen
`PV_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.0` ab. Ein exponiertes Deployment muss mindestens
`PV_API_KEY` setzen.
- 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:
```json
{
"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):
```json
{
"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.
- `sources` enthält nur tatsächlich zitierte Quellen, nicht sämtliche
Retrieval-Treffer.
- `conflicts` enthält mit `⚠` markierte, bereits in der belegten Antwort
vorkommende Konfliktpassagen und deren Quellen-IDs.
- `clarification_question` wird aus einer abschließenden Rückfrage übernommen.
- `assumptions` und `alternatives` sind 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_only` ist in v1 unveränderlich.
- `ratings_enabled` zeigt, ob diese Antwort über `/v1/ratings` bewertet 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:
```json
{
"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.
## `POST /v1/comments`
Kommentare sind von der Daumenbewertung unabhängig. Pro Antwort können mehrere
Kommentare protokolliert werden:
```json
{
"request_id": "odoo-request-123",
"comment": "Bitte diesen Fall in das Goldset aufnehmen."
}
```
Der Kommentar wird getrimmt, darf nicht leer sein und ist auf 2.000 Zeichen
begrenzt. Die Antwort enthält eine fortlaufende `comment_id`. Unbekannte
Request-IDs liefern `404`, deaktiviertes Audit `503`.
## Audit-Protokoll
Bei `PV_AUDIT_ENABLED=true` werden Interaktionen und Bewertungen in der über
`PV_AUDIT_DB_PATH` festgelegten SQLite-Datei gespeichert. Mehrere Kommentare pro Antwort liegen
in der Tabelle `comments`. 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:
```http
X-Request-ID: odoo-request-123
```
Erlaubt sind 1128 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:
1. Service-URL und Key verschlüsselt bzw. als Deployment-Secret verwalten;
2. Benutzerzugriff in Odoo über eine eigene Gruppe steuern;
3. nur die Frage und eine nicht personenbezogene `X-Request-ID` senden;
4. `answer`, `sources`, `conflicts` und `clarification_question` rendern;
5. `status` und `verified` zwingend auswerten;
6. 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.