Langsung ke konten

Panduan

Tutorial: percakapan multi-giliran

Panduan yang dapat dijalankan untuk mengevaluasi asisten percakapan dengan SDK Python: aplikasi Anda memutar ulang percakapan berskrip terhadap sesi baru, merekam apa yang dijawabnya sebagai artefak conversation/v1, dan dua evaluator menilai setiap percakapan secara utuh. Panduan ini berjalan offline dengan pengganti berskrip untuk model juri, lalu membandingkan perbaikan kandidat, dan diakhiri dengan alternatif mengevaluasi giliran sebagai kasus berklaster.

Istilah seperti interval, status keputusan, dan tindakan rilis didefinisikan dalam Konsep; Merekam apa yang dilakukan sistem membahas artefak.

Apa yang dilakukan dan tidak dilakukan Oloproof di sini

Oloproof melakukanAplikasi Anda melakukan
menyimpan percakapan berskrip sebagai dataset, identik untuk setiap sistemmenjalankan percakapan: mengajukan setiap giliran berskrip secara berurutan
memeriksa bahwa setiap giliran berskrip dijawab (ConversationCompleted)memiliki state sesi, memulai sesi baru per kasus, dan mengatur ulangnya
menilai seluruh transkrip dengan model (ConversationJudge)memutuskan apa yang terjadi saat tidak dapat melanjutkan, dan merekam bahwa ia berhenti
menghitung interval, membandingkan dua sistem, dan memutuskan terhadap kebijakanmerekam artefak conversation/v1

Oloproof tidak memiliki simulator pengguna: Oloproof tidak pernah menulis giliran pengguna, sehingga sisi pengguna adalah apa pun yang diskripkan dataset. Oloproof tidak memiliki metrik tingkat giliran di dalam percakapan yang direkam, dan tidak dapat memutar ulang percakapan yang direkam terhadap sistem baru. Evaluator percakapan hanya ada di SDK Python: ConversationCompleted dan ConversationJudge bukan tipe evaluator di oloproof.yaml, sehingga tutorial ini memakai skrip alih-alih oloproof run.

Prasyarat

  • Python 3.11 atau yang lebih baru dan pip install oloproof, seperti di mulai cepat.
  • File-file contoh, yang disertakan dalam paket. Salin ke direktori baru agar store eksekusi berada di sana:
oloproof init --example conversation ~/oloproof-conversation
cd ~/oloproof-conversation
FileApa itu
assistant.pyaplikasi yang diuji: asisten paket dengan state sesi
systems.pyadapternya: memutar ulang skrip, merekam conversation/v1
judge_offline.pypengganti berskrip untuk model juri
evaluate.pymenjalankan evaluasi, perbandingan, dan alternatif giliran
release.yamlkebijakan untuk satu eksekusi
comparison.yamlkebijakan untuk kandidat terhadap baseline
turns_release.yamlkebijakan untuk alternatif giliran
data/conversations.jsonl40 percakapan berskrip
data/turns.jsonlpercakapan yang sama, satu kasus per giliran

Tanpa kunci, tanpa jaringan, dan tanpa biaya penyedia, sampai langkah langsung opsional di akhir.

Aplikasinya

assistant.py menjawab pertanyaan tentang tiga paket harga. Aplikasi ini menyimpan satu bagian state, yaitu paket yang sedang dibicarakan, sehingga pertanyaan lanjutan seperti "Does that include SSO?" dapat menafsirkan "that". Baseline memiliki cacat yang disengaja: tidak mengingat paket, sehingga pertanyaan lanjutan dijawab tentang paket bawaan. Pengguna yang meminta berbicara dengan manusia mengakhiri percakapan dengan HandoffRequested.

class PlanAssistant:
    def __init__(self, *, remembers_plan):
        self.remembers_plan = remembers_plan
        self.reset()

    def reset(self):
        """Forget everything, so one conversation never leaks into the next."""
        self.current_plan = None

    def ask(self, question): ...

