Skip to content

Guías

Agentes y herramientas

Oloproof no dirige un agente. Su agente ejecuta su propio bucle, llama a sus propias herramientas y registra lo que ocurrió como un artefacto agent_trajectory/v1. Toda métrica de agente se lee a partir de ese registro, de modo que el registro es toda la integración.

examples/support_agent/ es el proyecto que ejecuta esta página: cuarenta solicitudes de reembolso, un agente determinista y ninguna credencial de proveedor.

Registrar una trayectoria

Dentro del sistema, construya los pasos a medida que ocurren y entréguelos al registrador del caso:

from oloproof import AgentConstraintCheck, AgentStep, AgentTrajectory, current_case, system


@system(name="support-agent", version="slice-e-example", records=("agent_trajectory/v1",))
def run(case):
    steps = []
    for name, arguments in plan(case):
        steps.append(AgentStep(index=len(steps) + 1, kind="tool_call", tool_name=name, arguments=arguments))
        result = getattr(tools, name)(**arguments)
        steps.append(AgentStep(index=len(steps) + 1, kind="tool_result", tool_name=name, result=result))

    current_case().agent_trajectory(
        AgentTrajectory(
            steps=tuple(steps),
            terminal_status="success" if refunded else "failure",
            truncated=truncated,
            step_limit=STEP_LIMIT if truncated else None,
            constraints=(
                AgentConstraintCheck(name="no_deletion", passed=deletion is None, step_index=...),
            ),
        )
    )
    return {"answer": "refunded" if refunded else "unresolved"}

Para qué sirve cada parte:

CampoQué registra
AgentStep.kindmessage, tool_call, tool_result, observation, decision o final
AgentStep.tool_name, arguments, resultla llamada y lo que devolvió
terminal_statussuccess, failure o unknown
truncated, step_limitque el agente alcanzó su límite y la traza se corta antes de tiempo
constraintscomprobaciones que hizo su entorno, cada una con el paso que observó
checkpointspuntos desde los que podría reanudarse una reproducción, como AgentCheckpoint

El sistema declara que registra la trayectoria, en el decorador y en oloproof.yaml:

system:
  name: support-agent
  version: slice-e-example
  callable: app:run
  records: [agent_trajectory/v1]

Sin records:, los evaluadores de agente se niegan a ejecutarse en lugar de contar todos los casos como faltantes.

Qué declara un caso

Un caso que debe llamar a herramientas concretas, en un orden concreto, lo indica bajo expected.tools:

{"id": "case_001", "input": {"order_id": "ord-002", "behaviour": "clean"}, "expected": {"answer": "refunded", "tools": ["lookup_order", "issue_refund"]}, "metadata": {"surface": "chat", "behaviour": "clean"}}

expected.tools: [] indica que el caso no espera ninguna llamada a herramientas, y se juzga en función de eso. Omitir la clave indica que la elección de herramientas no se aplica al caso: agent_tool_sequence y agent_no_undeclared_tool lo excluyen con no_declared_tools. El caso sale del denominador en lugar de contar como aprobado, porque una tasa inflada con casos que nunca se midieron no es una tasa. Un valor que no sea una lista de nombres de herramientas se rechaza antes de la ejecución.

Los evaluadores

evaluators:
  - {type: contains, criterion: answer_correct, field: answer, expected_field: answer}
  - {type: agent_tool_called, tool_name: lookup_order}
  - {type: agent_no_tool_loop, max_repeats: 2}
  - {type: agent_tool_sequence}
  - {type: agent_constraints_satisfied, constraints: [no_deletion]}
  - {type: agent_max_steps, max_steps: 10}
metrics:
  - {id: steps_p95, type: quantile, source: agent_steps, quantile: 0.95}
  - {id: tool_calls_p50, type: quantile, source: agent_tool_calls, quantile: 0.5}
