feat(scanner): Funde mit Akquise und Opportunities abgleichen
Neu gefundene Projekte mit Treffer werden einmalig per gpt-5.6-luna mit den Akquise-Eintraegen und Opportunities der letzten N Tage (Vorgabe 14) verglichen. Passt ein Eintrag, zeigt der Scanner "bereits beworben!" samt Begruendung. Schalter und Zeitraum (Tage/Wochen) in Einstellungen -> Scanner, Neupruefung von Hand im Detail. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
87ca52a34b
commit
af3ca33743
+71
-1
@@ -40,12 +40,81 @@ sequenceDiagram
|
||||
P->>M: E-Mail an Agent oder Standardempfaenger
|
||||
end
|
||||
end
|
||||
|
||||
opt Abgleich aktiv, Projekt hat Treffer, noch nicht geprüft
|
||||
P->>P: Akquise und Opportunities der letzten N Tage laden
|
||||
P->>AI: gpt-5.6-luna: Projekt vs. CRM-Einträge
|
||||
AI-->>P: { matches: [{ type, id, reason }] }
|
||||
P->>DB: already_applied, applied_refs, applied_check_at
|
||||
end
|
||||
```
|
||||
|
||||
Zwei Sparmassnahmen stecken bewusst **vor** dem OpenAI-Aufruf: der vorhandene
|
||||
Treffer (Unique-Key `agent_id, project_id`) und die Ausschlussbegriffe. Ein
|
||||
Projekt, das „Arbeitnehmerüberlassung" enthält, kostet damit keinen Cent.
|
||||
|
||||
## Abgleich „bereits beworben!"
|
||||
|
||||
Dieselbe Ausschreibung kommt oft mehrfach: über zwei Börsen, über mehrere
|
||||
Vermittler, mit anderem Titel und anderem Link. Damit niemand ein Projekt ein
|
||||
zweites Mal anbietet, gleicht `services/appliedCheck.js` jeden Fund mit der
|
||||
**Akquise-Liste** (`crm_acquisitions`) und den **Opportunities**
|
||||
(`crm_opportunities`) ab. Steht er dort schon, trägt der Treffer im Dashboard,
|
||||
unter *Unpassend* und im Detail die Markierung **bereits beworben!**; das Detail
|
||||
nennt die passenden Einträge samt Begründung.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["Projekt im Lauf verarbeitet"] --> B{"applied_check_active?"}
|
||||
B -->|nein| X["nichts tun"]
|
||||
B -->|ja| C{"schon geprüft?<br/>applied_check_at"}
|
||||
C -->|ja| X
|
||||
C -->|nein| D{"mindestens ein Treffer?"}
|
||||
D -->|nein| X
|
||||
D -->|ja| E["Kandidaten laden:<br/>Akquise found_at, Opportunities created_at<br/>der letzten N Tage, ohne Papierkorb"]
|
||||
E --> F{"Kandidaten?"}
|
||||
F -->|nein| N["nicht beworben"]
|
||||
F -->|ja| G{"gleicher Link?"}
|
||||
G -->|ja| Y["bereits beworben"]
|
||||
G -->|nein| H["gpt-5.6-luna, reasoning_effort low"]
|
||||
H -->|Treffer mit bekannter Id| Y
|
||||
H -->|keine| N
|
||||
N --> S["am Projekt speichern"]
|
||||
Y --> S
|
||||
```
|
||||
|
||||
| Einstellung | Vorgabe | Bedeutung |
|
||||
| --- | --- | --- |
|
||||
| `applied_check_active` | `true` | Schalter in *Einstellungen → Scanner* |
|
||||
| `applied_check_days` | `14` | Zeitraum zurück, in Tagen (Oberfläche: Tage oder Wochen), höchstens 365 |
|
||||
|
||||
Entscheidungen dahinter:
|
||||
|
||||
- **Eigenes Modell.** Die Bewertung CV gegen Projekt bleibt bei `gpt-4o-mini`;
|
||||
der Abgleich nutzt `gpt-5.6-luna` (Konstante `APPLIED_CHECK_MODEL`). Er muss
|
||||
umformulierte Ausschreibungen als dieselbe erkennen und ähnliche Rollen bei
|
||||
anderem Kunden auseinanderhalten - das ist ein Urteil, kein Textvergleich.
|
||||
Der Schlüssel ist derselbe `openai_api_key`.
|
||||
- **Einmal je Projekt, nur mit Treffer.** Die Börsen liefern bei jedem Lauf
|
||||
dieselben Projekte erneut; `applied_check_at` verhindert, dass sie jedes Mal
|
||||
bezahlt werden. Projekte ohne Treffer zeigt die Oberfläche nie an.
|
||||
- **Gleicher Link braucht keine KI.** Verglichen wird ohne Protokoll, `www`,
|
||||
Query und Schrägstrich am Ende.
|
||||
- **Nur Ids, die wir geschickt haben.** Nennt das Modell eine unbekannte Id,
|
||||
zählt sie nicht.
|
||||
- **Fehler setzen aus, nicht ab.** Scheitert der Aufruf (Schlüssel, Quote,
|
||||
Netz), bleibt der Abgleich für den Rest des Laufs aus und das Projekt
|
||||
ungeprüft - der nächste Lauf versucht es wieder. Die Bewertung läuft davon
|
||||
unberührt weiter.
|
||||
- **Momentaufnahme.** Geprüft wird beim Fund. Wird die Bewerbung erst danach
|
||||
in der Akquise erfasst, holt *Auf Bewerbung prüfen* im Detail den Abgleich
|
||||
von Hand nach (`POST /projects/:id/applied-check`, auch bei abgeschaltetem
|
||||
Automatismus).
|
||||
|
||||
Die drei Spalten an `projects` legt `initScannerDatabase()` per
|
||||
`ALTER TABLE ... ADD COLUMN IF NOT EXISTS` an - `sync()` ohne `alter` tut das in
|
||||
bestehenden Tabellen nicht.
|
||||
|
||||
## Wann läuft er
|
||||
|
||||
```mermaid
|
||||
@@ -215,7 +284,7 @@ Zertifikatswarnung. Der DNS-Eintrag muss beim Anbieter gelöscht werden.
|
||||
| Unpassend | `ScannerRejected.tsx` | Treffer mit Status `rejected` |
|
||||
| Suchagenten | `ScannerAgents.tsx` | Beraterprofile, CV-Upload |
|
||||
| Börsen-Konten | `ScannerPortals.tsx` | Verbindungsstatus, Zugangsdaten, Cookie-Import |
|
||||
| Einstellungen | `settings/ScannerSettings.tsx` | Intervall, Schwellenwerte, OpenAI, Ton, E-Mail |
|
||||
| Einstellungen | `settings/ScannerSettings.tsx` | Intervall, Schwellenwerte, Abgleich mit Akquise/Opportunities, OpenAI, Ton, E-Mail |
|
||||
|
||||
**Wichtig zum Ton:** Er hängt an der Dashboard-Komponente. Wer auf einen anderen
|
||||
Menüpunkt wechselt, hört nichts - die Komponente wird ausgebaut und die
|
||||
@@ -240,6 +309,7 @@ Alle unter `/api/scanner`, alle hinter `requireAuth`:
|
||||
| POST | `/portals/test-connection/:id` | Mini-Scrape bzw. Session-Prüfung |
|
||||
| GET | `/matches` | Filter: `status`, `agentId`, `minPercentage`, `limit` |
|
||||
| PUT/DELETE | `/matches/:id` | Status setzen, löschen |
|
||||
| POST | `/projects/:id/applied-check` | Abgleich „bereits beworben!" von Hand wiederholen |
|
||||
| GET/POST | `/settings` | Geheimnisse maskiert |
|
||||
| POST | `/scan` | Lauf starten, `409` wenn schon einer läuft |
|
||||
| GET | `/status` | läuft ein Lauf, Scheduler an, Datenbankname |
|
||||
|
||||
Reference in New Issue
Block a user