Inilah bagian yang Anda ganti dengan aplikasi Anda sendiri: klien chatbot, sesi agen, sesi HTTP ke layanan Anda. Apa pun itu, ia memiliki state dan pengaturan ulangnya sendiri; Oloproof hanya melihat apa yang direkam adapter.

Dataset: skrip adalah inputnya

Satu baris data/conversations.jsonl adalah satu percakapan:

{"expected": {"plan": "enterprise"}, "id": "conv_00", "input": {"turns": ["What does the enterprise plan cost?", "Does that include SSO?"]}, "metadata": {"pattern": "pronoun_followup"}}

Giliran pengguna adalah konten dataset, tercakup oleh digest suite dan identik untuk setiap sistem yang diukur terhadapnya; itulah yang membuat dua sistem dapat dibandingkan. expected adalah acuan yang ditunjukkan kepada juri. Ke-40 percakapan terdiri dari 24 dengan pertanyaan lanjutan yang tidak menyebut paket, 12 yang menyebut paket di setiap giliran, dan 4 yang meminta manusia pada giliran kedua dari tiga.

Adapternya

systems.py memulai sesi baru per kasus, mengajukan setiap giliran berskrip secara berurutan, dan merekam apa yang kembali:

from oloproof import CONVERSATION, current_case, system


def replay(case, *, remembers_plan):
    script = [str(turn) for turn in case["turns"]]
    session = PlanAssistant(remembers_plan=remembers_plan)  # a new session per case
    turns = []
    truncated = False
    for index, question in enumerate(script, start=1):
        try:
            reply = session.ask(question)
        except HandoffRequested:
            truncated = True  # the recording stops here and says so
            break
        turns.append({"index": index, "asked": question, "answer": reply["answer"]})
    current_case().artifact(
        CONVERSATION,
        {"turns": turns, "declared_turns": len(script), "truncated": truncated},
    )
    last = turns[-1]["answer"] if turns else None
    return {"answer": last, "turns_answered": len(turns)}


@system(name="plan-assistant", version="baseline", records=(CONVERSATION,))
def baseline(case):
    return replay(case, remembers_plan=False)


@system(name="plan-assistant", version="candidate-remembers-plan", records=(CONVERSATION,))
def candidate(case):
    return replay(case, remembers_plan=True)

Sesi baru per kasus itu penting: Oloproof menjalankan kasus secara bersamaan dan tanpa urutan tetap, dan sesi yang dibagi antar-kasus akan membiarkan state satu percakapan bocor ke percakapan lain. records= mendeklarasikan bahwa sistem merekam artefak itu; tanpanya evaluator percakapan ditolak sebelum apa pun berjalan, alih-alih menghitung setiap kasus sebagai hilang.

Artefak conversation/v1

Apa yang direkam baseline untuk conv_00, dari oloproof export RUN_ID (cases.jsonl dalam bundel):

{"declared_turns": 2, "truncated": false, "turns": [{"answer": "The enterprise plan costs a price agreed per contract.", "asked": "What does the enterprise plan cost?", "index": 1, ...}, {"answer": "The starter plan does not include SSO.", "asked": "Does that include SSO?", "index": 2, ...}]}

Dan untuk percakapan yang meminta manusia:

{"declared_turns": 3, "truncated": true, "turns": [{"answer": "The team plan costs $20 a month.", "asked": "What does the team plan cost?", "index": 1, ...}]}
FieldArti
turns[].indexgiliran berskrip mana yang dijawab ini; berurutan mulai dari 1
turns[].answerapa yang dikembalikan asisten, JSON apa pun
turns[].askedopsional, hanya untuk dibaca; mesin mencocokkan berdasarkan index
turns[].retrievalopsional, apa yang di-retrieve giliran itu, dalam bentuk retrieval/v1
declared_turnsberapa giliran yang dideklarasikan skrip
truncatedrekaman berhenti sebelum skrip selesai, apa pun yang memotongnya

