Chuyển đến nội dung

Hướng dẫn

Hướng dẫn: một bộ phân loại hoặc đầu ra có cấu trúc

Đánh giá một hàm Python gán nhãn cho các câu hỏi hỗ trợ, đọc lý do bản phát hành bị chặn, sửa các chỗ sai, và so sánh bản sửa với bản gốc, tất cả trên máy của bạn, không cần tài khoản, không cần mạng và không cần mô hình.

Bạn sẽ xây dựng gì

Một bot hỗ trợ trả về một đối tượng JSON có answer và label (refund, account hoặc other). Bạn sẽ đặt nó trước ba yêu cầu: nhãn đúng đủ thường xuyên, đầu ra luôn có đúng hình dạng, và không câu trả lời nào làm lộ thứ gì trông giống một số an sinh xã hội Hoa Kỳ. Hai trong số đó là kiểm tra định dạng không cần câu trả lời tham chiếu; một cái đo mức thành công của tác vụ so với một nhãn tham chiếu. Sự khác biệt này quan trọng, và trang này giữ chúng tách biệt.

Các thuật ngữ dùng bên dưới (trường hợp, lần chạy, chỉ số, khoảng, quy tắc, cổng) được định nghĩa trong Khái niệm.

Điều kiện tiên quyết

  • Python 3.11 trở lên.
  • Oloproof, được cài vào một môi trường ảo:
python3 -m venv .venv
. .venv/bin/activate
pip install oloproof
  • Dự án ví dụ và thay đổi ứng viên, đi kèm với gói. Sao chép cả hai vào các thư mục mới và làm việc trong thư mục thứ nhất; mọi tệp cũng được liệt kê bên dưới, nên bạn có thể tự gõ chúng:
oloproof init --example support_bot support-classifier
oloproof init --example classification support-change
cd support-classifier

Không có khóa API, tài khoản nhà cung cấp hay truy cập mạng nào được dùng ở bất cứ đâu trên trang này.

Các tệp

support-classifier/
  app.py              the application under test (a Python callable)
  oloproof.yaml       the suite: dataset, system, evaluators
  release.yaml        the release policy: rules the run is decided against
  data/support.jsonl  18 cases, one JSON object per line
  rubrics/helpful.md  a judge rubric, unused here

Chạy mọi lệnh từ thư mục support-classifier/. Oloproof giữ kho lưu trữ của nó trong .oloproof/ ở đó; xóa thư mục đó để bắt đầu lại từ đầu.

Ứng dụng và adapter của nó

Ứng dụng của bạn được gọi qua một adapter. Với một ứng dụng Python, adapter chính là hàm đó: Oloproof import nó, gọi nó một lần cho mỗi trường hợp với input của trường hợp, và ghi lại dictionary mà nó trả về làm đầu ra của trường hợp đó.

# app.py
from typing import Any

from oloproof import system


@system(name="support-bot", version="slice-a-example")
def answer(case: dict[str, Any]) -> dict[str, str]:
    question = str(case["question"]).lower()
    if "refund" in question:
        return {"answer": "Refunds are available within 30 days when the order is eligible.",
                "label": "refund"}
    if "password" in question or "login" in question:
        return {"answer": "Use password reset, then contact support if the login still fails.",
                "label": "account"}
    return {"answer": "A support specialist will follow up with the next step.", "label": "other"}

Để đánh giá bộ phân loại của chính bạn, hãy giữ mã của nó ở nguyên chỗ và viết một hàm mỏng như hàm này để gọi nó và trả về một dictionary. Hàm có thể là async. Oloproof gọi nó; nó không lưu trữ, cô lập hay đặt lại ứng dụng của bạn, nên mọi trạng thái mà ứng dụng giữ giữa các lần gọi là do bạn quản lý.

oloproof.yaml nêu tên hàm đó và các bộ đánh giá:

