Semantische Zuordnung
Das ESFC-Glossar verwendet fortschrittliche KI-gestützte semantische Zuordnung, um Begriffe aus 10 verschiedenen Lebensmittel- und Ökobilanz-Vokabularen zu verbinden und ermöglicht so intelligente quellenübergreifende Begriffszuordnung und Beziehungsentdeckung.
Überblick
Die semantische Zuordnung erstellt Beziehungen zwischen Begriffen aus verschiedenen Quellen (FoodEx2, Hestia, ecoinvent usw.) unter Verwendung einer ausgefeilten 4-Stufen-Zuordnungskaskade, die exakte Zuordnung, Synonymerkennung und KI-Embeddings kombiniert.
Hauptfunktionen:
- KI-gestützte Zuordnung - OpenAI- und Google-KI-Embeddings
- 4-Stufen-Kaskade - Mehrere Zuordnungsstrategien mit Fallbacks
- Qualitätsvalidierung - Konfidenzwertung und Qualitätsanalyse der Zuordnung
- Interaktives Debugging - Echtzeit-Visualisierung der Zuordnung
- Zero Configuration - Fallback auf Mock-Modus ohne API-Schlüssel
Zuordnungsstrategie
4-Stufen-Kaskade
Das semantische Zuordnungssystem verwendet einen kaskadierenden Ansatz und probiert zunehmend ausgefeilte Methoden:
Stufe 1: Kontextuelle Zuordnung (Höchste Konfidenz)
↓ (wenn keine Übereinstimmung)
Stufe 2: Exakte Namensübereinstimmung
↓ (wenn keine Übereinstimmung)
Stufe 3: Synonymzuordnung
↓ (wenn keine Übereinstimmung)
Stufe 4: Semantische Embedding-Suche
Jede Stufe hat unterschiedliche Konfidenzniveaus und Anwendungsfälle.
Stufe 1: Kontextuelle Zuordnung
Methode: Kombiniert Name + Kategorie für die Zuordnung Konfidenz: 0,95 - 1,0 (Sehr hoch) Anwendungsfall: Wenn sowohl Name als auch Kategoriekontext übereinstimmen
Algorithmus:
function contextualMatch(term: Term, targetTerms: Term[]): Match | null {
const searchKey = `${term.name} ${term.category}`.toLowerCase()
for (const target of targetTerms) {
const targetKey = `${target.name} ${target.category}`.toLowerCase()
if (searchKey === targetKey) {
return {
sourceId: term.id,
targetId: target.id,
confidence: 1.0,
method: 'contextual'
}
}
}
return null
}
Beispiel:
Quelle: FoodEx2 "Apple" (Kategorie: "Fruits")
Ziel: Hestia "Apple" (Kategorie: "Inputs & Products")
Übereinstimmung: ✅ Kontextuell (Konfidenz: 1,0)
Stufe 2: Exakte Namensübereinstimmung
Methode: Exakter String-Abgleich bei normalisierten Namen Konfidenz: 0,85 - 0,95 (Hoch) Anwendungsfall: Identische Namen über verschiedene Quellen hinweg
Normalisierung:
- Umwandlung in Kleinbuchstaben
- Whitespace trimmen
- Sonderzeichen entfernen
- Plurale behandeln (optional)
Algorithmus:
function exactMatch(term: Term, targetTerms: Term[]): Match | null {
const normalized = normalizeName(term.name)
for (const target of targetTerms) {
if (normalized === normalizeName(target.name)) {
return {
sourceId: term.id,
targetId: target.id,
confidence: 0.9,
method: 'exact'
}
}
}
return null
}
function normalizeName(name: string): string {
return name.toLowerCase().trim().replace(/[^a-z0-9\s]/g, '')
}
Beispiel:
Quelle: "Wheat grain"
Ziel: "wheat grain"
Übereinstimmung: ✅ Exakt (Konfidenz: 0,9)
Stufe 3: Synonymzuordnung
Methode: Integriertes Synonymwörterbuch Konfidenz: 0,70 - 0,85 (Mittel-Hoch) Anwendungsfall: Bekannte alternative Namen und häufige Variationen
Synonymwörterbuch:
const SYNONYMS = {
'beef': ['cattle meat', 'bovine meat'],
'pork': ['pig meat', 'swine meat'],
'milk': ['dairy milk', 'cow milk'],
'wheat': ['common wheat', 'bread wheat'],
'rice': ['paddy rice', 'rice grain'],
'CO₂': ['carbon dioxide', 'co2 emission'],
'CH4': ['methane', 'methane emission'],
'N2O': ['nitrous oxide', 'n2o emission']
}
Algorithmus:
function synonymMatch(term: Term, targetTerms: Term[]): Match | null {
const termSynonyms = getSynonyms(term.name)
for (const target of targetTerms) {
const targetNormalized = normalizeName(target.name)
for (const synonym of termSynonyms) {
if (normalizeName(synonym) === targetNormalized) {
return {
sourceId: term.id,
targetId: target.id,
confidence: 0.8,
method: 'synonym',
matchedSynonym: synonym
}
}
}
}
return null
}
Beispiel:
Quelle: "Beef"
Ziel: "Cattle meat"
Übereinstimmung: ✅ Synonym (Konfidenz: 0,8, über "cattle meat")
Stufe 4: Semantische Embedding-Suche
Methode: KI-gestützte Vektorähnlichkeit mit Embeddings Konfidenz: 0,50 - 0,70 (Mittel) Anwendungsfall: Semantische Ähnlichkeit, wenn exakte Übereinstimmungen fehlschlagen
KI-Anbieter:
-
OpenAI (Empfohlen)
- Modell:
text-embedding-3-small - Dimensionen: 1536
- Kosteneffektiv und genau
- Modell:
-
Google Generative AI (Alternative)
- Modell:
text-embedding-004 - Dimensionen: 768
- Gute Alternative zu OpenAI
- Modell:
-
Mock-Modus (Fallback)
- Deterministische stringbasierte Embeddings
- Kein API-Schlüssel erforderlich
- Geeignet für Tests
Algorithmus:
async function semanticMatch(
term: Term,
targetTerms: Term[],
provider: 'openai' | 'google'
): Promise<Match | null> {
// Embedding für Quellbegriff generieren
const sourceEmbedding = await generateEmbedding(
`${term.name} ${term.category}`,
provider
)
let bestMatch: Match | null = null
let highestSimilarity = 0
for (const target of targetTerms) {
// Embedding für Zielbegriff generieren
const targetEmbedding = await generateEmbedding(
`${target.name} ${target.category}`,
provider
)
// Kosinus-Ähnlichkeit berechnen
const similarity = cosineSimilarity(sourceEmbedding, targetEmbedding)
if (similarity > highestSimilarity && similarity > 0.5) {
highestSimilarity = similarity
bestMatch = {
sourceId: term.id,
targetId: target.id,
confidence: similarity,
method: 'semantic',
similarity: similarity
}
}
}
return bestMatch
}
function cosineSimilarity(a: number[], b: number[]): number {
const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0)
const magnitudeA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0))
const magnitudeB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0))
return dotProduct / (magnitudeA * magnitudeB)
}
Beispiel:
Quelle: "Grass-fed beef cattle"
Ziel: "Extensive pasture cattle production"
Embedding-Ähnlichkeit: 0,72
Übereinstimmung: ✅ Semantisch (Konfidenz: 0,72)
KI-Anbieter-Integration
OpenAI-Konfiguration
# API-Schlüssel setzen
export OPENAI_API_KEY="sk-..."
# Semantische Zuordnung ausführen
npm run match-glossaries
OpenAI-Funktionen:
- Neueste Embedding-Modelle
- Hohe Genauigkeit für Lebensmittel-/Ökobilanz-Terminologie
- Angemessene API-Kosten
- Schnelle Antwortzeiten
Beispiel-API-Aufruf:
import OpenAI from 'openai'
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
})
async function getEmbedding(text: string): Promise<number[]> {
const response = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: text,
dimensions: 1536
})
return response.data[0].embedding
}
Google-KI-Konfiguration
# API-Schlüssel setzen
export GOOGLE_API_KEY="AIza..."
# Semantische Zuordnung mit Google ausführen
npm run match-glossaries
Google-KI-Funktionen:
- Alternative zu OpenAI
- Gute mehrsprachige Unterstützung
- Wettbewerbsfähige Preisgestaltung
- Zuverlässige Embeddings
Beispiel-API-Aufruf:
import { GoogleGenerativeAI } from '@google/generative-ai'
const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY!)
async function getEmbedding(text: string): Promise<number[]> {
const model = genAI.getGenerativeModel({ model: 'text-embedding-004' })
const result = await model.embedContent(text)
return result.embedding.values
}
Mock-Modus (Ohne API-Schlüssel)
# Ohne API-Schlüssel ausführen (Mock-Embeddings)
npm run match-glossaries:mock
Mock-Modus-Funktionen:
- Deterministische stringbasierte Embeddings
- Keine API-Kosten
- Geeignet für Tests
- Reproduzierbare Ergebnisse
Mock-Algorithmus:
function mockEmbedding(text: string, dimensions: number = 1536): number[] {
const embedding = new Array(dimensions).fill(0)
for (let i = 0; i < text.length; i++) {
const charCode = text.charCodeAt(i)
embedding[i % dimensions] += charCode / 1000
}
// Normalisieren
const magnitude = Math.sqrt(
embedding.reduce((sum, val) => sum + val * val, 0)
)
return embedding.map(val => val / magnitude)
}
Datenbankintegration
Semantische Zuordnungen können in PostgreSQL mit pgvector für effiziente Ähnlichkeitssuche gespeichert werden:
pgvector-Einrichtung
-- pgvector-Erweiterung aktivieren
CREATE EXTENSION IF NOT EXISTS vector;
-- Embeddings-Tabelle erstellen
CREATE TABLE term_embeddings (
id SERIAL PRIMARY KEY,
term_id VARCHAR(255) NOT NULL,
term_name TEXT NOT NULL,
term_source VARCHAR(50) NOT NULL,
embedding vector(1536),
created_at TIMESTAMP DEFAULT NOW()
);
-- Index für Ähnlichkeitssuche erstellen
CREATE INDEX ON term_embeddings
USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);
-- Ähnliche Begriffe mit Kosinus-Distanz finden
SELECT
term_id,
term_name,
term_source,
1 - (embedding <=> query_embedding) as similarity
FROM term_embeddings
WHERE term_source != 'source_to_exclude'
ORDER BY embedding <=> query_embedding
LIMIT 10;
Embedding-Speicherung
import { Pool } from 'pg'
async function storeEmbedding(
termId: string,
termName: string,
source: string,
embedding: number[]
): Promise<void> {
const pool = new Pool({
connectionString: process.env.DATABASE_URL
})
await pool.query(
`INSERT INTO term_embeddings (term_id, term_name, term_source, embedding)
VALUES ($1, $2, $3, $4)`,
[termId, termName, source, `[${embedding.join(',')}]`]
)
}
Qualitätsvalidierung
Konfidenzwertung
Jeder Übereinstimmung wird ein Konfidenzwert basierend auf der Zuordnungsmethode zugewiesen:
| Methode | Konfidenzbereich | Qualität |
|---|---|---|
| Kontextuell | 0,95 - 1,0 | Ausgezeichnet |
| Exakt | 0,85 - 0,95 | Sehr gut |
| Synonym | 0,70 - 0,85 | Gut |
| Semantisch | 0,50 - 0,70 | Befriedigend |
Konfidenzschwellenwerte:
- Hohe Konfidenz (≥ 0,85): Automatische Akzeptanz
- Mittlere Konfidenz (0,70 - 0,84): Überprüfung empfohlen
- Niedrige Konfidenz (< 0,70): Manuelle Überprüfung erforderlich
Qualitätsindikatoren für Zuordnungen
Gute Zuordnungen:
- ✅ Hoher Konfidenzwert (≥ 0,85)
- ✅ Ähnliche Kategorien
- ✅ Gleiche Domäne (Lebensmittel, Ökobilanz, Verpackung)
- ✅ Konsistente Beschreibungen
Fragliche Zuordnungen:
- ⚠️ Mittlere Konfidenz (0,70 - 0,84)
- ⚠️ Unterschiedliche Kategorien
- ⚠️ Domänenübergreifende Zuordnung
- ⚠️ Teilweise Namensüberlappung
Schlechte Zuordnungen:
- Niedrige Konfidenz (< 0,70)
- ❌ Unverbundene Kategorien
- ❌ Unterschiedliche Domänen
- ❌ Semantische Nichtübereinstimmung
Validierungsmethoden
Automatisierte Validierung:
function validateMatch(match: Match): ValidationResult {
const issues: string[] = []
// Konfidenzschwellenwert prüfen
if (match.confidence < 0.5) {
issues.push('Konfidenz unter Schwellenwert')
}
// Kategoriekonsistenz prüfen
if (match.sourceCategory !== match.targetCategory) {
issues.push('Kategorienichtübereinstimmung')
}
// Domänenausrichtung prüfen
if (!domainsAlign(match.sourceDomain, match.targetDomain)) {
issues.push('Domänennichtübereinstimmung')
}
return {
valid: issues.length === 0,
confidence: match.confidence,
issues: issues,
recommendation: issues.length === 0 ? 'akzeptieren' : 'überprüfen'
}
}
Manuelle Überprüfung:
- Zuordnungen nach CSV/Excel exportieren
- Zuordnungen mit niedriger Konfidenz überprüfen
- Domänenübergreifende Zuordnungen verifizieren
- Validierungsentscheidungen dokumentieren
Performance-Optimierung
Stapelverarbeitung
Mehrere Begriffe in Stapeln verarbeiten, um API-Aufrufe zu reduzieren:
async function batchEmbeddings(
texts: string[],
batchSize: number = 100
): Promise<number[][]> {
const batches: number[][][] = []
for (let i = 0; i < texts.length; i += batchSize) {
const batch = texts.slice(i, i + batchSize)
const embeddings = await Promise.all(
batch.map(text => generateEmbedding(text))
)
batches.push(embeddings)
}
return batches.flat()
}
Caching-Strategie
Embeddings zwischenspeichern, um redundante API-Aufrufe zu vermeiden:
import fs from 'fs'
class EmbeddingCache {
private cache: Map<string, number[]>
private cacheFile: string
constructor(cacheFile: string) {
this.cacheFile = cacheFile
this.cache = this.loadCache()
}
async getEmbedding(text: string, provider: string): Promise<number[]> {
const cacheKey = `${provider}:${text}`
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey)!
}
const embedding = await generateEmbedding(text, provider)
this.cache.set(cacheKey, embedding)
this.saveCache()
return embedding
}
private loadCache(): Map<string, number[]> {
if (fs.existsSync(this.cacheFile)) {
const data = JSON.parse(fs.readFileSync(this.cacheFile, 'utf8'))
return new Map(Object.entries(data))
}
return new Map()
}
private saveCache(): void {
const data = Object.fromEntries(this.cache)
fs.writeFileSync(this.cacheFile, JSON.stringify(data, null, 2))
}
}
Rate-Limiting
API-Ratenbegrenzungen respektieren:
import pLimit from 'p-limit'
const limit = pLimit(10) // Maximal 10 gleichzeitige Anfragen
async function matchWithRateLimit(
sourceTerms: Term[],
targetTerms: Term[]
): Promise<Match[]> {
const matches = await Promise.all(
sourceTerms.map(term =>
limit(() => findBestMatch(term, targetTerms))
)
)
return matches.filter(m => m !== null) as Match[]
}
Anwendungsfälle
Vokabularübergreifende Zuordnung
FoodEx2 zu Hestia:
FoodEx2: A010101 (Weichweizen)
↓ semantische Zuordnung
Hestia: term/crop-wheat
↓ liefert
Umweltauswirkungsdaten
ecoinvent zu Eaternity:
ecoinvent: market for wheat grain | GLO
↓ semantische Zuordnung
Eaternity: FlowNode.product_name = "Wheat grain"
↓ ermöglicht
EOS-CO2-Fußabdruck-Berechnung
Benutzerdatenimport
CSV-Spaltenkopf-Zuordnung:
Benutzerkopf: "Produktname" (Deutsch)
↓ semantische Zuordnung
Eaternity-Eigenschaft: eaternity-property-productname
↓ zugeordnet zu
EOS-API-Feld: FlowNode.product_name
Forschungsanwendungen
Multi-Quellen-Ökobilanz:
Forschungsfrage: "CO2-Fußabdruck von Bio-Rindfleisch"
↓ semantische Zuordnung
FoodEx2: F010101 (Rind) + Bio-Facette
Hestia: term/livestock-cattle-organic
ecoinvent: cattle for slaughtering, organic | CH
↓ kombiniert
Umfassende Ökobilanz mit mehreren Datenquellen
Interaktives Debugging
Web-Interface-Funktionen
Die ESFC-Glossar-Website bietet interaktive Debugging-Werkzeuge:
Zuordnungsvisualisierung:
- Echtzeit-Anzeige der Zuordnungsqualität
- Konfidenzwert-Visualisierung
- Methodenindikator (kontextuell, exakt, Synonym, semantisch)
- Quell-/Zielbegriff-Vergleich
Debugging-Werkzeuge:
- Zuordnungserklärung (warum diese Zuordnung gewählt wurde)
- Alternative Zuordnungen (andere potenzielle Übereinstimmungen)
- Ähnlichkeitswerte für semantische Zuordnungen
- Kategorie- und Domänenvergleich
Exportoptionen:
- Zuordnungen nach CSV exportieren
- Beziehungsgraph herunterladen
- Zuordnungsbericht generieren
- Für manuelle Überprüfung speichern
Semantische Zuordnung ausführen
Befehlszeile
# Vollständige semantische Zuordnung (Produktion)
npm run match-glossaries
# Testmodus (Beispieldaten)
npm run match-glossaries:test
# Mock-Modus (keine API-Schlüssel)
npm run match-glossaries:mock
# Spezifische Quellenpaare
node scripts/glossary-matcher.js \
--source foodex2 \
--target hestia \
--output mappings.json
Konfiguration
// scripts/glossary-matcher.js Konfiguration
const config = {
provider: 'openai', // oder 'google'
verbose: true,
mockMode: false,
confidenceThreshold: 0.5,
maxResults: 10,
batchSize: 100,
cacheFile: './cache/embeddings.json'
}
Ausgabeformate
JSON:
{
"matches": [
{
"sourceId": "foodex2-A010101",
"sourceName": "Common wheat",
"targetId": "hestia-term-crop-wheat",
"targetName": "Wheat crop",
"confidence": 0.95,
"method": "contextual",
"validated": true
}
],
"statistics": {
"totalSourceTerms": 31601,
"totalTargetTerms": 36044,
"matchesFound": 15823,
"averageConfidence": 0.82,
"methodBreakdown": {
"contextual": 8934,
"exact": 4521,
"synonym": 1876,
"semantic": 492
}
}
}
Beziehungsgraph (JSON-LD):
{
"@context": "http://www.w3.org/2004/02/skos/core#",
"@graph": [
{
"@id": "foodex2:A010101",
"@type": "Concept",
"prefLabel": "Common wheat",
"exactMatch": "hestia:term-crop-wheat",
"relatedMatch": "ecoinvent:market-wheat-grain"
}
]
}
Best Practices
Zuordnungsstrategie
-
Mit hochkonfidenten Methoden beginnen
- Auf kontextuelle und exakte Zuordnungen setzen, wenn möglich
- Semantische Suche als Fallback verwenden
- Zuordnungen mit niedriger Konfidenz manuell validieren
-
Domänenspezifische Zuordnung
- Lebensmittelbegriffe zu Lebensmittelquellen zuordnen (FoodEx2, Hestia)
- Ökobilanz-Prozesse zu ecoinvent zuordnen
- Eigenschaften zu Eaternity-Schema zuordnen
-
Qualität vor Quantität
- Weniger hochqualitative Zuordnungen bevorzugen
- Fragliche Zuordnungen überprüfen und validieren
- Zuordnungsentscheidungen dokumentieren
Performance-Optimierung
-
Stapelverarbeitung
- Begriffe in Stapeln verarbeiten
- Gleichzeitige Anfragen verwenden (mit Limits)
- Embeddings zwischenspeichern
-
Inkrementelle Aktualisierungen
- Nur geänderte Begriffe neu zuordnen
- Zuordnungshistorie führen
- Validierungsstatus verfolgen
-
Ressourcenmanagement
- API-Nutzung und -Kosten überwachen
- Mock-Modus für Entwicklung verwenden
- Embeddings lokal zwischenspeichern
Verwandte Dokumentation
- Datenquellen - Übersicht aller 10 Quellen
- Eaternity-Schema - Eigenschaftszuordnung
- Datenformate - Beziehungsdaten exportieren
- FoodEx2-Referenz - Lebensmittelklassifizierungszuordnung
- Hestia-Referenz - Ökobilanz-Datenzuordnung