Saltar al contenido

Guías

Usuarios simulados

Una conversación guionizada prueba su asistente frente a un usuario que nunca reacciona. Un usuario simulado sí reacciona: un modelo interpreta a una persona que describe cada caso, escribe cada mensaje siguiente a partir de lo que su asistente acaba de decir, y se detiene cuando alcanza su objetivo o se rinde. Oloproof registra lo que pasó entre ellos, lo mide como cualquier conversación y deja que un gate decida sobre ello solo cuando las personas han encontrado realista al usuario simulado. El tutorial recorre un servicio de soporte que habla con clientes simulados.

Personajes

La entrada de cada caso contiene un personaje: quién es el usuario, qué quiere y qué 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 y goal son obligatorios. Escriba un objetivo que un lector pudiera comprobar solo a partir de la conversación: "que la tienda inicie un reembolso" se puede decidir a partir de lo que dice el asistente, "conseguir un reembolso" no, porque el dinero llega más tarde.
  • facts son lo que el usuario sabe y comparte solo cuando la conversación lo necesita, como un número de pedido. Su asistente nunca ve el personaje.
  • opening es el primer mensaje del usuario, palabra por palabra, cuando todos los sistemas deben recibir el mismo saludo.
  • max_turns limita los mensajes de este usuario y sustituye al valor del proyecto.

Un personaje es contenido del conjunto de datos: lo cubre el digest de la suite y es el mismo para todo sistema que se mida frente a él.

Declarar el usuario simulado

El modelo que interpreta todos los personajes se declara en el sistema, como un juez:

system:
  name: support-desk
  callable: desk:talk
  simulated_user:
    provider: openai_compatible
    base_url: http://localhost:11434/v1
    model: qwen2.5vl
    max_turns: 6

Todos los campos están en la referencia de configuración. Un modelo local no cuesta nada; uno alojado factura cada mensaje que escribe, y sus tokens se registran junto a la conversación y nunca se cuentan como coste de su sistema.

Su sistema conserva su bucle

Oloproof no dirige la conversación. Su sistema pide el primer mensaje del usuario, lo responde como responda a los usuarios, y devuelve cada respuesta para obtener el mensaje siguiente:

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 devuelve None en cuanto el usuario se detiene. Su sistema puede terminar la conversación por sí mismo con user.end(answer), como haría un traspaso a una persona; un sistema async def espera aopening(), areply(answer) y aend(answer). Oloproof registra lo que pasó por el manejador como conversation/v1 y cómo terminó como simulated_user/v1, y rechaza ambos si vienen de su sistema (Referencia del SDK).

Cómo termina una conversación

Después de cada respuesta de su sistema, se pregunta al modelo del usuario simulado, como una pregunta aparte a temperatura 0 y sin el juego de rol que la rodea, si esa respuesta cumplió el objetivo. Si lo cumplió, la conversación termina en goal_reached. Si no, el usuario escribe su siguiente mensaje, o se va (user_left), hasta haber enviado max_turns mensajes (turn_limit). Un sistema que se detiene sin responder al último mensaje deja la conversación unanswered y truncada.

Preguntar aparte es lo que hace utilizable un modelo local pequeño: interpretar a un personaje y decidir si se cumplió un objetivo son tareas distintas, y un modelo al que se le pide hacer ambas en una sola respuesta tiende a seguir charlando después de cumplir su objetivo, o a decir que se va cuando está satisfecho.

Medir las conversaciones

La conversación es la unidad: cada evaluador da un veredicto por conversación, así que todo intervalo, comparación y gate se aplica sin cambios.

TipoAprueba cuando
conversation_completedsu sistema respondió a cada mensaje
goal_reachedla comprobación del objetivo encontró el objetivo cumplido
turns_to_goallo hizo en max_turns de los mensajes del usuario como mucho
every_turncada respuesta cumple las condiciones declaradas: palabras que debe contener o no, un patrón, una longitud
conversation_judgeun juez de modelo de toda la transcripción la aprueba, una vez validado frente a etiquetas de personas

Las réplicas (replicates:) se encuentran con usuarios de semillas distintas, así que un personaje da varias conversaciones. La versión del usuario simulado (su modelo, su prompt, su temperatura y su presupuesto) forma parte de la clave de caché de cada ejecución, así que un usuario simulado distinto vuelve a medir su sistema.

Validar el usuario simulado

Un resultado sobre usuarios simulados alcanza a los usuarios reales solo en la medida en que el usuario simulado se les parece. Por eso toda regla sobre una ejecución cuyos usuarios fueron simulados es INSUFFICIENT_EVIDENCE con simulated_user_not_validated hasta que las personas hayan encontrado realistas sus conversaciones:

oloproof labels export RUN_ID --criterion simulated_user_realistic --sample 30 --local --out realism.csv
oloproof labels import realism.csv
oloproof simulated-users RUN_ID

La hoja muestra cada conversación extraída con el personaje y cómo terminó. Una persona marca passed como sí cuando un cliente real podría haber escrito la parte del usuario y haberla terminado ahí, de esa manera, lo que cubre también la comprobación del objetivo: una conversación que terminó en goal_reached sin que el objetivo se cumpliera no es realista. El usuario simulado queda validado en la suite cuando el límite inferior exacto de la proporción realista alcanza minimum_simulated_user_realism en release.yaml. Una conversación extraída que queda sin etiquetar cuenta como no realista, y una conversación es realista solo cuando todos los que la etiquetaron lo dicen.

Una validación cubre la versión de un usuario simulado en una suite, sea cual sea el sistema con el que habló, así que un candidato medido frente a los mismos personajes se decide con las etiquetas de la línea base. Un modelo nuevo, un ajuste de prompt nuevo o un conjunto de personajes nuevo necesita etiquetas nuevas. Poner require_validated_evaluators: false decide sobre usuarios simulados sin ellas, a sabiendas.

Simulados junto a reales

oloproof simulated-users RUN_ID --real REAL_RUN_ID pone la forma de las conversaciones simuladas junto a una ejecución de conversaciones reales (chats registrados reproducidos como conversation/v1): cuántos mensajes envió el usuario, qué longitud tenían, qué longitud tenían las respuestas, con qué frecuencia se cortó una conversación, y cómo terminaron las simuladas. Es descriptivo y nunca decide: comparar dos muestras no emparejadas necesita un método que Oloproof no ha admitido. Léalo para ver lo que las etiquetas de realismo pasan por alto, como usuarios simulados que escriben en una frase pulcra lo que los reales reparten en tres.

No soportado

  • Decidir sobre usuarios simulados en un espacio de trabajo alojado: sus decisiones siguen retenidas hasta que su cola de revisión pueda extraer y sellar ella misma etiquetas de realismo.
  • Un gate sobre cómo se comparan las conversaciones simuladas con las reales.
  • Tasas por turno dentro de una conversación, y reproducir una conversación a partir de un turno.
  • Usuarios simulados para un sistema HTTP: el propio código de su sistema ejecuta el bucle, así que debe ser un callable.