version: 1
project: support-bot-example
dataset: data/support.jsonl
system:
  name: support-bot
  version: slice-a-example
  callable: app:answer
  timeout_s: 30
evaluators:
  - type: exact_match
    criterion: exact_label
    field: label
  - type: json_schema
    criterion: format_valid
    field: null
    schema:
      type: object
      required: [answer, label]
      properties:
        answer: {type: string}
        label: {type: string}
      additionalProperties: false
  - type: regex
    criterion: pii_free
    field: answer
    pattern: '\b\d{3}-\d{2}-\d{4}\b'
    pass_if: no_match

Đầu ra được lưu đệm theo mã nguồn của hàm, version đã khai báo và config. Nếu hàm đọc các tệp khác (một prompt, một bảng quy tắc), hãy liệt kê chúng dưới system.code_paths, để việc sửa chúng sẽ chạy lại hệ thống.

Tập dữ liệu

Mỗi dòng một trường hợp. input chính xác là thứ hàm của bạn nhận làm case; expected là tham chiếu mà bộ đánh giá exact_match so sánh với:

{"id":"refund_00","input":{"question":"Can I get a refund for yesterday's order?"},"expected":{"label":"refund"}}
{"id":"account_04","input":{"question":"I can't sign in on my new phone."},"expected":{"label":"account"}}
{"id":"other_04","input":{"question":"I don't want a refund, I just need a copy of my receipt."},"expected":{"label":"other"}}

Với mỗi trường hợp, hàm trả về một đối tượng như {"answer": "Use password reset, ...", "label": "account"}.

Chọn các bộ đánh giá

Tiêu chíBộ đánh giáCần expectedNó đo gì
exact_labelexact_match trên labelcóthành công của tác vụ: nhãn là nhãn đúng
format_validjson_schema trên toàn bộ đầu rakhôngđịnh dạng: đối tượng có đúng hai trường chuỗi
pii_freeregex trên answer, pass_if: no_matchkhôngmột thuộc tính an toàn của văn bản

Một kiểm tra định dạng cho qua một câu trả lời sai nhưng đúng hình thức, nên nó không bao giờ có thể thay thế cho thành công của tác vụ. Một kiểm tra tác vụ cần một tham chiếu cho mọi trường hợp; khi một trường hợp không có, exact_match không thể chấm điểm nó. Các bộ đánh giá tất định không cần xác thực so với con người: chạy một bộ hai lần cho cùng một phán quyết.

Chính sách

release.yaml là thứ mà lần chạy được quyết định theo:

version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
warn_on: []
rules:
  - id: exact-label-floor
    metric: exact_label
    min: 0.70
  - id: valid-format
    metric: format_valid
    kind: observed_count
    max_failures: 0
  - id: pii-free
    metric: pii_free
    kind: observed_count
    max_failures: 0

exact-label-floor nói rằng nhãn phải đúng ít nhất 70% số lần, và chỉ đạt khi toàn bộ khoảng 95% nằm ở hoặc trên 0.70. Hai quy tắc observed_count không cho phép một thất bại nào trên các trường hợp bạn đã chạy; chúng mô tả các trường hợp này, không phải mọi câu hỏi mà người dùng sẽ hỏi.

Chạy

oloproof run

Đầu ra thật, đã rút gọn:

Run run_01M4... [DECIDED/COMPLETE]
Gate: BLOCK (exit 3)
│ exact-label-floor │ exact_label  │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold    │
│ valid-format      │ format_valid │ PASS                  │ observed_failures_within_limit │
│ pii-free          │ pii_free     │ PASS                  │ observed_failures_within_limit │
│ exact_label  │ 72.2%    │ [46.5%, 90.4%]  │ 13 / 18 observed · 0 missing · 0 excluded │
│ format_valid │ 100.0%   │ [81.4%, 100.0%] │ 18 / 18 observed · 0 missing · 0 excluded │
│ pii_free     │ 100.0%   │ [81.4%, 100.0%] │ 18 / 18 observed · 0 missing · 0 excluded │
Cache: execution 0 hit/18 miss; judgment 0 hit/54 miss

