Chuyển đến nội dung

Hướng dẫn

Hướng dẫn: một bàn hỗ trợ với các khách hàng mô phỏng

Đánh giá một trợ lý hội thoại với những người dùng phản ứng lại những gì nó nói. Ba mươi khách hàng mô phỏng, mỗi người có một mục tiêu và những thông tin họ biết, trò chuyện với bàn hỗ trợ của một cửa hàng; Oloproof đo xem mỗi người có đạt được điều họ tìm đến hay không, giữ lại mọi quyết định cho tới khi một người thật nhận thấy các khách hàng mô phỏng là chân thực, rồi quyết định một bản phát hành và một phép so sánh. Mọi bước đều chạy trên máy này mà không cần mô hình và không cần mạng, và một bước tùy chọn thay vào một mô hình thật để đóng vai các khách hàng.

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

Bàn hỗ trợ của một cửa hàng trả lời các yêu cầu hoàn tiền, các câu hỏi về giao hàng và các thay đổi địa chỉ. Ba câu hỏi quyết định một phiên bản có được phát hành hay không:

  • Mỗi khách hàng có đạt được điều họ tìm đến không? goal_reached, từ một kiểm tra sau mỗi câu trả lời.
  • Có câu trả lời nào hứa hẹn điều mà cửa hàng không thể hứa không? every_turn đọc mọi câu trả lời để tìm từ "guarantee".
  • Có thể tin rằng các câu trả lời nói lên được điều gì đó về khách hàng thật không? Chỉ khi một người thật đã đọc một mẫu các cuộc hội thoại và nhận thấy các khách hàng mô phỏng là chân thực.

Các thuật ngữ như trường hợp, lần chạy, cổng và bộ kiểm thử được định nghĩa trong Khái niệm; mọi điều về người dùng mô phỏng nằm trong Người dùng mô phỏng.

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

  • Python 3.11 trở lên, và Oloproof:
python3 -m venv .venv
. .venv/bin/activate
pip install oloproof
  • Dự án ví dụ, đi kèm với gói. Sao chép nó vào một thư mục mới và làm việc ở đó:
oloproof init --example simulated-users support-desk
cd support-desk
  • Cổng 8797 còn trống cho người dùng mô phỏng thay thế (nếu không, hãy đổi nó ở cả hai nơi).

Mọi bước cho tới "Tùy chọn: một mô hình thật đóng vai các khách hàng" đều ngoại tuyến và tất định: không khóa API, không tài khoản nhà cung cấp, không chi phí.

Các tệp

support-desk/
  desk.py                      the support desk under test (baseline)
  desk_v2.py                   the candidate change
  user_server.py               a stand-in simulated user speaking the OpenAI API on 127.0.0.1
  oloproof.yaml                the suite: the simulated user and four evaluators
  release.yaml                 rules for a run, with the realism bar
  compare.yaml                 a rule for a comparison
  data/customers.jsonl         30 personas
  labels/realism_verdicts.csv  one person's realism verdicts on the baseline's conversations
  fill_realism.py              copies those verdicts into a labelling sheet
  logged.py                    replays logged chats with human agents
  real.yaml                    a suite of 20 of those chats
  logged_release.yaml          a rule for the logged chats
  data/logged_chats.jsonl      the 20 chats

Chạy mọi lệnh từ support-desk/.

Bàn hỗ trợ giữ vòng lặp của nó

# desk.py (excerpt)
def talk(case: dict[str, Any]) -> dict[str, Any]:
    user = current_case().simulated_user()
    desk = Desk()
    message = user.opening()
    while message is not None:
        message = user.reply(desk.answer(message))
    return {"need": desk.need, "order": desk.order}

Bàn hỗ trợ xin tin nhắn đầu tiên của khách hàng, trả lời nó, và trao lại mỗi câu trả lời để nhận tin nhắn tiếp theo, cho tới khi khách hàng dừng lại. Nó không bao giờ thấy nhân vật. Oloproof ghi lại những gì đã trao đổi giữa hai bên; bàn hỗ trợ tự nó không ghi lại gì. Đường cơ sở này không thể thay đổi địa chỉ giao hàng, và nó hứa hoàn tiền "guaranteed within 5 days".

Các nhân vật

