Skip to content

Handleidingen

Agents en tools

Oloproof stuurt geen agent aan. Je agent draait zijn eigen lus, roept zijn eigen tools aan, en legt vast wat er gebeurde als een agent_trajectory/v1-artefact. Elke agentmetriek wordt uit dat record gelezen, dus het record is de hele integratie.

examples/support_agent/ is het project dat deze pagina draait: veertig terugbetalingsverzoeken, een deterministische agent, en geen providergegevens.

Een traject vastleggen

Bouw binnen het systeem de stappen op terwijl ze gebeuren en geef ze aan de caserecorder:

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"}

Waar elk deel voor dient:

VeldWat het vastlegt
AgentStep.kindmessage, tool_call, tool_result, observation, decision of final
AgentStep.tool_name, arguments, resultde aanroep en wat er terugkwam
terminal_statussuccess, failure of unknown
truncated, step_limitdat de agent zijn grens bereikte en het spoor voortijdig stopt
constraintscontroles die je omgeving uitvoerde, elk met de stap die ze observeerde
checkpointspunten waarvandaan een herhaling kan hervatten, als AgentCheckpoint

Het systeem declareert dat het het traject vastlegt, op de decorator en in oloproof.yaml:

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

Zonder records: weigeren de agentevaluators te draaien in plaats van elke case als ontbrekend te tellen.

Wat een case declareert

Een case die bepaalde tools in een bepaalde volgorde zou moeten aanroepen, zegt dat onder 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: [] zegt dat de case helemaal geen toolaanroepen verwacht, en wordt daarop beoordeeld. De sleutel weglaten zegt dat toolkeuze niet op de case van toepassing is: agent_tool_sequence en agent_no_undeclared_tool sluiten haar uit met no_declared_tools. Ze verlaat de noemer in plaats van als geslaagd te tellen, omdat een percentage dat is opgeblazen met cases die nooit zijn gemeten geen percentage is. Een waarde die geen lijst van toolnamen is, wordt voor de run geweigerd.

De evaluators

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
TypeSlaagt wanneerNeemt
agent_tool_calledde tool minstens min_calls keer werd aangeroepentool_name, min_calls (standaard 1)
agent_no_tool_loopgeen identieke aanroep, zelfde tool en argumenten, zich meer dan max_repeats keer achter elkaar herhaaltmax_repeats (standaard 2)
agent_tool_sequencede aangeroepen tools overeenkomen met expected.toolsordered (standaard true)
agent_no_undeclared_toolgeen tool buiten expected.tools werd aangeroepenniets
agent_constraints_satisfiedelke genoemde beperking die de omgeving vastlegde, slaagdeconstraints
agent_max_stepshet spoor hoogstens max_steps stappen nammax_steps

agent_steps en agent_tool_calls zijn de verdelingen achter de laatste, als kwantielmetrieken.

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 │

Een beperking gatet zoals elk ander criterium. no-deletion is een regel op een geobserveerd aantal met max_failures: 0: drie cases riepen delete_customer aan, en "dit mag niet gebeuren in de suite die we draaiden" heeft geen interval nodig om te beslissen.

Een spoor dat voortijdig stopt

Eén case bereikt de stappenlimiet van de agent, dus zijn spoor wordt vastgelegd als truncated. Elk aantal over een afgekapt spoor is een ondergrens, en een ondergrens beslecht sommige vragen en andere niet:

CriteriumDe afgekapte caseWaarom
agent_steps_le_10faaltde vastgelegde stappen bewijzen al dat een limiet van tien werd overschreden
agent_tool_lookup_order_calledontbreektde aanroep kan in het deel zitten dat niet werd vastgelegd
agent_no_tool_loopontbreekteen herhaling die het einde van het spoor bereikt, kan daarna doorgaan
steps_p95, tool_calls_p50ontbreekteen kwantiel over ondergrenzen is geen kwantiel

Ontbrekend in plaats van uitgesloten: de case blijft ongeobserveerd in de noemer, en het interval laat toe dat ze beide kanten op is gegaan. Daarom leest agent_tool_lookup_order_called [86.8%, 100.0%] over 39 geobserveerde cases in plaats van het smallere interval dat 39 cases alleen zouden geven.

Slices over trajecten

first_tool groepeert cases op de tool waarnaar de agent als eerste greep, welke tools de run ook aanriep. repeated_action scheidt cases die een identieke aanroep herhaalden van cases die dat niet deden. trajectory_length:4,8 deelt cases in op 1-4, 5-8 en 9 of meer stappen, op grenzen die je declareert omdat een bakgrens verandert wat een slice zegt.

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

Een lus is een signaal, geen verklaring. Niets in de output zegt dat een herhaalde aanroep de reden is dat een case faalde; alleen een herhaling die haar verwijderde, zou dat kunnen.

Meerdere agents

examples/triage_agents/ draait een team van drie: triage geeft elk verzoek door aan billing of tech, en een terugbetaling die billing niet mag uitvoeren, wordt aan een persoon doorgegeven. In een systeem van meerdere agents noemt elke stap de agent die haar zette, en een overdracht van de controle is een handoff-stap:

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"))

Een traject noemt de agent van elke stap of van geen enkele. Een opname die er maar enkele noemt, wordt geweigerd, en een die er geen noemt, ontbreekt voor elk criterium hieronder in plaats van ervoor te slagen. Een overdrachtsstap is optioneel, omdat de controle ook van hand wisselt wanneer de volgende stap door een andere agent wordt gezet. Het is hoe een spoor een overdracht vastlegt naar een agent die geen stap zet, zoals een persoon.

Een case declareert de agents waar ze doorheen zou moeten gaan onder 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]
TypeSlaagt wanneerNeemt
agent_routede agents die de controle hadden, met herhalingen samengevoegd, de expected.route van de case zijnniets
agent_tool_permissionselke toolaanroep werd gedaan door een agent die volgens de map die tool mag aanroepenpermissions
agent_max_handoffsde controle hoogstens max_handoffs keer van hand wisseldemax_handoffs

De route omvat de ontvanger van een overdracht, dus een spoor dat eindigt met een escalatie naar een persoon, routeert naar die persoon. Een case zonder expected.route wordt niet meegeteld door agent_route. De permissiemap is gesloten: een agent die ze niet noemt, mag geen enkele tool aanroepen.

│ 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 twee verzoeken voerde tech een terugbetaling uit. Beide slagen voor answer_correct en falen voor agent_tool_permissions: de klant kreeg het juiste antwoord van een systeem dat zijn eigen permissies schond. Eén verzoek wordt heen en weer gegeven tussen billing en tech tot aan de grens van de lus. Zijn spoor is afgekapt, dus het faalt voor de route en de overdrachtsgrens, die zijn begin al beslecht, en ontbreekt voor permissies, die het niet beslecht.

route groepeert cases op de route die hun agents namen, geschreven als triage>billing. Een afgekapt spoor sluit zich bij geen enkele routegroep aan, omdat zijn route een begin is van waar zijn agents daarna ook heen gingen.

Niets in de output zegt welke agent de schuld heeft. Een routeafwijking zegt waar twee routes uiteengaan. Dat een agent een mislukking veroorzaakte, is een bewering over wat er zou zijn gebeurd als hij anders had gehandeld, wat alleen een herhaling die die agent vervangt zou kunnen aantonen, en Oloproof draait er geen.

Verder lezen