Cách đọc:

  • 13 trên 18 nhãn đúng, 72,2%. Con số đó cao hơn 0.70, nhưng khoảng kéo xuống tới 46,5%: 18 trường hợp không thể cho thấy tỷ lệ thật ít nhất là 0.70. Vì vậy quy tắc là INSUFFICIENT_EVIDENCE, không phải PASS và cũng không phải FAIL.
  • Mọi đầu ra đều có đúng hình dạng và không cái nào chứa một số giống SSN, nên cả hai quy tắc định dạng đều đạt.
  • block_on liệt kê INSUFFICIENT_EVIDENCE, nên cổng chặn và lệnh thoát với 3. Thoát với 0 nghĩa là không có gì mà chính sách chặn; Cổng trong CI liệt kê mọi mã.

Chạy lại và dòng bộ nhớ đệm ghi execution 18 hit/0 miss: không có gì thay đổi, nên hàm không bị gọi.

Xem xét các thất bại

oloproof inspect RUN_ID --failures
oloproof inspect RUN_ID --case refund_04

RUN_ID là mã ở dòng đầu tiên trong đầu ra của lần chạy.

5 of 18 cases failed, errored or did not finish

refund_04
  output: {"answer": "A support specialist will follow up with the next step.", "label": "other"}
  exact_label: failed
...
other_04
  output: {"answer": "Refunds are available within 30 days when the order is eligible.", "label": "refund"}
  exact_label: failed
case refund_04
input: {
  "question": "I was charged twice this month and want my money back."
}
expected: {
  "label": "refund"
}
execution: OK, 1 ms
output: {
  "answer": "A support specialist will follow up with the next step.",
  "label": "other"
}
judgments:
  exact_label: failed
  format_valid: passed
  pii_free: passed

Quy luật trở nên rõ ràng khi bạn đọc các đầu vào: "money back", "reverse the payment", "sign in" và "two-factor" không có trong danh sách từ khóa, và other_04 nói "I don't want a refund", mà từ "refund" vẫn khớp. Lưu ý rằng refund_04 đạt cả hai kiểm tra định dạng trong khi lại sai: đó là khoảng cách giữa việc kiểm tra định dạng và việc đo lường thành công.

Có hai hành động tiếp theo có ý nghĩa ở đây. Sửa các chỗ sai (bên dưới), hoặc thêm trường hợp: với nhiều trường hợp hơn ở cùng độ chính xác, khoảng thu hẹp lại, và oloproof plan RUN_ID --run ước lượng cần bao nhiêu.

Thực hiện một thay đổi thật

Sao chép ../support-change/app.py đè lên app.py. Nó thêm các cách diễn đạt bị bỏ sót:

REFUND_WORDS = ("refund", "money back", "reverse the payment")
ACCOUNT_WORDS = ("password", "login", "sign in", "two-factor")


@system(name="support-bot", version="keywords-v2")
def answer(case: dict[str, Any]) -> dict[str, str]:
    question = str(case["question"]).lower()
    if any(word in question for word in REFUND_WORDS):
        ...

và đặt version: keywords-v2 dưới system trong oloproof.yaml, để lần chạy được ghi lại là phiên bản mới. Rồi:

oloproof run
Gate: ALLOW (exit 0)
│ exact-label-floor │ exact_label  │ PASS  │ lower_bound_meets_minimum      │
│ exact_label  │ 94.4%    │ [72.7%, 99.9%]  │ 17 / 18 observed · 0 missing · 0 excluded │

17 trên 18 đúng và cận dưới của khoảng, 72,7%, vượt qua 0.70, nên quy tắc đạt và lệnh thoát với 0. other_04 vẫn thất bại: bản sửa không đụng tới phủ định.

So sánh ứng viên với đường cơ sở

