Bài kiểm tra của bạn đã vượt qua vào thứ Hai. Cùng một đầu vào, cùng một mã, temperature=0. Đến thứ Ba thì thất bại, và bạn không thay đổi gì cả. Lệnh xác nhận kiểm tra một chuỗi chính xác, và mô hình đã trả về cùng một câu trả lời nhưng được diễn đạt hơi khác một chút. Bài kiểm tra báo đỏ, tác nhân thì ổn, và bây giờ bạn đang gỡ lỗi bộ kiểm thử thay vì sản phẩm của mình.
Đây là cái giá phải trả khi kiểm thử bất kỳ thứ gì gọi một mô hình ngôn ngữ. Đầu ra dịch chuyển, ngay cả khi bạn đã yêu cầu nó không làm vậy. Đặt nhiệt độ về 0 và bạn vẫn sẽ không nhận được các phản hồi giống hệt từng byte qua các lần chạy. Hầu hết các nhà phát triển học được điều này một cách khó khăn, một lần, và sau đó viết lại cách họ kiểm thử. Hướng dẫn này chỉ cho bạn cách viết các lệnh xác nhận có thể duy trì khi văn bản bên dưới chúng liên tục thay đổi. Đây là phần tìm hiểu sâu về chế độ lỗi thứ ba từ hướng dẫn của chúng tôi về tại sao tác nhân AI bị lỗi trong sản xuất.
Tại sao temperature=0 không có nghĩa là xác định
Nhiệt độ kiểm soát cách mô hình lấy mẫu token tiếp theo. Khi bằng 0, nó luôn chọn token có xác suất cao nhất, điều này nghe có vẻ có thể tái tạo được. Nhưng không phải vậy, và các lý do nằm bên dưới mô hình.
Phép toán dấu phẩy động không có tính chất kết hợp trên GPU. Cộng các số giống nhau theo một thứ tự khác và bạn sẽ nhận được một kết quả hơi khác ở chữ số thập phân cuối cùng. Sự khác biệt nhỏ đó có thể làm thay đổi thứ hạng của token cao nhất, và một token khác biệt sẽ thay đổi mọi thứ sau đó. Thứ tự của các phép cộng đó phụ thuộc vào cách nhà cung cấp gộp yêu cầu của bạn với các lưu lượng khác, phần cứng nào chạy nó, và phiên bản kernel nào được triển khai vào ngày hôm đó. Bạn không kiểm soát được bất cứ điều gì trong số đó.
Các nhà cung cấp cũng thay đổi mọi thứ ở phía họ. Họ thay đổi GPU, cập nhật thư viện suy luận, tái lượng tử hóa trọng số, và định tuyến cuộc gọi của bạn đến một khu vực khác. Một cuộc thảo luận dài trên vLLM đi sâu vào lý do tại sao một seed cố định và temperature=0 vẫn không đủ để tái tạo từng bit. Tóm lại: tính xác định là một thuộc tính của toàn bộ ngăn xếp phục vụ, không phải là một cờ bạn đặt trong yêu cầu của mình.
Vì vậy, hãy ngừng coi đầu ra giống hệt nhau là tiêu chuẩn. Mô hình cung cấp cho bạn một câu trả lời có cùng ý nghĩa, được diễn đạt theo cách nó xuất hiện trong lần chạy này. Các bài kiểm tra của bạn phải chấp nhận điều đó.
Các khẳng định chuỗi chính xác biến bộ kiểm thử của bạn trở nên không ổn định
Đây là cạm bẫy. Bạn viết assert response == "Your order total is $42.00." vì đó là những gì đã trả về lần đầu tiên. Nó vượt qua. Sau đó, mô hình trả về “Your total comes to $42.00” và bài kiểm tra thất bại trên một câu trả lời đúng.
Một bài kiểm tra thất bại trên một câu trả lời đúng còn tệ hơn là không có bài kiểm tra nào cả. Nhóm sẽ hiểu rằng bộ kiểm thử này chỉ kêu gào vô cớ. Mọi người chạy lại nó cho đến khi nó báo xanh, sau đó họ ngừng đọc các lỗi, rồi họ bỏ lỡ lỗi hồi quy thực sự bị chôn vùi trong tiếng ồn. Các bài kiểm tra không ổn định không chỉ lãng phí thời gian mà còn làm xói mòn niềm tin vào toàn bộ bộ kiểm thử, và chúng tôi đã viết trước đây về những gì gây ra các bài kiểm tra không ổn định và tại sao chúng lây lan. Đầu ra không xác định là một trong những cách nhanh nhất để tạo ra chúng.
Bản năng là ghim chặt đầu ra hơn: chụp chuỗi chính xác, tạo ảnh chụp nhanh, so sánh sự khác biệt. Điều đó làm cho sự không ổn định trở nên tồi tệ hơn, bởi vì bạn đã ghép bài kiểm tra của mình với một thứ chắc chắn sẽ thay đổi. Bạn cần hành động ngược lại.
Khẳng định về cấu trúc và ý nghĩa, không phải văn bản chính xác
Đầu ra có thể khác nhau, nhưng hợp đồng bên dưới nó thì không nên. Một nhân viên hỗ trợ có thể diễn đạt xác nhận hoàn tiền theo hàng trăm cách, nhưng mỗi phản hồi hợp lệ đều mang cùng một thông tin: số tiền hoàn lại, ID đơn hàng, trạng thái. Hãy kiểm tra các thông tin đó, không phải cách diễn đạt.
Đó là toàn bộ sự thay đổi. Ngừng hỏi “mô hình có nói chính xác điều này không” và bắt đầu hỏi “phản hồi có hình dạng, các trường và giá trị đúng trong phạm vi phù hợp không.” Những thuộc tính đó tồn tại sau khi diễn đạt lại. Một lỗi hồi quy thực sự, một trường bị thiếu, một số ngoài giới hạn, một payload bị định dạng sai, vẫn sẽ kích hoạt lệnh xác nhận. Dưới đây là các chiến lược để đưa điều này vào thực tế.
Xác thực phản hồi dựa trên lược đồ JSON
Nếu tác nhân của bạn trả về dữ liệu có cấu trúc, hãy định nghĩa một lược đồ JSON cho nó và xác thực mọi phản hồi dựa trên lược đồ đó. Lược đồ kiểm tra kiểu dữ liệu, các trường bắt buộc, các enum được phép và định dạng mà không quan tâm đến các giá trị cụ thể. Một trường status phải là một trong các giá trị refunded, pending hoặc denied. Một order_id phải khớp với mẫu ID của bạn. Một amount phải là một số, không phải chuỗi.
Đây là khẳng định mạnh nhất mà bạn có thể viết để chống lại phản hồi không xác định, bởi vì nó bắt được những lỗi gây hại: mô hình đã bỏ qua một trường, lồng đối tượng sai cách hoặc trả về văn xuôi trong khi bạn mong đợi JSON. Tải lược đồ phản hồi của bạn vào Apidog và xác thực các phản hồi trực tiếp của tác nhân dựa trên đó. Sự không khớp sẽ chỉ rõ trường bị lỗi, chứ không phải một chuỗi khác biệt dài 400 ký tự.
Khẳng định lệnh gọi công cụ có hình dạng và mục tiêu đúng
Khi tác nhân của bạn quyết định gọi một công cụ, hãy kiểm tra lệnh gọi đó, chứ không phải câu nói dẫn đến nó. Khẳng định ba điều: nó đã chọn đúng công cụ, nó nhắm đúng mục tiêu và payload khớp với lược đồ của công cụ. Một tác nhân đặt chỗ gọi POST /reservations nên gửi guests dưới dạng một số nguyên và một date hợp lệ, bất kể lý do ngôn ngữ tự nhiên nào đã tạo ra lệnh gọi đó.
Đây là nguyên tắc tương tự như việc xác thực một thân phản hồi, được áp dụng cho yêu cầu đi. Kiểm tra xem các tham số bắt buộc có tồn tại không, kiểu dữ liệu có đúng không và không có trường nào được tạo ra mà lại lén lút vào. Phương pháp kiểm thử các lệnh gọi API của tác nhân AI từ đầu đến cuối bao gồm việc nắm bắt các lược đồ công cụ đó và khẳng định chống lại chúng. Một payload lệnh gọi công cụ có một hợp đồng ngay cả khi cách diễn đạt xung quanh nó không có.
Sử dụng phạm vi số thay vì giá trị chính xác
Đối với bất kỳ số nào mà mô hình tạo ra hoặc chuyển qua, hãy khẳng định một phạm vi, không phải một giá trị. Một tác nhân giỏ hàng tính tổng. Bạn không biết con số chính xác trên mỗi lần chạy, giỏ hàng và quy tắc thuế, nhưng bạn biết rằng nó không thể âm và không thể vượt quá giá trị giỏ hàng cộng với phí vận chuyển và thuế tối đa. Vì vậy, hãy khẳng định điều đó: phản hồi chứa một total nằm giữa 0 và giới hạn trên đó.
Giới hạn duy nhất đó bắt được những lỗi quan trọng, một tổng âm, một tổng lớn gấp mười lần, một tổng bằng không trên một giỏ hàng đầy đủ, trong khi bỏ qua những biến thể mà bạn không quan tâm. Phạm vi hoạt động cho điểm tin cậy, số lượng mặt hàng, mức sử dụng token, ngân sách độ trễ và bất kỳ con số dẫn xuất nào. Chọn giới hạn rộng nhất mà vẫn báo lỗi khi có một lỗi thực sự.
Kiểm tra các khóa bắt buộc tồn tại và các trường bị cấm vắng mặt
Hai khẳng định đơn giản nhưng có trọng lượng lớn. Thứ nhất, các khóa bạn phụ thuộc vào phải hiện diện và không rỗng. Thứ hai, các khóa không bao giờ được xuất hiện thì phải vắng mặt. Một tác nhân xử lý yêu cầu hỗ trợ phải trả về một resolution, và nó không bao giờ được để lộ trường internal_notes hoặc raw_prompt cho khách hàng.
Các kiểm tra sự hiện diện và vắng mặt miễn nhiễm với việc thay đổi cách diễn đạt theo thiết kế, vì chúng kiểm tra bộ khung của phản hồi, chứ không phải nội dung của nó. Chúng cũng là biện pháp bảo vệ rẻ nhất của bạn chống lại toàn bộ loại rò rỉ quyền riêng tư, nơi mô hình hữu ích bao gồm một trường mà nó lẽ ra phải giữ riêng tư.
Sử dụng kiểm tra ngữ nghĩa và ngưỡng cho văn bản tự do
Đôi khi payload là văn xuôi và bạn vẫn cần kiểm tra nó. So khớp chính xác sẽ không hiệu quả, vì vậy hãy kiểm tra các thuộc tính thay thế. Phản hồi có chứa số đơn hàng bạn đã truyền vào không? Nó có nằm dưới giới hạn độ dài không? Nó có tránh danh sách đen các cụm từ bạn không bao giờ muốn gửi cho người dùng không?
Khi bạn thực sự cần kiểm tra ý nghĩa, hãy so sánh bằng độ tương đồng embedding với một câu trả lời tham chiếu và khẳng định điểm số vượt qua một ngưỡng, thay vì yêu cầu các chuỗi phải khớp. Coi các kiểm tra ngữ nghĩa này như một cổng thô, không phải một cổng chính xác. Chúng bắt được một phản hồi đi lạc đề. Chúng sẽ không bắt được lỗi thực tế tinh vi, vì vậy hãy kết hợp chúng với các khẳng định cấu trúc ở trên.
Chụp ảnh nhanh các phạm vi, không phải ảnh chụp nhanh chính xác
Kiểm thử snapshot vẫn có vị trí của nó, miễn là bạn chụp ảnh nhanh các phần ổn định. Đóng băng hình dạng của phản hồi, tập hợp các khóa, các kiểu dữ liệu, các giá trị enum và cho phép các trường tự do thay đổi trong giới hạn. Trên thực tế, snapshot của bạn ghi lại "phản hồi này có các khóa a, b, c, với b nằm trong phạm vi này và c từ tập hợp này" thay vì một khối văn bản chính xác bị đóng băng. Khi snapshot bị hỏng, nó bị hỏng do một thay đổi cấu trúc đáng xem xét, chứ không phải do một từ đồng nghĩa.
Trạng thái và bộ nhớ làm điều này khó hơn
Mọi điều trên giả định một yêu cầu vào, một phản hồi ra. Các tác nhân không hoạt động theo cách đó. Chúng mang theo bộ nhớ qua các lượt, và trạng thái đó nhân lên các nguồn biến thể.
Câu trả lời của một tác nhân có trạng thái phụ thuộc vào những gì nó đã truy xuất, những gì nó đã lưu trữ trước đó và thứ tự các lượt trước đó đã chạy. Hai lần chạy cùng một cuộc hội thoại có thể khác nhau vì một bước truy xuất đã xếp hạng tài liệu khác nhau, hoặc vì một bản tóm tắt được viết ở lượt thứ hai đã định hình lý do ở lượt thứ năm. Bây giờ đầu ra của bạn thay đổi vì hai lý do phức tạp: tính không xác định của chính mô hình và một trạng thái ban đầu khác. Giải thích của chúng tôi về cách bộ nhớ tác nhân AI hoạt động đi sâu vào nơi trạng thái đó tồn tại và cách nó được xây dựng.
Hai thói quen giúp giữ điều này có thể kiểm thử được. Thứ nhất, kiểm soát trạng thái bạn có thể. Gieo mầm bộ nhớ của tác nhân đến một điểm khởi đầu đã biết trước mỗi bài kiểm tra, để bạn chỉ thay đổi một thứ chứ không phải hai. Thứ hai, khẳng định các bất biến vẫn đúng bất kể đường dẫn. Số dư đang chạy không bao giờ được âm. Một cuộc trò chuyện đã đặt một chuyến bay nên kết thúc với chính xác một đặt chỗ, bất kể nó mất bao nhiêu lượt. Các khẳng định không phụ thuộc vào đường dẫn là những khẳng định tồn tại được trong một tác nhân có trạng thái, không xác định.
Giả lập các phụ thuộc để bài kiểm tra lặp lại
Bạn không thể diễn tập bất kỳ điều này với các API bên thứ ba trực tiếp. Họ giới hạn tốc độ của bạn, họ thay đổi dữ liệu của họ, và họ thêm một nguồn ngẫu nhiên thứ hai vào trên mô hình. Để có một bài kiểm tra có thể lặp lại, hãy ghim chặt mọi thứ không phải là hành vi bạn đang kiểm tra.
Giả lập các API mà tác nhân gọi và lập trình các phản hồi cố định. Bây giờ API thanh toán luôn trả về cùng một biên lai, API tìm kiếm luôn trả về cùng ba kết quả, và điều duy nhất còn lại đang chuyển động là lý luận của chính tác nhân, đó là điều bạn muốn quan sát. Một phụ thuộc được giả lập cũng cho phép bạn ép buộc các trường hợp biên mà một API lành mạnh sẽ không tạo ra theo yêu cầu, sau đó khẳng định tác nhân xử lý chúng. Hướng Apidog vào các phụ thuộc của tác nhân để thiết lập các bản giả lập đó với các nội dung ổn định, có thể kiểm soát, và kết hợp chúng với các khẳng định lược đồ ở trên. Điều này nằm trong thực hành rộng hơn của kiểm thử AI tác nhân, nơi giả lập và khẳng định hoạt động cùng nhau.
Apidog phù hợp ở đâu (và không phù hợp ở đâu)
Hãy rõ ràng về công việc của công cụ. Apidog là một nền tảng thiết kế, kiểm thử và giả lập API. Nó không phải là một framework tác nhân, một host mô hình, một runtime tác nhân, hay một nền tảng đánh giá và quan sát. Nó không xây dựng tác nhân của bạn, không chạy nó, không điều phối các bước của nó, hay chấm điểm lý luận của nó.
Điều nó sở hữu là lớp API mà tác nhân của bạn giao tiếp, và đó là nơi các bài kiểm thử này tồn tại. Hai điểm phù hợp trung thực. Bạn viết các khẳng định về các phản hồi API của tác nhân (xác thực lược đồ, hình dạng phản hồi, phạm vi số, khóa bắt buộc và bị cấm, hình dạng payload gọi công cụ) mà vẫn tồn tại được đầu ra không xác định. Và bạn giả lập các phụ thuộc của tác nhân để một bài kiểm thử chạy theo cùng một cách hai lần. Đó là điểm giao thoa mà Apidog lấp đầy: hợp đồng trên các yêu cầu và phản hồi, chứ không phải mô hình tạo ra chúng.
Kiểm tra hợp đồng, không phải cách diễn đạt
Tính không xác định không phải là một lỗi mà bạn có thể cấu hình để loại bỏ. Đó là một thuộc tính của việc chạy mô hình ngôn ngữ, và temperature=0 không tắt nó đi. Các nhóm đã phát hành các tác nhân đáng tin cậy đã ngừng chống lại nó. Họ kiểm tra những thứ không đổi, lược đồ, hình dạng, phạm vi, các trường bắt buộc, và họ để cách diễn đạt thay đổi. Làm như vậy, bộ kiểm thử của bạn sẽ trở nên tĩnh lặng theo cách tốt: nó vẫn xanh khi văn bản dịch chuyển, và chỉ báo đỏ khi có gì đó bị hỏng.
Hãy chọn một khẳng định không ổn định trong bộ kiểm thử của bạn tuần này và viết lại nó dưới dạng kiểm tra lược đồ và phạm vi. Tải Apidog để xác thực các phản hồi của tác nhân của bạn dựa trên hợp đồng và giả lập các phụ thuộc giúp các bài kiểm thử có thể lặp lại.