slices: [metadata.surface, first_tool, repeated_action, "trajectory_length:4,8"]
min_slice_support: 3
TipoPasa cuandoAdmite
agent_tool_calledla herramienta se llamó al menos min_calls vecestool_name, min_calls (por defecto 1)
agent_no_tool_loopninguna llamada idéntica, misma herramienta y argumentos, se repite más de max_repeats veces seguidasmax_repeats (por defecto 2)
agent_tool_sequencelas herramientas llamadas coinciden con expected.toolsordered (por defecto true)
agent_no_undeclared_toolno se llamó a ninguna herramienta fuera de expected.toolsnada
agent_constraints_satisfiedse cumplió cada restricción nombrada que registró el entornoconstraints
agent_max_stepsla traza tuvo como máximo max_steps pasosmax_steps

agent_steps y agent_tool_calls son las distribuciones que subyacen al último, como métricas de cuantil.

oloproof run
│ answer_correct                 │ 82.5%    │ [67.2%, 92.7%]       │ 33 / 40 observed · 0 missing · 0 excluded   │
│ agent_tool_lookup_order_called │ 100.0%   │ [86.8%, 100.0%]      │ 39 / 39 observed · 1 missing · 0 excluded   │
│ agent_no_tool_loop             │ 74.4%    │ [56.1%, 87.4%]       │ 29 / 39 observed · 1 missing · 0 excluded   │
│ agent_tool_sequence            │ 60.0%    │ [43.3%, 75.2%]       │ 24 / 40 observed · 0 missing · 0 excluded   │
│ agent_constraints_satisfied    │ 92.5%    │ [79.6%, 98.5%]       │ 37 / 40 observed · 0 missing · 0 excluded   │
│ agent_steps_le_10              │ 97.5%    │ [86.8%, 100.0%]      │ 39 / 40 observed · 0 missing · 0 excluded   │
│ steps_p95                      │ 10 steps │ [10, no bound] steps │ p95 of 39 observed · 1 missing · 0 excluded │
│ tool_calls_p50                 │ 2 calls  │ [2, 3] calls         │ p50 of 39 observed · 1 missing · 0 excluded │
│ no-deletion         │ agent_constraints_satisfied │ FAIL                  │ observed_failures_exceed_limit │

Una restricción actúa como gate como cualquier otro criterio. no-deletion es una regla de recuento observado con max_failures: 0: tres casos llamaron a delete_customer, y "esto no debe ocurrir en la suite que ejecutamos" no necesita ningún intervalo para decidirse.

Una traza que se corta antes de tiempo

Un caso alcanza el límite de pasos del agente, así que su traza se registra como truncated. Todo recuento sobre una traza truncada es una cota inferior, y una cota inferior resuelve algunas preguntas y otras no:

CriterioEl caso truncadoPor qué
agent_steps_le_10fallalos pasos registrados ya demuestran que se superó un límite de diez
agent_tool_lookup_order_calledfaltantela llamada puede estar en la parte que no se registró
agent_no_tool_loopfaltanteuna repetición que llega al final de la traza puede continuar más allá
steps_p95, tool_calls_p50faltanteun cuantil sobre cotas inferiores no es un cuantil

Faltante en lugar de excluido: el caso permanece en el denominador sin observar, y el intervalo admite que haya ido en cualquier sentido. Por eso agent_tool_lookup_order_called marca [86.8%, 100.0%] sobre 39 casos observados, en lugar del intervalo más estrecho que darían 39 casos por sí solos.

Segmentos sobre trayectorias

first_tool agrupa los casos por la herramienta a la que recurrió primero el agente, sean cuales sean las herramientas que llamó la ejecución. repeated_action separa los casos que repitieron una llamada idéntica de los que no. trajectory_length:4,8 agrupa los casos en 1-4, 5-8 y 9 o más pasos, con límites que usted declara porque el límite de un grupo cambia lo que dice un segmento.

│ first_tool=search          │ agent_no_tool_loop             │ 0.0%     │ [0.0%, 57.9%]        │ 0 / 6 observed · 1 missing · 0 excluded ·          │
│ first_tool=search          │ agent_tool_sequence            │ 0.0%     │ [0.0%, 41.0%]        │ 0 / 7 observed · 0 missing · 0 excluded ·          │
│ repeated_action=false      │ answer_correct                 │ 93.1%    │ [77.2%, 99.2%]       │ 27 / 29 observed · 0 missing · 0 excluded ·        │
│ repeated_action=true       │ answer_correct                 │ 60.0%    │ [26.2%, 87.9%]       │ 6 / 10 observed · 0 missing · 0 excluded ·         │