Quy tắc của lần chạy hỏi xem ứng viên có đạt mức sàn của bạn hay không. Một phép so sánh hỏi nó khác đường cơ sở thế nào, từng trường hợp một. Sao chép ../support-change/compare.yaml vào dự án; nó chứa một quy tắc so sánh:

version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
rules:
  - id: label-no-regression
    kind: non_inferiority
    metric: exact_label
    margin: 0.10
oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy compare.yaml
Comparison sha256:de76... of run_01M4...TJAD against run_01M4...ECVEF · 18 paired cases
exact_label: +22.2 points [-12.9, +57.0] · 18 paired · 0 missing · 0 excluded
format_valid: +0.0 points [-25.8, +25.8] · 18 paired · 0 missing · 0 excluded
pii_free: +0.0 points [-25.8, +25.8] · 18 paired · 0 missing · 0 excluded
Decisions
  label-no-regression  exact_label  non-inferiority, margin 10.0 points  INSUFFICIENT_EVIDENCE  interval_overlaps_margin
    about 3 more paired cases would decide it, if the difference holds (21 in total at 22% discordance)
Gate: BLOCK (exit 3)

Ứng viên đã sửa bốn trường hợp và không làm hỏng trường hợp nào, mức tăng ước lượng là 22 điểm. Nhưng chỉ bốn trường hợp thay đổi, và 18 trường hợp ghép cặp để lại một khoảng từ tệ hơn 12,9 điểm tới tốt hơn 57 điểm, vắt ngang biên 10 điểm. Phép so sánh chưa thể loại trừ khả năng ứng viên tệ hơn quá mức bạn chấp nhận, nên kết quả là INSUFFICIENT_EVIDENCE và thoát với 3. Dòng bên dưới là ước lượng định cỡ. Không có --policy, compare in ra các khác biệt, nói rằng release.yaml của dự án không khai báo quy tắc so sánh nào, và thoát với 0 vì không có gì được quyết định.

So sánh một ứng viên với một đường cơ sở giải thích biên và các loại quy tắc khác.

Khắc phục sự cố

Triệu chứngNguyên nhân và cách sửa
ModuleNotFoundError cho appChạy từ thư mục chứa app.py, hoặc đưa cho callable một đường dẫn module có thể import từ đó.
Một quy tắc nêu một chỉ số mà không bộ đánh giá nào tạo rametric của quy tắc phải bằng criterion của một bộ đánh giá; lỗi liệt kê các chỉ số hiện có.
Bạn đã sửa bộ phân loại nhưng lần chạy tái sử dụng mọi đầu raBộ nhớ đệm theo mã nguồn của callable; một tệp phụ trợ mà nó đọc phải được liệt kê dưới system.code_paths.
exact_label báo các trường hợp bị thiếuCác lần thực thi đó đã ném lỗi hoặc hết thời gian; oloproof inspect RUN_ID --failures cho thấy từng lỗi.
Lần chạy thoát với 3 dù ước lượng caoKhoảng, không phải ước lượng, mới quyết định. Thêm trường hợp hoặc chấp nhận một mức sàn thấp hơn, được quyết định trước lần chạy.

Giới hạn

  • SDK và YAML báo tỷ lệ đạt theo từng bộ đánh giá. Không có ma trận nhầm lẫn hay precision và recall theo từng lớp cho một bộ phân loại như thế này; một khối predictive: làm những việc đó cho một mô hình có điểm số (Mô hình dự đoán).
  • Các quy tắc observed_count mô tả các trường hợp bạn đã chạy; chúng không khẳng định gì về các đầu vào chưa thấy.
  • Một phép so sánh trên 18 trường hợp chỉ phân giải được các khác biệt lớn. Năm mươi trường hợp thật trở lên là một mức sàn hữu ích hơn.
  • oloproof.yaml không thể nêu một @evaluator tùy chỉnh; việc đó cần SDK (SDK).