Files
perinco-CRM/docs/14-ki.md
T

6.1 KiB

14 KI: Schlüssel und Modelle

Das CRM ruft OpenAI an drei Stellen auf. Schlüssel und Modell pflegen Administratoren unter Einstellungen → KI - ohne SSH, ohne Neustart.

Einstellungen, Reiter KI: Schlüssel mit den letzten vier Zeichen, Modell je Einsatzzweck

Wo die KI arbeitet

Einsatzzweck Code Schlüssel in crm_settings Vorgabe
Scanner: Bewertung Lebenslauf gegen Projekt scanner/services/matchingEngine.js ai_model_matching gpt-4o-mini
Scanner: Abgleich „bereits beworben" scanner/services/appliedCheck.js ai_model_applied gpt-5.6-luna
Erweiterte Suche routes/search.js ai_model_search gpt-4o-mini (bzw. OPENAI_MODEL)

Die Vorgaben sind genau die Modelle, die vor der Einstellung fest im Code standen. Wer nichts umstellt, merkt also nichts. Steht in crm_settings kein oder ein ungültiger Eintrag, gilt die Vorgabe.

flowchart LR
    UI["Einstellungen → KI"] -->|"PUT /api/ai/key"| V{"GET /v1/models<br/>mit neuem Schlüssel"}
    V -->|"abgelehnt"| X["Fehlermeldung,<br/>alter Schlüssel bleibt"]
    V -->|"ok"| K[("perinco_scanner.settings<br/>openai_api_key")]
    UI -->|"PUT /api/ai/models"| M[("perinco_CRM.crm_settings<br/>ai_model_*")]
    K --> AI["server/src/ai.js<br/>aiKey() / modelFor()"]
    E["OPENAI_API_KEY<br/>server/.env"] -.->|"nur wenn DB leer"| AI
    M --> AI
    AI --> S1["Scanner-Bewertung"]
    AI --> S2["Abgleich bereits beworben"]
    AI --> S3["Erweiterte Suche"]

Schlüssel

Es gibt genau einen OpenAI-Schlüssel für Scanner und CRM. Er liegt unverändert als openai_api_key in der Scanner-Tabelle settings - dort, wo er schon immer lag. Nichts musste umziehen. Ersatzweise gilt OPENAI_API_KEY aus server/.env.

  • Anzeige nur mit Ende. Die Oberfläche zeigt sk-… und die letzten vier Zeichen, dazu die Quelle (hier hinterlegt oder server/.env). Mehr gibt der Server nie heraus, auch nicht an Administratoren. Bei Schlüsseln unter zwölf Zeichen steht nur ja.
  • Vor dem Speichern geprüft. Ein neuer Schlüssel wird erst gegen GET https://api.openai.com/v1/models ausprobiert. Lehnt OpenAI ab, bleibt der alte gültig, und die Meldung von OpenAI steht im Fehlertext. Der Abruf kostet nichts.
  • Verwerfen leert den hinterlegten Wert. Danach gilt wieder die .env - oder, wenn dort nichts steht, gibt es keinen Schlüssel mehr.
  • Der Schlüssel liegt unverschlüsselt in der Scanner-Datenbank, wie bisher. Ein Dump von perinco_scanner enthält ihn also.

Im Reiter Scanner gibt es kein Schlüsselfeld mehr. POST /api/scanner/settings nimmt openai_api_key aus Kompatibilität weiter an.

Modelle

Das Dropdown füllt sich live von OpenAI: alle Modelle, die der Schlüssel nutzen darf, gefiltert auf Textmodelle (gpt-*, o1/o3/o4…, chatgpt-*, ohne Audio, Bild, Embedding, Realtime, TTS, Suche, Moderation). Die aktuell eingestellten Modelle stehen immer mit drin, die Vorgabe ist markiert.

Ist die Liste nicht abrufbar (kein Schlüssel, OpenAI nicht erreichbar), zeigt das Dropdown eine feste Auswahl, und die Seite sagt, warum.

Die Wahl gilt ab dem nächsten Aufruf - es gibt keinen Zwischenspeicher.

Reasoning- und klassische Modelle

Die beiden Modellfamilien nehmen unterschiedliche Parameter. Damit ein Wechsel im Dropdown keinen 400 von OpenAI auslöst, entscheidet isReasoningModel() (Namen o1…, o3…, gpt-5…, gpt-6… und jede spätere Generation) je Aufruf:

Reasoning (o*, gpt-5*, gpt-6* …) klassisch (gpt-4o*, gpt-4.1*)
Scanner-Bewertung reasoning_effort: low temperature: 0.1
Abgleich „bereits beworben" reasoning_effort: low temperature: 0
Erweiterte Suche max_completion_tokens max_completion_tokens

max_completion_tokens verstehen beide Familien, das alte max_tokens die Reasoning-Modelle nicht.

Bis zum 24.09.2026 erkannte das Muster nur gpt-5…. gpt-6-luna galt damit als klassisch, bekam temperature: 0.1 und OpenAI lehnte jeden Aufruf ab („Only the default (1) value is supported"). Deshalb zählt jetzt jede Generation ab 5 als Reasoning-Modell.

Vergleich Scanner-Bewertung, 24.09.2026 (33 echte Projekte aus Lisa, Ariane und Tobias, neuer Bewertungsprompt, siehe 03 Scanner):

gpt-4o-mini gpt-6-luna (reasoning_effort: low)
Fehler 0 0
Ø Antwortzeit 1,3 s 2,8 s
Ø Token rein / raus 5337 / 108 5336 / 172 (davon 35 Reasoning)
passende Rollen 85-95 82-98
fachfremde Rollen meist 0 oder 30 0-15, feiner abgestuft
Preis je 1 Mio. Token rein / raus (Standard, Stand 24.09.2026) 0,15 / 0,60 0,10 / 0,50
Kosten je Bewertung (ohne Cache) 0,087 ct $ 0,062 ct $
hochgerechnet auf ~9 500 Bewertungen je Woche ~8,20 $ ~5,90 $

Luna ist also rund 28 % günstiger, obwohl es mehr Token ausgibt: die Eingabe (Prompt plus Lebenslauf, rund 5 300 Token) macht über 90 % der Kosten aus, und die ist bei Luna billiger. Gecachte Eingabe kostet bei Luna 0,01 $ statt 0,075 $ je 1 Mio. Token - Systemprompt und Lebenslauf stehen am Anfang jeder Anfrage eines Agenten, das Einsparpotenzial ist dort also noch größer.

Luna begründet genauer und bleibt nah am Lebenslauf. Grenzfälle (übertragbare Erfahrung) bewertet es spürbar großzügiger als gpt-4o-mini.

Endpunkte

Alle unter /api/ai, nur für Administratoren.

Methode Pfad Zweck
GET /settings Schlüsselzustand, Modell je Zweck, Auswahlliste
PUT /key { key } - prüfen, dann speichern
DELETE /key hinterlegten Schlüssel verwerfen
PUT /models { matching?, applied?, search? } - Namen geprüft gegen ^[A-Za-z0-9][A-Za-z0-9._:-]{0,99}$

Dateien

Datei Aufgabe
server/src/ai.js Schlüssel, Modellwahl, Modellliste, Aufruf der Suche
server/src/routes/ai.js die vier Endpunkte
src/components/settings/AiSettings.tsx Reiter KI
server/test/ai.test.mjs Modellnamen und Reasoning-Erkennung