↓Chuyển đến nội dung chính
  1. Bài viết/

Bài 01: Ảo tưởng kiểm soát — tư duy hệ thống trong kỷ nguyên AI

Cắm LLM vào một hệ thống quen với logic if-else thì lời hứa ‘cùng đầu vào, cùng đầu ra’ vỡ ngay. Bài này chỉ ra chỗ vỡ nằm ở đâu, và ba lớp hàng rào dựng lại quyền kiểm soát: chặn định dạng sai từ lúc sinh chữ, cam kết tỉ lệ lỗi bằng số, và đặt cầu dao ngay bên trong mô hình.

I. Khi cùng đầu vào không còn cho cùng đầu ra #

Phần mềm truyền thống hứa: cùng đầu vào, cùng đầu ra. Gắn mô hình ngôn ngữ vào luồng nghiệp vụ, lời hứa đó không còn đúng: code if-else đi theo bảng định sẵn, còn mô hình viết chữ theo xác suất như một Markov chain. Bịa nội dung và lặp không dừng vì thế là hệ quả của cách sinh chữ, không phải bug vá được bằng một câu if. Ba lớp hàng rào giúp lấy lại quyền kiểm soát mà không đòi mô hình luôn đúng. Chúng chặn chữ sai định dạng ngay lúc sinh, cam kết tỉ lệ lỗi bằng một con số, và ngắt luồng sinh chữ theo tín hiệu nội bộ bên trong mô hình.

Hãy gửi cùng một câu hỏi cho ChatGPT hai lần. Rất có thể bạn nhận về hai câu trả lời khác nhau. Với người làm phần mềm, đó là dấu hiệu một lời hứa mấy chục năm vừa hết hiệu lực: cùng đầu vào thì cùng đầu ra.

Lời hứa đó đến từ code if-else, loại code viết sẵn từng nhánh “nếu thế này thì làm thế kia”. Bộ test tự động báo đỏ sáng thứ Hai thì câu hỏi đầu tiên luôn là “cuối tuần ai sửa gì?” Câu hỏi đó hoàn toàn hợp lý: với code như vậy, không ai sửa thì không gì đổi.

LLM, viết tắt của Large Language Model, là loại mô hình AI đứng sau các chatbot như ChatGPT: nhận một đoạn chữ và viết tiếp. Gắn nó vào tính năng chính, kỹ sư chuyển sang điều phối những thành phần chỉ đúng theo xác suất.

Suốt bài, tôi dùng một ví dụ giả định: một app giao đồ ăn ở TP.HCM gắn chatbot LLM để trả lời khách về đơn hàng. Với lối nghĩ “kiểm soát tuyệt đối” cũ, chatbot này có thể qua mọi bài test, rồi sai trên diện rộng khi gặp câu hỏi chưa ai thử. Lý do nằm ở chỗ hai kiểu phần mềm khác nhau.

Abstract isometric representation of an FSM transitioning into a chaotic probabilistic system, clean line art, modern anime style, flat color, cel shading, minimalist, high contrast, pure titanium silver and dark graphite color palette, subtle academic cinnabar fire accents highlighting the chaotic nodes, professional technical aesthetic, pure off-white background, 4k, vector style.

II. Code if-else đi theo bảng, LLM đi theo xác suất #

Karpathy (2017) gọi code do người viết tay là Software 1.0, còn mô hình học từ dữ liệu là Software 2.0.

Software 1.0 chạy như một Finite State Machine (FSM). FSM là cỗ máy có hữu hạn trạng thái, kèm một bảng: đang ở trạng thái nào, gặp sự kiện gì, thì chuyển sang đâu. Máy bán nước tự động là một FSM: đang chờ tiền, nhận đủ 10 nghìn, chuyển sang nhả lon. Mỗi bước chuyển hoặc chắc chắn xảy ra, hoặc không bao giờ. Mọi đường đi nằm sẵn trong bảng, nên có lỗi thì dò theo bảng là ra.

Hệ thống LLM chạy như một Absorbing Markov Chain.1 Token là mẩu chữ mô hình đọc và viết, thường là một từ hoặc một phần của từ. Ở mỗi bước, LLM nhìn toàn bộ đoạn chữ đã có, gán cho từng token trong từ điển của nó một khả năng được chọn, rồi rút thăm theo các khả năng đó. Từ điển có cỡ hàng chục tới hàng trăm nghìn token, và quá trình dừng khi rút trúng token kết thúc, viết là <EOS>.

