Nỗi khổ của mô hình thông minh
Cứ đuổi theo một ý tưởng dev là lại cắm đầu vào implement, chẳng có thời gian mà chia sẻ 😅.
Dạo này nỗi trăn trở (mà cũng vui đấy?) liên quan đến AI của tôi chính là cái dilemma với mấy con model thông minh này.
Cleaning Roster, và ba ngày với Opus
Tôi xây một hệ thống quản lý dựa trên cleaning roster, điều kiện phức tạp đến mức task thực sự nằm ở chỗ làm sao để implement đơn giản nhất có thể.
Thế là tôi mang con model tốt nhất mà mình có quyền dùng, Opus 5, ra giải thích luồng suy nghĩ của mình, và nó giải thích từng vấn đề cần giải quyết một cách trôi chảy, tự tin đến mức chẳng có lý do gì để không tin.
Vậy là tôi mất trọn ba ngày chỉ để hiểu bài giảng học thuật này 😓. Nói chính xác hơn, trong ba ngày đó tôi vừa làm vừa va vấp trực tiếp, hiểu được kiến trúc nó đề xuất và hoàn thành việc implement.
Nhưng hiểu xong rồi thì tôi lại ghét cái overhead phải accommodate quá nhiều kiến trúc vào hệ thống của mình, chỉ để làm một tính năng gọi request đến AI API.
Thế là tôi lại dành thêm ba ngày nữa để strip hết những gì không cần thiết. Kết quả là tốn một đống token để sinh code, rồi lại tốn một đống token để xóa nó đi (và quan trọng nhất là thời gian — quý hơn cả vàng!!).
Dù vậy, model tôi chọn dùng vẫn cứ là Opus. Có budget rồi thì việc gì phải dùng Sonnet?
Nhưng mà…
Suốt một tuần, tôi thất bại trong việc thuần hóa con ngựa hoang Opus này. Mỗi câu lệnh, tôi đều van nài nó giải thích đơn giản một chút.
Không, đôi khi tôi còn nói thẳng là đừng có ra vẻ thông minh mà giải thích kiểu biết tuốt như vậy. Nhưng dù sao đi nữa, nó vẫn phức tạp. Dù sao đi nữa, nó vẫn đào sâu vào vấn đề.
Đây có thực sự là thứ tôi muốn không?
Trên đời tồn tại những vấn đề phức tạp, đúng vậy. Nhưng việc có cần kiến trúc phức tạp hay không lại là chuyện khác.
Tôi là người đơn giản. Nếu viết code phức tạp, sau này tôi vẫn có thể quay lại sửa cho đơn giản hơn — nhưng kiến trúc thì khác. Một khi đã nhồi cấu trúc phức tạp vào từ tầng kiến trúc, mọi thứ implement dựa trên đó về sau đều trở nên phức tạp theo.
Nói cụ thể hơn, đây là tính năng gửi API call đến AI từ backend .NET, rồi để user xác nhận kết quả, nhưng lại phải accommodate đủ thứ cơ chế kỹ thuật vào đó.
Server fail thì recover thế nào, state lưu trong DB ra sao, hoàn tất thì tự động trigger event thế nào.. vân vân. Nói ngắn gọn là đang cố xây một tính năng mà chỉ cần bấm một cái là mọi thứ tự lo hết, kiểu quá đà.
(Nói cách khác, cố làm mọi thứ tự động đến mức cuối cùng biến thành một hệ thống mà chính user cũng không biết chuyện gì đang xảy ra.)
Nhưng tôi thuộc phe worse is better. Tôi thích những implementation mà phần bên trong lộ ra rõ ràng, minh bạch, vì cần truyền tín hiệu rõ ràng cho user biết chuyện gì đang xảy ra ngay lúc đó.
Thế nên tôi quyết định xử lý phần async bằng hook ở frontend. (Dù refresh trang là mất, nhưng vậy vẫn ổn.) Cách này giúp backend giữ nguyên sự đơn giản với REST API đồng bộ như cũ, và quan trọng hơn là tôi có thể giải thích rõ ràng cho user biết nó hoạt động thế nào.
Và tôi cũng định hướng là sẽ implement kiểu xử lý bất đồng bộ này ở frontend càng nhiều càng tốt, để làm nhanh và thử nghiệm nhanh.
Bên trái là đề xuất của Opus — dàn dựng backend tự xử lý mọi thứ mà người dùng không hề hay biết. Bên phải là cái tôi thực sự chọn — xử lý đồng bộ minh bạch dựa trên frontend hook — worse is better.
Vậy nên, bất cứ khi nào review code, tôi cũng muốn mình có thể nhanh chóng chẩn đoán chỗ bị lỗi và nắm bắt được ngay khi cần.
Nói ngắn gọn là dùng Sonnet để thiết kế kiến trúc. Còn debug thì vẫn dùng Opus (con ngựa hoang này khi đào sâu vào edge case thì có khả năng suy luận điên rồ. Đáng sợ thật 😬).
Nói ngắn gọn, ít nhất là trong việc thiết kế hệ thống, tôi bắt đầu cố tình chọn một model hơi “ngu” hơn một chút. Nhân tiện, nếu bạn thấy lý thuyết này thú vị, tôi rất khuyên xem video dưới đây, giải thích cách trực tiếp loại bỏ những tính năng “thông minh” đã giúp xây dựng hệ thống máy bay phản lực không lỗi như thế nào :)
Vậy rốt cuộc đã làm được gì?
Xin giới thiệu vài tính năng tôi đã làm trong thời gian qua :)
1. Tính năng Cleaning Roster
Chia làm hai lớp: ai trực lịch dọn dẹp tuần đó, và ai thực sự đã dọn dẹp, được ghi nhận theo từng tuần, từng nhà. Kể cả khi các tenant tự đổi lịch trực cho nhau, bảng vẫn tự nhiên phản ánh đúng :)
Đây là màn hình mà phía tenant nhìn thấy. Lịch trực tuần đó, hướng dẫn dọn dẹp, và upload ảnh đều nằm gọn trên một trang.
2. Tính năng đọc Cleaning Roster tự động
Hiện tại cleaning roster đang ở dạng hình ảnh. Bản gốc là một bảng trông như thế này.


Chỉ cần bấm nút này, nó sẽ tự động đọc và điền vào các ô. Đây là chiến lược parse hình ảnh -> lưu dữ liệu có cấu trúc, đúng kiểu việc mà LLM làm rất tốt.

3. Ai chưa dọn dẹp?
Từ phía admin, những ai chưa dọn dẹp sẽ hiện hết ra theo từng tuần, gộp lại một chỗ luôn :) Còn xuất được cả danh sách email nữa.
4. Ghi chú Inline (Control + .)
Và tôi cũng đã xây tính năng ghi chú nội bộ, để admin chỉ cần bấm Control + . là có thể xem trực tiếp các comment gắn với dữ liệu đang có trong khu vực làm việc lúc đó. Đây là một cách implement rất flexible, có thể kéo nhanh các ghi chú liên quan thông qua node (là một developer, tôi thấy khá thú vị khi xử lý việc lưu trữ các liên kết động này), nhưng đứng từ góc nhìn của user thì việc phải tự quản lý ghi chú có vẻ lại là một overhead. Nên tạm thời tôi để việc phát triển thêm tính năng này sang một bên.
Sau này có lẽ tôi sẽ thử để AI đóng vai trò assistant trong khu vực này, nhưng tạm thời bỏ qua đã. Có lẽ vì đây không phải loại tính năng tự chạy một cách kỳ diệu.
Và tôi sẽ tiếp tục đăng dần các tính năng nhỏ khác khi làm xong :)