Guias
Usuários simulados
Uma conversa roteirizada testa o seu assistente contra um usuário que nunca reage. Um usuário simulado reage: um modelo interpreta uma pessoa que cada caso descreve, escreve cada mensagem seguinte a partir do que o seu assistente acabou de dizer, e para quando o seu objetivo é alcançado ou quando desiste. O Oloproof registra o que passou entre os dois, mede isso como qualquer conversa, e só deixa um gate decidir sobre isso depois que pessoas consideraram o usuário simulado realista. O tutorial percorre uma central de atendimento conversando com clientes simulados.
Personas
A entrada de cada caso contém uma persona: quem o usuário é, o que ele quer e o que ele sabe.
{"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 e goal são obrigatórios. Escreva um objetivo que um leitor possa verificar só pela conversa: "fazer a loja iniciar um reembolso" pode ser decidido pelo que o assistente diz, "receber um reembolso" não pode, porque o dinheiro chega depois.
- facts são o que o usuário sabe e só compartilha quando a conversa precisa, como um número de pedido. O seu assistente nunca vê a persona.
- opening é a primeira mensagem do usuário, palavra por palavra, quando todo sistema deve ser abordado da mesma forma.
- max_turns limita as mensagens desse usuário, substituindo o valor do projeto.
Uma persona é conteúdo do dataset: ela é coberta pelo digest da suíte e é a mesma para todo sistema medido contra ela.
Declarando o usuário simulado
O modelo que interpreta todas as personas é declarado no sistema, como um juiz:
system:
name: support-desk
callable: desk:talk
simulated_user:
provider: openai_compatible
base_url: http://localhost:11434/v1
model: qwen2.5vl
max_turns: 6Todos os campos estão na referência de configuração. Um modelo local não custa nada; um hospedado cobra cada mensagem que escreve, e os seus tokens são registrados ao lado da conversa e nunca contados como custo do seu sistema.
O seu sistema mantém o seu loop
O Oloproof não conduz a conversa. O seu sistema pede a primeira mensagem do usuário, responde a ela do jeito que responde aos usuários, e devolve cada resposta para receber a próxima mensagem:
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 retorna None quando o usuário para. O seu sistema pode encerrar a conversa ele mesmo com user.end(answer), como faria uma transferência para uma pessoa; um sistema async def usa await em aopening(), areply(answer) e aend(answer). O Oloproof registra o que passou pelo handle como conversation/v1 e como a conversa terminou como simulated_user/v1, e recusa qualquer um dos dois vindo do seu sistema (Referência do SDK).
Como uma conversa termina
Depois de cada resposta do seu sistema, o modelo do usuário simulado é consultado, numa pergunta separada com temperatura 0 e sem a encenação em volta, sobre se aquela resposta cumpriu o objetivo. Se cumpriu, a conversa termina como goal_reached. Caso contrário, o usuário escreve a sua próxima mensagem, ou vai embora (user_left), até ter enviado max_turns mensagens (turn_limit). Um sistema que para sem responder à última mensagem deixa a conversa unanswered e truncada.
Perguntar separadamente é o que torna utilizável um modelo local pequeno: interpretar uma persona e decidir se um objetivo foi alcançado são tarefas diferentes, e um modelo a quem se pedem as duas numa só resposta tende a continuar conversando depois de alcançar o objetivo, ou a dizer que está indo embora quando está satisfeito.
Medindo as conversas
A conversa é a unidade: cada avaliador dá um veredito por conversa, então todo intervalo, comparação e gate se aplica sem mudança.
| Tipo | Passa quando |
|---|---|
| conversation_completed | o seu sistema respondeu a todas as mensagens |
| goal_reached | a verificação do objetivo constatou que o objetivo foi alcançado |
| turns_to_goal | isso aconteceu em até max_turns das mensagens do usuário |
| every_turn | toda resposta cumpre as condições declaradas: palavras que ela precisa conter ou não pode conter, um padrão, um comprimento |
| conversation_judge | um juiz baseado em modelo da transcrição inteira a aprova, depois de validado contra os rótulos das pessoas |
As réplicas (replicates:) encontram usuários com sementes diferentes, então uma persona são várias conversas. A versão do usuário simulado (o seu modelo, prompt, temperatura e orçamento) faz parte da chave de cache de cada execução, então um usuário simulado diferente mede o seu sistema de novo.
Validando o usuário simulado
Um resultado obtido com usuários simulados só vale para usuários reais na medida em que o usuário simulado se parece com eles. Por isso, toda regra sobre uma execução cujos usuários foram simulados fica INSUFFICIENT_EVIDENCE com simulated_user_not_validated até que pessoas tenham considerado as suas conversas realistas:
oloproof labels export RUN_ID --criterion simulated_user_realistic --sample 30 --local --out realism.csv
oloproof labels import realism.csv
oloproof simulated-users RUN_IDA planilha mostra cada conversa sorteada com a persona e como ela terminou. Uma pessoa marca passed como sim quando um cliente real poderia ter escrito o lado do usuário e terminado a conversa ali, daquele jeito, o que cobre também a verificação do objetivo: uma conversa que terminou como goal_reached quando o objetivo não foi alcançado não é realista. O usuário simulado é validado na suíte quando o limite inferior exato da proporção realista atinge minimum_simulated_user_realism em release.yaml. Uma conversa sorteada que fica sem rótulo conta como não realista, e uma conversa só é realista quando todos que a rotularam dizem isso.
Uma validação cobre a versão de um usuário simulado em uma suíte, qualquer que seja o sistema com que ele conversou, então um candidato medido contra as mesmas personas é decidido com os rótulos da linha de base. Um novo modelo, uma nova configuração de prompt ou um novo conjunto de personas precisa de novos rótulos. Definir require_validated_evaluators: false decide sobre usuários simulados sem eles, conscientemente.
Simulado ao lado do real
oloproof simulated-users RUN_ID --real REAL_RUN_ID coloca a forma das conversas simuladas ao lado de uma execução de conversas reais (chats registrados reproduzidos como conversation/v1): quantas mensagens o usuário enviou, qual o tamanho delas, qual o tamanho das respostas, com que frequência uma conversa foi interrompida, e como as simuladas terminaram. Isso é descritivo e nunca decide: comparar duas amostras não pareadas exige um método que o Oloproof não admitiu. Leia isso pelo que os rótulos de realismo deixam passar, como usuários simulados que escrevem numa frase arrumada o que os reais espalham por três.
Sem suporte
- Decidir sobre usuários simulados em um espaço de trabalho hospedado: as suas decisões ficam retidas até que a sua fila de revisão consiga sortear e selar rótulos de realismo por conta própria.
- Um gate sobre como as conversas simuladas se comparam com as reais.
- Taxas por turno dentro de uma conversa, e reproduzir uma conversa a partir de um turno.
- Usuários simulados para um sistema HTTP: o próprio código do seu sistema conduz o loop, então ele precisa ser um callable.