Markov chain là chuỗi bước đi mà bước kế tiếp chỉ phụ thuộc chỗ đang đứng, như trò đi ô bằng xúc xắc: ô tiếp theo chỉ tùy ô hiện tại và lần gieo. “Absorbing” nghĩa là có ít nhất một ô không có đường ra, như ô đích. Với LLM, ô đích là <EOS>, còn chỗ đang đứng là toàn bộ đoạn chữ đã viết. Bàn cờ vì thế lớn tới mức không ai liệt kê hết các ô; phép so sánh lệch ở chỗ đó. FSM có tới đích hay không thì đọc được từ bảng; với LLM, đó chỉ là một xác suất.

Bỏ qua khác biệt này, bạn sẽ gặp Degenerate Loop:2 mô hình rơi vào một vòng lặp mà khả năng rút trúng <EOS> nhỏ tới mức trần độ dài tới trước. Trần đó là max_tokens, số token tối đa cho một câu trả lời. Ở app giao đồ ăn, chatbot viết “Xin lỗi vì sự bất tiện. Xin lỗi vì sự bất tiện.” cho tới khi chạm trần 4.096 token.

Mỗi lần hỏng kiểu này đốt hết ngân sách token của câu trả lời và giữ một chỗ xử lý trên server trong vài chục giây. Đó có lẽ là lời xin lỗi dài nhất khách từng nhận, và vì nhà cung cấp tính tiền theo token, chữ nào trong đó cũng có giá.

Ảo giác là khi mô hình viết ra điều sai nhưng nghe rất hợp lý. Nó và vòng lặp chung một gốc: quy trình sinh chữ chọn token theo khả năng, không bước nào kiểm tra đúng sai hay bảo đảm dừng.

Edge case là tình huống hiếm mà lúc thiết kế không ai nghĩ tới. Vá bằng if-else nghĩa là mỗi trường hợp thêm một nhánh kiểm tra, cho tới khi code phình to không ai dám sửa. Trong khi với mô hình xác suất, danh sách edge case không bao giờ đóng lại. Ngành phần mềm thật ra đã gặp dạng bài toán này từ trước.

III. Hệ phân tán đã gặp bài toán này từ trước #

Hệ phân tán là hệ thống chạy trên nhiều máy nối mạng, như app giao đồ ăn có máy lo đơn, máy lo thanh toán. Vận hành nó cũng là ra quyết định khi không bao giờ có đủ thông tin chắc chắn.

Định lý CAP phát biểu ràng buộc đó: khi mạng giữa hai nhóm máy bị đứt, hệ thống chỉ giữ được một trong hai tính chất. Hoặc nhất quán, nơi nào cũng đọc ra cùng dữ liệu mới nhất; hoặc sẵn sàng, nơi nào được hỏi cũng trả lời. Không cấu hình nào giữ được cả hai, vì nhóm máy bên kia đang không liên lạc được.

LLM đứng trước một lựa chọn cùng dạng, dù đây là phép so sánh chứ không phải định lý. Thiếu dữ kiện, mô hình hoặc từ chối trả lời, giữ tính đúng nhưng mất sẵn sàng; hoặc viết một câu trôi chảy, giữ sẵn sàng nhưng mất tính đúng. LLM thương mại thường nghiêng về đường thứ hai, và người thiết kế hệ thống phải đổi mặc định đó.

Trong buổi demo, “lúc nào cũng trả lời được” là một tính năng. Ngoài đời, đó là lúc chatbot báo khách “đơn của anh sẽ tới trong 5 phút” cho một đơn chưa có tài xế nào nhận. Hai kỹ thuật của hệ phân tán giúp đổi mặc định này.

RAG làm chỗ neo dữ liệu. RAG, viết tắt của Retrieval-Augmented Generation, là cách tra tài liệu thật trước, rồi đưa đoạn tài liệu đó vào prompt để mô hình trả lời dựa trên nó. Prompt là đoạn chữ gửi vào mô hình. Với chatbot giao đồ ăn, đó là tra trạng thái đơn trong database trước khi trả lời.

Kiến thức mô hình học lúc huấn luyện là một bản chụp từ trước. Dùng RAG giống như đọc dữ liệu từ nguồn gốc thay vì từ một bản sao cũ: câu trả lời bám vào thứ kiểm chứng và truy lại được. RAG giảm chứ không xóa ảo giác, vì mô hình vẫn có thể đọc sai tài liệu.

Biểu quyết kiểu BFT cho hệ nhiều agent. Agent là chương trình dùng LLM để tự làm một chuỗi việc, như tra đơn rồi tính tiền hoàn. Byzantine Fault Tolerance (BFT) là bài toán nhóm máy cần thống nhất quyết định khi vài máy hỏng, và agent bịa số liệu đóng đúng vai máy hỏng đó.