Artefak diperiksa saat direkam: rekaman dengan giliran lebih sedikit dari yang dideklarasikan harus menyatakan truncated: true, indeks harus berurutan, dan rekaman tidak dapat menjawab lebih banyak giliran daripada yang ditanyakan. Artefak yang cacat menghentikan eksekusi dengan SystemContractError.

Kedua evaluator, dan mengapa keduanya

  • ConversationCompleted bersifat deterministik: apakah asisten menjawab setiap giliran berskrip? Evaluator ini berjalan lebih dulu karena klaim lain apa pun tentang percakapan yang berhenti pada giliran pertama dari tiga adalah klaim tentang percakapan yang berbeda. Percakapan yang terpotong gagal di sini; itu sebuah hasil, bukan kasus yang hilang.
  • ConversationJudge adalah juri model atas seluruh transkrip, setiap giliran pengguna dan asisten, karena kegagalan yang dipersalahkan pada produk percakapan bersifat relasional: jawaban yang bertentangan dengan jawaban dari giliran sebelumnya hanya salah jika dilihat di sampingnya. Percakapan yang terpotong dinilai berdasarkan apa yang direkam, dan transkrip memberi tahu juri di mana ia berhenti.
evaluators = [
    ConversationCompleted(),
    ConversationJudge(criterion="plan_coherent", provider=..., model=..., rubric_text=RUBRIC),
]

Menilai secara offline

Juri memerlukan model. Untuk berjalan tanpa jaringan, judge_offline.py memberikan penyedia berskrip, helper yang sama yang dipakai pengujian Oloproof sendiri (FakeProvider dari internal mesin, bukan API publik). Penyedia ini menjawab setiap prompt juri dengan satu aturan tetap: lolos jika setiap giliran asisten menyebut paket yang disebut acuan. Itu membuat penilaian deterministik dan tutorial dapat direproduksi. Ia tidak mengukur apa pun tentang perilaku model juri sungguhan.

Kebijakannya

release.yaml:

version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
rules:
  - {id: completion-floor, metric: conversation_completed, min: 0.75}
  - {id: coherence-floor, metric: plan_coherent, min: 0.80}

Jalankan

python evaluate.py
baseline run run_...
  conversation_completed: 0.900 [0.763, 0.972] over 40 conversations
  plan_coherent: 0.400 [0.249, 0.567] over 40 conversations
  gate BLOCK (exit 3)
  completion-floor: PASS (lower_bound_meets_minimum)
  coherence-floor: INSUFFICIENT_EVIDENCE (evaluator_not_validated)
  first failing conversation:
    conversation_completed: passed=True {'turns_recorded': 2, 'turns_declared': 2, 'truncated': False}
    plan_coherent: passed=False {'provider_model': 'offline-rule'}

Cara membacanya:

  • Penyelesaian adalah 36 dari 40, yaitu empat penyerahan ke manusia. Batas bawahnya melampaui 0.75, sehingga aturan itu PASS.
  • Koherensi adalah 40%, dan aturannya berstatus INSUFFICIENT_EVIDENCE dengan evaluator_not_validated, bukan FAIL. Aturan seorang juri tidak memutuskan sampai juri itu diukur terhadap label manusia (require_validated_evaluators aktif secara bawaan; Juri menjelaskannya). Estimasinya tetap ditampilkan, dan tetap merupakan bukti: hanya saja tidak dapat merilis atau memblokir sendiri.
  • gate BLOCK (exit 3): kebijakan memblokir pada INSUFFICIENT_EVIDENCE. Keluar 3 adalah status itu, bukan kegagalan.

Memvalidasi pengganti offline tidak akan bermakna, karena itu aturan yang ditulis untuk contoh ini. Dengan juri sungguhan, beri label pada sampel eksekusi dengan oloproof review RUN_ID --criterion plan_coherent --by YOU --sample 20 lalu jalankan oloproof evaluators validate EVALUATOR_ID --by YOU.

Periksa percakapan yang gagal

