Human-in-the-Loop Không Giết Thông Lượng: Bài Học Từ Hệ Thống Text-to-SQL

28 tháng 8, 2026·10 phút đọc

Bài viết phân tích cách một đội ngũ kỹ thuật đã chuyển từ việc xem xét thủ công toàn bộ hành động của agent sang cơ chế định tuyến dựa trên rủi ro, giúp giảm độ trễ và tăng hiệu quả trong khi vẫn đảm bảo an toàn. Tác giả chia sẻ kinh nghiệm thực tế về các tín hiệu đánh giá rủi ro, quản lý hàng đợi phê duyệt và nơi con người thực sự tạo ra giá trị.

Human-in-the-Loop Không Giết Thông Lượng: Bài Học Từ Hệ Thống Text-to-SQL

Ba tuần sau khi đưa agent text-to-SQL vào sử dụng cho đội phân tích nội bộ, có người yêu cầu nó "dọn dẹp các hàng kiểm thử trong bảng khuyến mãi." Agent hiểu "dọn dẹp" là "xóa", hiểu "hàng kiểm thử" là bất cứ thứ gì có cờ is_test hoặc tên chứa từ "test", và tạo ra một câu lệnh DELETE có thể xóa 40% một bảng mà nhiều dashboard phụ thuộc vào.

Câu lệnh DELETE đó đã không được thực thi vì chúng tôi đã chặn việc thực thi phía sau một bước phê duyệt của con người cho bất kỳ thao tác nào không phải SELECT. Người xem xét đã phát hiện ra truy vấn, đặt một câu hỏi làm rõ, và toàn bộ thao tác bị hủy. Tuy nhiên, sáu tuần sau, chính hàng đợi phê duyệt này lại trở thành vấn đề bị phàn nàn nhiều nhất trong mọi cuộc họp rà soát. Các nhà phân tích phải chờ 20, thậm chí 40 phút để một người nhìn qua truy vấn và bấm nút duyệt, trong khi phần lớn chúng đều đúng và không có gì để bác bỏ.

Một cơ chế an toàn quá rộng không thất bại một cách an toàn, nó thất bại một cách chậm chạp. Và những kiểu thất bại chậm thường có xu hướng bị ai đó đang chịu áp lực tiến độ âm thầm vô hiệu hóa.

Bản năng tạo cảm giác an toàn ban đầu

Phiên bản đầu tiên của sự giám sát con người trong hầu hết các hệ thống agent thường giống nhau: bất kỳ hành động nào vượt quá quyền đọc sẽ được chuyển đến một người trước khi thực thi. Đây là một quy tắc dễ viết và dễ giải thích trong buổi đánh giá bảo mật. Trong vài tuần đầu, nó mang lại cảm giác đúng mức thận trọng.

Nhưng đó cũng là phiên bản mở rộng kém nhất. Lý do không nằm ở việc con người xử lý chậm, mà là quy tắc không phân biệt giữa một lệnh DELETE chạm vào 40% bảng dữ liệu của ba dashboard với một lệnh DELETE một hàng duy nhất mà người dùng yêu cầu bằng khóa chính 30 giây trước. Cả hai đều vào cùng một hàng đợi và chờ đợi phía sau những việc khác. Người phê duyệt không có tín hiệu nào cho biết cái nào đáng dành 5 giây, cái nào đáng dành 5 phút.

Điểm gãy của hàng đợi

Sau một thời gian, thời gian chờ phê duyệt trung bình đã vượt quá 15 phút. Các người phê duyệt bắt đầu duyệt theo lô, lướt qua năm hoặc sáu truy vấn một lúc, làm giảm chất lượng đánh giá. Họ bấm nút duyệt cho những thứ họ chưa đọc kỹ, vì nếu không họ sẽ tụt lại phía sau. Phần lớn truy vấn đều ổn, nên lối tắt này hoạt động tốt cho đến khi nó bắt đầu sai sót.

Đây là kiểu lỗi không bao giờ xuất hiện trong bản thiết kế: sự giám sát quá rộng không trở nên cẩn thận hơn khi tải tăng, mà trở nên nhanh và nông hơn, làm xói mòn chính thứ nó được tạo ra để bảo vệ. Tình trạng này được gọi là "mệt mỏi vì đóng dấu cao su" — sự cảnh giác là nguồn lực có hạn, nếu bạn dùng nó cho những thứ không cần, bạn sẽ không còn đủ cho điều thực sự cần.

Định tuyến theo rủi ro, không theo loại thao tác

Giải pháp của chúng tôi không phải là "làm hàng đợi nhanh hơn", mà là chấp nhận rằng hầu hết hành động của agent không cần người xem xét, và xây dựng hệ thống có thể phân biệt trước khi hành động đến màn hình của bất kỳ ai.