Cách xử lý cũng tương tự: hỏi ba agent độc lập cùng một câu, rồi lấy đáp án được ít nhất hai bên đồng ý. Khi cả ba vênh nhau, hệ thống ghi log để người vào xem lại.3 Cái giá phải trả là chi phí token tăng gấp ba, kèm thời gian chờ lâu hơn nếu gọi lần lượt. Vì vậy, cách này chỉ đáng dùng cho những quyết định quan trọng. Ba lớp hàng rào tiếp theo đặt ngay quanh và bên trong mô hình.

A clear, side-by-side technical illustration of two mechanical input-output systems on a pristine minimalist white table. Left side “Deterministic”: A titanium silver funnel perfectly dropping solid silver cubes into a neat, straight line. Right side “Probabilistic”: A titanium silver funnel dropping solid silver cubes, but they transform into a glowing, swirling cloud of cinnabar red data particles before landing in a scattered, unpredictable pattern. Modern anime vector art style, clean line art, flat colors, cel shading, bright and relaxing atmosphere, purely informative conceptual diagram, pure off-white background.

IV. Lớp 1: chặn sai định dạng ngay lúc sinh chữ #

Giả sử sau mỗi cuộc trò chuyện, chatbot phải gửi cho hệ thống hoàn tiền một đoạn JSON. JSON là định dạng dữ liệu các phần mềm gửi cho nhau qua API; bên nhận chỉ đọc được đúng dạng nó chờ.

Ở mỗi bước, mô hình chấm một điểm thô cho mọi token trong từ điển, gọi là logit. Logit sau đó đổi thành một probability distribution: bảng phần trăm cơ hội của từng token, cộng lại đúng 100%. Không bước nào trong quy trình đó bắt mô hình viết đúng cú pháp JSON.

Constrained Decoding chen vào giữa hai bước đó. Nó hạ logit của token không hợp lệ xuống âm vô cùng, đưa cơ hội của chúng về đúng 0%, còn tỉ lệ của token hợp lệ được chia lại theo mức cũ. Token nào hợp lệ do một FSM quyết định, kèm bảng tính sẵn cho từng trạng thái, nên mỗi bước chỉ chậm thêm rất ít.

Cỗ máy đó dựng từ JSON Schema hoặc regex. JSON Schema mô tả một đoạn JSON hợp lệ phải có trường nào, giá trị nào. Regex là một mẫu chuỗi ký tự, như “đúng 10 chữ số”.