Mỗi trường hợp là một khách hàng: 12 người muốn hoàn tiền, 10 người muốn biết khi nào một đơn hàng đến, và 8 người đã chuyển nhà. Ví dụ:

{"id": "c25", "input": {"persona": {"description": "A retired teacher who ordered a cookbook last week. Writes short, polite messages.", "goal": "Have the order of a cookbook delivered to my new address.", "facts": {"order": "B-35", "postcode": "10275"}}}}

Khách hàng chỉ cung cấp mã đơn hàng và mã bưu chính khi được hỏi. Ba nhân vật cũng đặt một opening, tin nhắn đầu tiên của họ, nguyên văn từng chữ.

Bộ kiểm thử

version: 1
project: support-desk
dataset: data/customers.jsonl
system:
  name: support-desk
  version: baseline
  callable: desk:talk
  # A model plays each case's persona; here the stand-in in user_server.py.
  simulated_user:
    provider: openai_compatible
    model: stand-in-user
    base_url: http://127.0.0.1:8797/v1
    max_turns: 6
evaluators:
  # The desk answered every message the customer sent.
  - type: conversation_completed
  # The customer said they got what they came for.
  - type: goal_reached
  # ... within three of their own messages.
  - type: turns_to_goal
    max_turns: 3
  # No answer promises what the shop cannot promise.
  - type: every_turn
    criterion: no_promises
    not_contains: [guarantee]
    casefold: true

Người dùng mô phỏng thay thế

Một người dùng mô phỏng là một mô hình đóng vai mỗi nhân vật. Ở đây đó là user_server.py, nói cùng API đó trên 127.0.0.1 và không phải là một mô hình: nó đọc nhân vật từ prompt mà Oloproof gửi, nêu mục tiêu của nó, cung cấp mã đơn hàng hoặc mã bưu chính khi được hỏi, rời đi khi bàn hỗ trợ không thể giúp, và, khi được hỏi liệu mục tiêu của nó đã đạt được chưa, trả lời có khi câu trả lời cuối cùng của bàn hỗ trợ bắt đầu bằng "Done:". Khởi động nó trong một terminal thứ hai và để nó chạy:

python user_server.py --port 8797

Chính sách

version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
require_validated_evaluators: true
minimum_simulated_user_realism: 0.80
rules:
  - id: no-promises
    metric: no_promises
    kind: observed_count
    max_failures: 0
  - id: resolved
    metric: goal_reached
    min: 0.80

Không câu trả lời nào được hứa hẹn. Ít nhất 80% khách hàng phải đạt được mục tiêu của họ, chỉ được quyết định khi cận dưới của khoảng vượt qua 80%. Và không quy tắc nào quyết định trên các khách hàng mô phỏng cho tới khi con người nhận thấy ít nhất 80% các cuộc hội thoại của họ là chân thực, theo cùng một loại cận dưới.

Chạy

oloproof run
Gate: BLOCK (exit 3)
│ no-promises │ no_promises  │ INSUFFICIENT_EVIDENCE │ simulated_user_not_validated │
│ resolved    │ goal_reached │ INSUFFICIENT_EVIDENCE │ simulated_user_not_validated │
│ conversation_completed │ 100.0%   │ [88.4%, 100.0%] │ 30 / 30 observed · 0 missing · 0 excluded │
│ goal_reached           │ 73.3%    │ [54.1%, 87.8%]  │ 22 / 30 observed · 0 missing · 0 excluded │
│ goal_within_3_turns    │ 73.3%    │ [54.1%, 87.8%]  │ 22 / 30 observed · 0 missing · 0 excluded │
│ no_promises            │ 60.0%    │ [40.6%, 77.4%]  │ 18 / 30 observed · 0 missing · 0 excluded │

Mọi khách hàng đều được trả lời, 22 trên 30 người đạt được mục tiêu, và 12 cuộc hội thoại chứa một lời hứa hẹn. Chưa có gì được quyết định: các khách hàng này là mô phỏng, và chưa ai kiểm tra rằng họ cư xử như khách hàng thật.

Đọc các khách hàng mô phỏng

oloproof simulated-users RUN_ID
  Realism: no conversation has been drawn and labelled yet.
  Not validated: nothing labelled yet, against a bar of 80.0%.