Định tuyến dựa trên rủi ro — Hệ thống chấm điểm mọi hành động của agent dựa trên một loạt tín hiệu và chỉ chuyển lên cho con người khi vượt ngưỡng rủi ro. Mọi thứ dưới ngưỡng được thực thi ngay lập tức mà không cần ai đụng tới. Câu hỏi trung thực không phải là "có nên để một số hành động tự thực thi không?", mà là "bạn thực sự đã từng xem xét kỹ những hành động nào?". Nếu câu trả lời là "không, chúng tôi chỉ đóng dấu cao su", bạn đã có cơ chế tự phê duyệt rồi, chỉ là đang trả thêm 15 phút độ trễ để giả vờ ngược lại.

Một cổng phê duyệt con người bao phủ mọi thao tác ghi có thể trở thành lá chắn trách nhiệm hơn là kiểm soát an toàn hiệu quả.

Router cần nhìn thấy điều gì

Việc xây dựng router (bộ định tuyến) mất nhiều thời gian hơn so với việc tạo hàng đợi, và điều đó là đúng. Hàng đợi chỉ là hệ thống ống nước, còn router mới là sự phán xét thực sự, được tự động hóa và làm rõ ràng.

Chúng tôi đã xem lại nhật ký phê duyệt từ sáu tuần đầu và tìm ra bốn tín hiệu quan trọng:

  • Phạm vi ảnh hưởng (Blast radius): Không phải "có phải là thao tác ghi không", mà là "nó chạm bao nhiêu hàng và mức độ đảo ngược ra sao". Một lệnh DELETE theo khóa chính khác hoàn toàn về mức rủi ro với một lệnh DELETE có WHERE trỏ tới hàng nghìn hàng. Chúng tôi từng dùng EXPLAIN nhưng ước lượng của planner không đáng tin với dữ liệu lệch. Giải pháp tốt hơn: chạy mệnh đề WHERE như một câu đếm thực, giới hạn tối đa 50.000 hàng, để có con số thực tế với chi phí dự đoán được.

  • Độ nhạy của bảng (Table sensitivity): Một danh sách trắng tĩnh do người quản lý schema duy trì. Các bảng liên quan đến thanh toán, xác thực hoặc yêu cầu tuân thủ quy định sẽ nhận điểm rủi ro tối thiểu bất kể truy vấn trông như thế nào. Đây là phần tôi sẽ không bao giờ để học máy tự quyết định.

  • Khoảng cách ngữ nghĩa với các truy vấn đã được duyệt: Chúng tôi lưu chỉ mục embedding của các truy vấn đã được duyệt trước đó và kiểm tra mức độ tương đồng. Một yêu cầu giống hệt 50 truy vấn đã duyệt có rủi ro khác với một yêu cầu hoàn toàn mới, vì sự mới lạ chính là nơi agent dễ hiểu sai ý định nhất.

  • Độ đồng thuận giữa các lần tạo lại: Chúng tôi thử dùng độ tin cậy cấp token của mô hình nhưng nó không đáng tin. Thay vào đó, chúng tôi tạo lại cùng một truy vấn hai hoặc ba lần ở nhiệt độ cao hơn một chút và kiểm tra xem kết quả có khớp nhau không. Nếu không khớp, đó là tín hiệu mạnh về sự mơ hồ thực sự.

def compute_risk_score(query_plan, resamples, embedding_index):
    blast_radius = bounded_row_count(query_plan, cap=50_000)  # đếm thực, không phải ước đoán
    table_floor = SENSITIVE_TABLE_FLOOR.get(query_plan.target_table, 0.0)
    novelty = 1 - max_similarity(query_plan.intent_embedding, embedding_index)
    disagreement = 1 - resample_agreement(query_plan, resamples)
    
    score = (
        0.40 * normalise(blast_radius)
        + 0.25 * table_floor
        + 0.20 * novelty
        + 0.15 * disagreement
    )
    return max(score, table_floor)  # bảng nhạy cảm không bao giờ dưới ngưỡng tối thiểu

Trọng số không phải là điều quan trọng, chúng sẽ thay đổi tùy hệ thống. Cấu trúc mới là điều quan trọng: phạm vi ảnh hưởng và độ nhạy bảng chiếm ưu thế vì chúng là hai tín hiệu thực sự tương quan với "điều tồi tệ xảy ra nếu sai".

Tách hàng đợi khỏi người dùng

Một nửa của giải pháp không liên quan gì đến router, mà liên quan đến trải nghiệm người dùng khi một hành động bị chờ phê duyệt.