Giả sử schema chỉ cho phép {"status": "ok"} hoặc {"status": "error"}, và mỗi từ là một token. Sau {"status": ", nếu mô hình chấm “fine” 67%, “ok” 24% và “error” 9%, Constrained Decoding đưa “fine” về 0% và chia lại thành “ok” 73%, “error” 27%. Token sai có điểm cao đến đâu, đầu ra vẫn đúng định dạng.

Đây là ràng buộc cứng, không cần mô hình “hiểu” schema, nên tỉ lệ sai cú pháp về 0 và phần lớn code gọi lại mô hình khi đọc JSON lỗi có thể bỏ. Một ngoại lệ cần nhớ là câu trả lời bị cắt ở trần max_tokens trước khi kịp đóng ngoặc.

Tôi quý lớp này vì hiếm có thứ gì khi làm với AI được bảo đảm cứng thay vì “thường thì”. Chỉ có điều, thứ được bảo đảm cứng ở đây là dấu ngoặc: {"status": "ok"} cho một lần hoàn tiền thất bại vẫn là JSON hợp lệ. Nội dung đúng hay sai là việc của lớp thứ hai.

V. Lớp 2: cam kết tỉ lệ lỗi bằng một con số #

Độ tự tin là con số từ 0 đến 100% gắn với một câu trả lời, lấy từ xác suất của các token đã viết, hoặc do mô hình tự báo khi được hỏi. Con số này thường cao hơn nhiều so với tỉ lệ đúng thật.

Một nguyên nhân là RLHF. RLHF là kỹ thuật tinh chỉnh mô hình theo điểm người chấm, và người chấm thường thưởng câu dứt khoát, dễ nghe, hiếm khi thưởng câu “tôi không chắc”. Trọng số là hàng tỉ con số bên trong mô hình, được chỉnh khi huấn luyện; qua rất nhiều lượt chấm, chúng bị đẩy về phía giọng chắc chắn, kể cả khi nội dung sai.

Mức khớp giữa độ tự tin và tỉ lệ đúng gọi là calibration, và thước đo độ lệch là Expected Calibration Error (ECE). Cách tính: gom câu trả lời vào 10 rổ theo độ tự tin, rồi so trong từng rổ. Rổ “tự tin 90%” đúng 90% số lần thì ECE gần 0. Rổ đó chỉ đúng 30% thì con số tự tin đang gây hiểu nhầm, như đoán bừa câu trắc nghiệm rồi tô thật đậm cho chắc.

Giả sử chatbot trả lời bốn câu với độ tự tin 97–99% và chỉ đúng một câu. Tự tin bình quân 98,25%, đúng 25%, lệch 73 điểm phần trăm: ECE bằng 0,7325. Mọi cảnh báo và ngưỡng tự động dựa trên độ tự tin của mô hình đều đứng trên con số lệch đó.

Đào sâu: code tính ECE cho bốn câu trả lời

Code dưới đây tính ECE cho bốn câu ở trên, cộng khoảng lệch của từng rổ theo tỉ trọng số câu. Cả bốn câu rơi vào rổ 90–100%, nên kết quả là $|0{,}9825 - 0{,}25| = 0{,}7325$. “Sequence-level” nghĩa là tính cho cả câu trả lời, không cho từng token.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import numpy as np

def calculate_sequence_ece(confidences: np.ndarray, accuracies: np.ndarray, M: int = 10) -> float:
    """
    Đo độ vênh giữa mức tự tin mô hình khai báo và tỉ lệ nó thực sự trả lời đúng.
    Gom các câu trả lời vào M rổ theo độ tự tin, rồi lấy chênh lệch bình quân
    giữa "tự tin trung bình" và "đúng trung bình" của từng rổ.
    """
    bin_boundaries = np.linspace(0, 1, M + 1)
    ece = 0.0

    for i in range(M):
        mask = (confidences > bin_boundaries[i]) & (confidences <= bin_boundaries[i+1])
        if np.any(mask):
            acc_bin = np.mean(accuracies[mask])
            conf_bin = np.mean(confidences[mask])
            ece += np.abs(conf_bin - acc_bin) * np.mean(mask)

    return ece

# Mô phỏng một mô hình bị ép trả lời trôi chảy nhưng rỗng
sim_conf = np.array([0.99, 0.98, 0.99, 0.97]) # Khai báo: gần như chắc chắn
sim_acc = np.array([1, 0, 0, 0])              # Thực tế: đúng 1 trên 4

print(f"Sequence-Level ECE: {calculate_sequence_ece(sim_conf, sim_acc):.4f}")
# Output: 0.7325

Conformal Risk Control đổi cách đặt vấn đề. Calibration set là một bộ câu hỏi đã có đáp án đúng. Thay vì làm mô hình bớt tự tin, cách này chạy mô hình trên bộ đó để dò ra một ngưỡng độ tự tin. Câu trả lời dưới ngưỡng sẽ không gửi cho khách mà chuyển cho người xử lý.

Bạn đặt trước một trần lỗi, ví dụ 5%. Ngưỡng được thuật toán chọn sao cho cứ 100 câu hỏi mới thì không quá 5 câu bị trả lời sai. Những câu khó chuyển sang cho người xử lý thì không tính là lỗi. Đây là bảo đảm chứng minh được bằng toán học chứ không dựa vào cảm giác, với điều kiện câu hỏi mới cùng kiểu với calibration set.4 Menu đổi hay khách hỏi theo kiểu mới thì phải chạy lại calibration.

Cái giá: mọi câu bị từ chối thành việc của người, và hạ trần lỗi từ 10% xuống 5% thường đẩy tỉ lệ chuyển cho người lên rõ rệt. Đây là núm vặn đổi rủi ro lấy chi phí vận hành, không phải nút “làm cho đúng”. Ở app giao đồ ăn, nó quyết định cần bao nhiêu nhân viên chăm sóc khách hàng trực giờ trưa.

VI. Lớp 3: ngắt luồng từ tín hiệu bên trong mô hình #

Đo rủi ro sau khi câu trả lời viết xong vẫn là đo muộn. Lớp thứ ba dựa trên Mechanistic Interpretability. Hướng nghiên cứu này mở mô hình ra xem bên trong nó tính toán thế nào.

Bên trong LLM là một mạng nơ-ron: nhiều lớp phép tính nối tiếp, dùng các trọng số đã học. Ở mỗi bước, trạng thái bên trong là một dãy hàng nghìn con số. Gần như không con số nào ứng với một khái niệm đọc được, vì mỗi khái niệm trải ra trên rất nhiều con số.

Sparse Autoencoder là một mạng phụ tách dãy số đó thành hàng chục nghìn “công tắc”, với ràng buộc hầu hết công tắc phải tắt ở mỗi thời điểm. Nhờ vậy, phần lớn công tắc mang một nghĩa đủ cụ thể để con người gọi tên, bằng cách xem chúng sáng lên trước những đoạn chữ nào (Bricken và cộng sự, 2023).

Circuit breaker là cơ chế quen thuộc của hệ phân tán: một phần hệ thống lỗi liên tục thì các phần khác tạm ngừng gọi nó, như cầu dao nhảy khi chập điện. Nếu tìm được nhóm công tắc gắn với ảo giác, ta có thể đặt một circuit breaker bên trong mô hình. Hễ nhóm đó sáng lên, hệ thống cắt luồng sinh chữ ngay tại token đó và chuyển cho người, trước khi câu trả lời hỏng tới tay khách.

Kỹ thuật này mới, tốn kém, và hiện chủ yếu chạy được ở nơi có toàn quyền truy cập trọng số mô hình.5 Với đội chỉ gọi mô hình qua API của nhà cung cấp, hai lớp trên mới là thứ triển khai được ngay. Xếp theo độ hấp dẫn khi kể trong buổi họp, lớp này đứng đầu. Xếp theo khả năng chạy thật cho khách trong quý này, nó đứng cuối.

VII. Ba lớp hàng rào, xếp theo thứ tự nên làm #

Bảng dưới gói lại ba lớp hàng rào, xếp từ trên xuống theo thứ tự nên làm. Thứ tự này tình cờ cũng đi từ rẻ tới đắt, một điều hiếm gặp khi làm kiến trúc.

Lớp hàng ràoChặn đượcKhông chặn đượcCái giá phải trả
Constrained DecodingSai cú pháp, sai schemaNội dung sai nhưng đúng định dạngSinh chữ chậm thêm vài phần trăm
Conformal Risk ControlTỉ lệ lỗi vượt trần đã cam kếtTừng lỗi đơn lẻTỉ lệ chuyển cho người tăng; cần calibration set
Internal Circuit BreakerẢo giác, ngay lúc đang viếtLỗi không để lại dấu vết bên trongCần quyền truy cập trọng số; tốn thêm hạ tầng

Câu nên hỏi đội kỹ thuật: tính năng AI của mình đang có lớp nào trong ba lớp này, trần lỗi là bao nhiêu, và calibration chạy lại lần cuối khi nào?

A clear technical illustration of a futuristic containment architecture. A highly structured, geometric cage made of thick, perfectly straight titanium silver bars. Securely contained inside the transparent cage is a vibrant, shifting, glowing sphere of interconnected cinnabar red data nodes (representing unpredictable AI weights). The silver cage is stable, orderly, and unyielding, effectively controlling the chaotic red energy inside. Modern anime background art style, highly detailed vector aesthetic, clean line art, flat colors, cel shading, bright, informative and easy to understand, pure off-white background.

VIII. Tài liệu tham khảo #


  1. Absorbing Markov Chain: chuỗi có ít nhất một trạng thái vào rồi không ra được; với mô hình sinh văn bản, đó là <EOS>. Shannon (1948) đã dùng Markov chain để sinh những đoạn văn giống tiếng Anh. ↩︎

  2. Degenerate Loop: về lý thuyết, nếu từ mọi trạng thái đều có đường tới <EOS>, chuỗi gần như chắc chắn sẽ dừng. Nhưng thời gian chờ có thể dài hơn trần max_tokens rất nhiều, nhất là khi khả năng tới <EOS> đổi theo chính đoạn chữ đang lặp. ↩︎

  3. BFT đầy đủ, khi các thành viên tự thống nhất với nhau, cần ít nhất 3f + 1 thành viên để chịu f thành viên trả lời bậy (Lamport, Shostak & Pease, 1982); ba agent cộng một bộ đếm phiếu đáng tin chỉ là biểu quyết đa số. Biểu quyết cũng chỉ giúp khi các agent sai độc lập, mà ba agent cùng mô hình, cùng prompt hay sai giống nhau. ↩︎

  4. Angelopoulos và cộng sự (2024). Điều kiện chính xác là exchangeability: đổi chỗ câu hỏi mới với câu hỏi trong calibration set thì phân phối không đổi. Bảo đảm là về giá trị trung bình của tỉ lệ lỗi, và calibration set càng nhỏ thì ngưỡng càng phải chặt. ↩︎

  5. Khi áp dụng cho các mô hình lớn, Sparse Autoencoder còn là hướng nghiên cứu mở: huấn luyện không hề rẻ, công tắc đọc được mới phủ một phần hành vi của mô hình, và cách làm đòi hỏi đọc các con số trung gian bên trong mạng, thứ API thương mại không mở ra. ↩︎