Features (median [quartiles]; descriptive, never a gate):
  user messages           2 [1, 2]
  user message length     49 [10, 67]
  How the simulated conversations ended: goal_reached 73%, user_left 27%

Tám người rời đi là những khách hàng đã chuyển nhà: đường cơ sở không thể thay đổi địa chỉ.

Gán nhãn các cuộc hội thoại về mức chân thực

Rút một mẫu ngẫu nhiên các cuộc hội thoại của lần chạy vào một bảng tính, nhờ một người đọc từng cuộc hội thoại, cùng với nhân vật và cách nó kết thúc, và đánh dấu liệu một khách hàng thật có thể đã viết phía khách hàng và kết thúc nó ở đó, theo cách đó, hay không. Ở đây fill_realism.py đóng vai người đó, sao chép các phán quyết của họ từ labels/realism_verdicts.csv:

oloproof labels export RUN_ID --criterion simulated_user_realistic --sample 30 --local --out realism.csv
python fill_realism.py realism.csv
oloproof labels import realism.csv
oloproof simulated-users RUN_ID
  Realism: 29 of 30 drawn conversations labelled realistic (30 labelled), [82.8%, 99.9%] at 95%
  Validated: the lower bound clears the bar of 80.0%.

Người đó thấy một cuộc hội thoại không chân thực: khách hàng c05 mở đầu bằng "Order: F-15. Refund.", nghe như một biểu mẫu chứ không phải một con người. Với 29 trên 30, cận dưới chính xác là 82.8%, trên ngưỡng, nên giờ các quy tắc có thể quyết định trên các khách hàng này.

oloproof gate RUN_ID --policy release.yaml
no-promises: FAIL (observed_failures_exceed_limit)
resolved: INSUFFICIENT_EVIDENCE (interval_overlaps_threshold)
Gate: BLOCK (exit 1)

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

desk_v2.py thay đổi địa chỉ khi nó đã có mã đơn hàng và mã bưu chính mới, và nói rằng nó đã bắt đầu một khoản hoàn tiền "usually arrives within 5 days" thay vì hứa hẹn điều đó. Sao chép nó đè lên desk.py, đặt version: candidate dưới system trong oloproof.yaml, giữ người dùng mô phỏng thay thế tiếp tục chạy, và:

oloproof run
Gate: ALLOW (exit 0)
│ no-promises │ no_promises  │ PASS  │ observed_failures_within_limit │
│ resolved    │ goal_reached │ PASS  │ lower_bound_meets_minimum      │

Ứng viên đã gặp cùng các khách hàng mô phỏng trên cùng bộ kiểm thử, nên mức chân thực đo được trên các cuộc hội thoại của đường cơ sở bao gồm cả nó: một lần xác thực bao gồm một người dùng mô phỏng trên một bộ kiểm thử, bất kể nó đã trò chuyện với hệ thống nào.

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

oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy compare.yaml
goal_reached: +26.7 points [+1.2, +54.1] · 30 paired · 0 missing · 0 excluded
  resolves-more  goal_reached  superiority  PASS  difference_above_zero

Mỗi khách hàng đã gặp cả hai bàn hỗ trợ, nên phép so sánh được ghép cặp theo từng khách hàng.

Mô phỏng bên cạnh thật

Nhãn chân thực đánh giá từng cuộc hội thoại riêng lẻ. Đặt các khách hàng mô phỏng bên cạnh khách hàng thật cho thấy những gì một mẫu có thể bỏ sót. real.yaml phát lại 20 cuộc trò chuyện mà các nhân viên của cửa hàng đã có với khách hàng thật:

oloproof run --config real.yaml --policy logged_release.yaml
oloproof simulated-users BASELINE_RUN_ID --real REAL_RUN_ID
                          simulated (run_01M52Q1C)    real (run_01M52Q25)
  user messages           2 [1, 2]                    3 [3, 4]
  user message length     49 [10, 67]                 13 [4, 32]

Khách hàng thật gửi nhiều tin nhắn hơn và ngắn hơn: họ nói điều họ muốn trên ba dòng, trong khi người dùng thay thế nói nó trong một câu gọn gàng. Điều đó đáng biết về bất kỳ người dùng mô phỏng nào, và nó không bao giờ quyết định điều gì ở đây: so sánh hai mẫu không ghép cặp cần một phương pháp mà Oloproof chưa chấp nhận.

