Blog/Retrieval & Suche
RAG über Produktkataloge: aus PIM-Daten Antworten machen, die halten
Produktdaten sind strukturiert, Chunking wie bei PDFs scheitert. So serialisiert man PIM-Attribute, leitet Spezifikationsfragen an Filter und bleibt exakt.
Balázs Csorba··10 Min. Lesezeit
- RAG
- PIM
- Product data
- B2B e-commerce
- Metadata filtering

Das Wichtigste in Kürze
- Katalogfragen haben Typen: Artikelnummern brauchen exakte Suche, Spezifikationen strukturierte Filter, und nur beschreibende Fragen passen gut zur Vektorsuche.
- PIM-Werte sind typisiert und tragen Sprache, Kanal und Einheit, wie die Tripel aus locale, scope und data bei Akeneo; mit Bezeichnungen und Einheiten serialisieren, nie mit rohen Attributcodes.
- Einen Text pro Produktmodell indexieren und Variantenattribute als strukturierte Payload halten, damit eine Frage nach Größe oder Gewinde nicht von einem Embedding abhängt.
- Das LLM Filter in ein Schema extrahieren lassen, das gegen das Attributmodell validiert wird, dann gefilterte hybride Suche ausführen; Preise und Bestand kommen live aus dem Shop, nicht aus dem Index.
- Mit erwarteten SKUs pro Anfrage evaluieren und dem Vokabular Exact, Substitute, Complement und Irrelevant aus Amazons ESCI-Datensatz.
Die meisten RAG-Tutorials beginnen bei Dokumenten: PDFs, Wiki-Seiten, Handbücher. Ein Produktkatalog ist etwas anderes. In einem PIM ist das Wissen bereits strukturiert: typisierte Attribute, Einheiten, Varianten, Klassifikationen, Übersetzungen pro Sprache und Werte pro Vertriebskanal. Wer das zu Text plättet und wie ein Handbuch chunkt, wirft genau die Struktur weg, die Katalogfragen beantwortbar macht.
Dieser Beitrag ist die Datenseite eines Katalogassistenten: wie PIM-Daten aussehen, wie man sie für das Retrieval serialisiert, welche Fragen zu Filtern statt zu Embeddings gehören und wie man prüft, dass die Antworten die richtige SKU nennen. Er baut auf semantischer Produktsuche für B2B-Shops auf, wo es um Ranking geht, und setzt voraus, dass die Sync-Probleme aus Pimcore-ERP-Delta-Sync vorgelagert gelöst sind.
Wie sehen PIM-Daten tatsächlich aus?
Nehmen wir Akeneo als Beispiel. Die values eines Produkts sind nach Attributcode geordnet, und jeder Code verweist auf ein Array von Wertobjekten mit locale, scope und data. Ein lokalisierbares Attribut hat einen Wert pro Sprache, ein kanalabhängiges einen pro Kanal, und eines, das beides ist, einen pro Kombination aus Sprache und Kanal. Ein metrischer Wert ist keine Zahl, sondern ein Objekt mit amount, unit und symbol, wobei das Symbol aus der Maßeinheitenfamilie stammt.
"values": {
"name": [{ "locale": "de_DE", "scope": null, "data": "Sechskantschraube DIN 933" }],
"length": [{ "locale": null, "scope": null,
"data": { "amount": "50", "unit": "MILLIMETER", "symbol": "mm" } }],
"material": [{ "locale": null, "scope": null, "data": "stainless_a2" }],
"description": [{ "locale": "de_DE", "scope": "ecommerce", "data": "..." },
{ "locale": "de_DE", "scope": "print", "data": "..." }]
}Varianten fügen eine zweite Ebene hinzu. In Akeneo legt eine Familienvariante ein oder zwei Variantenebenen fest, die Achsen pro Ebene und welche Attribute gemeinsam sind und welche variieren, mit bis zu drei Anreicherungsebenen: Wurzel-Produktmodell, Unter-Produktmodell und Variantenprodukt. Nur das Variantenprodukt hat eine SKU, das Produktmodell hat einen Code. Pimcore geht mit dem Classification Store einen anderen Weg: Schlüssel mit Datentypen, zu Gruppen zusammengefasst, Gruppen an Objekten, optionale Lokalisierung und Mengenwerte, die eine Zahl mit einer Einheit verbinden.
Viele technische B2B-Kataloge sind zusätzlich nach ETIM klassifiziert, laut Herausgeber dem internationalen Klassifikationsstandard für technische Produkte: Klassen mit einer einheitlichen Merkmalsliste und vordefinierten Werten, verfügbar in 17 Sprachen. ETIM 10.0 erschien im Dezember 2024 und wird als IXF ausgeliefert, einem XML-Format. Liefern die Lieferanten ETIM-Daten, gibt es bereits ein markenübergreifendes Attributvokabular, und das ist für Filter Gold wert.
Welche Katalogfrage braucht welches Retrieval?
Der nützlichste Designschritt ist, die erwarteten Fragen nach der Art des nötigen Retrievals zu sortieren. Bei einem B2B-Katalog ist mit einer ganz anderen Mischung als bei einem Support-Wiki zu rechnen: viele Fragen sind Lookups oder Einschränkungen, weniger sind offen.
| Frage | Beispiel | Weg |
|---|---|---|
| Kennung | Habt ihr 4711-050? | Exakter Lookup auf SKU, EAN, Lieferantennummer |
| Spezifikation | M8-Edelstahlschrauben, mindestens 50 mm | Strukturierter Filter, dann Ranking |
| Vergleich | Unterschied zwischen X200 und X300? | Beide Datensätze holen, Attribute vergleichen |
| Beschreibend | Etwas für draußen in Meeresnähe | Hybride Suche über Text |
| Beziehung | Welche Mutter passt zu dieser Schraube? | PIM-Verknüpfungen, dann Lookup |
| Kaufmännisch | Was kostet es mich, ist es lagernd? | Live-Aufruf an Shop oder ERP, nie der Index |
Amazons Shopping Queries Dataset ist eine nützliche Referenz für das Vokabular. Seine rund 130.000 Anfragen und 2,6 Millionen menschlichen Relevanzurteile kennzeichnen jedes Paar aus Anfrage und Produkt als Exact (erfüllt alle Vorgaben der Anfrage), Substitute (verfehlt einige Aspekte, taugt aber als Ersatz), Complement (erfüllt die Anfrage nicht, passt aber zu einem exakten Artikel) oder Irrelevant. Ein Katalogassistent sollte wissen, welche der vier Kategorien er anbietet, und es sagen: einen Ersatz so zu präsentieren, als wäre er exakt, ist der klassische Weg, das Vertrauen eines B2B-Einkäufers zu verlieren.
Ein Dokument pro was: Produkt, Modell oder Variante?
Der erste Reflex ist ein Dokument pro SKU. Bei Katalogen mit Varianten funktioniert das im Textindex schlecht. Vierzig Längen derselben Schraube teilen Name, Beschreibung und die meisten Attribute, also füllt eine beschreibende Anfrage die Top-Treffer mit Beinahe-Duplikaten eines Produkts und drängt jede Alternative hinaus. Vierzig fast identische Texte einzubetten kostet außerdem Speicher und Indexierungszeit ohne Gewinn.
Besser funktioniert es, den Datensatz zu teilen. Der Textindex erhält ein Dokument pro Produktmodell, gebaut aus den gemeinsamen Attributen und einer kompakten Zusammenfassung der Variantenachsen, etwa Längen 20 bis 120 mm, Edelstahl A2 und A4. Die Variantenattribute liegen als strukturierte Payload vor, entweder am selben Punkt im Vektorspeicher oder in einer eigenen Tabelle mit dem Modellcode als Schlüssel. Das Retrieval findet das Modell über die Bedeutung, ein Filter oder Lookup wählt die Variante. Einfache Produkte ohne Varianten sind ihr eigenes Modell.
Wie werden Attribute zu Text?
Der Serialisierer ist das am meisten unterschätzte Teil einer Katalog-RAG-Pipeline. Embedding-Modelle und LLMs lesen Bezeichnungen, keine Codes: stainless_a2 und MILLIMETER sagen wenig, Material: Edelstahl A2 und Länge: 50 mm sagen viel. Ein paar Regeln, die ich anwende:
- Jeden Code in seine Bezeichnung auflösen, in der Zielsprache: Attributnamen, Auswahloptionen, Einheiten über ihr Symbol.
- Einen Kanal wählen. Kanalabhängige Werte unterscheiden sich; den E-Commerce-Scope indexieren, nicht den Text des Druckkatalogs.
- Ein Text pro Sprache, mit einer ausdrücklichen Fallback-Regel, damit eine deutsche Anfrage nie versehentlich einen halb englischen Datensatz findet.
- Leere und interne Attribute weglassen: Einkaufspreise, Workflow-Flags, Importzeitstempel. Sie erzeugen Rauschen und leaken manchmal.
- Kennungen auch in den Text: SKU, EAN und Lieferantennummern gehören ins BM25-Feld, weil Nutzer sie einfügen.
def serialize(model, variants, locale, labels, channel="ecommerce"):
def val(values, code):
for v in values.get(code, []):
if v["locale"] in (locale, None) and v["scope"] in (channel, None):
d = v["data"]
if isinstance(d, dict) and "amount" in d: # metric value
return f'{d["amount"]} {d["symbol"]}'
return labels.option(code, d, locale) if isinstance(d, str) else d
return None
lines = [f'# {val(model["values"], "name")}']
for code in labels.common_attributes(model["family"]):
v = val(model["values"], code)
if v not in (None, "", []):
lines.append(f'{labels.attribute(code, locale)}: {v}')
# summarise the variant axes instead of repeating every variant
for axis in labels.axes(model["family_variant"]):
seen = sorted({str(val(x["values"], axis)) for x in variants})
lines.append(f'{labels.attribute(axis, locale)} (variants): {", ".join(seen)}')
lines.append("SKUs: " + " ".join(x["identifier"] for x in variants))
return "\n".join(lines)Ob man die Attribute als Schlüssel-Wert-Zeilen, Markdown-Tabelle oder JSON darstellt, sollte man testen statt glauben. Die Studie Table Meets LLM fand, dass die Leistung von LLMs bei Tabellenaufgaben mit Eingabeentscheidungen wie Format und Reihenfolge des Inhalts schwankt; das eigene Embedding-Modell und die eigenen Anfragen entscheiden, welches Format am besten gefunden wird. Ich beginne mit Zeilen der Form Bezeichnung: Wert, weil sie für BM25, Embeddings und das LLM gleichermaßen funktionieren.
Warum gehören Spezifikationen in Filter statt in Embeddings?
Ein dichtes Embedding komprimiert einen Text zu einer Richtung, die erfasst, worum es geht. Es ist nicht dafür gebaut, 48 mm von 50 mm zu unterscheiden oder zu wissen, dass mindestens 50 die 48 ausschließt. BM25 trifft das Token 50, hat aber ebenso keine Ahnung von Bereichen oder Einheiten. Einschränkungen sollten daher aus der Frage extrahiert und als Filter auf typisierte Felder angewendet werden. Vektordatenbanken unterstützen das direkt; in Qdrant erledigt das eine must-Klausel mit einer range-Bedingung und den Grenzen gte oder lte, und die Dokumentation empfiehlt Payload-Indizes für jedes Feld, auf das gefiltert wird.
{
"query": "<embedding of: stainless hex screw for outdoor use>",
"filter": {
"must": [
{ "key": "thread", "match": { "value": "M8" } },
{ "key": "material", "match": { "any": ["stainless_a2", "stainless_a4"] } },
{ "key": "length_mm", "range": { "gte": 50 } },
{ "key": "locale", "match": { "value": "de_DE" } }
]
},
"limit": 20
}Die Filterextraktion ist ein kleiner LLM-Aufruf mit strukturierter Ausgabe: dem Modell die Liste der filterbaren Attribute mit Typen, erlaubten Optionen und kanonischen Einheiten geben und ein JSON-Objekt zurückgeben lassen. Dann im Code validieren. Unbekannte Attributcodes fallen weg, Optionswerte müssen existieren, und Einheiten werden vor dem Vergleich normalisiert, aus 5 cm wird 50 mm. Das ist dieselbe Disziplin wie bei Text-to-SQL mit Leitplanken: Das Modell schlägt vor, ein Schema entscheidet. Lässt sich eine Einschränkung nicht abbilden, bleibt sie in der Textanfrage, statt still zu verschwinden, und der Nutzer erfährt, welche Filter angewendet wurden.
Was gehört nie in den Index?
Preise, Bestand und Liefertermine. In B2B-Shops sind Preise häufig kundenspezifisch, mit Kontraktpreisen, Staffeln und Rabatten, und der Bestand bewegt sich minütlich. Ein Index-Schnappschuss dieser Werte ist per Design falsch und zeigt schlimmstenfalls einem Kunden die Konditionen eines anderen. Sie gehören weder in den Text noch in die Payload. Braucht die Antwort sie, ruft der Assistent den Preis- und Verfügbarkeitsendpunkt des Shops mit der Sitzung des angemeldeten Kunden auf, so wie es die Storefront tut. Die Werkzeugseite dazu habe ich in einem KI-Assistenten für den B2B-Shop-Support behandelt.
Für den Rest ist Aktualität ein Sync-Problem. Ein Produktmodell neu serialisieren und einbetten, wenn sich eines seiner gemeinsamen Attribute oder Labels ändert, und nur die Payload aktualisieren, wenn sich ein Variantenattribut ändert. Das ist günstig, weil kein Embedding beteiligt ist. Ein geändertes Options-Label, etwa ein im Deutschen umbenanntes Material, betrifft jedes Modell, das es nutzt; deshalb die Änderungserkennung am Hash des serialisierten Texts festmachen, nicht allein am Änderungszeitstempel des PIM.
Wie helfen Sprachen und Klassifikationen?
Katalogdaten sind oft besser übersetzt als jedes Wiki, weil das PIM Übersetzungen pro Attribut erzwingt. Das nutzen: ein serialisierter Text pro Sprache, bei der Anfrage nach Sprache gefiltert, mit dokumentiertem Fallback bei fehlender Übersetzung. Optionen und Einheiten sind in der Payload sprachneutral, Filter funktionieren also auf Deutsch, Englisch und Ungarisch gleich, und nur der Textindex ist sprachspezifisch. Die allgemeinen Abwägungen beim sprachübergreifenden Retrieval stehen in mehrsprachigem RAG für Deutsch und Ungarisch.
Klassifikationen wie ETIM bringen einen zweiten Vorteil. Weil eine Klasse eine einheitliche Merkmalsliste hat, kann der Prompt zur Filterextraktion pro Klasse gebaut werden: Ist die Frage etwa einer Schraubenklasse zugeordnet, werden nur deren Merkmale als Filter angeboten. Das hält den Prompt kurz und die extrahierten Filter gültig, auch über Lieferanten hinweg, deren eigene Attributnamen sich unterscheiden.
Wie testet man einen Katalogassistenten?
Katalog-RAG hat gegenüber Dokument-RAG einen Vorteil: Die Ground Truth ist eindeutig. Für jede Testanfrage lassen sich die richtigen SKUs oder Produktmodelle notieren, was die Retrieval-Evaluation zu Trefferquote und Recall at k auf IDs macht, statt Passagen zu beurteilen. Ich baue das Set aus Suchlogs und Vertriebsfragen auf, gruppiert nach den Fragetypen aus der Tabelle oben, und kennzeichne die erwarteten Ergebnisse mit den ESCI-Kategorien, damit ein Ersatz nicht als Treffer zählt. Die Metriken selbst behandelt RAG evaluieren.
- Kennungsanfragen: 100 % exakte Treffer erwarten; alles darunter ist ein Tokenizer- oder Feldmapping-Fehler, kein Modellproblem.
- Spezifikationsanfragen: den extrahierten Filter gegen einen handgeschriebenen erwarteten Filter prüfen, bevor man auf Ergebnisse schaut.
- Anfragen zu fehlenden Daten: Die richtige Antwort ist nicht angegeben; erfundene Werte zählen als Fehler.
- Kaufmännische Anfragen: prüfen, dass der Preis aus dem Live-Aufruf kommt, indem man mit zwei Kunden mit unterschiedlichen Konditionen testet.
Wo sollte man anfangen?
Mit dem Serialisierer und dem Kennungspfad beginnen, weil sie günstig sind und die peinlichsten Fehler beseitigen. Dann Filterextraktion für die fünf bis zehn Attribute ergänzen, die in echten Fragen am häufigsten vorkommen, nicht für alle. Erst danach Textindex, Chunkgrößen und Reranker abstimmen. Wird der Katalog auch mit strukturierten Daten veröffentlicht, kann dasselbe Attribut-Mapping beides speisen, wie in Schema.org-Strukturdaten für B2B-Shops beschrieben. Das Muster darunter ist einfach: Das PIM hat die harte Arbeit der Strukturierung schon erledigt, und ein guter Katalogassistent bewahrt diese Struktur bis zur Antwort.
Quellen
- Akeneo API: Products, product values, metric values and product models
- Akeneo Help Center: What about products with variants?
- Pimcore documentation: Classification Store
- ETIM International: Classification
- ETIM International: ETIM releases and the IXF format
- Reddy et al.: Shopping Queries Dataset, a large-scale ESCI benchmark for improving product search (2022)
- Amazon Science: esci-data repository
- Qdrant documentation: Filtering
- Sui et al.: Table Meets LLM, can large language models understand structured table data? (WSDM 2024)
Häufige Fragen
Kann ich einfach meinen Produktexport einbetten und RAG nutzen?
Ja, und beschreibende Fragen werden halbwegs beantwortet. Bei Artikelnummern, numerischen Spezifikationen und Variantenunterschieden scheitert es, weil Embeddings Zahlen und exakte Codes nicht zuverlässig vergleichen. Einen Textindex mit strukturierten Filtern auf den Attributen kombinieren.
Sollte jede Variante ein eigenes Dokument sein?
Für den Textindex meist nicht. Varianten eines Produktmodells teilen fast den ganzen Text und verdrängen sich gegenseitig aus den Top-Treffern. Das Produktmodell indexieren, Variantenattribute als Payload speichern und die genaue Variante per Filter oder Lookup auflösen.
Woher kommen Preise und Bestand?
Zum Antwortzeitpunkt aus Shop oder ERP. Im B2B sind Preise oft kundenspezifisch und der Bestand ändert sich ständig, ein Index-Schnappschuss ist also per Design falsch. Der Assistent sollte einen Preis- und Verfügbarkeitsendpunkt mit dem Kontext des angemeldeten Kunden aufrufen.
Hilft ETIM bei RAG?
Ja, wo die Daten damit klassifiziert sind. ETIM definiert Klassen mit einer einheitlichen Merkmalsliste und vordefinierten Werten. Das liefert lieferantenübergreifend konsistente Attributnamen für Filter und ein mehrsprachiges Vokabular.
Wie verhindere ich, dass der Assistent Spezifikationen erfindet?
Die Attributwerte als strukturierte Daten mit Einheiten an das Modell geben, es anweisen, nur daraus zu antworten, und ein leeres Attribut als ausdrückliches „nicht angegeben“ behandeln. Mit Fragen nach Attributen testen, von denen man weiß, dass sie fehlen.