Định lý CAP cho FDE: vì sao trợ lý AI vẫn nói đơn hàng chưa hủy
Khi trợ lý AI trả lời bằng dữ liệu cũ hơn hệ thống của khách, lỗi thường không nằm ở model mà ở mức nhất quán chưa ai chọn một cách có chủ đích.
Trợ lý AI thấy dữ liệu cũ là cái giá của việc ưu tiên khả dụng; ưu tiên nhất quán thì phải chấp nhận lỗi hoặc chờ.
Đồ hoạ: FDE Times
Tóm tắt nhanh
- Trợ lý AI thấy dữ liệu chậm hơn hệ thống gốc thường là hệ quả của việc chọn khả dụng thay vì nhất quán, không phải lỗi model.
- Partition tolerance là bắt buộc; lựa chọn thật chỉ là C hay A khi mạng có sự cố.
- Mức nhất quán mạnh tốn kém hơn: DynamoDB tính phí đọc strongly consistent gấp đôi và GSI không hỗ trợ kiểu đọc này.
Trưởng nhóm vận hành của khách gửi bạn một ảnh chụp màn hình. Cô vừa hủy một đơn hàng trong hệ thống quản lý đơn, quay sang hỏi trợ lý AI mà đội bạn vừa deploy, và nhận câu trả lời rằng đơn đó “đang được giao”. Cô hỏi đơn giản: “Con bot này có đọc dữ liệu thật không?”
Phản xạ đầu tiên của nhiều engineer là nghi model bịa. Nhưng trước khi sửa prompt, hãy kiểm tra một khả năng khác: model có thể đã đọc đúng những gì nó được đưa, và thứ nó được đưa là một bản sao chưa kịp cập nhật.
Đó là lý do một FDE cần hiểu định lý CAP và các mức nhất quán, không phải để thi phỏng vấn mà để trả lời câu hỏi của trưởng nhóm vận hành kia một cách trung thực, rồi sửa đúng chỗ.
CAP thực ra hỏi bạn điều gì?
CAP có ba chữ, nhưng cách hiểu phổ biến “chọn hai trong ba” dễ gây lạc hướng. CAP FAQ của Henry Robinson định nghĩa Consistency là nhất quán tuyến tính (linearizable): hệ thống hành xử như thể chỉ chạy trên một máy. Robert Greiner diễn đạt dễ nhớ hơn: một lần đọc luôn trả về lần ghi gần nhất.
Availability theo CAP cũng chặt hơn nghĩa thường ngày. Mọi yêu cầu gửi tới một node không bị lỗi đều phải nhận được phản hồi, có thể chậm bao lâu cũng được, nhưng phản hồi lỗi thì không tính.
Còn P, partition tolerance, thì không phải thứ để chọn. Greiner viết thẳng rằng mạng không đáng tin, nên hệ phân tán buộc phải chịu được phân vùng. CAP FAQ còn nói rõ hơn: nếu bạn mô tả database phân tán của mình là “CA”, bạn đang hiểu sai điều gì đó.
Vậy câu hỏi thật chỉ còn một: khi mạng giữa các node bị cắt, hệ thống nên từ chối trả lời để giữ đúng (CP), hay trả lời bằng dữ liệu đang có dù có thể cũ (AP)?
Khi mạng ổn, chuyện vẫn chưa xong
Một điểm hay bị bỏ qua: CAP FAQ nhấn mạnh rằng định lý này không nói gì về việc tình huống phân vùng xảy ra thường hay hiếm. Nó chỉ mô tả lựa chọn bắt buộc ở thời điểm xấu nhất, không cho bạn biết trade-off ấy hiện ra bao nhiêu lần mỗi ngày.
Vì thế cần tách bạch hai chuyện. CP hay AP là cách hệ thống hành xử lúc mạng bị chia cắt; còn đọc strongly hay eventually consistent, như cờ ConsistentRead của DynamoDB ở phần dưới, là thiết lập cho từng lần đọc, áp dụng cả khi mạng hoàn toàn bình thường.
Trong thực tế, thứ khách cảm nhận hằng ngày là mức nhất quán thứ hai này. Strong consistency đảm bảo đọc thấy ngay thay đổi, đổi lại khả dụng thấp hơn và độ trễ cao, hợp với nghiệp vụ như chuyển tiền. Weak consistency không hứa lần đọc sau thấy ngay thay đổi, bù lại khả dụng cao và độ trễ thấp.
Eventual consistency là một dạng weak consistency: cập nhật rồi sẽ hiện ra, nhưng trong lúc chờ có thể tồn tại nhiều phiên bản dữ liệu mâu thuẫn nhau. Greiner gọi đúng tên cơ chế: chọn AP nghĩa là trả dữ liệu cục bộ có thể đã cũ. Đó chính là con đường khiến trợ lý AI thấy thế giới chậm hơn hệ thống gốc.
Ví dụ: lần theo một đơn hàng bị hủy
Thử hình dung kiến trúc sau. Hệ thống đơn hàng của khách ghi vào một bảng DynamoDB global table ở Region A. Trợ lý AI chạy ở Region B, gần người dùng, và đọc từ replica ở Region B qua một tool get_order_status.
Tài liệu AWS cho biết với global tables ở chế độ mặc định, thay đổi thường được sao sang các replica khác trong vòng một giây và chỉ eventually consistent giữa các Region.
Giả sử nhân viên hủy đơn lúc 10:00:00.000, rồi gõ câu hỏi cho trợ lý lúc 10:00:00.400. Nếu bản sao chưa tới, tool đọc ra “đang giao”, model trả lời trung thực theo dữ liệu sai.
Ngay trong một Region cũng có cạm bẫy tương tự. DynamoDB mặc định đọc eventually consistent, nên kết quả có thể chưa phản ánh lần ghi vừa hoàn tất; đọc lại sau ít lâu mới thấy bản mới. Muốn đọc mạnh, bạn phải bật nó:
import boto3
table = boto3.resource("dynamodb").Table("orders")
def get_order_status(order_id: str, fresh: bool = False) -> dict:
resp = table.get_item(
Key={"order_id": order_id},
ConsistentRead=fresh, # True = strongly consistent, mặc định là False
)
return resp.get("Item", {})
Cái giá được ghi rõ: đọc eventually consistent chỉ tốn một nửa chi phí so với đọc strongly consistent, và Global Secondary Index không hỗ trợ đọc strongly consistent. Nếu tool của bạn tra đơn qua GSI theo số điện thoại khách, cờ fresh=True ở trên không cứu được bạn; phải tra lại theo khóa chính.
Còn với bài toán giữa các Region, AWS có chế độ MRSC đồng bộ lần ghi sang Region khác trước khi trả về. Khi đó, chi phí được chuyển sang phía ghi: hệ thống của khách chờ lâu hơn để trợ lý đọc đúng hơn.
Năm bước để tự làm ở site khách
Bước đầu tiên là vẽ đường đi của dữ liệu, từ nơi được ghi tới nơi trợ lý đọc. Mỗi chặng (replica, cache, index tìm kiếm, pipeline đồng bộ ban đêm) là một chỗ dữ liệu có thể chậm, và bạn cần ghi độ trễ quan sát được cho từng chặng.
Bước thứ hai là phân loại câu hỏi mà trợ lý sẽ nhận, theo câu hỏi nghiệp vụ chứ không theo bảng dữ liệu:
| Loại câu hỏi | Ví dụ | Mức nhất quán nên dùng |
|---|---|---|
| Hệ quả tiền hoặc pháp lý | “Đơn này đã hoàn tiền chưa?” | Strong, đọc từ bản gốc |
| Trạng thái vận hành vừa đổi | “Đơn tôi vừa hủy đã hủy chưa?” | Strong, tra theo khóa chính |
| Tra cứu tham khảo | “Chính sách đổi trả là gì?” | Eventual là đủ |
| Báo cáo tổng hợp | “Tuần này có bao nhiêu đơn hủy?” | Eventual, ghi rõ mốc thời gian |
Bước thứ ba là hỏi khách, không tự quyết. Theo Greiner, chọn CP hay AP phụ thuộc vào yêu cầu nghiệp vụ: đọc ghi nguyên tử thì chọn nhất quán, nghiệp vụ chịu được độ trễ đồng bộ thì chọn khả dụng. Câu hỏi hiệu quả là “nếu trợ lý trả lời bằng dữ liệu cũ vài giây, điều tệ nhất có thể xảy ra là gì?”
Bước thứ tư là cho tool trả về cả dấu thời gian của dữ liệu, rồi hướng dẫn model nói ra điều đó với những câu hỏi nhạy cảm, kiểu “theo dữ liệu cập nhật lúc 10:00:00”.
Bước thứ năm là chấp nhận rằng hệ CP có thể trả lỗi hoặc timeout khi chờ node bị phân vùng, và viết sẵn câu trả lời lịch sự cho tình huống đó thay vì để agent đoán.
Những sai lầm hay gặp
Sai lầm phổ biến nhất là đổ lỗi cho model và đi sửa prompt. Prompt nào cũng không làm một replica cũ thành mới; hãy kiểm tra dữ liệu tool trả về trước khi chạm vào prompt.
Sai lầm thứ hai là bật strong consistency cho mọi thứ “cho chắc”. Bạn trả gấp đôi chi phí đọc, tăng độ trễ, và với GSI thì thậm chí không bật được. Phần lớn câu hỏi tham khảo không cần mức đó.
Sai lầm thứ ba là chỉ test trên môi trường dev một Region, nơi độ trễ sao chép gần như vô hình. Bug kiểu “đơn vừa hủy vẫn đang giao” chỉ hiện ra khi người dùng thật ghi rồi hỏi ngay, đúng thứ demo nội bộ hiếm khi làm.
Sai lầm cuối cùng là quên rằng eventual consistency có thể sinh xung đột giữa các phiên bản. Nếu trợ lý cũng được phép ghi, chẳng hạn đổi địa chỉ giao hàng, bạn phải biết hệ thống giải quyết hai lần ghi chồng nhau thế nào trước khi cho agent quyền đó.
Một bài tập nhỏ để khép lại
Chọn một nghiệp vụ bạn quen, chẳng hạn bot hỗ trợ khách của một ví điện tử. Viết ra năm câu hỏi người dùng hay hỏi, rồi với mỗi câu ghi ba thứ: dữ liệu nằm ở đâu, trợ lý nên đọc strong hay eventual, và nếu đọc phải bản cũ vài giây thì hậu quả là gì.
Sau đó chọn đúng một câu bạn xếp vào nhóm strong và tự hỏi: nếu tra cứu đó phải đi qua một index phụ, bạn sẽ đổi thiết kế tool thế nào? Làm xong, bạn có sẵn một bảng giống bước thứ hai ở trên để mang tới buổi discovery đầu tiên.
Đưa kỹ năng này vào CV
Khi đọc job description FDE, nếu gặp những cụm như “integrate with customer systems of record” hay “real-time data”, bạn nên chuẩn bị sẵn một câu chuyện về cách mình xử lý dữ liệu chậm. Đó cũng là lúc nên hỏi ngược nhà tuyển dụng: agent của họ đọc dữ liệu từ bản gốc hay từ bản sao?
Trong CV, một dòng như “phân loại 4 nhóm truy vấn của agent theo yêu cầu độ tươi, chuyển nhóm liên quan hoàn tiền sang đọc strongly consistent” nói nhiều hơn mọi dòng “thành thạo hệ phân tán”.
Lần tới có người hỏi “con bot có đọc dữ liệu thật không?”, câu trả lời tốt nhất không phải là “có” hay “không”, mà là “nó đọc dữ liệu của lúc nào, và anh chị đã đồng ý với độ trễ đó chưa”.