All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 28s
CI / test-go-edu-search (push) Successful in 27s
CI / test-python-klausur (push) Successful in 1m45s
CI / test-python-agent-core (push) Successful in 16s
CI / test-nodejs-website (push) Successful in 21s
- edu-search-service von breakpilot-pwa nach breakpilot-lehrer kopiert (ohne vendor) - opensearch + edu-search-service in docker-compose.yml hinzugefuegt - voice-service aus docker-compose.yml entfernt (jetzt in breakpilot-core) - geo-service aus docker-compose.yml entfernt (nicht mehr benoetigt) - CI/CD: edu-search-service zu Gitea Actions und Woodpecker hinzugefuegt (Go lint, test mit go mod download, build, SBOM) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
410 lines
14 KiB
Markdown
410 lines
14 KiB
Markdown
# edu-search-service
|
|
|
|
Spezialisierter Suchdienst für deutsche Bildungsinhalte - eine Alternative zu Tavily, optimiert für den deutschen Bildungssektor.
|
|
|
|
## Übersicht
|
|
|
|
Der edu-search-service crawlt, extrahiert und indiziert Bildungsinhalte von deutschen Bildungsquellen (Kultusministerien, Bildungsserver, wissenschaftliche Studien, etc.) und stellt eine Such-API bereit.
|
|
|
|
### Features
|
|
|
|
- **BM25 Keyword-Suche** mit German Analyzer (OpenSearch)
|
|
- **Semantic Search** mit Embeddings (OpenAI oder Ollama)
|
|
- **Hybrid Search** kombiniert BM25 + Vektor-Ähnlichkeit
|
|
- **Automatisches Tagging** für Dokumenttyp, Fächer, Schulstufe, Bundesland
|
|
- **Trust-Score** basierend auf Domain-Reputation und Content-Qualität
|
|
- **Rate-Limited Crawler** mit robots.txt Respekt
|
|
- **Admin API** für Seed-Verwaltung und Crawl-Steuerung
|
|
|
|
## Architektur
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
|
│ edu-search-service │
|
|
├─────────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌─────────┐ ┌───────────┐ ┌────────┐ ┌─────────┐ │
|
|
│ │ Crawler │───▶│ Extractor │───▶│ Tagger │───▶│ Indexer │ │
|
|
│ └─────────┘ └───────────┘ └────────┘ └─────────┘ │
|
|
│ │ │ │
|
|
│ ▼ ▼ │
|
|
│ ┌─────────┐ ┌────────────┐ │
|
|
│ │ Seeds │ │ OpenSearch │ │
|
|
│ └─────────┘ └────────────┘ │
|
|
│ │ │
|
|
│ ┌────────────┐ │ │
|
|
│ │ Search API │◀──────────────────┘ │
|
|
│ └────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Komponenten
|
|
|
|
### Crawler (`internal/crawler/`)
|
|
- Rate-Limited HTTP Client (Standard: 0.2 req/sec pro Domain)
|
|
- Denylist-Support für ungewünschte Domains
|
|
- **Seeds aus Backend-API** (primär) oder lokale Seed-Files (Fallback)
|
|
- URL-Normalisierung und Deduplication
|
|
- Seed-Metadaten: Trust-Boost, Crawl-Tiefe, Kategorie, Bundesland
|
|
- **Crawl-Status-Feedback** an Backend (Dokumentenzahl, Dauer, Fehler)
|
|
|
|
### Robots (`internal/robots/`)
|
|
- **robots.txt Parser** mit Caching (24h TTL)
|
|
- Unterstützt `Disallow`, `Allow`, `Crawl-delay`
|
|
- Wildcard-Patterns (`*`) und End-Anchors (`$`)
|
|
- User-Agent-spezifische Regeln
|
|
- Leniente Behandlung bei fehlenden robots.txt
|
|
|
|
### Extractor (`internal/extractor/`)
|
|
- HTML-Extraktion mit goquery
|
|
- **PDF-Textextraktion** mit ledongthuc/pdf Bibliothek
|
|
- `ExtractPDF()` - Standard-Extraktion mit GetPlainText
|
|
- `ExtractPDFWithMetadata()` - Seiten-weise Extraktion für mehr Kontrolle
|
|
- Fallback-Extraktion bei beschädigten PDFs
|
|
- Automatische Titel-Erkennung (erste signifikante Zeile)
|
|
- Heading-Erkennung (All-Caps, nummerierte Zeilen)
|
|
- Metadaten-Extraktion (og:title, description, etc.)
|
|
- Content-Feature-Berechnung (Ad-Density, Link-Density)
|
|
- Sprach-Erkennung (Deutsch/Englisch)
|
|
|
|
### Tagger (`internal/tagger/`)
|
|
- Regelbasiertes Tagging via YAML-Konfiguration
|
|
- DocType-Erkennung (Lehrplan, Arbeitsblatt, Studie, etc.)
|
|
- Fächer-Erkennung (Mathematik, Deutsch, etc.)
|
|
- Schulstufen-Erkennung (Grundschule, Sek I/II, etc.)
|
|
- Bundesland-Erkennung aus URL-Patterns
|
|
- Trust-Score-Berechnung
|
|
|
|
### Quality (`internal/quality/`)
|
|
- **Multi-Faktor Quality-Score** (0-1)
|
|
- Content Length (20%)
|
|
- Heading Structure (15%)
|
|
- Link/Ad Quality (15%)
|
|
- Text-to-HTML Ratio (15%)
|
|
- Metadata Presence (10%)
|
|
- Language Clarity (10%)
|
|
- Content Freshness (10%)
|
|
- PDF-Specific Signals (5%)
|
|
- Konfigurierbare Gewichtungen
|
|
- Date-Indicator-Extraktion für Frische-Bewertung
|
|
|
|
### Indexer (`internal/indexer/`)
|
|
- OpenSearch 2.11 Client
|
|
- German Analyzer für BM25
|
|
- Bulk-Indexierung
|
|
- Custom Mapping für Bildungsdokumente
|
|
|
|
### Search (`internal/search/`)
|
|
- Multi-Match Query mit Boosting
|
|
- Filter für alle Taxonomie-Felder
|
|
- Function-Score mit Trust/Quality-Boosting
|
|
- Highlighting-Support
|
|
- **Drei Suchmodi:**
|
|
- `keyword` - Klassische BM25-Suche (Default)
|
|
- `semantic` - Reine Vektor-Ähnlichkeitssuche (k-NN)
|
|
- `hybrid` - Kombination aus BM25 und Vektor-Score
|
|
|
|
### Embedding (`internal/embedding/`)
|
|
- **OpenAI Provider** - `text-embedding-3-small` (1536 Dimensionen)
|
|
- **Ollama Provider** - Lokale Modelle (z.B. `nomic-embed-text`, 384-768 Dim.)
|
|
- Batch-Embedding für effiziente Indexierung
|
|
- Automatische Text-Kürzung (max. 30.000 Zeichen)
|
|
|
|
### Scheduler (`internal/scheduler/`)
|
|
- **Automatisches Crawling** in konfigurierbaren Intervallen
|
|
- Default: täglich um 2:00 Uhr (minimale Auswirkung)
|
|
- Manuelles Triggern via Admin-API
|
|
- Status-Tracking (letzter Lauf, nächster Lauf, Ergebnis)
|
|
|
|
## API Endpoints
|
|
|
|
### Public Endpoints
|
|
|
|
| Method | Endpoint | Beschreibung |
|
|
|--------|----------|--------------|
|
|
| GET | `/v1/health` | Health Check (kein Auth) |
|
|
| POST | `/v1/search` | Suche ausführen |
|
|
| GET | `/v1/document` | Einzeldokument abrufen |
|
|
|
|
### Admin Endpoints (Auth erforderlich)
|
|
|
|
| Method | Endpoint | Beschreibung |
|
|
|--------|----------|--------------|
|
|
| GET | `/v1/admin/seeds` | Alle Seeds abrufen |
|
|
| POST | `/v1/admin/seeds` | Neuen Seed erstellen |
|
|
| PUT | `/v1/admin/seeds/:id` | Seed aktualisieren |
|
|
| DELETE | `/v1/admin/seeds/:id` | Seed löschen |
|
|
| GET | `/v1/admin/stats` | Crawl-Statistiken |
|
|
| POST | `/v1/admin/crawl/start` | Crawl starten |
|
|
|
|
## API Dokumentation
|
|
|
|
### POST /v1/search
|
|
|
|
**Request Body:**
|
|
```json
|
|
{
|
|
"q": "Lehrplan Mathematik Gymnasium",
|
|
"mode": "keyword",
|
|
"limit": 10,
|
|
"offset": 0,
|
|
"filters": {
|
|
"language": ["de"],
|
|
"doc_type": ["Lehrplan"],
|
|
"school_level": ["Gymnasium"],
|
|
"state": ["BY", "NW"],
|
|
"subjects": ["Mathematik"],
|
|
"min_trust_score": 0.5
|
|
},
|
|
"include": {
|
|
"snippets": true,
|
|
"highlights": true
|
|
}
|
|
}
|
|
```
|
|
|
|
**Such-Modi (`mode`):**
|
|
| Mode | Beschreibung |
|
|
|------|--------------|
|
|
| `keyword` | BM25-Textsuche (Default) |
|
|
| `semantic` | Vektor-Ähnlichkeitssuche via Embeddings |
|
|
| `hybrid` | Kombination: 70% BM25 + 30% Vektor-Score |
|
|
|
|
> **Hinweis:** `semantic` und `hybrid` Modi erfordern `SEMANTIC_SEARCH_ENABLED=true` und konfigurierte Embedding-Provider.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"query_id": "q-12345",
|
|
"results": [
|
|
{
|
|
"doc_id": "uuid-...",
|
|
"title": "Lehrplan Mathematik Gymnasium Bayern",
|
|
"url": "https://www.isb.bayern.de/...",
|
|
"domain": "isb.bayern.de",
|
|
"language": "de",
|
|
"doc_type": "Lehrplan",
|
|
"school_level": "Gymnasium",
|
|
"subjects": ["Mathematik"],
|
|
"scores": {
|
|
"bm25": 12.5,
|
|
"trust": 0.85,
|
|
"quality": 0.9,
|
|
"final": 10.6
|
|
},
|
|
"snippet": "Der Lehrplan für das Fach Mathematik...",
|
|
"highlights": ["<em>Lehrplan</em> für das Fach <em>Mathematik</em>..."]
|
|
}
|
|
],
|
|
"pagination": {
|
|
"limit": 10,
|
|
"offset": 0,
|
|
"total_estimate": 156
|
|
}
|
|
}
|
|
```
|
|
|
|
### Filter-Optionen
|
|
|
|
| Filter | Werte |
|
|
|--------|-------|
|
|
| `language` | `de`, `en` |
|
|
| `doc_type` | `Lehrplan`, `Arbeitsblatt`, `Unterrichtsentwurf`, `Erlass_Verordnung`, `Pruefung_Abitur`, `Studie_Bericht`, `Sonstiges` |
|
|
| `school_level` | `Grundschule`, `Sek_I`, `Gymnasium`, `Berufsschule`, `Hochschule`, `Alle`, `NA` |
|
|
| `state` | `BW`, `BY`, `BE`, `BB`, `HB`, `HH`, `HE`, `MV`, `NI`, `NW`, `RP`, `SL`, `SN`, `ST`, `SH`, `TH` |
|
|
| `subjects` | `Mathematik`, `Deutsch`, `Englisch`, `Geschichte`, `Physik`, `Biologie`, `Chemie`, etc. |
|
|
|
|
## Konfiguration
|
|
|
|
### Umgebungsvariablen
|
|
|
|
| Variable | Beschreibung | Default |
|
|
|----------|--------------|---------|
|
|
| `PORT` | Server Port | `8084` |
|
|
| `OPENSEARCH_URL` | OpenSearch URL | `http://opensearch:9200` |
|
|
| `OPENSEARCH_USERNAME` | OpenSearch User | `admin` |
|
|
| `OPENSEARCH_PASSWORD` | OpenSearch Passwort | `admin` |
|
|
| `INDEX_NAME` | Index Name | `bp_documents_v1` |
|
|
| `USER_AGENT` | Crawler User Agent | `BreakpilotEduCrawler/1.0` |
|
|
| `RATE_LIMIT_PER_SEC` | Requests pro Sekunde/Domain | `0.2` |
|
|
| `MAX_DEPTH` | Max Crawl-Tiefe | `4` |
|
|
| `MAX_PAGES_PER_RUN` | Max Seiten pro Crawl | `500` |
|
|
| `SEEDS_DIR` | Seed-Dateien Verzeichnis | `./seeds` |
|
|
| `RULES_DIR` | Tagging-Regeln Verzeichnis | `./rules` |
|
|
| `EDU_SEARCH_API_KEY` | API Key für Auth | `` |
|
|
| `BACKEND_URL` | URL zum Python Backend | `http://backend:8000` |
|
|
| `SEEDS_FROM_API` | Seeds aus API laden | `true` |
|
|
| **Semantic Search** | | |
|
|
| `SEMANTIC_SEARCH_ENABLED` | Semantic Search aktivieren | `false` |
|
|
| `EMBEDDING_PROVIDER` | Provider: `openai`, `ollama`, `none` | `none` |
|
|
| `OPENAI_API_KEY` | API Key für OpenAI Embeddings | `` |
|
|
| `EMBEDDING_MODEL` | Embedding-Modell | `text-embedding-3-small` |
|
|
| `EMBEDDING_DIMENSION` | Vektor-Dimension | `1536` |
|
|
| `OLLAMA_URL` | Ollama Server URL | `http://ollama:11434` |
|
|
| **Scheduler** | | |
|
|
| `SCHEDULER_ENABLED` | Automatisches Crawling aktivieren | `false` |
|
|
| `SCHEDULER_INTERVAL` | Crawl-Intervall | `24h` (täglich) |
|
|
|
|
## Installation & Start
|
|
|
|
### Docker (empfohlen)
|
|
|
|
```bash
|
|
# Im edu-search-service Verzeichnis
|
|
docker compose up -d
|
|
|
|
# Logs anzeigen
|
|
docker compose logs -f edu-search
|
|
|
|
# Nur der Service (OpenSearch extern)
|
|
docker build -t edu-search-service .
|
|
docker run -p 8084:8084 \
|
|
-e OPENSEARCH_URL=http://host.docker.internal:9200 \
|
|
edu-search-service
|
|
```
|
|
|
|
### Lokal (Entwicklung)
|
|
|
|
```bash
|
|
# Dependencies installieren
|
|
go mod download
|
|
|
|
# Service starten
|
|
go run cmd/server/main.go
|
|
|
|
# Tests ausführen
|
|
go test -v ./...
|
|
```
|
|
|
|
## Seed-Kategorien
|
|
|
|
| Kategorie | Beschreibung | Beispiele |
|
|
|-----------|--------------|-----------|
|
|
| `federal` | Bundesweite Institutionen | KMK, BMBF, IQB |
|
|
| `states` | Landeskultusbehörden | Kultusministerien, Landesinstitute |
|
|
| `science` | Wissenschaftliche Studien | PISA, IGLU, TIMSS |
|
|
| `universities` | Hochschulen | Pädagogische Hochschulen |
|
|
| `schools` | Schulen direkt | Schulhomepages |
|
|
| `portals` | Bildungsportale | Lehrer-Online, 4teachers |
|
|
| `eu` | EU-Bildungsprogramme | Erasmus+, Eurydice |
|
|
| `authorities` | Schulbehörden | Regierungspräsidien |
|
|
|
|
## Tagging-Regeln
|
|
|
|
Die YAML-Regeldateien im `rules/` Verzeichnis definieren das Tagging:
|
|
|
|
- `doc_type_rules.yaml` - Dokumenttyp-Erkennung
|
|
- `subject_rules.yaml` - Fächer-Erkennung
|
|
- `level_rules.yaml` - Schulstufen-Erkennung
|
|
- `trust_rules.yaml` - Trust-Score-Berechnung
|
|
|
|
### Beispiel: doc_type_rules.yaml
|
|
|
|
```yaml
|
|
doc_types:
|
|
Lehrplan:
|
|
strong_terms:
|
|
- Lehrplan
|
|
- Kernlehrplan
|
|
- Bildungsplan
|
|
medium_terms:
|
|
- Curriculum
|
|
- Kompetenzerwartungen
|
|
url_patterns:
|
|
- /lehrplan
|
|
- /kernlehrplan
|
|
|
|
priority_order:
|
|
- Pruefung_Abitur
|
|
- Lehrplan
|
|
- Arbeitsblatt
|
|
```
|
|
|
|
## Projektstruktur
|
|
|
|
```
|
|
edu-search-service/
|
|
├── cmd/
|
|
│ └── server/
|
|
│ └── main.go # Entry Point
|
|
├── internal/
|
|
│ ├── api/
|
|
│ │ └── handlers/
|
|
│ │ ├── handlers.go # Search & Health Handler
|
|
│ │ └── admin_handlers.go # Admin API Handler
|
|
│ ├── config/
|
|
│ │ └── config.go # Konfiguration
|
|
│ ├── crawler/
|
|
│ │ ├── crawler.go # URL Fetcher
|
|
│ │ └── api_client.go # Backend API Client (Seeds)
|
|
│ ├── robots/
|
|
│ │ └── robots.go # robots.txt Parser & Checker
|
|
│ ├── embedding/
|
|
│ │ └── embedding.go # Embedding Provider (OpenAI/Ollama)
|
|
│ ├── extractor/
|
|
│ │ └── extractor.go # HTML/PDF Extraktion
|
|
│ ├── indexer/
|
|
│ │ └── mapping.go # OpenSearch Indexer
|
|
│ ├── pipeline/
|
|
│ │ └── pipeline.go # Crawl Orchestrierung
|
|
│ ├── quality/
|
|
│ │ └── quality.go # Multi-Faktor Quality Scoring
|
|
│ ├── scheduler/
|
|
│ │ └── scheduler.go # Automatisches Crawl-Scheduling
|
|
│ ├── search/
|
|
│ │ └── search.go # Search Service (Keyword/Semantic/Hybrid)
|
|
│ └── tagger/
|
|
│ └── tagger.go # Regelbasiertes Tagging
|
|
├── rules/
|
|
│ ├── doc_type_rules.yaml
|
|
│ ├── subject_rules.yaml
|
|
│ ├── level_rules.yaml
|
|
│ └── trust_rules.yaml
|
|
├── seeds/
|
|
│ ├── federal.txt
|
|
│ ├── states.txt
|
|
│ └── denylist.txt
|
|
├── Dockerfile
|
|
├── docker-compose.yml
|
|
├── go.mod
|
|
└── README.md
|
|
```
|
|
|
|
## Abhängigkeiten
|
|
|
|
| Package | Version | Beschreibung | Lizenz |
|
|
|---------|---------|--------------|--------|
|
|
| `github.com/gin-gonic/gin` | v1.9+ | HTTP Framework | MIT |
|
|
| `github.com/opensearch-project/opensearch-go/v2` | v2.3+ | OpenSearch Client | Apache-2.0 |
|
|
| `github.com/PuerkitoBio/goquery` | v1.8+ | HTML Parser | BSD-3-Clause |
|
|
| `github.com/ledongthuc/pdf` | v0.0.0-20240201 | PDF Text Extraktion | MIT |
|
|
| `gopkg.in/yaml.v3` | v3.0+ | YAML Parser | MIT |
|
|
| `github.com/google/uuid` | v1.4+ | UUID Generation | BSD-3-Clause |
|
|
| `golang.org/x/net` | v0.19+ | HTML Utilities | BSD-3-Clause |
|
|
|
|
## Tests ausführen
|
|
|
|
```bash
|
|
# Alle Tests
|
|
go test -v ./...
|
|
|
|
# Mit Coverage
|
|
go test -cover ./...
|
|
|
|
# Nur Tagger Tests
|
|
go test -v ./internal/tagger/...
|
|
|
|
# Nur Crawler Tests
|
|
go test -v ./internal/crawler/...
|
|
```
|
|
|
|
## Lizenz
|
|
|
|
Proprietär - BreakPilot GmbH
|
|
|
|
## Kontakt
|
|
|
|
- Security Issues: security@breakpilot.com
|
|
- Bugs: https://github.com/breakpilot/edu-search-service/issues
|