Tùy chọn: một mô hình thật đóng vai các khách hàng

Bước này rời khỏi lộ trình ngoại tuyến. Nó cần một mô hình trò chuyện: cục bộ là Ollama (ollama pull qwen2.5vl, hoặc bất kỳ mô hình trò chuyện nào), không khóa và không chi phí; trên đám mây là provider: anthropic hoặc openai với api_key_env, tính phí theo mỗi tin nhắn. Trong oloproof.yaml, trỏ người dùng mô phỏng tới nó:

  simulated_user:
    provider: openai_compatible
    model: qwen2.5vl
    base_url: http://localhost:11434/v1
    max_turns: 6

Ollama trả lời từng yêu cầu một, nên hãy đặt concurrency: {system: 1} và một timeout_s vài phút. Một mô hình khác là một người dùng mô phỏng khác: các nhãn chân thực ở trên không bao gồm nó, nên các cuộc hội thoại của nó cần mẫu riêng trước khi bất kỳ quy tắc nào quyết định trên chúng.

Mười trong số các nhân vật, do qwen2.5vl đóng trên máy này với bàn hỗ trợ ứng viên, đã đạt mục tiêu trong 8 cuộc hội thoại, trong khi người dùng thay thế đạt được nó trong cả 30. Mô hình đã tìm ra điều mà người dùng thay thế không thể: hai khách hàng hỏi về "status" và "shipping status" của đơn hàng, những từ mà bàn hỗ trợ không biết, và lần nào cũng chỉ nhận lại lời chào của nó. Một người đã dùng hết số tin nhắn. Bàn hỗ trợ hiểu lần thử thứ ba của người kia, và khách hàng đó vẫn rời đi: kiểm tra mục tiêu, một phán đoán của mô hình, đã bỏ sót câu trả lời. Một nhãn chân thực là nơi điều đó lộ ra: một khách hàng thật khi được cho biết lúc nào đơn hàng của họ đến sẽ không rời đi trong bất mãn. Khác với người dùng thay thế, mô hình cũng thường đưa ra mã đơn hàng trước khi được hỏi, và viết một tin nhắn dài hơn ở chỗ người dùng thay thế viết hai tin nhắn ngắn.

Khắc phục sự cố

Triệu chứngNguyên nhân và cách sửa
Mọi quy tắc đều là INSUFFICIENT_EVIDENCE với simulated_user_not_validatedChưa ai gán nhãn các cuộc hội thoại của người dùng mô phỏng này trên bộ kiểm thử này, hoặc tỷ lệ chân thực không vượt qua ngưỡng. Hãy gán nhãn một mẫu như trên.
a case for a simulated user needs a 'persona' object in its inputMột trường hợp không có nhân vật, hoặc nhân vật nằm dưới một khóa khác: hãy đặt persona_field.
provider connection failed ở mọi trường hợpNgười dùng mô phỏng thay thế không chạy, hoặc lắng nghe trên một cổng khác với cổng mà base_url nêu.
the simulated user's goal check answered ...Mô hình không trả lời kiểm tra mục tiêu bằng có hoặc không. Hãy thử một mô hình khác, hoặc một mô hình lớn hơn.
conversation/v1 is written by OloproofBàn hỗ trợ đã tự ghi lại cuộc hội thoại. Hãy bỏ phần đó: Oloproof ghi lại những gì đã đi qua người dùng mô phỏng.

Giới hạn

  • Khách hàng thay thế là một quy tắc. Các cuộc hội thoại của nó cho thấy cơ chế; các cuộc hội thoại của một mô hình thật lộn xộn hơn, như bước tùy chọn cho thấy.
  • Việc một mục tiêu có đạt được hay không là phán đoán của mô hình của người dùng mô phỏng, chỉ được kiểm tra qua các nhãn chân thực.
  • Một không gian làm việc được lưu trữ hiện chưa quyết định trên người dùng mô phỏng.
  • Khách hàng mô phỏng không phải là khách hàng thật: một người dùng mô phỏng đã được xác thực là bằng chứng về những khách hàng giống các nhân vật của nó, trong chừng mực con người thấy nó chân thực, và không xa hơn.