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:
| Campo | Qué registra |
|---|---|
| AgentStep.kind | message, tool_call, tool_result, observation, decision o final |
| AgentStep.tool_name, arguments, result | la llamada y lo que devolvió |
| terminal_status | success, failure o unknown |
| truncated, step_limit | que el agente alcanzó su límite y la traza se corta antes de tiempo |
| constraints | comprobaciones que hizo su entorno, cada una con el paso que observó |
| checkpoints | puntos 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| Tipo | Pasa cuando | Admite |
|---|---|---|
| agent_tool_called | la herramienta se llamó al menos min_calls veces | tool_name, min_calls (por defecto 1) |
| agent_no_tool_loop | ninguna llamada idéntica, misma herramienta y argumentos, se repite más de max_repeats veces seguidas | max_repeats (por defecto 2) |
| agent_tool_sequence | las herramientas llamadas coinciden con expected.tools | ordered (por defecto true) |
| agent_no_undeclared_tool | no se llamó a ninguna herramienta fuera de expected.tools | nada |
| agent_constraints_satisfied | se cumplió cada restricción nombrada que registró el entorno | constraints |
| agent_max_steps | la traza tuvo como máximo max_steps pasos | max_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:
| Criterio | El caso truncado | Por qué |
|---|---|---|
| agent_steps_le_10 | falla | los pasos registrados ya demuestran que se superó un límite de diez |
| agent_tool_lookup_order_called | faltante | la llamada puede estar en la parte que no se registró |
| agent_no_tool_loop | faltante | una repetición que llega al final de la traza puede continuar más allá |
| steps_p95, tool_calls_p50 | faltante | un 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]| Tipo | Pasa cuando | Admite |
|---|---|---|
| agent_route | los agentes que tuvieron el control, con las repeticiones colapsadas, son el expected.route del caso | nada |
| agent_tool_permissions | cada llamada a herramienta la hizo un agente al que el mapa permite llamar a esa herramienta | permissions |
| agent_max_handoffs | el control cambió de manos como máximo max_handoffs veces | max_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
- Registrar lo que hizo un sistema trata los demás artefactos tipados.
- Segmentos trata el soporte de los segmentos y por qué los segmentos nunca actúan como gate.