SDK menulis ke store yang sama yang dibaca CLI, .oloproof/ di direktori tempat Anda menjalankannya:

oloproof inspect RUN_ID --case conv_00
output: {
  "answer": "The starter plan does not include SSO.",
  "turns_answered": 2
}
judgments:
  conversation_completed: passed
  plan_coherent: failed
    judge text, not verified:
      every answer is about enterprise: False

Pengguna bertanya tentang paket enterprise dan pertanyaan lanjutannya dijawab tentang paket starter. oloproof inspect RUN_ID --failures mencantumkan setiap percakapan yang gagal; ke-24 pertanyaan lanjutan tanpa nama paket gagal dengan cara yang sama. Tindakan berikutnya ada di aplikasi: simpan paket di state sesi.

Perubahan kandidat, dan perbandingannya

candidate di systems.py menetapkan remembers_plan=True. evaluate.py menjalankan kedua sistem atas skrip yang sama dan membandingkannya kasus demi kasus di bawah comparison.yaml:

rules:
  - {id: coherence-better, kind: superiority, metric: plan_coherent}
  - {id: completion-no-worse, kind: non_inferiority, metric: conversation_completed, margin: 0.05}
comparison, candidate minus baseline
  conversation_completed: +0.000 [-0.127, +0.127] over 40 pairs
  plan_coherent: +0.600 [+0.337, +0.817] over 40 pairs
  gate BLOCK (exit 3)
  coherence-better: INSUFFICIENT_EVIDENCE (evaluator_not_validated)
  completion-no-worse: INSUFFICIENT_EVIDENCE (interval_overlaps_margin)

Selisih koherensi besar dan intervalnya tidak memuat nol, tetapi juri belum divalidasi, sehingga aturannya masih tidak memutuskan. Penyelesaian tidak berubah, dan 40 pasangan tidak dapat menunjukkan bahwa selisihnya dalam lima poin: interval mencapai 12.7 poin ke kedua arah. Keduanya menunjuk ke langkah berikutnya yang sama: validasi juri dan tambahkan percakapan.

Alternatifnya: giliran sebagai kasus, dianalisis sebagai klaster

Putusan tingkat percakapan menyatakan seberapa sering percakapan berjalan baik, bukan giliran mana yang salah. Karena Oloproof tidak memiliki metrik tingkat giliran di dalam percakapan yang direkam, jalan lainnya adalah menjadikan setiap giliran kasus tersendiri dan mengikat giliran-giliran satu percakapan dengan group_id:

{"expected": {"plan": "enterprise"}, "group_id": "conv_00", "id": "conv_00_t2", "input": {"history": ["What does the enterprise plan cost?"], "question": "Does that include SSO?"}}

Sistem memutar ulang riwayat berskrip ke dalam sesi baru, lalu menjawab giliran itu:

@system(name="plan-assistant-turns", version="baseline")
def turn_baseline(case):
    session = PlanAssistant(remembers_plan=False)
    for earlier in case["history"]:
        session.ask(str(earlier))
    return session.ask(str(case["question"]))

Giliran-giliran dari satu percakapan tidak independen, sehingga begitu ada kasus yang memiliki group_id, suite dianalisis per klaster dengan metode aproksimasi yang harus diterima kebijakan (turns_release.yaml menetapkan allow_approximate_methods: true; Kasus berkelompok menjelaskannya):

turns as cases: turn_plan 0.667 [0.588, 0.749] over 72 turns
  gate BLOCK (exit 1)
  turn-plan-floor: FAIL (upper_bound_below_minimum)

Pertukarannya:

Satu kasus per percakapanSatu kasus per giliran, berklaster
Unit tingkatnyapercakapan yang berjalan baikgiliran yang dijawab dengan benar
Ukuran sampel efektifjumlah percakapantetap jumlah percakapan, bukan giliran
Giliran mana yang gagalbaca transkripnyasetiap giliran memiliki putusan sendiri
Riwayat yang dilihat setiap giliranjawaban asisten sendiri sebelumnyagiliran pengguna sebelumnya dari skrip, diputar ulang
Menangkap penyimpangan yang disebabkan jawabannya sendiri sebelumnyayatidak, setiap giliran dimulai dari riwayat berskrip
EvaluatorConversationCompleted, ConversationJudge (hanya SDK)evaluator apa pun, di YAML atau SDK

