Tám tháng làm việc trong hàng đợi hợp nhất (merge queue) của GitHub Actions dạy cho bạn những điều mà các bảng so sánh tính năng không bao giờ làm được. Một framework có thể cung cấp năm mươi chỉ số, các bảng điều khiển (dashboard) đẹp mắt và các trích dẫn từ những phòng nghiên cứu uy tín. Nếu nó chặn việc triển khai của bạn chỉ vì điểm số "vibe check" thay đổi từ 0,72 xuống 0,68 đối với cùng một đoạn mã, thì nó còn tệ hơn cả vô dụng. Nó trở thành một mối đe dọa trực tiếp đến tốc độ phát hành (shipping velocity) của bạn.
Đó là bộ lọc mà hầu hết các bài tổng hợp đánh giá LLM đều bỏ lỡ. Họ đếm các khả năng. Họ hiếm khi đặt ra câu hỏi duy nhất quan trọng trong một merge queue: liệu bước kiểm tra này có vượt qua hoặc thất bại theo cùng một cách chính xác trong mọi lần chạy hay không?
Tôi đã học được điều này thông qua việc thực hiện những công việc không mấy dễ chịu. Tôi đã tích hợp sáu framework đánh giá LLM mã nguồn mở vào một pipeline CI thực tế. Chúng đã chạy trên các pull request thực tế trong tám tháng. Hai cái đã giành được quyền tiếp tục đóng vai trò là người gác cổng (gatekeepers). Những cái còn lại bị hạ cấp xuống thành các dashboard tư vấn, chuyển sang các công việc chạy hàng đêm (nightly jobs), hoặc bị loại bỏ hoàn toàn. Bài học này rất đắt giá và khắc nghiệt: cấu trúc mang tính xác định (deterministic) sẽ đánh bại chất lượng mang tính xác suất (probabilistic) khi bạn đang bảo vệ nhánh chính (main branch).
Nhiệm vụ thực sự của một Merge Gate
Một CI gate không phải là môi trường nghiên cứu. Nó là một nhân viên bảo vệ (bouncer). Mục đích duy nhất của nó là xem xét một thay đổi cụ thể và trả lời có hoặc không. Có, PR này có thể gia nhập nhánh chính. Không, nó không thể. Câu trả lời đó cần phải có trong vài giây, chi phí cực thấp và không bao giờ thay đổi một cách tùy tiện sau đó. Nếu bạn chạy lại cùng một pipeline cho cùng một commit vào một ngày thứ Ba yên tĩnh và một ngày thứ Sáu hối hả, kết quả phải giống hệt nhau.
Đây là nơi hầu hết các framework đánh giá LLM vấp ngã. Chúng được xây dựng bởi các nhà khoa học dữ liệu dành cho các nhà khoa học dữ liệu. Họ tối ưu hóa cho sự hiểu biết sâu sắc (insight), sự khám phá và việc chấm điểm tinh vi. Một merge queue tối ưu hóa cho các quyết định nhị phân, tốc độ và không có sự chập chờn (flakiness). Hai mục tiêu đó chỉ chồng lấn lên nhau một phần.
Tại sao LLM-as-Judge làm hỏng Merge Queue
Những công cụ thất bại trong bài kiểm tra của tôi đều có chung một sai lầm trong thiết kế: chúng phụ thuộc quá nhiều vào các lệnh gọi LLM-as-judge như một cơ chế gác cổng chính.
Một prompt LLM-as-judge yêu cầu mô hình chấm điểm một đầu ra theo thang điểm từ một đến mười, hoặc chọn câu trả lời tốt hơn trong hai câu trả lời, hoặc đánh giá tính chính xác về mặt thực tế. Cách tiếp cận này rất mạnh mẽ để hiểu các xu hướng chất lượng. Nhưng nó là "thuốc độc" đối với một bước kiểm tra CI mang tính chặn (blocking). Cùng một đầu vào có thể tạo ra các điểm số khác nhau vào các ngày khác nhau vì temperature, phiên bản mô hình và định dạng prompt đều tạo ra nhiễu. Khi điểm số đó gắn liền với một ngưỡng cứng và một mã thoát (exit code) cứng, hàng đợi của bạn sẽ bị chặn bởi những "bóng ma" (những lỗi không xác định).
Các thất bại sẽ kéo theo nhau một cách nhanh chóng. Một bước kiểm tra không mang tính xác định (nondeterministic) sẽ tạo ra sự ùn tắc trong hàng đợi. Các kỹ sư sẽ học cách thử lại cho đến khi con số đạt mức mong muốn, điều này vô tình rèn luyện cho đội ngũ thói quen lờ đi các bản build bị lỗi (red builds). Chi phí token tăng lên vì mỗi lần thử lại đều tiêu tốn thêm credit API. Tệ nhất là, tín hiệu trở nên vô nghĩa. Một bản build lỗi (red build) nên có nghĩa là "bạn đã tạo ra một lỗi". Nếu nó có nghĩa là "mô hình giám khảo hôm nay thức dậy với tâm trạng khó tính", thì sự tin tưởng sẽ bị xói mòn.
Những cái tên sống sót làm điều gì khác biệt
Promptfoo và DeepEval sống sót vì chúng coi các bước kiểm tra mang tính xác định là đối tượng ưu tiên hàng đầu (first-class citizens) và coi điểm số từ LLM judge là các tín hiệu phụ, không mang tính chặn. Chúng hiểu rằng một cổng gác cần một mã thoát, chứ không phải một số thực dấu phẩy động kèm theo một "quan điểm".
Promptfoo, được phát hành theo giấy phép MIT, được xây dựng cho dòng lệnh (command line). Nó thực hiện các khẳng định (assertions) như khớp regex, xác thực JSON schema, kiểm tra sự tồn tại (contains checks) và so sánh chuỗi chính xác. Những thứ này không hề cầu kỳ. Chúng chỉ là các lệnh grep và jq được nâng cấp lên. Đó chính xác là lý do tại sao chúng hoạt động hiệu quả trong CI. Một regex hoặc là khớp, hoặc là không. Một JSON schema hoặc là hợp lệ, hoặc là báo lỗi. Promptfoo trả về các mã thoát Unix tiêu chuẩn, vì vậy GitHub Actions có thể hiểu một cách tự nhiên khi nào cần dừng việc hợp nhất. Nó không phụ thuộc vào ngôn ngữ (language-agnostic) vì hoạt động như một công cụ CLI. Bạn không cần phải cài đặt cả một hệ sinh thái Python bên trong một repo dịch vụ Node.js chỉ để xác thực đầu ra.
DeepEval, được cấp phép theo Apache 2.0, là lựa chọn cho các đội ngũ Python. Nó tích hợp giống như pytest. Bạn viết các bài kiểm tra bằng cú pháp quen thuộc, và một lỗi sẽ chặn toàn bộ bộ kiểm tra (suite) một cách tự nhiên. DeepEval cung cấp một danh mục chỉ số khổng lồ, nhưng chi tiết quan trọng là bạn phải sử dụng chúng một cách cẩn thận. Hãy dựa vào các chỉ số mang tính xác định hoặc heuristic cho các cổng gác. Nếu bạn sử dụng G-Eval hoặc các bộ chấm điểm dựa trên judge khác, hãy bao bọc chúng trong các trình tạo báo cáo không mang tính chặn thay vì sử dụng các lệnh khẳng định cứng (hard asserts). Khi được sử dụng theo cách này, DeepEval mang lại cho bạn sự tiện dụng của một framework kiểm thử mà không gặp phải sự chập chờn của một sổ tay nghiên cứu (research notebook).
Vị trí của bốn framework còn lại
Bốn framework không sống sót với vai trò là cổng gác vẫn có giá trị. Chúng chỉ đơn giản là thuộc về những vị trí khác trong chuỗi công cụ (toolchain) của bạn.
Future AGI (Apache 2.0) cung cấp hơn năm mươi chỉ số và hướng đến các đội ngũ đang xây dựng các SDK tùy chỉnh. Các chỉ số này rất toàn diện. Vấn đề là công cụ này yêu cầu bạn phải tự viết bộ khung (harness) để vận hành nó trong một hàng đợi CI. Trong bối cảnh nghiên cứu, đây là một sự đánh đổi hợp lý. Trong một hàng đợi merge (merge queue), mỗi lớp kết nối tùy chỉnh đều là một nguồn gây mất ổn định mới. Nó là một công cụ đánh giá năng lực tốt, nhưng chưa phải là một "người gác cổng" sẵn sàng.
RAGAS (Apache 2.0) vượt trội trong việc đo lường chất lượng tạo phản hồi tăng cường truy xuất (RAG). Các chỉ số về độ trung thực (faithfulness) và mức độ liên quan của câu trả lời (answer relevance) thực sự hữu ích để hiểu hiệu suất của cơ sở tri thức theo thời gian. Đáng tiếc là các chỉ số này phụ thuộc rất nhiều vào các LLM đóng vai trò giám khảo (LLM judges). Chúng rất tuyệt vời cho một tác vụ kiểm tra chất lượng hàng đêm để đăng các xu hướng lên Slack. Nhưng chúng lại là những "người bảo vệ" kém hiệu quả cho một pull request. Hãy đưa RAGAS vào quy trình phân tích định kỳ, thay vì dùng nó để chặn các lượt merge.
Arize Phoenix sử dụng giấy phép Elastic License 2.0 và nằm ở một điểm giao thoa hoàn toàn khác. Nó kết nối việc truy vết phân tán (distributed tracing) với việc đánh giá, giúp bạn có khả năng quan sát (observability) lý do tại sao một mô hình lại hành xử theo một cách nhất định. Bạn sẽ cần đến nó khi đang gỡ lỗi một sự cố trên môi trường production hoặc truy vết một hiện tượng ảo giác (hallucination) về một đoạn dữ liệu truy xuất kém chất lượng. Bạn không muốn một công cụ truy vết quyết định xem nhánh tính năng của một lập trình viên junior có được phép triển khai hay không. Kiến trúc của nó được xây dựng để cung cấp thông tin chuyên sâu, chứ không phải để làm các cổng kiểm soát nhị phân (binary gates).
MLflow Evaluate (Apache 2.0) thừa hưởng nền tảng từ việc theo dõi thử nghiệm (experiment tracking). Nó khá nặng nề. Việc đưa nó vào một CI image tinh gọn sẽ làm tăng thời gian khởi động và các phụ thuộc (dependencies), làm chậm mọi tác vụ. Nếu bạn bắt buộc phải sử dụng nó trong một pipeline, hãy chỉ sử dụng các chỉ số heuristic của nó để kiểm tra cấu trúc. Ngay cả khi đó, bạn vẫn đang đi ngược lại thiết kế cơ bản của framework này. MLflow muốn ghi lại các lần chạy và so sánh các thử nghiệm qua nhiều tuần. Trong khi đó, một hàng đợi merge cần một kết luận trong vòng chưa đầy một phút.
Các quy tắc thực tế để kiểm soát (Gating)
Nếu bạn không rút ra được gì khác từ thử nghiệm này, hãy nhớ ba quy tắc sau.
Thứ nhất, hãy kiểm soát cấu trúc, đừng kiểm soát "cảm giác" (vibe). Bạn có thể bắt buộc đầu ra phải là JSON hợp lệ. Bạn có thể bắt buộc nó phải chứa các khóa (keys) cần thiết. Bạn có thể bắt buộc một nhãn phân loại phải thuộc về một enum được cho phép. Những kiểm tra này nhanh, rẻ và có tính xác định (deterministic). Bạn không thể bắt buộc một cách đáng tin cậy rằng một bản tóm tắt là "thân thiện" hay một bản viết lại là "sáng tạo". Những phẩm chất đó thuộc về việc đánh giá của con người hoặc đánh giá theo lô định kỳ, chứ không phải trong các cổng kiểm soát tự động.
Thứ hai, nếu một điểm số thay đổi dù đầu vào không đổi, hãy hạ cấp nó ngay lập tức. Hãy chạy bộ thử nghiệm đánh giá của bạn hai lần trên cùng một artifact. Nếu bất kỳ chỉ số nào chuyển từ đạt (pass) sang thất bại (fail), nó đã mất quyền chặn việc merge. Hãy chuyển nó sang một bảng điều khiển mang tính tham khảo, nơi mà sự biến động là điều có thể dự đoán và chấp nhận được.
Thứ ba, hãy tôn trọng mã thoát (exit code). Một báo cáo HTML đẹp mắt với biểu ngữ màu đỏ không thể dừng một lượt merge. Một mã thoát khác không (nonzero exit code) thì có thể. Công cụ đánh giá của bạn phải nói ngôn ngữ bản địa của nền tảng CI. Standard out là dành cho con người. Exit codes là dành cho máy móc.
Bài học rút ra
Chúng ta vẫn đang ở giai đoạn đầu trong việc tìm hiểu cách kiểm thử các ứng dụng chạy bằng LLM. Sự cám dỗ là đối xử với việc đánh giá giống như một thang điểm của con người: tinh tế, theo ngữ cảnh và hơi chủ quan. Điều đó có thể hiệu quả trong một bài báo nghiên cứu, nhưng sẽ thất bại trong một hàng đợi merge.
Sau tám tháng chạy thực tế, pipeline của tôi hiện chạy Promptfoo để xác nhận cấu trúc và schema trên các dịch vụ, và DeepEval cho các kiểm tra hành vi ở phía Python vốn có thể ánh xạ rõ ràng sang các điều kiện đạt/thất bại. Mọi thứ khác sẽ được báo cáo lên các bảng điều khiển hàng đêm. Hàng đợi hiện đã ổn định. Tín hiệu rõ ràng. Đội ngũ đã có thể tin tưởng vào một bản build báo đỏ trở lại.
Bạn không cần thêm nhiều chỉ số tại cổng kiểm soát của mình. Bạn cần ít chỉ số hơn nhưng phải luôn nói lên sự thật trong mọi lần chạy.
Dựa trên bài kiểm tra và bài viết gốc được chia sẻ trên Dev.to. Để thảo luận thêm về việc xây dựng các hệ thống AI đáng tin cậy, hãy tham gia cộng đồng GyaanSetu trên Telegram.