Trong phiên bản ngây thơ, yêu cầu của người dùng chỉ đứng yên, agent im lặng, giao diện xoay vòng. Người dùng không thể phân biệt giữa "đang có người xem xét" và "hệ thống bị kẹt". Chúng tôi chuyển sang mô hình ticket: hành động được leo thang sẽ được xác nhận ngay lập tức, người dùng được thông báo rõ rằng hành động này cần xem xét và mất khoảng bao lâu, và họ có thể tiếp tục làm việc với những thứ không phụ thuộc vào kết quả.

async def handle_agent_action(query_plan, risk_score, threshold, user_session):
    if risk_score < threshold:
        result = await execute(query_plan)
        return AgentResponse(status="completed", result=result)
    
    ticket = await approval_queue.enqueue(query_plan, risk_score)
    await notify_user(
        user_session,
        f"Thao tác này cần xem xét nhanh trước khi chạy — thường dưới "
        f"{approval_queue.p90_wait_minutes()} phút. Tôi sẽ báo bạn khi xong.",
    )
    return AgentResponse(status="pending_review", ticket_id=ticket.id)

Điều này không giảm thời gian xem xét thực tế. Nó ngăn thời gian xem xét bị hiểu là lỗi hệ thống. Chờ 40 phút được thông báo trước và không chặn công việc khác cảm giác hoàn toàn khác với chờ 40 phút trông như hệ thống treo.

Nơi con người thực sự tạo ra giá trị

Sau khi router hoạt động vài tuần, chúng tôi có thể xem lại nhật ký phê duyệt và hỏi: "trên các truy vấn được leo thang, người xem xét có phát hiện ra lỗi không, hay họ vẫn chỉ bấm nút duyệt?"

Kết quả rõ ràng hơn tôi mong đợi. Người xem xét thực sự hữu ích khi cách diễn giải ý định của agent có vẻ hợp lý nhưng sai — một yêu cầu mà đồng nghiệp con người sẽ hiểu theo một cách, còn agent hiểu theo cách khác. Họ không đánh giá cú pháp SQL, mà đánh giá liệu "dọn dẹp các hàng kiểm thử" có thực sự nghĩa là "xóa 40% bảng này". Đó là phán xét mà mô hình vẫn kém khi sự mơ hồ nằm ở ý định chứ không phải truy vấn.

Họ gần như vô dụng với các truy vấn đúng cơ học, nơi sự mơ hồ đã được giải quyết trong cuộc trò chuyện trước đó. Một lệnh UPDATE có phạm vi hẹp cho một hàng, tạo ra từ một chỉ dẫn rõ ràng, nằm trong hàng đợi chờ ai đó nhìn qua — không ai thêm được giá trị gì ở đó.

Việc con người xem xét mang lại giá trị nhiều hơn cho các hành động mơ hồ, có phạm vi ảnh hưởng lớn so với các hành động thông thường, có phạm vi nhỏ. Ai cũng đồng ý với điều đó về mặt lý thuyết, nhưng hầu như không có cổng phê duyệt nào được xây dựng theo nguyên tắc đó.

Kết luận

Giải pháp này không làm cho vấn đề xem xét biến mất, nó chỉ di chuyển vấn đề. Thay vì yêu cầu một người đánh giá mọi thao tác ghi, chúng tôi yêu cầu một hàm chấm điểm quyết định thao tác nào xứng đáng có người xem. Đây là câu hỏi hẹp hơn và trung thực hơn, nhưng chưa phải là câu hỏi đã được giải quyết.

Các trọng số của router cần được tinh chỉnh định kỳ. Các mẫu truy vấn thay đổi khi sản phẩm phát triển, chỉ mục embedding của "ý định đã duyệt" cần được cắt tỉa để tránh coi những mẫu cũ không còn phù hợp là quen thuộc. Một router được hiệu chỉnh tốt ở tháng một có thể âm thầm trở nên quá thoáng hoặc quá chặt vào tháng tư.

Bước tiếp theo hiển nhiên là đóng vòng lặp: tự động đưa kết quả bị từ chối và được chấp thuận vào trọng số để router tự điều chỉnh. Nhưng một vòng lặp phản hồi tự động trên một ngưỡng liên quan đến an toàn là thứ cần được giám sát. Có điều gì đó tôi không tin tưởng ở một hệ thống tự trở nên ít thận trọng hơn, chỉ dựa trên một chuỗi phê duyệt bình yên gần đây. Đó thường chính là điều kiện để xảy ra sự cố tiếp theo. Hiện tại, chúng tôi vẫn điều chỉnh thủ công theo lịch trình, với ai đó xem xét điều gì đã thay đổi và tại sao trước khi triển khai. Chậm hơn, nhưng đó là sự đánh đổi trung thực hơn.

Chia sẻ:FacebookX
Nội dung tổng hợp bằng AI, mang tính tham khảo. Xem bài gốc ↗