Un bucle es una señal, no una explicación. Nada en la salida dice que una llamada repetida sea la causa del fallo de un caso; solo podría decirlo una reproducción que la eliminara.

Varios agentes

examples/triage_agents/ ejecuta un equipo de tres: triage entrega cada solicitud a billing o a tech, y un reembolso que billing no puede emitir se entrega a una persona. En un sistema de varios agentes, cada paso nombra al agente que lo dio, y una transferencia de control es un paso handoff:

steps.append(AgentStep(index=1, kind="message", agent="triage", arguments={"request": request}))
steps.append(AgentStep(index=2, kind="handoff", agent="triage", to_agent="billing"))
steps.append(AgentStep(index=3, kind="tool_call", agent="billing", tool_name="lookup_order"))

Una trayectoria nombra al agente de todos los pasos o de ninguno. Un registro que solo nombra algunos se rechaza, y uno que no nombra ninguno queda como faltante para todos los criterios siguientes en lugar de aprobarlos. Un paso de traspaso es opcional, ya que el control también cambia de manos cuando el siguiente paso lo da otro agente. Es la forma en que una traza registra una transferencia a un agente que no da ningún paso, como una persona.

Un caso declara los agentes por los que debe pasar bajo expected.route:

{"id": "case_012", "input": {"topic": "billing", "request": "I was charged twice for order ord-012", "order_id": "ord-012", "behaviour": "escalated"}, "expected": {"answer": "escalated", "route": ["triage", "billing", "person"]}, "metadata": {"topic": "billing", "behaviour": "escalated"}}
evaluators:
  - {type: contains, criterion: answer_correct, field: answer, expected_field: answer}
  - {type: agent_route}
  - type: agent_tool_permissions
    permissions:
      triage: []
      billing: [lookup_order, issue_refund]
      tech: [search_kb]
  - {type: agent_max_handoffs, max_handoffs: 2}
slices: [route]
TipoPasa cuandoAdmite
agent_routelos agentes que tuvieron el control, con las repeticiones colapsadas, son el expected.route del casonada
agent_tool_permissionscada llamada a herramienta la hizo un agente al que el mapa permite llamar a esa herramientapermissions
agent_max_handoffsel control cambió de manos como máximo max_handoffs vecesmax_handoffs

La ruta incluye al receptor de un traspaso, así que una traza que termina escalando a una persona tiene como destino a la persona. Un caso sin expected.route no lo cuenta agent_route. El mapa de permisos es cerrado: un agente que no aparece en él no puede llamar a ninguna herramienta.

│ answer_correct         │ 96.7%    │ [82.7%, 100.0%] │ 29 / 30 observed · 0 missing · 0 excluded │
│ agent_route            │ 86.7%    │ [69.2%, 96.3%]  │ 26 / 30 observed · 0 missing · 0 excluded │
│ agent_tool_permissions │ 93.1%    │ [73.4%, 99.2%]  │ 27 / 29 observed · 1 missing · 0 excluded │
│ agent_handoffs_le_2    │ 86.7%    │ [69.2%, 96.3%]  │ 26 / 30 observed · 0 missing · 0 excluded │

En dos solicitudes, tech emitió un reembolso. Ambas pasan answer_correct y fallan agent_tool_permissions: el cliente obtuvo la respuesta correcta de un sistema que violó sus propios permisos. Una solicitud va y viene entre billing y tech hasta el límite del bucle. Su traza está truncada, así que falla la ruta y el límite de traspasos, que su prefijo ya resuelve, y queda como faltante para los permisos, que su prefijo no resuelve.

route agrupa los casos por la ruta que siguieron sus agentes, escrita como triage>billing. Una traza truncada no se une a ningún grupo de ruta, porque su ruta es un prefijo de adondequiera que hayan ido sus agentes después.

Nada en la salida dice qué agente tiene la culpa. Una divergencia de rutas indica dónde se separan dos rutas. Que un agente causó un fallo es una afirmación sobre lo que habría ocurrido si hubiera actuado de otro modo, algo que solo podría mostrar una reproducción que sustituyera a ese agente, y Oloproof no ejecuta ninguna.

Siguientes pasos