Skip to content

Przewodniki

Agenci i narzędzia

Oloproof nie steruje agentem. Twój agent wykonuje własną pętlę, wywołuje własne narzędzia i zapisuje to, co się wydarzyło, jako artefakt agent_trajectory/v1. Każda metryka agenta jest odczytywana z tego zapisu, więc zapis stanowi całą integrację.

examples/support_agent/ to projekt, który uruchamia ta strona: czterdzieści wniosków o zwrot, deterministyczny agent i żadnych poświadczeń dostawcy.

Zapisywanie trajektorii

Wewnątrz systemu buduj kroki w miarę ich występowania i przekazuj je do rejestratora przypadku:

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

Do czego służy każda część:

PoleCo zapisuje
AgentStep.kindmessage, tool_call, tool_result, observation, decision lub final
AgentStep.tool_name, arguments, resultwywołanie i to, co zwróciło
terminal_statussuccess, failure lub unknown
truncated, step_limitże agent osiągnął swój limit, a ślad urywa się przedwcześnie
constraintskontrole wykonane przez Twoje środowisko, każda z krokiem, który zaobserwowała
checkpointspunkty, od których odtworzenie mogłoby wznowić działanie, jako AgentCheckpoint

System deklaruje, że zapisuje trajektorię, w dekoratorze i w oloproof.yaml:

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

Bez records: ewaluatory agentów odmawiają uruchomienia, zamiast liczyć każdy przypadek jako brakujący.

Co deklaruje przypadek

Przypadek, który powinien wywołać określone narzędzia w określonej kolejności, podaje to pod 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: [] oznacza, że przypadek nie oczekuje żadnych wywołań narzędzi, i jest na tej podstawie oceniany. Pominięcie klucza oznacza, że wybór narzędzi nie dotyczy przypadku: agent_tool_sequence i agent_no_undeclared_tool wykluczają go z no_declared_tools. Przypadek opuszcza mianownik, zamiast liczyć się jako zaliczony, ponieważ wskaźnik zawyżony przypadkami, których nigdy nie zmierzono, nie jest wskaźnikiem. Wartość, która nie jest listą nazw narzędzi, jest odrzucana przed uruchomieniem.

Ewaluatory

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
TypZalicza, gdyPrzyjmuje
agent_tool_callednarzędzie zostało wywołane co najmniej min_calls razytool_name, min_calls (domyślnie 1)
agent_no_tool_loopżadne identyczne wywołanie, to samo narzędzie i argumenty, nie powtarza się więcej niż max_repeats razy z rzędumax_repeats (domyślnie 2)
agent_tool_sequencewywołane narzędzia odpowiadają expected.toolsordered (domyślnie true)
agent_no_undeclared_toolnie wywołano żadnego narzędzia spoza expected.toolsnic
agent_constraints_satisfiedkażde nazwane ograniczenie zapisane przez środowisko zostało spełnioneconstraints
agent_max_stepsślad wykonał co najwyżej max_steps krokówmax_steps

agent_steps i agent_tool_calls to rozkłady stojące za ostatnim z nich, jako metryki kwantylowe.

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 │

Ograniczenie bramkuje jak każde inne kryterium. no-deletion to reguła liczby obserwacji z max_failures: 0: trzy przypadki wywołały delete_customer, a stwierdzenie „to nie może się zdarzyć w zestawie, który uruchomiliśmy” nie potrzebuje przedziału do rozstrzygnięcia.

Ślad, który urywa się przedwcześnie

Jeden przypadek osiąga limit kroków agenta, więc jego ślad jest zapisany jako truncated. Każda liczba z przyciętego śladu jest dolną granicą, a dolna granica rozstrzyga niektóre pytania, a innych nie:

KryteriumPrzycięty przypadekDlaczego
agent_steps_le_10nie zaliczazapisane kroki już dowodzą, że limit dziesięciu został przekroczony
agent_tool_lookup_order_calledbrakującywywołanie może znajdować się w części, która nie została zapisana
agent_no_tool_loopbrakującypowtórzenie sięgające końca śladu może trwać dalej
steps_p95, tool_calls_p50brakującykwantyl z dolnych granic nie jest kwantylem

