9.9 KiB
Hướng dẫn viết & maintain các file AGENTS.md
AGENTS.md là bộ quy ước, thông tin, business logic ngắn gọn mà AI/Tools (như Claude Code, Codex, Cursor, Antigravity, ...) nạp vào context khi làm việc. Nó quyết định AI/tools hiểu dự án đến đâu và là nguồn rule chính cho /code-review (hoặc các hàm tương tự). Nguyên tắc số một: đây là bộ luật, không phải tài liệu — mỗi dòng thừa làm loãng tín hiệu và khiến AI dễ lạc đề.
Mục lục
- Ba loại CLAUDE.md — đặt ở đâu, dùng cho gì
- Phân cấp & scope
- Cấu trúc chuẩn (8 phần)
- Cạm bẫy: khi .md phản tác dụng (nhất là model yếu)
- Tự động cập nhật (ít sửa tay)
- Cập nhật thủ công
1. Ba loại CLAUDE.md
| Loại | Vị trí | Phạm vi | Theo git? |
|---|---|---|---|
| Cá nhân (global) | Nằm trong thư mục của tools như ~/.claude/CLAUDE.md hoặc .cursor/rules/*.mdc |
Mọi dự án trên máy bạn | Không |
| Dự án (root) | ./CLAUDE.md hoặc AGENTS.md, ... (nằm ở folder root repo) |
Cả dự án đó | Có (cả team thấy và sử dụng chung được) |
| Thư mục con | <module>/*.md |
Chỉ code trong thư mục đó | Có |
a) Cá nhân — ~/.claude/CLAUDE.md (tương tự ở folder khác cho các tools khác)
- Dùng cho: phong cách & nguyên tắc làm việc của riêng bạn, áp cho mọi dự án (ví dụ: nguyên tắc Karpathy, "ưu tiên dùng skill", "lập kế hoạch & hỏi trước khi code").
- KHÔNG để: lệnh build/test hay business rule riêng của một dự án — vì nó áp cho tất cả.
- Lưu ý: chỉ tồn tại trên máy bạn, không đi theo repo nên đồng đội không thấy.
b) Dự án — ./CLAUDE.md ở gốc repo (tương tự ở folder khác cho các tools khác)
- Dùng cho: mọi thứ thuộc về dự án — tổng quan, lệnh build/test/lint, quy ước, business rules, nên làm gì và không được làm gì.
- Đặt ở gốc repo (không phải trong
.claude/), được commit nên cả team clone về là có. - Đây là vị trí chắc chắn được nạp và quan trọng nhất cho chất lượng
/code-review.
c) Thư mục con — <module>/CLAUDE.md (tương tự ở folder khác cho các tools khác)
- Dùng cho: rule chỉ dùng cho một module cụ thể (ví dụ
src/payment/CLAUDE.md: rule về idempotency, xử lý refund). - Vì sao quan trọng:
/code-reviewchỉ áp rule có cùng path hoặc thư mục cha của file đang xét. Đặt rule đúng chỗ → review đúng trọng tâm, không lôi rule chỗ khác vào.
Các file được cộng dồn: khi làm trong một thư mục con, ví dụ như: Claude sẽ nạp đồng thời CLAUDE.md cá nhân + gốc dự án + thư mục con.
2. Phân cấp & scope
- Rule chung → gốc repo (
./AGENTS.md). - Rule riêng module →
<module>/AGENTS.md. Đừng nhồi hết vào root. - Rule cá nhân, mọi dự án →
~/.claude/CLAUDE.mdhoặc.cursor/rules/*.mdchoặc tương tự với các tools khác. /code-reviewxét rule theo scope phân cấp — đặt sai chỗ thì hoặc bị bỏ qua, hoặc áp nhầm sang code không liên quan.
3. Cấu trúc chuẩn (8 phần)
Phần 1, 2, 4, 5, 6 là cốt lõi; 3, 7 tùy dự án; 8 tự lớn theo thời gian. (File cá nhân chủ yếu dùng phần 4 + 6; file dự án dùng đủ; file thư mục con chỉ phần thông tin liên quan về module.)
- [1] Tổng quan dự án (bắt buộc): tên, mô tả ngắn, tech stack, env. 3–5 dòng.
- [2] Lệnh hay dùng (bắt buộc): build / test / lint / dev / deploy — để AI chạy đúng thay vì đoán.
- [3] Cấu trúc thư mục (tùy chọn): chỉ ghi thư mục dễ nhầm (ví dụ: logic ở
services/, không ởapi/). - [4] Quy ước code (bắt buộc): naming, pattern, error handling — những rule linter không bắt được.
- [5] Business rules (bắt buộc): logic nghiệp vụ AI không thể tự đoán (ví dụ: không cancel order đã "shipped"). Quan trọng nhất — viết dạng ràng buộc/cấm, không phải mô tả (xem §4).
- [6] Do / Don't (bắt buộc): dạng cấm rõ ràng, ngắn, dứt khoát (ví dụ: KHÔNG commit
.env). - [7] References (tùy chọn): import file ngoài bằng
@[...].mdđể giữ file chính gọn. - [8] Learned patterns (tự cập nhật): phần Claude tự ghi sau mỗi phiên, kèm ngày.
Mẹo giữ gọn: phần dài thì tách ra file riêng rồi
@import(ví dụ@KarpathyCodeGuidelines.md). Dùng import cho cả file cá nhân lẫn dự án.
Thứ tự viết lần đầu: làm 1, 2, 6 trước (~30 phút), rồi bổ sung 4, 5 dần theo lúc thấy AI làm sai. Không cần hoàn hảo ngay.
4. Cạm bẫy: khi .md phản tác dụng (nhất là model yếu)
Bài học thực tế: /init và model tự sinh file mô tả CÁI ĐANG CÓ, không phải RÀNG BUỘC phải giữ. Đó là lý do file tự sinh hay phản tác dụng — càng rõ với model yếu (minimax-m3, ...).
Vì sao ngược: model yếu đọc một fact như "logic mã hóa userId lặp ở 3 template + PagesController" thành lời mời refactor → tự gộp/xóa code nhạy cảm không ai yêu cầu. (Case thật repo này: m3 xóa cả 2 block mã hóa AES trùng trong PagesController chỉ vì file .md "tả" chúng trùng nhau.)
Nguyên tắc: mô tả → ràng buộc. Mỗi dòng phải là cái model KHÔNG đọc code ra được: intent, invariant, gotcha, cấm. Fact thuần (model tự đọc được) = nhiễu → xóa.
| ❌ Mô tả (fact — model tự đọc được) | ✅ Ràng buộc + ý đồ (đáng giữ) |
|---|---|
"canAccessAnniCoupon() join anni_coupon_register_info" |
"Whitelist anniversary CHỈ quyết ở canAccessAnniCoupon(). KHÔNG hardcode danh sách chỗ khác." |
| "Mã hóa userId lặp ở 3 template + controller" | "3 block mã hóa userId là CỐ Ý (mỗi surface khác param). KHÔNG gộp/xóa trừ khi task yêu cầu." |
"LotteryService dùng getUserIndex() % ratioCycle" |
"Lottery deterministic CÓ CHỦ ĐÍCH, KHÔNG phải random. Đừng 'sửa' thành rand()." |
Viết cho model yếu:
- Câu cấm mệnh lệnh (KHÔNG / CHỈ / PHẢI), không phải câu tả. Model yếu làm theo lệnh, không tự suy ý đồ.
- Mỗi gotcha kèm hậu quả ("...nếu không, request bị Gateway chặn trước khi tới action").
- "Trông như bug nhưng cố ý" → nói thẳng + lý do. Không nói → model sẽ "sửa" cho gãy.
Checklist trước khi commit mỗi file .md — mỗi dòng tự hỏi:
- Model đọc code ra được dòng này không? → Được thì xóa (nhiễu).
- Có phải invariant / intent / gotcha / cấm không? → Không thì xóa.
- Đặt đúng scope chưa (rule module ↔ nằm trong file của module đó)?
/init = bản nháp, KHÔNG phải bản cuối. Bắt buộc 1 lượt người sửa: xóa fact, đổi "tả" thành "cấm", thêm ý đồ. Commit thẳng output /init chính là nguồn "hiệu quả ngược".
5. Tự động cập nhật
Tools AI không tự viết lại các file AGENTS.md hoàn toàn — và đó là điều tốt, vì file tự phình liên tục thì sẽ nhiều rác --> sẽ làm hỏng /code-review. Nếu chúng ta muốn muốn cập nhật các file .md mà gần như không phải sửa tay thì sẽ có một số cách:
- Dùng
#(ít ảnh hưởng nhất): gõ tin nhắn bắt đầu bằng#, ví dụ# luôn dùng dayjs thay vì moment→ Claude tự ghi vào memory và hỏi bỏ vào file nào (cá nhân / dự án / thư mục con). - Lệnh
/update-memory(tự tạo): tạo.claude/commands/update-memory.mdyêu cầu Claude ràgit log+ phiên gần đây, đề xuất rule high-signal cho bạn duyệt. Chạy cuối ngày hoặc sau mỗi feature. - Hook (tùy chọn): gắn hook sự kiện
Stopđể nhắc cập nhật. ⚠️ Đừng để hook tự ghi không qua duyệt — dễ tạo vòng lặp và làm file rác.
6. Cập nhật thủ công
/init: quét codebase sinh CLAUDE.md nháp. Nếu đã có file, nó bổ sung chứ không ghi đè, nhưng có thể sắp xếp lại — nêngit committrước khi chạy để dễ rollback. Hợp cho lúc khởi tạo / làm mới toàn diện. ⚠️ Output là nháp — bắt buộc sửa tay theo §4 trước khi commit./memory: liệt kê các file memory và mở ra sửa trực tiếp; dùng khi muốn dọn/sắp xếp lại.- Sửa file như thường: CLAUDE.md chỉ là markdown — mở editor sửa, lưu, lần sau Claude tự đọc lại (không cần restart).
- Giữ gọn: thêm rule mới thì xóa rule lỗi thời; mỗi rule một dòng; phần dài thì tách file rồi
@import.
Quy trình khuyên dùng:
/init(1 lần) → dùng#bắt rule trong lúc làm → chạy/update-memoryđịnh kỳ & duyệt.
Lưu ý về session dài: context đầy dần sẽ làm rule bị "loãng" và Claude dễ quên — chia nhỏ session (
/clearsau mỗi task là tốt nhất, nếu vẫn muốn giữ context hiện tại cho task tiếp theo thì hãy/compactnhé) và giữ file và context ngắn giúp rule được ưu tiên hơn.