- Để tận dụng LLM hiệu quả trong các codebase quy mô lớn, yếu tố cốt lõi là đầu tư vào ‘hướng dẫn (guidance)’ và ‘giám sát (oversight)’
- Hướng dẫn cung cấp ngữ cảnh và môi trường để giúp LLM đưa ra lựa chọn tốt hơn, còn giám sát có vai trò kiểm chứng kết quả và định hướng
- Việc xây dựng thư viện prompt để LLM hiểu được các quy tắc, tài liệu và best practice của codebase là rất quan trọng
- Quản lý technical debt cùng với sự đơn giản, tính mô-đun và tính nhất quán của cấu trúc mã có liên hệ trực tiếp với việc cải thiện khả năng hiểu mã và năng suất của LLM
- Hệ thống giám sát và kiểm chứng tự động giúp hỗ trợ LLM tạo ra mã an toàn và nhất quán là chìa khóa cho khả năng mở rộng dài hạn
Các khái niệm cốt lõi để mở rộng LLM
- Dù cách áp dụng LLM vào codebase quy mô lớn vẫn chưa được chuẩn hóa, đầu tư vào hướng dẫn và giám sát được xem là cách tiếp cận hiệu quả nhất
- Hướng dẫn (Guidance) là ngữ cảnh và môi trường giúp LLM đưa ra lựa chọn đúng, còn giám sát (Oversight) đảm nhiệm việc kiểm chứng kết quả được tạo ra và điều chỉnh phương hướng
Đầu tư vào hướng dẫn
- Để LLM đạt được khả năng ‘one-shotting’ — tạo ra mã chất lượng cao chỉ trong một lần thử — cần có hướng dẫn rõ ràng
- Ngược lại, khi kết quả không phù hợp và cần chỉnh sửa thủ công thì đó là rework, vốn kém hiệu quả
- Vì LLM tạo ra mọi lựa chọn trong mã nguồn như tên biến, cấu trúc hàm, tech stack..., nên lý tưởng nhất là prompt chỉ chứa yêu cầu nghiệp vụ, còn phần còn lại có thể được suy luận hoặc đã được mã hóa sẵn
Xây dựng thư viện prompt
- Thư viện prompt là tập hợp ngữ cảnh dành cho LLM, bao gồm tài liệu, best practice và bản đồ cấu trúc của codebase
- Mỗi khi đầu ra của LLM bị lệch hướng, hãy xem lại “cần làm rõ điều gì” và bổ sung vào thư viện
- Cân bằng giữa tính bao quát và tính ngắn gọn là điều quan trọng
- Trong ví dụ, các tài liệu như
@prompts/How_To_Write_Views.md, @prompts/The_API_File.md được cung cấp cho LLM để hướng dẫn phát triển tính năng
- Prompt cần đủ cụ thể, nhưng vẫn phải rà soát mọi dòng của đoạn mã được tạo ra
Môi trường và chất lượng mã
- Codebase có nhiều technical debt sẽ làm giảm hiệu quả sử dụng LLM
- Trường hợp của Meta được nhắc đến như một ví dụ cho thấy technical debt khiến mục tiêu tự động hóa khó đạt được
- Mã sạch, tính mô-đun, cách đặt tên rõ ràng và cấu trúc đơn giản giúp nâng cao mức độ hiểu và độ chính xác của LLM
- Trong ví dụ về Django, mỗi app đặt điểm vào tại file
_api.py để LLM có thể nhanh chóng tìm được chức năng cần thiết thông qua cấu trúc này
- Ví dụ:
visit_api.handoff_to_doctor(user) để thống nhất một đầu mối truy cập từ bên ngoài
- Mẫu
_api được ghi rõ trong thư viện prompt để dẫn LLM tham chiếu đúng vị trí
Đầu tư vào giám sát
- Tự động hóa bằng LLM nên được tiếp cận theo hướng tăng cường sức mạnh cho đội ngũ, thay vì thay thế kỹ sư
- Giám sát dẫn tới đầu tư vào đội ngũ, alignment và workflow
- Ở cấp độ đội ngũ, việc nâng cao năng lực thiết kế là rất quan trọng, và điều này gắn trực tiếp với chất lượng kiến trúc
- Các cách để cải thiện năng lực thiết kế bao gồm đọc sách, blog, mã nguồn; sao chép các tác phẩm mẫu; và luyện tập tự hiện thực hóa
- Ví dụ: mở rộng cảm quan thiết kế bằng cách phân tích mã của TLDraw, SerenityOS Jakt
Giám sát tự động
- Một phần việc kiểm chứng thiết kế có thể được tự động hóa bằng chương trình
- Ví dụ: phản hồi ngay trong môi trường khi có lỗi kiểu hoặc vi phạm quy tắc
- ‘An toàn (safety)’ là việc bảo vệ các tầng trừu tượng
- Theo định nghĩa của Pierce, một ngôn ngữ an toàn đảm bảo lập trình viên không vô tình phá vỡ các tầng trừu tượng
- Ví dụ: quy tắc cấm truy cập trực tiếp vào file nội bộ giữa các app Django có thể được tự động hóa bằng script kiểm tra dựa trên AST
- Phát hiện các truy cập trái phép dạng
from visit import logic.internal_file
Kiểm chứng (Verification)
- Ngoài thiết kế và triển khai, giai đoạn kiểm chứng như code review và QA cũng là yếu tố thiết yếu để đảm bảo chất lượng
- Khi khối lượng công việc tăng lên, tốc độ rà soát trở thành nút thắt cổ chai, nên bài viết đề xuất các hướng cải thiện sau
- Giảm rào cản để có thể thực hiện QA ngay cả khi không có môi trường phát triển
- Xây dựng môi trường giúp viết test đơn giản hơn, chẳng hạn tạo dữ liệu test
- Ghi chép các phản hồi PR lặp đi lặp lại để LLM có thể tự động thực hiện một phần review
- Tích hợp các quy tắc bảo mật như giá trị mặc định của framework
Kết luận và các quan sát bổ sung
- LLM đặc biệt hoạt động tốt trong các dự án greenfield
- Vì không có ngữ cảnh sẵn có và yêu cầu về tính nhất quán thấp hơn
- Khi dự án ngày càng lớn, tính nhất quán và tính mô-đun sẽ quyết định năng suất
- Cấu trúc mô-đun tái sử dụng các thành phần đã được kiểm chứng là chìa khóa để phát triển hiệu quả
1 bình luận
Ý kiến trên Hacker News
Khi mô hình ngày càng được cải thiện, chúng đã có thể xử lý codebase phức tạp hoặc các file dài
Vì vậy tôi đã tạo một framework loop đơn giản dùng lặp đi lặp lại
Chạy vòng lặp này trong 20~30 phút thì ra kết quả khá dùng được. Cuối cùng, cốt lõi vẫn là quản lý context và tạo vòng phản hồi từ test
Nếu bắt đầu khám phá bằng từ khóa “explore” thì có thể Research mà không cần viết kế hoạch riêng hay reset context
Tuy nhiên, chúng có xu hướng giả định ngôn ngữ code là C hoặc Python, nên đôi khi tạo ra mã thiếu cấu trúc kiểu tách thành năm hàm thay vì đóng gói trạng thái thành object
Ngoài ra, claude thường hay bỏ qua CLAUDE.md, nên nếu cho nó đọc file đó trước rồi mới “explore” thì sẽ ổn định hơn
Các mô hình mới nhất khá giỏi trong việc bỏ bớt context vô ích, nhưng với các mô hình cũ thì cách tiếp cận dựa trên tài liệu kế hoạch vẫn thường tốt hơn
Rất nhiều lúc nó làm ngược hẳn kế hoạch và guideline, hoặc đọc lại cùng một câu mà vẫn rút ra kết luận trái ngược
Có thời gian tôi tin rằng có thể xây quy trình lấy LLM làm trung tâm, nhưng giờ thì bớt chắc chắn hơn
Khi mô hình ở “trạng thái tốt” thì ổn, nhưng để đưa nó vào trạng thái đó vẫn còn phụ thuộc vào may rủi
Tôi đã thêm các lệnh tùy chỉnh như
/research_codebase,/create_plan,/implement_planvào Claude CodeNếu review và chỉnh sửa cẩn thận thì nó hoạt động rất tốt, nhưng vẫn chưa thể lan rộng ra cả nhóm
Tôi chỉ reset context khi tạo tính năng hoàn toàn mới. Trên Codespaces thì ổn, nhưng tính năng Tasks thì gần như vô dụng
Nếu giao việc lớn thì đôi khi nó lại đi chệch hướng, nên phải luôn theo dõi sát
Nó cũng rất hữu ích cho repriming context, nên tôi dùng khá thường xuyên
Để thư viện prompt trở nên hữu ích thì cần cải tiến lặp đi lặp lại
Mỗi khi LLM hơi lệch hướng, hãy tự hỏi “mình đáng ra phải làm rõ điều gì hơn?” rồi thêm câu trả lời đó vào prompt
Nếu chỉ bấm Enter hoặc auto-approve thì chỉ lãng phí token. Thay vào đó, hãy quan sát xem LLM bị mắc ở đâu rồi ghi ngắn gọn vào CLAUDE.md
Khi file context trở nên lớn, hãy tách nó theo từng loại công việc
Trường hợp dùng chính của tôi là khám phá codebase, lần theo execution path, và cung cấp tóm tắt các file cần thiết. Nếu chỉ định trước cách trình bày kết quả cho từng loại câu hỏi thì hiệu quả sẽ cao hơn nhiều
Tôi gọi đây là “Student Pattern (Fresh Eyes)”. Một subagent đọc tài liệu hoặc mã bằng góc nhìn của người mới và tìm ra điểm gây nhầm lẫn, mâu thuẫn, thông tin thiếu
Nó đặc biệt giỏi trong việc phát hiện tri thức ngầm mà lập trình viên dễ bỏ sót. Rất hữu ích như một bước trước khi review tài liệu mới hoặc đánh giá prompt
Dù đã nhiều lần yêu cầu nó đọc CLAUDE.md, nó vẫn thường bỏ qua, và ngay sau khi bắt đầu session cũng có thể bỏ sót ngẫu nhiên
Dù đã chuẩn bị đủ tài liệu và lệnh, nhiều khi nó vẫn “quên”
Tôi đang thử nghiệm cách cấu trúc codebase thân thiện với agent
Tôi tách dự án thành một đồ thị có hướng dựa trên nix flakes để mỗi node có môi trường phát triển độc lập
Nếu chạy Claude Code trong flake devshell thì nó chỉ nhận thức phạm vi tương ứng, giúp tránh quá tải context
Tôi để các flakes cộng tác với nhau qua input/output, gửi yêu cầu tính năng và test cho nhau
Tôi nghĩ kiểu phân mảnh context này là chìa khóa để giảm vấn đề token bùng nổ
Thay vì chỉ cải thiện workflow hiện có, ta cần thiết kế một cấu trúc mới để LLM có thể mở rộng dễ dàng
Tôi đang khám phá mối liên hệ giữa các thuộc tính như “khả năng kiểm thử, khả năng mở rộng, tính bảo mật” với cấu trúc code
Tôi cũng từng làm thử điều tương tự với dự án dựa trên Docker, và sẽ rất hay nếu có công cụ tự động hóa việc này
LLM giải thích rất hay các chủ đề tôi không biết rõ, nhưng trong lĩnh vực chuyên môn thì lại sai một cách đầy tự tin
Tôi có góc nhìn khác. Cách tốt nhất để nâng hiệu năng LLM là nhúng ý nghĩa ngay vào chính codebase
Nói cách khác, mã được cấu trúc kiểu DDD (Domain-Driven Design) cũng sẽ dễ hiểu hơn với LLM
Cố ép công cụ xử lý mã phức tạp chỉ là lãng phí tiền bạc
Cuối cùng, điều LLM chứng minh là triết lý DDD nhấn mạnh “ngôn ngữ và ý nghĩa” là đúng
Bài liên quan: DDD & the Simplicity Gospel
Với câu hỏi “Vì sao LLM hoạt động tốt trong greenfield project?”, tôi lại có trải nghiệm ngược lại
Khi một pattern lặp lại 2~3 lần trong codebase, LLM sẽ học nó và sao chép lại một cách nhất quán
Nhưng “nhất quán” không đồng nghĩa với “chất lượng”. Nếu chỉ theo đuổi tính nhất quán mà không có tiêu chuẩn thì sẽ thành mã không thể bảo trì
Câu “LLM không hiểu nổi đoạn mã mà kỹ sư còn không hiểu” là đúng, nhưng chiều ngược lại thì không đúng
Có rất nhiều trường hợp con người hiểu được nhưng agent thì không
Làm cho codebase dễ hiểu với agent còn khó hơn làm cho nó dễ hiểu với con người
Cũng có người nói rằng “nếu chuyển phản hồi sang cho máy tính thì tỉ lệ thành công ngay từ một phát sẽ tăng lên”, nhưng điều đó gần giống như đang khẳng định P=NP
Việc kiểm chứng dễ không có nghĩa là việc tìm lời giải cũng dễ
Những ngôn ngữ như ATS hay Idris cho phép viết chứng minh tính đúng đắn kèm với code
Nếu lập luận đó đúng thì LLM phải thể hiện tốt nhất ở các ngôn ngữ như vậy
Nhưng thực tế không phải vậy. Vì thế hiện giờ tôi nghĩ chờ mô hình được cải thiện thì hơn
Chính vì những vấn đề này mà tôi cho rằng framework có tính áp đặt mạnh sẽ giúp tăng năng suất code với AI
Vì LLM đã biết sẵn các quy tắc của framework nên không cần guideline riêng nữa
Trong khi đó Go, Rust, Elixir, C# cần nhiều dependency và chỉ thị hơn hẳn
Rust cho kết quả tốt nhưng lại kéo vào hơn 200 package, nên khá nặng nề
Nguyên tắc “Garbage in, garbage out” là đúng, nhưng không áp dụng hoàn toàn với LLM
Dù được huấn luyện trên dữ liệu nhiễu của toàn bộ Internet, nó vẫn hoạt động khá tốt
Hallucination thường xảy ra vì context không chính xác hơn là vì nhiễu đơn thuần
Ngay cả một codebase lộn xộn về cấu trúc, nếu chứa nhiều thông tin thì vẫn có thể cung cấp context hữu ích
Rốt cuộc mọi người đang học lại các nguyên tắc cơ bản
Họ đang nhận ra lại rằng tài liệu hóa (= thư viện prompt) và cấu trúc mã gọn gàng có thể tăng tốc phát triển