Có lẽ bạn đã thấy nhiều bài blog về lập trình bằng AI agent hay "vibecoding", nơi tác giả thao thao bất tuyệt về mọi điều kỳ diệu mà các agent có thể làm dựa trên những bằng chứng vụn vặt mơ hồ; về việc agent sẽ dẫn đến sự thui chột kỹ năng lập trình ra sao; hay việc chúng xâm phạm đến chủ quyền của tâm hồn con người thế nào, vân vân và mây mây. Đây KHÔNG phải là một trong những bài viết đó. Coi như tôi đã cảnh báo bạn trước.
Tháng 5 năm ngoái, tôi từng viết một bài blog có tiêu đề: Là một người dùng LLM kinh nghiệm, thực tế tôi không thường xuyên sử dụng LLM tạo sinh, như một phản hồi tương phản trước làn sóng cường điệu xung quanh sự phổ biến ngày càng tăng của lập trình dạng agent (agentic coding). Trong bài đó, tôi đã lưu ý rằng mặc dù LLM chắc chắn không vô dụng—chúng có thể trả lời các câu hỏi lập trình đơn giản nhanh hơn và chính xác hơn so với việc tôi tự viết—nhưng với các "agent" thì lại là một câu chuyện khó thuyết phục hơn: chúng khó đoán, đắt đỏ, và sự cường điệu xung quanh chúng là quá mức so với những kết quả thực tế mà tôi thấy khi sử dụng cá nhân. Tuy nhiên, tôi kết luận rằng mình vẫn sẵn lòng đón nhận agent nếu LLM cải thiện đủ tốt để giải quyết mọi lo ngại của tôi và trở nên đáng tin cậy hơn.
Trong những tháng sau đó, tôi vẫn tiếp tục công việc thực tế của một Nhà khoa học dữ liệu (Data Scientist) đồng thời cập nhật những LLM mới nhất xuất hiện trên OpenRouter. Vào tháng 8, Google thông báo ra mắt AI tạo ảnh Nano Banana với một API đi kèm cực kỳ khó dùng, vì vậy tôi đã phát hành mã nguồn mở gói Python
gemimg đóng vai trò như một trình bao (wrapper) cho API này. Đây không phải là một dự án gì đó quá hào hứng: có rất ít không gian hoặc nhu cầu cho việc triển khai sáng tạo, và sự hài lòng của tôi đối với nó nằm ở giá trị thực tế mà nó mang lại hơn là bản thân việc viết ra công cụ đó. Vì vậy, như một thử nghiệm, tôi đã quăng toàn bộ mã nguồn đã hoàn thiện tính năng vào nhiều LLM mới nổi trên OpenRouter và yêu cầu chúng xác định cũng như sửa lỗi trong mã Python: nếu nó thất bại, đó là một bài kiểm tra tốt cho năng lực hiện tại của LLM; nếu nó thành công, chất lượng phần mềm cho người dùng sẽ tăng lên và tôi chẳng có phản đối đạo đức nào với việc đó cả. Các LLM thực sự đã giúp ích: ngoài việc thêm các chú thích hàm (docstrings) và gợi ý kiểu (type hints) chuẩn chỉnh, chúng còn xác định được những cách triển khai mã nguồn tối ưu hơn (Pythonic) cho nhiều khối code khác nhau.Khoảng thời gian này, các đồng nghiệp của tôi đang thúc đẩy việc dùng GitHub Copilot trong Visual Studio Code như một công cụ hỗ trợ lập trình, đặc biệt là xoay quanh phiên bản Claude Sonnet 4.5 mới ra mắt lúc bấy giờ. Đối với công việc khoa học dữ liệu của tôi, Sonnet 4.5 trong Copilot không mấy hữu ích và có xu hướng tạo ra các tệp Jupyter Notebook quá rườm rà, nên tôi không mấy ấn tượng. Tuy nhiên, vào tháng 11, Google phát hành Nano Banana Pro, buộc tôi phải cập nhật ngay
gemimg để tương thích với mô hình mới. Sau khi thử nghiệm với Nano Banana Pro, tôi phát hiện ra rằng mô hình này có thể tạo ảnh với lưới tùy ý (ví dụ: 2x2, 3x2)—một quy trình làm việc cực kỳ thực tế—vì vậy tôi nhanh chóng viết một bản mô tả kỹ thuật để triển khai hỗ trợ và cắt từng ảnh nhỏ ra để lưu riêng biệt. Tôi biết quy trình này triển khai bằng thư viện Pillow thì khá đơn giản nhưng lại cực kỳ tẻ nhạt, nên tôi cảm thấy đủ an tâm để yêu cầu Copilot: "Tạo tệp `grid.py` triển khai lớp `Grid` như mô tả trong issue #15". Và nó đã làm chính xác như vậy, dù có vài lỗi ở những phần không được nhắc tới trong bản mô tả (ví dụ: nhầm lẫn thứ tự hàng/cột), nhưng chúng dễ dàng được sửa bằng các câu lệnh (prompt) cụ thể hơn. Ngay cả khi tính đến việc xử lý lỗi, đó vẫn là một bước tiến đáng kể về năng suất để tôi cảm thấy lạc quan hơn về khả năng của agent, nhưng vẫn chưa đủ để biến tôi thành một kẻ cuồng AI.Vào tháng 11, chỉ vài ngày trước lễ Tạ ơn, Anthropic đã tung ra Claude Opus 4.5 và dĩ nhiên các đồng nghiệp của tôi rất tò mò liệu nó có phải là một sự cải tiến vượt bậc so với Sonnet 4.5 hay không. Việc Anthropic tung ra Opus 4.5 ngay trước một kỳ nghỉ lễ lớn là điều rất đáng ngờ, vì các công ty thường làm vậy để "chôn vùi" những thông báo gây thất vọng, do người dùng tiềm năng còn đang bận rộn tụ họp với gia đình và bạn bè. May mắn thay, tôi chẳng có bạn bè cũng chẳng có gia đình ở San Francisco, nên tôi có thừa thời gian để kiểm chứng phiên bản Opus mới này.
Đôi lời về tệp AGENTS.md
Một khía cạnh về agent mà tôi chưa từng nghiên cứu kỹ nhưng biết rằng nó rất cần thiết để đạt được kết quả tốt là khái niệm tệp
AGENTS.md: một tệp tin có thể kiểm soát các hành vi cụ thể của agent như định dạng mã nguồn (code formatting). Nếu tệp này tồn tại trong thư mục gốc của dự án, agent sẽ tự động đọc và về lý thuyết là tuân thủ mọi quy tắc bên trong đó. Điều này tương tự như "system prompts" (câu lệnh hệ thống) cho các cuộc gọi LLM thông thường. Và nếu bạn đã theo dõi các bài viết của tôi, bạn sẽ biết tôi có một sự "nghiện" không hề nhẹ đối với các system prompt cực kỳ chi tiết, kèm theo những tiểu xảo như VIẾT HOA TOÀN BỘ để tăng khả năng tuân thủ các quy tắc quan trọng (vâng, cách này vẫn rất hiệu quả). Vì không tìm được một tệp mẫu AGENTS.md nào cho Python mà mình ưng ý, tôi đã yêu cầu Opus 4.5 tự tạo ra một bản:"Thêm một tệp `AGENTS.md` định hướng cho chất lượng mã nguồn Python tốt. Nó phải cực kỳ chi tiết. Các quy tắc quan trọng hơn phải sử dụng chữ hoa, ví dụ: `MUST` (PHẢI)."
Sau đó, tôi đã thêm vào một vài sở thích cá nhân và các công cụ được đúc rút từ những thất bại trước đó khi làm việc với agent trong Python: sử dụng
uv và .venv thay vì cài đặt Python gốc, sử dụng polars thay vì pandas để xử lý dữ liệu, chỉ lưu trữ bí mật/API key/mật khẩu trong .env và đảm bảo .env đã nằm trong .gitignore, v.v. Hầu hết các ràng buộc này không bảo agent phải làm gì, mà là làm điều đó như thế nào. Nhìn chung, việc thêm một quy tắc vào AGENTS.md mỗi khi tôi gặp phải một hành vi cơ bản mà mình không thích tỏ ra rất hiệu quả. Ví dụ, các agent rất thích dùng emoji vô tội vạ — thứ mà tôi cực kỳ ghét — nên tôi đã thêm một quy tắc:BAO GIỜ (NEVER) sử dụng emoji, hoặc các ký tự unicode mô phỏng emoji (ví dụ: ✓, ✗).
Các agent cũng có xu hướng để lại rất nhiều chú thích mã nguồn thừa thãi, vì vậy tôi đã thêm một quy tắc khác để ngăn chặn điều đó:
(MUST) tránh đưa vào các chú thích dư thừa mang tính lặp lại hoặc tự giải thích (ví dụ: các trường hợp mà chỉ cần nhìn thoáng qua là có thể hiểu mã nguồn làm gì, hoặc tên hàm đã cung cấp đủ thông tin về chức năng của nó, khiến chú thích không có tác dụng gì ngoài việc làm lãng phí thời gian của người dùng).
Tệp AGENTS.md cập nhật nhất dành cho Python của tôi có sẵn tại đây. Trong suốt thời gian làm việc với Opus, nó đã tuân thủ mọi quy tắc bất chấp độ dài của tệp. Những lúc tôi vô tình truy vấn agent mà không có tệp AGENTS.md, sự khác biệt lộ ra rất rõ ràng. Tôi sẽ không ngạc nhiên nếu tệp tin này chính là điểm khác biệt chính giữa những người nhận được kết quả tốt và những người nhận kết quả tệ từ agent, mặc dù sự thành công thường vẫn có những ý kiến trái chiều.
Một lưu ý nhỏ là nếu bạn đang sử dụng Claude Code, tệp này phải được đặt tên là CLAUDE.md vì Anthropic thích làm khác người; bài blog này sẽ chỉ dùng tên AGENTS.md để đảm bảo tính nhất quán.
Lần đầu tiếp xúc với Opus
Sau khi đã thiết lập xong tệp AGENTS.md, tôi nghiên cứu kỹ hơn về các phương pháp viết câu lệnh (prompt) chuẩn chỉnh cho agent để xem liệu mình có bỏ lỡ điều gì dẫn đến hiệu suất kém khi làm việc với Sonnet 4.5 hay không.
*Trích từ hướng dẫn nhanh (quickstart) của Claude Code:
"Hãy thử những câu lệnh đơn giản như: 'Thêm kiểm thử đơn vị (unit tests) cho tệp này' hoặc 'Tái cấu trúc hàm này để dễ đọc hơn'."*
Những gợi ý câu lệnh của Anthropic tuy đơn giản, nhưng bạn không thể đưa cho một LLM một câu hỏi mở như vậy mà lại mong đợi kết quả đúng ý mình muốn! Bạn — với tư cách là người dùng — có khả năng là một người kỹ tính một cách tiềm thức, và luôn có những yêu cầu chức năng mà agent sẽ không thể tự động áp dụng một cách thần kỳ được, bởi vì nó không thể đọc tâm trí và nó hoạt động giống như một "vị thần đèn" thực thụ (chỉ làm chính xác những gì được bảo).
Phương pháp viết prompt của tôi là viết từng câu lệnh riêng lẻ — vốn có thể rất dài — vào một tệp Markdown riêng (tệp này có thể được theo dõi bằng git), sau đó "tag" (gắn thẻ) agent với câu lệnh đó và yêu cầu nó triển khai nội dung tệp Markdown đó. Khi công việc hoàn tất và được kiểm tra thủ công, tôi sẽ tự tay commit phần việc đó lên git với thông điệp tham chiếu đến tệp prompt cụ thể, nhờ đó tôi có được hệ thống theo dõi nội bộ rất chặt chẽ.

