A/B-Experimente
Suchexperimente teilen Live-Traffic mit fester Zuordnung pro Besucher auf zwei Suchprofile auf, berichten Klicks und Uplift und übernehmen oder verwerfen den Gewinner. Beschreibt auch die ältere Aufteilung über settings.abTest.
Relevanzarbeit ohne Messung ist Geschmack. AACSearch teilt deshalb Traffic auf zwei Suchprofile auf und zählt die Ergebnisse getrennt — ohne dass Sie zwei Indizes betreiben oder Nutzer etwas merken.
Suchexperimente
Ein Suchexperiment gehört zu einem Index und vergleicht zwei Arme, control und treatment, die jeweils auf ein gespeichertes Suchprofil zeigen (oder auf das Standardprofil des Index, wenn kein Profil gesetzt ist). Solange ein Experiment running ist, bekommt jede REST-Suche und jede Suche des Embed-Widgets auf diesem Index das Profil eines Arms.
curl -sS -X POST "$AAC_API/experiments?index=documentation" \
-H "Authorization: Bearer $AAC_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"weights v2","controlProfileId":"prof_control","treatmentProfileId":"prof_weights_v2","treatmentWeight":50,"status":"running"}'| Feld | Bedeutung |
|---|---|
name | Pflicht, bis 200 Zeichen |
controlProfileId / treatmentProfileId | Profile dieses Index; null bedeutet das Standardprofil des Index |
treatmentWeight | Anteil der Einheiten in treatment, 0–100, Standard 50. 0 und 100 leiten alle in einen Arm, ohne das Experiment zu stoppen |
controlName / treatmentName | Bezeichnungen, Standard A und B |
status | draft (Standard), running, paused, finished |
Alle /experiments-Routen nehmen ?index=<slug> und brauchen einen Schlüssel mit settings:write. Die Konsole bietet dieselben Funktionen auf der Seite Experimente des Index.
| Route | Wirkung |
|---|---|
GET /experiments | Experimente des Index auflisten |
POST /experiments | Experiment anlegen |
PATCH /experiments/{id} | Felder oder Status ändern |
POST /experiments/{id}/promote | Treatment-Profil (oder control, wenn treatment fehlt) zum Standard des Index machen und das Experiment beenden |
POST /experiments/{id}/rollback | Control-Profil als Standard wiederherstellen und das Experiment beenden |
GET /experiments/{id}/report | Pro Arm Zuordnungen, Suchen, Klicks, CTR, Conversions, Conversion-Rate, Umsatz und Uplift; days 1–90, Standard 14 |
Statuswechsel lösen die Webhooks experiment.started und experiment.finished aus.
Wer in welchem Arm landet
Die Zuordnung ist ein Hash des Einheitsschlüssels: dieselbe Einheit bekommt immer denselben Arm, über Instanzen und Deploys hinweg. Einheitsschlüssel ist der erste vorhandene Wert aus:
session_id— Ihre dauerhafte anonyme Besucher-ID (client_idim Widget),user_id,visit_id,- sonst der API-Schlüssel (REST) bzw. der Aussteller der Embed-Session (Widget) — dieser Traffic teilt sich einen Arm.
Senden Sie eine stabile session_id aus dem Frontend; ohne sie ist ein ganzer serverseitiger Schlüssel eine Einheit. Die erste Zuordnung einer Einheit wird gespeichert und nie geändert, der Besucher bleibt also für die ganze Laufzeit im selben Arm.
Pro Index wird jeweils nur ein Experiment ausgespielt: Laufen mehrere, wird eines davon gewählt. Ein explizites profile= im Request hat immer Vorrang vor dem Experiment — so zeigt die Konsole eine Variante in der Vorschau, ohne das Experiment zu stören.
Den Bericht lesen
Jede Suche, über die ein Experiment entscheidet, wird mit ihrem Arm protokolliert, und jedes Ereignis trägt die query_id der Suche, aus der es stammt — die Zuordnung ist daher für jede Einheitsart (Schlüssel, Besucher, Nutzer, Besuch) exakt. Jede Variante meldet Zuordnungen (Einheiten, die dem Arm ausgesetzt waren), die vom Arm bedienten Suchen sowie Klicks, Conversions und Umsatz (revenue_cents, Summe der value_cents der Conversions) auf diesen Suchen. Senden Sie query_id mit Ihren Ereignissen — Ereignisse ohne sie werden nicht zugeordnet. ctr und conversion_rate beziehen sich auf Suchen; uplift ist der relative CTR-Unterschied von treatment gegenüber control, oder null, solange einer Seite Daten fehlen. Suchen, die vor dieser Zuordnung protokolliert wurden, werden nicht gezählt.
Älteres Verfahren: settings.abTest
Indizes können weiterhin die ältere Aufteilung pro Schlüssel in ihren Einstellungen tragen. Sie greift nur, wenn kein Experiment läuft und der Request kein Profil nennt. Für Neues sind Suchexperimente vorzuziehen; der Rest dieser Seite beschreibt das ältere Verfahren.
Wie die Aufteilung funktioniert
Die Einstellung sitzt am Index:
{
"abTest": {
"enabled": true,
"profiles": ["control", "weights-v2"],
"split": 50
}
}| Feld | Typ | Bedeutung |
|---|---|---|
enabled | boolean | Experiment aktiv. Bei false oder fehlendem Block läuft alles auf default |
profiles | string[] | Genau zwei Profil-Slugs; beide müssen am Index existieren |
split | number | Prozentualer Anteil für profiles[0], 1–99 |
Der Bucket wird aus einem Hash von Schlüssel-ID und Index-ID gebildet: sha256(keyId:indexId), erstes Byte modulo 100, dann Vergleich mit split. Damit gilt:
- Stabil pro Schlüssel. Derselbe Schlüssel landet bei jedem Request im selben Bucket — sonst würden Nutzer zwischen zwei Rankingvarianten springen und beide Messungen verwässern.
- Gleichverteilt über Schlüssel. Viele Keys ergeben ungefähr die eingestellte Verteilung, ein einzelner Key ergibt nie 50 %.
- Ein
profile-Parameter gewinnt. Wer explizit?profile=setzt, umgeht das Experiment — genau so testen Sie eine Variante von Hand.
Ein Blick auf den tatsächlich verwendeten Bucket steht in jeder Antwort:
curl -sS -i -G "$AAC_API/indexes/documentation/search" \
-H "Authorization: Bearer $AAC_KEY" \
--data-urlencode "q=preise" | grep -i '^x-aac-profile'x-aac-profile: weights-v2Sind die Profile fehlerhaft konfiguriert, scheitert die Suche nicht still: Ein abTest mit weniger als zwei Profilen wird ignoriert und alles läuft auf default; ein Profil-Slug, der am Index nicht existiert, ergibt dagegen 404 PROFILE_NOT_FOUND. Ein Tippfehler in der Konfiguration fällt damit sofort auf, statt eine Variante klammheimlich nie auszuliefern.
Auswerten
curl -sS "$AAC_API/analytics/experiments?days=30&index=documentation" \
-H "Authorization: Bearer $AAC_KEY"{
"days": 30,
"index": "documentation",
"buckets": [
{
"profile": "control",
"searches": 8240,
"zero_rate": 0.11,
"avg_latency_ms": 18,
"avg_top_score": 1720.4
},
{
"profile": "weights-v2",
"searches": 8106,
"zero_rate": 0.07,
"avg_latency_ms": 19,
"avg_top_score": 2145.8
}
],
"request_id": "req_3b5d7f9a1c3e5b7d9f1a3c5e"
}| Metrik | Aussage |
|---|---|
searches | Volumen pro Bucket — prüfen Sie zuerst, ob die Aufteilung wie eingestellt aussieht |
zero_rate | Anteil Anfragen ohne Treffer; fast immer die wichtigste Zahl |
avg_latency_ms | Eine Variante, die Relevanz gewinnt und Latenz verliert, ist nicht automatisch besser |
avg_top_score | Durchschnittlicher Spitzen-Score; steigt mit schärferen Matches, ist aber kein Qualitätsmaß |
Was hier nicht steht: Klicks und Conversions pro Bucket. Die stehen in der Gesamtübersicht und lassen sich nicht nach Profil aufschlüsseln, weil Klick-Events nur die Anfrage referenzieren. Wenn Sie eine Variante bis zur Conversion bewerten wollen, loggen Sie das Profil aus dem x-aac-profile-Header mit.
days wird auf 1–90 begrenzt. Ohne index aggregiert der Endpunkt über alle Indizes der Organisation — sinnvoll nur, wenn überall dasselbe Experiment läuft.
Vorgehen
- Baseline stehen lassen.
controlist das aktuell ausgelieferte Ranking, nicht ein leeres Profil. - Eine Variable ändern. Ein Profil, das
query_by-Gewichte undnum_typosändert, sagt Ihnen nicht, was gewirkt hat. - Mindestens eine Woche laufen lassen. Relevanzunterschiede unter 2 Prozentpunkten Null-Ergebnis-Rate brauchen Volumen, bevor sie von Rauschen zu trennen sind.
- Auf Null-Ergebnisse schauen, dann auf Klicks. Eine Variante, die weniger findet, hat oft den
prefix- oder Tippfehler-Spielraum zu eng gestellt. - Beenden, indem Sie
enabledauffalsesetzen und das Gewinnerprofil als Standard hinterlegen. Die Buckets verschwinden dann aus/analytics/experiments, weil nur noch ein Profil bedient wird.
Grenzen
- Zwei Arme, nicht mehr. Für drei Varianten brauchen Sie zwei aufeinanderfolgende Experimente.
- Zuweisung pro Schlüssel, nicht pro Nutzer. Das Widget nutzt für alle Besucher denselben API-Schlüssel, aus dem die Embed-Sessions stammen, und damit denselben Bucket. Wollen Sie pro Besucher randomisieren, stellen Sie pro Sitzung einen eigenen Schlüssel aus — dann messen Sie aber Traffic pro Schlüssel, nicht pro Person.
- Konfiguration nur in der Konsole.
settings.abTestwird über die Index-Einstellungen geschrieben; die öffentliche API bietet keinen Schreibendpunkt dafür. - Kein Automatismus. Nichts schaltet den Gewinner automatisch live, und es gibt keine Signifikanzprüfung — die Zahlen sind beschreibend.
Sprach- und Bildsuche
Den Index mit einer Aufnahme oder einem Bild abfragen — POST /voice und POST /image, beide machen aus dem Input zuerst eine Textanfrage.
Connectoren und Reindexierung
Dokumente per Sitemap, URL, JSON oder CSV nach Zeitplan einlesen und einen Index bei Schemaänderung neu bauen, ohne eine Abfrage zu verlieren.