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 GetPlainTextExtractPDFWithMetadata()- 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:
{
"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:
semanticundhybridModi erfordernSEMANTIC_SEARCH_ENABLED=trueund konfigurierte Embedding-Provider.
Response:
{
"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)
# 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)
# 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-Erkennungsubject_rules.yaml- Fächer-Erkennunglevel_rules.yaml- Schulstufen-Erkennungtrust_rules.yaml- Trust-Score-Berechnung
Beispiel: doc_type_rules.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
# 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