Guias
Tutorial: uma central de atendimento diante de clientes simulados
Avalie um assistente conversacional contra usuários que reagem ao que ele diz. Trinta clientes simulados, cada um com um objetivo e os fatos que conhece, conversam com a central de atendimento de uma loja; o Oloproof mede se cada um conseguiu o que veio buscar, retém toda decisão até que uma pessoa tenha considerado os clientes simulados realistas, e então decide um lançamento e uma comparação. Todo passo roda nesta máquina sem modelo e sem rede, e um passo opcional troca os clientes por um modelo real.
O que você vai construir
A central de atendimento de uma loja responde a reembolsos, perguntas sobre entrega e mudanças de endereço. Três perguntas decidem se uma versão pode ser lançada:
- Cada cliente conseguiu o que veio buscar? goal_reached, a partir de uma verificação depois de cada resposta.
- Alguma resposta prometeu o que a loja não pode prometer? every_turn lê cada resposta procurando a palavra "guarantee".
- Dá para confiar que as respostas dizem algo sobre clientes reais? Só depois que uma pessoa tiver lido uma amostra das conversas e considerado os clientes simulados realistas.
Termos como caso, execução, gate e suíte são definidos em Conceitos; tudo sobre usuários simulados está em Usuários simulados.
Pré-requisitos
- Python 3.11 ou posterior, e o Oloproof:
python3 -m venv .venv
. .venv/bin/activate
pip install oloproof- O projeto de exemplo, que vem com o pacote. Copie-o para um novo diretório e trabalhe nele:
oloproof init --example simulated-users support-desk
cd support-desk- A porta 8797 livre para o usuário simulado substituto (se não estiver, mude-a nos dois lugares).
Todo passo até "Opcional: um modelo real como os clientes" é offline e determinístico: nenhuma chave de API, nenhuma conta de provedor, nenhum custo.
Os arquivos
support-desk/
desk.py the support desk under test (baseline)
desk_v2.py the candidate change
user_server.py a stand-in simulated user speaking the OpenAI API on 127.0.0.1
oloproof.yaml the suite: the simulated user and four evaluators
release.yaml rules for a run, with the realism bar
compare.yaml a rule for a comparison
data/customers.jsonl 30 personas
labels/realism_verdicts.csv one person's realism verdicts on the baseline's conversations
fill_realism.py copies those verdicts into a labelling sheet
logged.py replays logged chats with human agents
real.yaml a suite of 20 of those chats
logged_release.yaml a rule for the logged chats
data/logged_chats.jsonl the 20 chatsExecute todos os comandos a partir de support-desk/.
A central mantém o seu próprio loop
# desk.py (excerpt)
def talk(case: dict[str, Any]) -> dict[str, Any]:
user = current_case().simulated_user()
desk = Desk()
message = user.opening()
while message is not None:
message = user.reply(desk.answer(message))
return {"need": desk.need, "order": desk.order}A central pede a primeira mensagem do cliente, responde a ela, e devolve cada resposta para receber a próxima mensagem, até o cliente parar. Ela nunca vê a persona. O Oloproof registra o que passou entre os dois; a própria central não registra nada. Esta linha de base não consegue mudar um endereço de entrega, e promete reembolsos "guaranteed within 5 days".
As personas
Cada caso é um cliente: 12 querem um reembolso, 10 querem saber quando um pedido chega, e 8 se mudaram. Por exemplo:
{"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"}}}}O cliente informa o número do pedido e o CEP só quando alguém pede. Três personas também definem um opening, a sua primeira mensagem palavra por palavra.
A suíte
version: 1
project: support-desk
dataset: data/customers.jsonl
system:
name: support-desk
version: baseline
callable: desk:talk
# A model plays each case's persona; here the stand-in in user_server.py.
simulated_user:
provider: openai_compatible
model: stand-in-user
base_url: http://127.0.0.1:8797/v1
max_turns: 6
evaluators:
# The desk answered every message the customer sent.
- type: conversation_completed
# The customer said they got what they came for.
- type: goal_reached
# ... within three of their own messages.
- type: turns_to_goal
max_turns: 3
# No answer promises what the shop cannot promise.
- type: every_turn
criterion: no_promises
not_contains: [guarantee]
casefold: trueO usuário simulado substituto
Um usuário simulado é um modelo que interpreta cada persona. Aqui é user_server.py, que fala a mesma API em 127.0.0.1 e não é um modelo: ele lê a persona no prompt que o Oloproof envia, declara o seu objetivo, informa o número do pedido ou o CEP quando alguém pede, vai embora quando a central não consegue ajudar e, quando perguntado se o seu objetivo foi alcançado, diz sim quando a última resposta da central começa com "Done:". Inicie-o em um segundo terminal e deixe-o rodando:
python user_server.py --port 8797A política
version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
require_validated_evaluators: true
minimum_simulated_user_realism: 0.80
rules:
- id: no-promises
metric: no_promises
kind: observed_count
max_failures: 0
- id: resolved
metric: goal_reached
min: 0.80Nenhuma resposta pode prometer. Pelo menos 80% dos clientes precisam alcançar o seu objetivo, o que só é decidido quando o limite inferior do intervalo supera 80%. E nenhuma regra decide sobre clientes simulados até que pessoas tenham considerado pelo menos 80% das suas conversas realistas, pelo mesmo tipo de limite inferior.
Execute
oloproof runGate: BLOCK (exit 3)
│ no-promises │ no_promises │ INSUFFICIENT_EVIDENCE │ simulated_user_not_validated │
│ resolved │ goal_reached │ INSUFFICIENT_EVIDENCE │ simulated_user_not_validated │
│ conversation_completed │ 100.0% │ [88.4%, 100.0%] │ 30 / 30 observed · 0 missing · 0 excluded │
│ goal_reached │ 73.3% │ [54.1%, 87.8%] │ 22 / 30 observed · 0 missing · 0 excluded │
│ goal_within_3_turns │ 73.3% │ [54.1%, 87.8%] │ 22 / 30 observed · 0 missing · 0 excluded │
│ no_promises │ 60.0% │ [40.6%, 77.4%] │ 18 / 30 observed · 0 missing · 0 excluded │Todo cliente foi respondido, 22 de 30 alcançaram o seu objetivo, e 12 conversas contêm uma promessa. Nada está decidido ainda: esses clientes são simulados, e ninguém verificou se eles se comportam como os reais.
Leia os clientes simulados
oloproof simulated-users RUN_ID Realism: no conversation has been drawn and labelled yet.
Not validated: nothing labelled yet, against a bar of 80.0%.
Features (median [quartiles]; descriptive, never a gate):
user messages 2 [1, 2]
user message length 49 [10, 67]
How the simulated conversations ended: goal_reached 73%, user_left 27%Os oito que foram embora são os clientes que se mudaram: a linha de base não consegue mudar um endereço.
Rotule as conversas quanto ao realismo
Sorteie uma amostra aleatória das conversas da execução em uma planilha, peça a uma pessoa que leia cada uma, com a persona e como ela terminou, e marque se um cliente real poderia ter escrito o lado do cliente e terminado a conversa ali, daquele jeito. Aqui fill_realism.py faz o papel dessa pessoa, copiando os vereditos dela de labels/realism_verdicts.csv:
oloproof labels export RUN_ID --criterion simulated_user_realistic --sample 30 --local --out realism.csv
python fill_realism.py realism.csv
oloproof labels import realism.csv
oloproof simulated-users RUN_ID Realism: 29 of 30 drawn conversations labelled realistic (30 labelled), [82.8%, 99.9%] at 95%
Validated: the lower bound clears the bar of 80.0%.A pessoa considerou uma conversa não realista: o cliente c05 começa com "Order: F-15. Refund.", que parece um formulário, não uma pessoa. Com 29 de 30, o limite inferior exato é 82,8%, acima do patamar, então as regras agora podem decidir sobre esses clientes.
oloproof gate RUN_ID --policy release.yamlno-promises: FAIL (observed_failures_exceed_limit)
resolved: INSUFFICIENT_EVIDENCE (interval_overlaps_threshold)
Gate: BLOCK (exit 1)Faça uma mudança real
desk_v2.py muda um endereço assim que tem o pedido e o novo CEP, e diz que iniciou um reembolso que "usually arrives within 5 days" em vez de prometê-lo. Copie-o por cima de desk.py, defina version: candidate em system no oloproof.yaml, mantenha o substituto rodando, e:
oloproof runGate: ALLOW (exit 0)
│ no-promises │ no_promises │ PASS │ observed_failures_within_limit │
│ resolved │ goal_reached │ PASS │ lower_bound_meets_minimum │O candidato encontrou os mesmos clientes simulados na mesma suíte, então o realismo medido nas conversas da linha de base o cobre: uma validação cobre um usuário simulado em uma suíte, qualquer que seja o sistema com que ele conversou.
Compare o candidato com a linha de base
oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy compare.yamlgoal_reached: +26.7 points [+1.2, +54.1] · 30 paired · 0 missing · 0 excluded
resolves-more goal_reached superiority PASS difference_above_zeroCada cliente encontrou as duas centrais, então a comparação é pareada cliente a cliente.
Simulado ao lado do real
Os rótulos de realismo julgam cada conversa isoladamente. Colocar os clientes simulados ao lado de clientes reais mostra o que uma amostra pode deixar passar. real.yaml reproduz 20 chats que os atendentes humanos da loja tiveram com clientes reais:
oloproof run --config real.yaml --policy logged_release.yaml
oloproof simulated-users BASELINE_RUN_ID --real REAL_RUN_ID simulated (run_01M52Q1C) real (run_01M52Q25)
user messages 2 [1, 2] 3 [3, 4]
user message length 49 [10, 67] 13 [4, 32]Os clientes reais enviaram mais mensagens, e mais curtas: eles dizem o que querem em três linhas, onde o substituto diz isso numa frase arrumada. Vale a pena saber disso sobre qualquer usuário simulado, e aqui isso nunca decide nada: comparar duas amostras não pareadas exige um método que o Oloproof não admitiu.
Opcional: um modelo real como os clientes
Este passo sai do caminho offline. Ele precisa de um modelo de chat: localmente, o Ollama (ollama pull qwen2.5vl, ou qualquer modelo de chat), sem chave e sem custo; na nuvem, provider: anthropic ou openai com api_key_env, cobrado por mensagem. No oloproof.yaml, aponte o usuário simulado para ele:
simulated_user:
provider: openai_compatible
model: qwen2.5vl
base_url: http://localhost:11434/v1
max_turns: 6O Ollama atende uma requisição por vez, então defina concurrency: {system: 1} e um timeout_s de alguns minutos. Um modelo diferente é um usuário simulado diferente: os rótulos de realismo acima não o cobrem, então as suas conversas precisam da sua própria amostra antes que qualquer regra decida sobre elas.
Dez das personas, interpretadas por qwen2.5vl nesta máquina contra a central candidata, alcançaram o seu objetivo em 8 conversas, enquanto o substituto o alcançou em todas as 30. O modelo encontrou o que o substituto não conseguia: dois clientes pediram o "status" e o "shipping status" do seu pedido, palavras que a central não conhece, e receberam de volta a saudação dela todas as vezes. Um ficou sem mensagens. A central entendeu a terceira tentativa do outro, e esse cliente foi embora mesmo assim: a verificação do objetivo, um julgamento de modelo, não percebeu a resposta. É num rótulo de realismo que isso aparece: um cliente real a quem disseram quando o seu pedido chega não vai embora insatisfeito. Ao contrário do substituto, o modelo também muitas vezes deu o número do pedido antes de alguém pedir, e escreveu uma mensagem mais longa onde o substituto escreveu duas curtas.
Solução de problemas
| Sintoma | Causa e correção |
|---|---|
| Toda regra fica INSUFFICIENT_EVIDENCE com simulated_user_not_validated | Ninguém rotulou as conversas desse usuário simulado nessa suíte, ou a proporção realista não atinge o patamar. Rotule uma amostra como acima. |
| a case for a simulated user needs a 'persona' object in its input | Um caso não tem persona, ou ela está sob outra chave: defina persona_field. |
| provider connection failed em todos os casos | O substituto não está rodando, ou escuta em outra porta que não a que base_url indica. |
| the simulated user's goal check answered ... | O modelo não respondeu à verificação do objetivo com sim ou não. Tente outro modelo, ou um maior. |
| conversation/v1 is written by Oloproof | A central registrou a conversa ela mesma. Remova isso: o Oloproof registra o que passou pelo usuário simulado. |
Limitações
- O cliente substituto é uma regra. As suas conversas mostram a mecânica; as de um modelo real são mais bagunçadas, como mostra o passo opcional.
- Se um objetivo foi alcançado é o julgamento do modelo do usuário simulado, verificado só por meio dos rótulos de realismo.
- Um espaço de trabalho hospedado ainda não decide sobre usuários simulados.
- Clientes simulados não são clientes reais: um usuário simulado validado é evidência sobre clientes parecidos com as suas personas, na medida em que as pessoas o consideraram realista, e não além disso.