Alternatif giliran mengecualikan empat percakapan penyerahan ke manusia, sehingga ke-72 gilirannya berasal dari 36 percakapan. Di sini alternatif ini dapat memutuskan di mana juri tidak bisa, karena ExactMatch bersifat deterministik dan tidak memerlukan validasi.

Opsional: model juri langsung

Langkah ini memerlukan model yang dilayani di mesin Anda. Langkah ini tidak dijalankan oleh tutorial offline maupun pengujiannya. Dengan Ollama berjalan dan llama3.1 sudah diunduh:

python evaluate.py --live

Juri kemudian memanggil http://localhost:11434/v1 dengan provider="openai_compatible". Server loopback tidak memerlukan kunci dan tidak mengirim apa pun keluar dari mesin. Penyedia cloud memerlukan kuncinya di lingkungan, mengirim setiap transkrip ke penyedia itu, dan memakan biaya per penilaian. Putusan model sungguhan berbeda dari putusan pengganti, sehingga angka di atas akan berubah, dan aturannya tetap berstatus evaluator_not_validated sampai Anda memvalidasinya.

Pemecahan masalah

GejalaPenyebab dan perbaikan
this evaluator needs exactly one conversation/v1 artifact; the case recorded 0Adapter tidak memanggil current_case().artifact(CONVERSATION, ...), atau memunculkan exception sebelumnya. Tetap rekam meski percakapan berhenti lebih awal.
malformed conversation/v1 artifact: ... 0 of 2 turns recorded and truncated is falseEksekusi berhenti dengan SystemContractError. Rekaman dengan giliran lebih sedikit dari declared_turns harus menetapkan truncated: true.
conversation turn indexes must be contiguous starting at 1Beri nomor giliran 1, 2, 3 sesuai giliran berskrip yang dijawabnya.
evaluator 'conversation_completed' needs conversation/v1 artifacts, but system ... does not declare that it records themTambahkan records=(CONVERSATION,) ke dekorator @system.
Input tag 'conversation_completed' found using 'type' does not match any of the expected tags dari oloproof runEvaluator percakapan hanya ada di SDK. Gunakan skrip seperti di sini.
Jawaban bocor antar-percakapanSebuah sesi dibagi antar-kasus. Buat satu per kasus.
Aturan koherensi tidak pernah memutuskanJuri belum divalidasi. Validasi, atau tetapkan require_validated_evaluators: false dengan sadar.

Keterbatasan

  • Tidak ada simulator pengguna: setiap giliran pengguna berasal dari skrip dataset, sehingga percakapan tidak dapat bercabang berdasarkan apa yang dikatakan asisten.
  • Tidak ada metrik tingkat giliran di dalam percakapan yang direkam; gunakan giliran sebagai kasus berklaster, dengan pertukaran di atas.
  • Tidak ada pemutaran ulang percakapan: percakapan yang direkam tidak dapat dijalankan ulang terhadap sistem lain. Membandingkan dua sistem berarti masing-masing memutar ulang skrip yang sama.
  • ConversationCompleted dan ConversationJudge hanya ada di SDK Python.
  • Hanya teks: juri ditunjukkan teks JSON, tidak pernah gambar atau audio.
  • Juri offline adalah aturan berskrip. Putusannya menunjukkan mekanismenya, bukan akurasi juri sungguhan.

Langkah selanjutnya

  • Juri membahas penyedia, validasi, dan rekalibrasi.
  • Kasus berkelompok membahas group_id dan persetujuan metode aproksimasi.
  • API Python membahas evaluate dan evaluate_comparison.
  • Agen membahas batas yang sama untuk loop alat sebuah agen.