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ęść:
| Pole | Co zapisuje |
|---|---|
| AgentStep.kind | message, tool_call, tool_result, observation, decision lub final |
| AgentStep.tool_name, arguments, result | wywołanie i to, co zwróciło |
| terminal_status | success, failure lub unknown |
| truncated, step_limit | że agent osiągnął swój limit, a ślad urywa się przedwcześnie |
| constraints | kontrole wykonane przez Twoje środowisko, każda z krokiem, który zaobserwowała |
| checkpoints | punkty, 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| Typ | Zalicza, gdy | Przyjmuje |
|---|---|---|
| agent_tool_called | narzędzie zostało wywołane co najmniej min_calls razy | tool_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ędu | max_repeats (domyślnie 2) |
| agent_tool_sequence | wywołane narzędzia odpowiadają expected.tools | ordered (domyślnie true) |
| agent_no_undeclared_tool | nie wywołano żadnego narzędzia spoza expected.tools | nic |
| agent_constraints_satisfied | każde nazwane ograniczenie zapisane przez środowisko zostało spełnione | constraints |
| agent_max_steps | ślad wykonał co najwyżej max_steps kroków | max_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:
| Kryterium | Przycięty przypadek | Dlaczego |
|---|---|---|
| agent_steps_le_10 | nie zalicza | zapisane kroki już dowodzą, że limit dziesięciu został przekroczony |
| agent_tool_lookup_order_called | brakujący | wywołanie może znajdować się w części, która nie została zapisana |
| agent_no_tool_loop | brakujący | powtórzenie sięgające końca śladu może trwać dalej |
| steps_p95, tool_calls_p50 | brakujący | kwantyl 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]| Typ | Zalicza, gdy | Przyjmuje |
|---|---|---|
| agent_route | agenci, którzy mieli kontrolę, po scaleniu powtórzeń, to expected.route przypadku | nic |
| agent_tool_permissions | każde wywołanie narzędzia wykonał agent, któremu mapa pozwala wywoływać to narzędzie | permissions |
| agent_max_handoffs | kontrola zmieniała się co najwyżej max_handoffs razy | max_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
- Rejestrowanie tego, co zrobił system omawia pozostałe typowane artefakty.
- Wycinki omawia wsparcie wycinków i dlaczego wycinki nigdy nie bramkują.