0

Một AI Đưa Tôi 6 Mẫu Hoá Đơn "Thật". 5 Cái Hoàn Toàn Là Bịa.

Tôi đang xây 1 công cụ đọc file XML hoá đơn điện tử VAT — loại file mà pháp luật Việt Nam bắt buộc phải có cho mọi hoá đơn điện tử hợp lệ, bất kể ai phát hành. Mục tiêu: đọc được file từ bất kỳ nhà cung cấp nào, chuẩn hoá về đúng 1 định dạng JSON.

Để test cho chắc trước khi dùng thật, tôi nhờ 1 AI khác gom giúp vài file XML mẫu — mỗi file gắn nhãn 1 nhà cung cấp hoá đơn điện tử lớn ở Việt Nam, cộng thêm 1 file "chuẩn chung" — tổng cộng 6 file.

Tôi chạy thử code với cả 6 file. Nó từ chối thẳng 5 cái.

Phản ứng đầu tiên: chết rồi, code hỏng ngay từ ngày đầu, 5 nhà cung cấp khác nhau, 5 kiểu lỗi khác nhau. Vài phút sau, tôi nhận ra đây thực ra là kết quả đúng — và tìm hiểu vì sao lại dẫn tôi tới 1 chỗ tôi không ngờ tới.

Cú lật kèo

Trước khi vội kết luận code sai, tôi làm 1 việc rất "chán": đọc README mà chính AI kia viết kèm bộ file mẫu. Có 1 dòng quan trọng:

"dữ liệu bên trong (tên công ty, mã số thuế, số tiền) là dữ liệu giả lập cho kiểm thử, không phải hoá đơn thực đã phát hành... lấy từ 1 kho mã nguồn mở."

Giả lập. Không phải "gần giống thật". Giả lập bởi 1 lập trình viên chưa từng thấy hoá đơn Việt Nam thật, viết ra để test cho 1 dự án hoàn toàn khác của họ trên GitHub — rồi được 1 AI gom lại thành "mẫu hoá đơn" vì trông đúng hình dạng cần tìm.

Thế là thay vì tin vào chính bộ test của mình, tôi quay lại đối chiếu tên thẻ XML với văn bản pháp luật gốc.

Điều đáng nói về hoá đơn điện tử Việt Nam: từ 2022, mọi nhà cung cấp đều bắt buộc dùng đúng 1 cấu trúc thẻ XML do Tổng cục Thuế quy định. Không phải "tương tự nhau" — mà là giống hệt. TTChung, NBan, NMua, MST, DChi — toàn bộ là chữ viết tắt tiếng Việt, quy định bằng nghị định, vì chính hệ thống của cơ quan thuế phải đọc được hoá đơn từ bất kỳ ai phát hành.

5 trong 6 file "mẫu" của tôi dùng tên thẻ kiểu <SellerInfo>, <BuyerTaxCode>, <InvoiceNumber> — tên tiếng Anh rất gọn gàng, rất hợp lý nếu nhìn thoáng qua.

Nhưng theo tôi tìm hiểu, không có hoá đơn điện tử VN thật nào từng dùng những tên thẻ đó. Không ai ở Việt Nam được phép tự đặt tên thẻ riêng — đó chính là mục đích của việc ra quy định thống nhất. Code của tôi không hề hỏng. Nó đã nhận diện đúng 5 file là hư cấu (dù rất "tự tin" và nhất quán nội bộ), và chỉ chấp nhận đúng 1 file duy nhất dùng cấu trúc thật theo luật.

Tôi đã không làm gì

Tôi có thể "dạy khôn" cho code để nó cũng nhận ra 5 tên thẻ bịa kia, cho ra kết quả đẹp hơn — 6/6 thay vì 1/6. Tôi không làm vậy. Dạy cho code chấp nhận 1 định dạng không tồn tại trong thực tế không phải là "linh hoạt hơn" — đó chỉ là 1 cách phức tạp hơn để sai, kèm thêm sự tự tin không đáng có. Và khi hoá đơn thật đầu tiên tới, nó sẽ phải "chen chân" qua đống nhiễu đó thay vì khớp thẳng.

Nên tôi giữ nguyên code theo đúng chuẩn pháp luật, chỉ giữ lại đúng 1 file mẫu khớp thật, rồi chờ dữ liệu thật.

Rồi hoá đơn thật bắt đầu tới

Vài ngày sau, người tôi xây công cụ này cho bắt đầu tự đào lại email và cổng tra cứu của họ — 1 hoá đơn tiền điện, vài hoá đơn mua hàng online, 1 hoá đơn phòng khám, 1 hoá đơn logistics cũ từ 2021, và cuối cùng là 1 hoá đơn giao dịch giữa 2 doanh nghiệp thật.

3 lỗi lộ ra. Không lỗi nào trong số đó xuất hiện ở bất kỳ file "mẫu" nào của AI kia — vì chỉ có chạm vào dữ liệu thật mới tìm ra được.

Lỗi 1 — rò rỉ thông tin cá nhân. Code có thêm 1 chế độ "dữ liệu thô": ngoài kết quả đã chuẩn hoá sạch sẽ, nó còn xuất kèm toàn bộ cặp thẻ/giá trị đọc được, để không ai mất dữ liệu vì tôi lỡ quên map 1 trường nào đó. Tôi cũng có 1 tính năng bảo vệ riêng tư: nếu người mua không có mã số thuế (dấu hiệu là cá nhân, không phải doanh nghiệp), tên và địa chỉ của họ sẽ bị ẩn khỏi kết quả. Ẩn khỏi kết quả đã chuẩn hoá — đúng vậy. Không ai báo cho phần "dữ liệu thô" biết về quy tắc đó. Hoá đơn thật đầu tiên tôi test — 1 hoá đơn tiền điện — để lộ thẳng tên thật, địa chỉ nhà, và email của người mua qua đúng cái trường "debug" tôi thêm vào cho tiện. 2 tính năng (bảo vệ riêng tư và xuất dữ liệu thô) chưa bao giờ được "nói chuyện" với nhau.