Tôi đã hoàn toàn phớt lờ lời khuyên của Anthropic và viết một câu lệnh kiểm thử phức tạp hơn dựa trên một tình huống sử dụng mà tôi thông thạo, nhờ đó tôi có thể kiểm chứng chất lượng mã nguồn của agent. Năm 2021, tôi từng viết một đoạn mã để thu thập siêu dữ liệu (metadata) từ các video trên một kênh YouTube bất kỳ bằng YouTube Data API, nhưng tài liệu của API này rất tệ, khó hiểu, và các đoạn mã Python cũ của tôi cũng chẳng ra sao. Tôi có đăng ký theo dõi kênh YouTube SiIvagunner, một kênh có phong cách khá dị (chế nhạc với những giai điệu khác hẳn dự đoán), họ đăng hàng trăm video mỗi tháng với ảnh thu nhỏ và tiêu đề rất mơ hồ, khiến tôi không thể biết video nào hay nếu không dựa vào lượt xem. Những siêu dữ liệu video này có thể giúp tôi tìm ra các video chất lượng mà mình đã bỏ lỡ, vì vậy tôi nảy ra một ý tưởng thú vị để thử thách Opus 4.5:
tạo một script Python mạnh mẽ, từ một ID kênh YouTube cho trước, có thể thu thập dữ liệu từ YouTube Data API và lưu trữ tất cả siêu dữ liệu video vào một cơ sở dữ liệu SQLite. YOUTUBE_API_KEY đã có sẵn trong tệp .env. Tài liệu về endpoint của kênh: [link] ID kênh dùng để kiểm thử là: UC9ecwl3FTG66jIKA9JRDtmg Bạn PHẢI tuân thủ TẤT CẢ các quy tắc SAU ĐÂY trong quá trình triển khai: Không sử dụng bộ SDK khách của Google. Hãy sử dụng REST API với thư viện httpx. Bao gồm các số liệu tổng hợp hợp lý, ví dụ: số lượng bình luận trên video. Bao gồm các cột channel_id và retrieved_at trong cấu trúc cơ sở dữ liệu.
Kết quả là một script hoạt động ngay từ lần chạy đầu tiên, thu thập được tới 20.000 video (giới hạn tối đa). Script Python này có chất lượng mã nguồn rất chuẩn (Pythonic), tuân thủ nghiêm ngặt các quy tắc tôi đã đặt ra trong AGENTS.md, và nó mạnh mẽ hơn hẳn đoạn mã cũ năm 2021 của tôi. Đây chắc chắn không phải kiểu kết quả mà tôi từng gặp ở Sonnet 4.5. Tuy nhiên, có một vấn đề nhỏ: phần nhật ký (logging) được triển khai một cách ngây ngô khiến API key bị lộ ra trên cửa sổ console. Tôi đã thêm một quy tắc mới vào AGENTS.md để ngăn chặn việc này, nhưng thực ra đây là lỗi của YouTube API khi khuyến khích việc dùng API key làm tham số trong một yêu cầu GET.
Tiếp theo, tôi đưa ra một câu lệnh mang tính định hướng khoa học dữ liệu hơn để kiểm tra kỹ năng của Opus 4.5 trong lĩnh vực này:
tạo một Jupyter Notebook sử dụng thư viện polars để xử lý dữ liệu, thực hiện phân tích dữ liệu khám phá (EDA) một cách kỹ lưỡng cho tất cả các cột dữ liệu được lưu trong youtube_videos.db. Phân tích này phải có khả năng mở rộng cho bất kỳ channel_id đầu vào nào.
Kết quả Jupyter Notebook thu được... thực sự rất kỹ lưỡng. Đó là lỗi của tôi khi yêu cầu phân tích "cho tất cả các cột", mặc dù vậy, nó vẫn có thể tự suy luận ra nhu cầu phân tích theo thời gian (ví dụ: tổng số video tải lên hàng tháng theo thời gian) dù tôi không hề đề cập rõ ràng trong câu lệnh.
Phần phân tích hàng tháng này đã gợi ý cho tôi một ý tưởng: liệu Opus 4.5 có thể thiết kế một ứng dụng web nhỏ để xem các video đứng đầu theo từng tháng không? Đây là cơ hội để tôi thử nghiệm xem Opus 4.5 hoạt động ra sao với các framework ít phổ biến hơn React hoặc các framework thành phần JavaScript khác mà LLM thường mặc định gợi ý. Ở đây, tôi sẽ thử dùng FastAPI, Pico CSS cho phần giao diện (vì chúng ta không cần một framework JavaScript cho việc này), và HTMX để tương tác giữa máy khách/máy chủ một cách nhẹ nhàng:
tạo một ứng dụng FastAPI "đủ tầm để xuất hiện trên Hacker News", sử dụng HTMX để tạo tính tương tác và PicoCSS để định dạng giao diện, xây dựng một ứng dụng mang phong cách YouTube dựa trên dữ liệu từ youtube_videos.db. Ứng dụng này phải có một trang web tương tác hiển thị các video hàng đầu của mỗi tháng, bao gồm cả các trình phát video YouTube nhúng có thể nhấn vào được.
Mã nguồn Python của ứng dụng web FastAPI rất tốt với việc tích hợp logic các route của HTMX và các thành phần giao diện nhỏ (partials), nhưng Opus 4.5 còn "tự tung tự tác" một cách đầy thú vị với yêu cầu "phong cách YouTube" trong câu lệnh: hình thu nhỏ của video mô phỏng y hệt thumbnail của YouTube kèm theo thời lượng video, và khi nhấn vào sẽ tải ngay một trình phát video nhúng! Toàn bộ mã nguồn này hiện đã được mở trên kho lưu trữ GitHub này.
Tất cả các bài kiểm tra này đều cho kết quả tốt hơn nhiều so với mong đợi của tôi, nhất là sau những trải nghiệm tệ hại trước đây với các agent. Liệu có phải tôi đã tự "thao túng tâm lý" chính mình khi trở thành một kẻ hoài nghi agent? Làm thế nào mà một LLM vốn bị coi là "ra đời để thế chỗ" lại cuối cùng giải quyết được các vấn đề về agent của tôi? Bất chấp kỳ nghỉ lễ, trên X (Twitter) và Hacker News cũng tràn ngập những câu chuyện tương tự về sự khác biệt khổng lồ giữa Sonnet 4.5 và Opus 4.5, vì vậy rõ ràng là đã có điều gì đó thực sự thay đổi.
Dĩ nhiên, chỉ một trình thu thập API và một ứng dụng xem dữ liệu thì chưa đủ để đưa ra tuyên bố kiểu "OPUS 4.5 THAY ĐỔI TẤT CẢ" trên mạng xã hội, nhưng nó đủ để tôi bớt hoài nghi và lạc quan hơn về lập trình bằng agent. Đây là một lời mời gọi để tiếp tục tạo ra những nhiệm vụ khó khăn hơn cho Opus 4.5 giải quyết. Từ thời điểm này trở đi, tôi cũng sẽ chuyển sang sử dụng Claude Code chạy trên terminal, vì quy trình làm việc của tôi đủ đơn giản và không cần đến giao diện người dùng hay những thứ rườm rà khác.
Còn nữa....
Nguồn bài viết từ Tác giả Minimaxir