Handleidingen
Gesimuleerde gebruikers
Een gescript gesprek test je assistent tegen een gebruiker die nooit reageert. Een gesimuleerde gebruiker reageert wel: een model speelt een persoon die elke case beschrijft, schrijft elk volgend bericht op basis van wat je assistent net zei, en stopt wanneer zijn doel bereikt is of hij het opgeeft. Oloproof legt vast wat er tussen hen werd uitgewisseld, meet het zoals elk gesprek, en laat een gate er pas over beslissen zodra mensen de gesimuleerde gebruiker realistisch hebben bevonden. De tutorial loopt een supportdesk door die met gesimuleerde klanten praat.
Persona's
De input van elke case bevat een persona: wie de gebruiker is, wat hij wil en wat hij weet.
{"id": "c25", "input": {"persona": {
"description": "A retired teacher who ordered a cookbook last week. Writes short, polite messages.",
"goal": "Have the order of a cookbook delivered to my new address.",
"facts": {"order": "B-35", "postcode": "10275"}
}}}- description en goal zijn verplicht. Schrijf een doel dat een lezer alleen aan de hand van het gesprek kan controleren: "laat de winkel een terugbetaling starten" valt te beslissen uit wat de assistent zegt, "krijg een terugbetaling" niet, omdat het geld later binnenkomt.
- facts is wat de gebruiker weet en alleen deelt wanneer het gesprek erom vraagt, zoals een ordernummer. Je assistent ziet de persona nooit.
- opening is het eerste bericht van de gebruiker, woord voor woord, voor wanneer elk systeem op dezelfde manier moet worden begroet.
- max_turns begrenst de berichten van deze gebruiker en gaat voor op die van het project.
Een persona is inhoud van de dataset: ze valt onder de digest van de suite en is dezelfde voor elk systeem dat ertegen wordt gemeten.
De gesimuleerde gebruiker declareren
Het model dat elke persona speelt, wordt op het systeem gedeclareerd, zoals een judge:
system:
name: support-desk
callable: desk:talk
simulated_user:
provider: openai_compatible
base_url: http://localhost:11434/v1
model: qwen2.5vl
max_turns: 6Elk veld staat in de configuratiereferentie. Een lokaal model kost niets; een gehost model rekent elk bericht af dat het schrijft, en zijn tokens worden naast het gesprek vastgelegd en nooit als kosten van je systeem geteld.
Je systeem houdt zijn eigen lus
Oloproof stuurt het gesprek niet aan. Je systeem vraagt om het eerste bericht van de gebruiker, beantwoordt het zoals het gebruikers altijd beantwoordt, en geeft elk antwoord terug voor het volgende bericht:
from oloproof import current_case
def talk(case):
user = current_case().simulated_user()
message = user.opening()
while message is not None:
message = user.reply(my_assistant.answer(message))
return {}reply geeft None terug zodra de gebruiker stopt. Je systeem kan het gesprek zelf beëindigen met user.end(answer), zoals een overdracht aan een mens zou doen; een async def-systeem wacht op aopening(), areply(answer) en aend(answer). Oloproof legt vast wat er door de handle ging als conversation/v1 en hoe het eindigde als simulated_user/v1, en weigert beide van je systeem (SDK-referentie).
Hoe een gesprek eindigt
Na elk antwoord van je systeem wordt het model van de gesimuleerde gebruiker, als aparte vraag bij temperatuur 0 en zonder het rollenspel eromheen, gevraagd of dat antwoord het doel heeft bereikt. Zo ja, dan eindigt het gesprek als goal_reached. Anders schrijft de gebruiker zijn volgende bericht, of vertrekt hij (user_left), totdat hij max_turns berichten heeft gestuurd (turn_limit). Een systeem dat stopt zonder het laatste bericht te beantwoorden, laat het gesprek unanswered en afgekapt achter.
Apart vragen is wat een klein lokaal model bruikbaar maakt: een persona spelen en beslissen of een doel bereikt is, zijn verschillende taken, en een model dat gevraagd wordt beide in één antwoord te doen, praat vaak door nadat zijn doel bereikt was, of zegt dat het vertrekt terwijl het tevreden is.
De gesprekken meten
Het gesprek is de eenheid: elke evaluator geeft één oordeel per gesprek, dus elk interval, elke vergelijking en elke gate geldt ongewijzigd.
| Type | Slaagt wanneer |
|---|---|
| conversation_completed | je systeem elk bericht beantwoordde |
| goal_reached | de doelcontrole vaststelde dat het doel bereikt was |
| turns_to_goal | dat gebeurde binnen max_turns van de berichten van de gebruiker |
| every_turn | elk antwoord voldoet aan gedeclareerde voorwaarden: woorden die het moet bevatten of niet mag bevatten, een patroon, een lengte |
| conversation_judge | een modeljudge van het hele transcript het laat slagen, zodra hij is gevalideerd tegen labels van mensen |
Replicaten (replicates:) ontmoeten gebruikers met verschillende seeds, dus één persona levert meerdere gesprekken op. De versie van de gesimuleerde gebruiker (zijn model, prompt, temperatuur en budget) maakt deel uit van de cachesleutel van elke uitvoering, dus een andere gesimuleerde gebruiker meet je systeem opnieuw.
De gesimuleerde gebruiker valideren
Een resultaat over gesimuleerde gebruikers zegt alleen iets over echte gebruikers voor zover de gesimuleerde gebruiker op hen lijkt. Dus elke regel over een run waarvan de gebruikers gesimuleerd waren, is INSUFFICIENT_EVIDENCE met simulated_user_not_validated, totdat mensen zijn gesprekken realistisch hebben bevonden:
oloproof labels export RUN_ID --criterion simulated_user_realistic --sample 30 --local --out realism.csv
oloproof labels import realism.csv
oloproof simulated-users RUN_IDDe sheet toont elk getrokken gesprek met de persona en hoe het eindigde. Een mens zet passed op yes wanneer een echte klant de kant van de gebruiker had kunnen schrijven en het gesprek daar, op die manier, had kunnen beëindigen, wat ook de doelcontrole dekt: een gesprek dat eindigde als goal_reached terwijl het doel niet bereikt was, is niet realistisch. De gesimuleerde gebruiker is gevalideerd op de suite wanneer de exacte ondergrens van het realistische aandeel minimum_simulated_user_realism in release.yaml haalt. Een getrokken gesprek dat ongelabeld blijft, telt als onrealistisch, en een gesprek is alleen realistisch wanneer iedereen die het labelde dat zegt.
Eén validatie dekt één versie van één gesimuleerde gebruiker op één suite, met welk systeem hij ook sprak, dus over een kandidaat die tegen dezelfde persona's is gemeten, wordt beslist op de labels van de baseline. Een nieuw model, een nieuwe promptinstelling of een nieuwe set persona's heeft nieuwe labels nodig. Met require_validated_evaluators: false wordt er zonder die labels over gesimuleerde gebruikers beslist, bewust.
Gesimuleerd naast echt
oloproof simulated-users RUN_ID --real REAL_RUN_ID zet de vorm van de gesimuleerde gesprekken naast een run met echte (gelogde chats, opnieuw afgespeeld als conversation/v1): hoeveel berichten de gebruiker stuurde, hoe lang ze waren, hoe lang de antwoorden waren, hoe vaak een gesprek werd afgebroken, en hoe de gesimuleerde gesprekken eindigden. Het is beschrijvend en beslist nooit: twee ongepaarde steekproeven vergelijken vraagt een methode die Oloproof niet heeft toegelaten. Lees het voor wat realismelabels missen, zoals gesimuleerde gebruikers die in één nette zin schrijven wat echte gebruikers over drie berichten verspreiden.
Niet ondersteund
- Beslissen over gesimuleerde gebruikers in een gehoste werkruimte: de beslissingen daar blijven achtergehouden totdat de reviewwachtrij zelf realismelabels kan trekken en verzegelen.
- Een gate op hoe gesimuleerde gesprekken zich verhouden tot echte.
- Percentages per beurt binnen een gesprek, en een gesprek opnieuw afspelen vanaf een beurt.
- Gesimuleerde gebruikers voor een HTTP-systeem: de eigen code van je systeem draait de lus, dus het moet een callable zijn.