Brakujący, a nie wykluczony: przypadek pozostaje w mianowniku jako nieobserwowany, a przedział dopuszcza, że mógł potoczyć się w którąkolwiek stronę. Dlatego agent_tool_lookup_order_called wskazuje [86.8%, 100.0%] dla 39 obserwowanych przypadków, a nie węższy przedział, jaki dałoby samo 39 przypadków.

Wycinki według trajektorii

first_tool grupuje przypadki według narzędzia, po które agent sięgnął najpierw, niezależnie od tego, jakie narzędzia wywołało uruchomienie. repeated_action oddziela przypadki, które powtórzyły identyczne wywołanie, od tych, które tego nie zrobiły. trajectory_length:4,8 dzieli przypadki na przedziały 1-4, 5-8 oraz 9 lub więcej kroków, według granic, które deklarujesz, ponieważ granica przedziału zmienia to, co mówi wycinek.

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

Pętla jest sygnałem, a nie wyjaśnieniem. Nic w wyniku nie mówi, że powtórzone wywołanie jest przyczyną niepowodzenia przypadku; mogłoby to pokazać tylko odtworzenie, które by je usunęło.

Kilku agentów

examples/triage_agents/ uruchamia zespół trzech: triage przekazuje każde zgłoszenie do billing lub tech, a zwrot, którego billing nie może wydać, jest przekazywany osobie. W systemie złożonym z kilku agentów każdy krok podaje agenta, który go wykonał, a przekazanie kontroli jest krokiem 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"))

Trajektoria podaje agenta każdego kroku albo żadnego. Zapis, który podaje tylko niektórych, jest odrzucany, a zapis, który nie podaje żadnego, jest brakujący dla każdego poniższego kryterium, zamiast je zaliczać. Krok przekazania jest opcjonalny, ponieważ kontrola zmienia się także wtedy, gdy następny krok wykonuje inny agent. W ten sposób ślad zapisuje przekazanie do agenta, który nie wykonuje żadnego kroku, na przykład osoby.

Przypadek deklaruje agentów, przez których powinien przejść, pod 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]
TypZalicza, gdyPrzyjmuje
agent_routeagenci, którzy mieli kontrolę, po scaleniu powtórzeń, to expected.route przypadkunic
agent_tool_permissionskażde wywołanie narzędzia wykonał agent, któremu mapa pozwala wywoływać to narzędziepermissions
agent_max_handoffskontrola zmieniała się co najwyżej max_handoffs razymax_handoffs

Trasa obejmuje odbiorcę przekazania, więc ślad, który kończy się eskalacją do osoby, prowadzi do tej osoby. Przypadek bez expected.route nie jest liczony przez agent_route. Mapa uprawnień jest zamknięta: agent, którego nie wymienia, nie może wywołać żadnego narzędzia.

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

W dwóch zgłoszeniach tech wydał zwrot. Oba zaliczają answer_correct i nie zaliczają agent_tool_permissions: klient otrzymał właściwą odpowiedź od systemu, który złamał własne uprawnienia. Jedno zgłoszenie jest przekazywane tam i z powrotem między billing i tech aż do limitu pętli. Jego ślad jest przycięty, więc nie zalicza trasy ani limitu przekazań, które jego prefiks już rozstrzyga, i jest brakujący dla uprawnień, których nie rozstrzyga.

route grupuje przypadki według trasy, którą przeszli ich agenci, zapisanej jako triage>billing. Przycięty ślad nie trafia do żadnej grupy trasy, ponieważ jego trasa jest prefiksem tego, dokądkolwiek jego agenci poszli dalej.

Nic w wyniku nie mówi, który agent jest winny. Rozbieżność tras mówi, gdzie dwie trasy się rozchodzą. To, że agent spowodował niepowodzenie, jest twierdzeniem o tym, co by się stało, gdyby postąpił inaczej, co mogłoby pokazać tylko odtworzenie podstawiające tego agenta, a Oloproof go nie uruchamia.

Co dalej