Files
pv-agent/docs/API.md
T

6.8 KiB
Raw Blame History

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
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_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:

{
  "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.
  • 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.

Der Client darf verified=false nicht als normale Fachantwort darstellen. Empfohlen ist ein sichtbarer Warnzustand ohne automatische Folgeverarbeitung.

Request-Korrelation

Ein Client kann einen technisch neutralen Header mitsenden:

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.