Skip to content

Guide

Agenti e strumenti

Oloproof non pilota un agente. Il tuo agente esegue il proprio ciclo, chiama i propri strumenti e registra ciò che è accaduto come artefatto agent_trajectory/v1. Ogni metrica sugli agenti viene letta da quel record, quindi il record è l'intera integrazione.

examples/support_agent/ è il progetto eseguito in questa pagina: quaranta richieste di rimborso, un agente deterministico e nessuna credenziale di provider.

Registrare una traiettoria

All'interno del sistema, costruisci i passi man mano che avvengono e passali al registratore 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"}

A che cosa serve ciascuna parte:

CampoChe cosa registra
AgentStep.kindmessage, tool_call, tool_result, observation, decision o final
AgentStep.tool_name, arguments, resultla chiamata e ciò che è tornato
terminal_statussuccess, failure o unknown
truncated, step_limitche l'agente ha raggiunto il proprio limite e la traccia si interrompe prima della fine
constraintsi controlli effettuati dal tuo ambiente, ciascuno con il passo che ha osservato
checkpointsi punti da cui una riproduzione potrebbe riprendere, come AgentCheckpoint

Il sistema dichiara di registrare la traiettoria, nel decoratore e in oloproof.yaml:

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

Senza records:, i valutatori degli agenti si rifiutano di essere eseguiti anziché contare ogni caso come mancante.

Che cosa dichiara un caso

Un caso che dovrebbe chiamare strumenti particolari, in un ordine particolare, lo indica sotto 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: [] dice che il caso non si aspetta alcuna chiamata a strumenti, e viene giudicato su questo. Omettere la chiave dice che la scelta degli strumenti non si applica al caso: agent_tool_sequence e agent_no_undeclared_tool lo escludono con no_declared_tools. Esce dal denominatore anziché contare come un pass, perché un tasso gonfiato da casi mai misurati non è un tasso. Un valore che non è un elenco di nomi di strumenti viene rifiutato prima dell'esecuzione.

I valutatori

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
TipoPassa quandoAccetta
agent_tool_calledlo strumento è stato chiamato almeno min_calls voltetool_name, min_calls (predefinito 1)
agent_no_tool_loopnessuna chiamata identica, stesso strumento e stessi argomenti, si ripete più di max_repeats volte di seguitomax_repeats (predefinito 2)
agent_tool_sequencegli strumenti chiamati corrispondono a expected.toolsordered (predefinito true)
agent_no_undeclared_toolnon è stato chiamato alcuno strumento al di fuori di expected.toolsnulla
agent_constraints_satisfiedogni vincolo indicato registrato dall'ambiente è stato superatoconstraints
agent_max_stepsla traccia ha richiesto al massimo max_steps passimax_steps

agent_steps e agent_tool_calls sono le distribuzioni che stanno dietro all'ultimo, come metriche di quantile.

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 │

Un vincolo fa da gate come qualsiasi altro criterio. no-deletion è una regola sul conteggio osservato con max_failures: 0: tre casi hanno chiamato delete_customer, e "questo non deve accadere nella suite che abbiamo eseguito" non ha bisogno di alcun intervallo per essere deciso.

Una traccia che si interrompe prima della fine

Un caso raggiunge il limite di passi dell'agente, quindi la sua traccia viene registrata come truncated. Ogni conteggio su una traccia troncata è un limite inferiore, e un limite inferiore risolve alcune domande e non altre:

CriterioIl caso troncatoPerché
agent_steps_le_10falliscei passi registrati dimostrano già che un limite di dieci è stato superato
agent_tool_lookup_order_calledmancantela chiamata potrebbe trovarsi nella parte non registrata
agent_no_tool_loopmancanteuna ripetizione che arriva alla fine della traccia potrebbe proseguire oltre
steps_p95, tool_calls_p50mancanteun quantile su limiti inferiori non è un quantile

Mancante anziché escluso: il caso resta nel denominatore come non osservato, e l'intervallo ammette che possa essere andato in un senso o nell'altro. È per questo che agent_tool_lookup_order_called riporta [86.8%, 100.0%] su 39 casi osservati anziché l'intervallo più stretto che darebbero 39 casi da soli.

Slice sulle traiettorie

first_tool raggruppa i casi in base allo strumento a cui l'agente è ricorso per primo, qualunque siano gli strumenti chiamati dall'esecuzione. repeated_action separa i casi che hanno ripetuto una chiamata identica da quelli che non lo hanno fatto. trajectory_length:4,8 suddivide i casi in fasce di 1-4, 5-8 e 9 o più passi, ai limiti che dichiari, perché un confine di fascia cambia ciò che una slice dice.

│ 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 ciclo è un segnale, non una spiegazione. Nulla nell'output dice che una chiamata ripetuta sia il motivo per cui un caso è fallito; potrebbe dirlo solo una riproduzione che la rimuovesse.

Più agenti

examples/triage_agents/ esegue una squadra di tre: triage passa ogni richiesta a billing o a tech, e un rimborso che billing non può emettere viene passato a una persona. In un sistema di più agenti, ogni passo indica l'agente che lo ha compiuto, e un trasferimento del controllo è un passo 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 traiettoria indica l'agente di ogni passo o di nessuno. Una registrazione che ne indica solo alcuni viene rifiutata, e una che non ne indica nessuno risulta mancante per ogni criterio qui sotto anziché superarlo. Un passo di passaggio di consegne è facoltativo, poiché il controllo cambia mano anche quando il passo successivo viene compiuto da un altro agente. È il modo in cui una traccia registra un trasferimento a un agente che non compie alcun passo, come una persona.

Un caso dichiara gli agenti attraverso cui dovrebbe passare sotto 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]
TipoPassa quandoAccetta
agent_routegli agenti che hanno avuto il controllo, con le ripetizioni compresse, sono l'expected.route del casonulla
agent_tool_permissionsogni chiamata a strumento è stata fatta da un agente a cui la mappa consente di chiamare quello strumentopermissions
agent_max_handoffsil controllo è passato di mano al massimo max_handoffs voltemax_handoffs

Il percorso include il destinatario di un passaggio di consegne, quindi una traccia che termina con un'escalation a una persona ha come percorso la persona. Un caso senza expected.route non viene conteggiato da agent_route. La mappa dei permessi è chiusa: un agente che non vi compare non può chiamare alcuno strumento.

│ 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 │

In due richieste tech ha emesso un rimborso. Entrambe superano answer_correct e falliscono agent_tool_permissions: il cliente ha ottenuto la risposta giusta da un sistema che ha violato i propri permessi. Una richiesta viene rimbalzata avanti e indietro tra billing e tech fino al limite del ciclo. La sua traccia è troncata, quindi fallisce il percorso e il limite di passaggi di consegne, che il suo prefisso risolve già, e risulta mancante per i permessi, che il prefisso non risolve.

route raggruppa i casi in base al percorso seguito dai loro agenti, scritto triage>billing. Una traccia troncata non entra in alcun gruppo di percorso, perché il suo percorso è un prefisso di qualunque direzione abbiano poi preso i suoi agenti.

Nulla nell'output dice quale agente sia da biasimare. Una divergenza di percorso dice dove due percorsi si separano. Che un agente abbia causato un fallimento è un'affermazione su ciò che sarebbe accaduto se avesse agito diversamente, che solo una riproduzione che sostituisse quell'agente potrebbe mostrare, e Oloproof non ne esegue.

Dove andare dopo

da gate.