AACSearch
Anleitungen

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"}'
FeldBedeutung
namePflicht, bis 200 Zeichen
controlProfileId / treatmentProfileIdProfile dieses Index; null bedeutet das Standardprofil des Index
treatmentWeightAnteil der Einheiten in treatment, 0–100, Standard 50. 0 und 100 leiten alle in einen Arm, ohne das Experiment zu stoppen
controlName / treatmentNameBezeichnungen, Standard A und B
statusdraft (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.

RouteWirkung
GET /experimentsExperimente des Index auflisten
POST /experimentsExperiment anlegen
PATCH /experiments/{id}Felder oder Status ändern
POST /experiments/{id}/promoteTreatment-Profil (oder control, wenn treatment fehlt) zum Standard des Index machen und das Experiment beenden
POST /experiments/{id}/rollbackControl-Profil als Standard wiederherstellen und das Experiment beenden
GET /experiments/{id}/reportPro 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:

  1. session_id — Ihre dauerhafte anonyme Besucher-ID (client_id im Widget),
  2. user_id,
  3. visit_id,
  4. 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
	}
}
FeldTypBedeutung
enabledbooleanExperiment aktiv. Bei false oder fehlendem Block läuft alles auf default
profilesstring[]Genau zwei Profil-Slugs; beide müssen am Index existieren
splitnumberProzentualer 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-v2

Sind 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"
}
MetrikAussage
searchesVolumen pro Bucket — prüfen Sie zuerst, ob die Aufteilung wie eingestellt aussieht
zero_rateAnteil Anfragen ohne Treffer; fast immer die wichtigste Zahl
avg_latency_msEine Variante, die Relevanz gewinnt und Latenz verliert, ist nicht automatisch besser
avg_top_scoreDurchschnittlicher 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

  1. Baseline stehen lassen. control ist das aktuell ausgelieferte Ranking, nicht ein leeres Profil.
  2. Eine Variable ändern. Ein Profil, das query_by-Gewichte und num_typos ändert, sagt Ihnen nicht, was gewirkt hat.
  3. Mindestens eine Woche laufen lassen. Relevanzunterschiede unter 2 Prozentpunkten Null-Ergebnis-Rate brauchen Volumen, bevor sie von Rauschen zu trennen sind.
  4. Auf Null-Ergebnisse schauen, dann auf Klicks. Eine Variante, die weniger findet, hat oft den prefix- oder Tippfehler-Spielraum zu eng gestellt.
  5. Beenden, indem Sie enabled auf false setzen 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.abTest wird ü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.

On this page