Lỗi 2 — mất tên người mua trong im lặng. Chuẩn thật có 2 tên thẻ khác nhau cho "ai là người mua": 1 thẻ dùng khi là tổ chức/công ty, 1 thẻ khác khi là cá nhân. Tôi chỉ biết tới thẻ dành cho tổ chức — không file mẫu giả nào từng dùng thẻ còn lại, vì người viết ra chúng cũng không biết nó tồn tại. Hoá đơn thật từ người mua cá nhân bắt đầu tới, dùng đúng cái thẻ thứ 2 đó. Code đọc, không tìm thấy gì dưới thẻ nó đang tìm, và vui vẻ báo cáo "xử lý xong, không có vấn đề gì". Nó không nói dối — nó chỉ không biết những gì nó không biết.

Lỗi 3 — lỗi khiến tôi lo nhất. 1 hoá đơn từ 2021 tới, dùng phiên bản chuẩn cũ hơn mọi thứ tôi từng test. Cấu trúc tổng thể giống nhau, nhưng số hoá đơn và ngày lập — 2 trường quan trọng nhất để định danh/chống trùng 1 hoá đơn — nằm dưới tên thẻ hoàn toàn khác. Code lại báo "thành công hoàn toàn" trong khi âm thầm trả về rỗng cho cả 2 trường.

Đây là lỗi khiến tôi phải đổi cách toàn bộ hệ thống báo lỗi. Không thể chỉ kiểm tra "có tìm thấy đúng khối cấu trúc lớn hay không" — tôi phải kiểm tra thêm "có thực sự lấy được giá trị cho những trường quan trọng hay không", và báo rõ ràng khi không có — thay vì 1 dấu tích xanh che giấu 1 giá trị rỗng.

Điều tôi cứ nghĩ mãi

Bộ dữ liệu giả kia không hề có ác ý, và chính AI tạo ra nó cũng đã nói thẳng đó là dữ liệu giả lập — chỉ là tôi hoàn toàn có thể đã không kiểm tra kỹ. Nó cũng vô dụng như 1 "đối thủ" để rèn luyện code. 1 file test hỏng thật sự sẽ báo lỗi rõ ràng, ầm ĩ. Còn 1 file "trông có vẻ đúng nhưng là hư cấu" thì lặng lẽ pass qua, và không dạy cho bạn được gì — vì nó được tạo ra để tối ưu cho việc "trông giống hoá đơn Việt Nam", chứ không phải "là 1 hoá đơn Việt Nam".

Dữ liệu thật thì không có vấn đề đó. Nó không quan tâm có "hợp lý" hay không. Nó cứ là chính nó — kể cả 2 tên thẻ tôi chưa từng nghe, và 1 phiên bản chuẩn cũ tôi không ngờ vẫn còn tồn tại ngoài đời.

Tôi không nghĩ bài học ở đây là "đừng dùng dữ liệu test do AI tạo ra". Tôi vẫn dùng AI cho rất nhiều phần của dự án này, kể cả việc nhờ gom bộ mẫu đầu tiên đó. Bài học hẹp hơn: khi bạn đang kiểm chứng với 1 chuẩn bên ngoài, do luật pháp quy định, không nằm trong tầm kiểm soát của bạn — thì "tạo ra 1 file vượt qua được test của chính mình" và "khớp với thực tế" là 2 tuyên bố khác nhau hoàn toàn. Và chỉ 1 trong 2 cái đó kiểm chứng được — bằng cách quay lại đúng nguồn gốc pháp lý, thay vì tin vào file mẫu.

Vài câu hỏi tôi tự đặt ra nếu là người đọc

Vậy dữ liệu test do AI tạo có "tệ" không? Không hẳn — nó được ghi chú thẳng là dữ liệu giả lập, và vẫn hữu ích như 1 hình mẫu ban đầu. Nếu có sai thì là do tôi dừng kiểm tra quá sớm, không phải lỗi của công cụ.

Giờ khi gặp 1 tên thẻ chưa từng thấy, code làm gì? Nó không đoán mò. Nó báo đúng những gì đọc được, đánh dấu rõ trường nào đang rỗng dù đáng lẽ phải có, và kèm theo dữ liệu thô để người dùng tự đối chiếu — thay vì trả về 1 giá trị null âm thầm.

Sao không "dạy" code nhận luôn mọi biến thể tên thẻ gặp được? Với tên thẻ bịa, hoàn toàn không nghịch lý — chấp nhận đồng nghĩa với việc tin hư cấu như luật thật. Với biến thể thật thuộc 1 phiên bản chuẩn cũ nhưng vẫn hợp pháp thì có — và đó chính xác là chuyện đã xảy ra ở lỗi số 3.

Đây có phải vấn đề riêng của Việt Nam không? Tên thẻ cụ thể thì đúng là riêng. Nhưng cái khuôn mẫu — dữ liệu test do AI tạo ra, đủ "hợp lý" để vượt qua bài test của chính mình mà không thực sự khớp với chuẩn bên ngoài bạn đang nhắm tới — thì không riêng gì Việt Nam.


Công cụ đã lên Apify Store dưới tên Vietnam E-Invoice XML Normalizer — gọi trực tiếp được qua MCP nếu bạn đang xây AI Agent làm sổ sách cần đọc hoá đơn điện tử VN.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí