SSD Nodes Learn Hosting plans →
Hướng dẫn Matt ConnorBởi Matt Connor · Cập nhật ngày 2026-08-22

Cách dùng AGENTS.md lồng nhau trong monorepo

Một AGENTS.md 600 dòng ở root nhanh lỗi thời và làm tốn context. Tách file theo service giúp agent chỉ đọc đúng quy tắc của thư mục đang sửa.

Ý nghĩa của AGENTS.md lồng nhau trong monorepo

AGENTS.md lồng nhau trong monorepo nghĩa là có một file nhỏ ở root của repository và thêm một file bên trong mỗi thư mục service. File ở root chứa một số ít quy tắc áp dụng ở mọi nơi, cùng với sơ đồ vị trí của các file còn lại. File của từng service chứa các command và quy ước chỉ áp dụng cho thư mục đó. Khi chỉnh sửa services/worker/queue.py, agent sẽ đọc file ở root và file của worker, không tốn context cho phần front end mà nó sẽ không động đến.

Không cần cài đặt gì. AGENTS.md là một quy ước, và project upstream nói rõ điều đó:

AGENTS.md chỉ là Markdown tiêu chuẩn. Bạn có thể dùng bất kỳ heading nào; agent chỉ phân tích nội dung bạn cung cấp.

Đó là lý do bạn nên học kỹ kỹ thuật này. Format sẽ không tự thay đổi. Những thứ gây lỗi là vị trí đặt file và việc bảo trì, và cả hai đều là trách nhiệm của bạn.

Vì sao một AGENTS.md lớn duy nhất ở root dần không còn hiệu quả?

Một AGENTS.md dài 600 dòng ở root của repository chứa web app, background worker và một thư mục Terraform sẽ thất bại theo 4 cách riêng biệt.

Nó trở nên lỗi thời vì không ai chịu trách nhiệm duy trì. Kỹ sư đổi tên một test script trong apps/web đang chỉnh sửa các file dưới apps/web. AGENTS.md ở root không nằm trong diff đó, nên không reviewer nào thấy sự không khớp. 6 tuần sau, file vẫn mô tả một bước build không còn tồn tại, còn người gây ra thay đổi đã quên mất việc đó.

Nó tiêu tốn context trong mọi task. Các file này được load khi session bắt đầu, trước khi agent biết bạn sẽ yêu cầu gì. Tài liệu của Claude Code đưa ra một con số cụ thể: "target under 200 lines per CLAUDE.md file. Longer files consume more context and reduce adherence." Codex dừng merge các instruction file khi tổng kích thước đạt 32 KiB, mặc định là project_doc_max_bytes. Một file root mô tả 4 service sẽ dùng ngân sách đó cho 3 service không liên quan trong mọi task.

Các instruction bắt đầu mâu thuẫn nhau. Thư mục web cần pnpm test. Worker cần pytest -q. Khi viết chung trong một file, mỗi rule chỉ đúng trong một số trường hợp, nên agent phải đoán rule nào được áp dụng. Tài liệu Claude Code mô tả kết quả như sau: "if two rules contradict each other, Claude may pick one arbitrarily." File theo từng thư mục loại bỏ việc phải đoán, vì mỗi lần chỉ có một trong 2 rule nằm trong context. Khi một rule mà bạn chắc chắn đã viết rõ ràng vẫn bị bỏ qua, hãy xử lý các lý do instruction không bao giờ có hiệu lực thay vì viết lại câu đó lần thứ 4.

Nó chứa các thông tin mà agent có thể đọc trực tiếp từ code. Ví dụ như cây thư mục, danh sách dependency và phần tóm tắt chức năng của từng package. Kiểm tra /doctor của Claude Code được tạo ra chính xác để loại bỏ các nội dung này. Nó "cuts content Claude can derive from the codebase, such as directory layouts, dependency lists, and architecture overviews" và giữ lại "pitfalls, rationale, and conventions that differ from tool defaults." Đây là phép thử tốt nhất tôi biết để xác định một dòng có thực sự thuộc về file hay không.

Agent đọc file ở root hay chỉ đọc file gần nhất?

Đây là điểm khiến nhiều người hiểu sai về model, vì vậy nên trích dẫn quy ước upstream thay vì diễn giải lại:

Đặt thêm một file AGENTS.md bên trong mỗi package. Agent tự động đọc file gần nhất trong cây thư mục, vì vậy file gần nhất sẽ được ưu tiên và mỗi subproject có thể cung cấp bộ hướng dẫn riêng.

Về xung đột:

AGENTS.md gần file được chỉnh sửa nhất sẽ được áp dụng; prompt trực tiếp của người dùng trong chat có quyền ưu tiên cao hơn mọi thứ.

Cụm “được ưu tiên” khiến nhiều người hiểu là “file ở root bị bỏ qua”. Không phải vậy. Trong các tool triển khai quy ước này, mọi file trên đường dẫn từ root của repository đến working directory đều được đọc và ghép lại. File gần nhất chỉ được ưu tiên khi hai file đưa ra hướng dẫn khác nhau về cùng một nội dung.

Codex mô tả rõ cơ chế này: “Codex nối các file từ root xuống dưới, ngăn cách chúng bằng các dòng trống. Các file gần directory hiện tại hơn sẽ ghi đè hướng dẫn trước đó.” Claude Code cũng duyệt cùng đường dẫn để tìm file tương ứng. Các file trong những directory nằm phía trên working directory “được load đầy đủ khi khởi động”, và “tất cả file được phát hiện đều được nối vào context thay vì ghi đè lẫn nhau”. Các directory bên dưới working directory hoạt động khác: Claude Code load các file đó theo nhu cầu, “khi Claude đọc file trong những directory đó”.

Có 2 hệ quả thực tế. File ở root là phần tiền tố trong mọi session của repository, vì vậy hãy xem mỗi dòng trong đó là một dòng bạn phải trả chi phí hàng trăm lần mỗi tuần. File theo từng directory không tốn chi phí khi agent làm việc ở nơi khác. Vì vậy, phần hướng dẫn chi tiết nên đặt ở đó.

Hành vi này đã được kiểm tra dựa trên tài liệu của Codex và Claude Code vào tháng 8 năm 2026. Các tool triển khai quy ước hơi khác nhau và cũng có thể thay đổi, vì vậy hãy xác nhận quy tắc load file của agent mà team bạn đang sử dụng.

Bố cục mẫu cho một repository có ba service

repo/
  AGENTS.md                   rules true everywhere, plus the map
  apps/web/AGENTS.md          TypeScript client, Vite, Vitest
  services/worker/AGENTS.md   Python queue consumer, pytest
  infra/AGENTS.md             Terraform and the deploy scripts

File ở root được giữ ngắn một cách có chủ đích. File này chỉ nêu nơi cần tìm và chỉ chứa các rule áp dụng cho mọi thư mục.

# AGENTS.md

This is a monorepo. Each top-level directory ships its own AGENTS.md.
Read this file and the AGENTS.md nearest the code you are editing
before you change anything.

- `apps/web` browser client
- `services/worker` queue consumer
- `infra` Terraform and deploy scripts

## Rules for the whole repository

- The package manager is `pnpm`. `npm install` writes a second lockfile
  that CI ignores, so the install you tested is not the install that ships.
- Any `generated/` directory is build output. Edit the schema in
  `schemas/` and run `pnpm codegen` instead.
- `.env.local` holds real credentials. Do not read it and do not print it.
- If you change code in a directory, update that directory's AGENTS.md
  in the same commit.

File ở từng thư mục là nơi chứa phần chi tiết. Độ dài của file có thể tương ứng với mức độ phức tạp của thư mục đó.

# apps/web

Browser client. Vite and React, TypeScript with `strict` on.

## Commands

- `pnpm dev` serves on port 5173.
- `pnpm test` runs Vitest once and exits.
- `pnpm typecheck` runs `tsc --noEmit`.

## Conventions

- One component per file under `src/components/`.
- All HTTP goes through `src/api/client.ts`. Do not call `fetch` directly,
  because the client attaches the auth header and retries on 429.

## Traps

- `pnpm build` does not type check. Vite strips the types instead of
  checking them, so a broken type still produces a green build.
  Run `pnpm typecheck` as a separate step.

File của worker có cùng cấu trúc nhưng nội dung khác: install command, pytest -q, lý do consumer phải luôn idempotent và migration phải chạy trước khi test pass. File infra là nơi ghi các rule ngăn agent gây hỏng hệ thống. Không bao giờ chạy terraform apply. Chỉ chạy terraform plan rồi dừng lại, đồng thời nêu rõ state backend đã được cấu hình để agent không cố khởi tạo một state backend mới.

Hãy lưu ý những gì không xuất hiện trong bất kỳ file nào: mô tả mục đích của từng service. Phần đó dành cho con người. Upstream cũng phân định như vậy: "README.md files are for humans: quick starts, project descriptions, and contribution guidelines", còn AGENTS.md chứa "the extra, sometimes detailed context coding agents need: build steps, tests, and conventions." Phần phân tách giữa AGENTS.md và README dành cho con người trình bày ranh giới này theo từng câu. Một file DESIGN.md ghi lại lý do code có cấu trúc như hiện tại trình bày file thứ ba, dùng để giải thích các quyết định thay vì các command.

Ai cập nhật file khi code thay đổi?

Chỉ cần một quy tắc và ghi quy tắc đó trong file ở root: ai thay đổi code trong một thư mục thì phải cập nhật AGENTS.md của thư mục đó trong cùng commit.

Quy tắc này hiệu quả vì lý do cơ học, không phải vì văn hóa. File theo từng thư mục nằm trong cùng diff với code, nên reviewer của pull request thấy cả hai cùng lúc. File ở root thuộc về tất cả mọi người, nghĩa là không thực sự thuộc về ai, và không bao giờ xuất hiện trong diff mà mọi người đang đọc.

Hãy kiểm tra quy tắc này trong pull request. Kiểm tra sẽ tìm AGENTS.md gần nhất phía trên mỗi file đã thay đổi, rồi báo cáo khi file đó chưa được chỉnh sửa.

#!/usr/bin/env bash
# Warn when code changed but the nearest AGENTS.md above it did not.
changed=$(git diff --name-only origin/main...HEAD)

nearest_doc() {
  d=$(dirname "$1")
  while [ "$d" != "." ]; do
    if [ -f "$d/AGENTS.md" ]; then echo "$d/AGENTS.md"; return; fi
    d=$(dirname "$d")
  done
  echo "AGENTS.md"
}

printf '%s\n' "$changed" | while read -r f; do
  [ -n "$f" ] || continue
  case "$f" in AGENTS.md|*/AGENTS.md) continue ;; esac
  doc=$(nearest_doc "$f")
  printf '%s\n' "$changed" | grep -Fqx "$doc" && continue
  echo "note: $f changed but $doc was not updated"
done

Với một branch đã thay đổi API client nhưng không chỉnh sửa tài liệu, output sẽ như sau:

note: apps/web/src/api/client.ts changed but apps/web/AGENTS.md was not updated

Hãy để đây là cảnh báo thay vì lỗi. Một hard gate sẽ khiến mọi người thêm một dòng trống vào file để CI chuyển sang màu xanh. Một file được chỉnh sửa chỉ để làm hài lòng robot còn kém giá trị hơn cả việc không có file. Cảnh báo cung cấp cho reviewer một câu hỏi cần đặt ra. Đó mới là phần thực sự hiệu quả.

Làm thế nào để phát hiện một file AGENTS.md đã lỗi thời?

Bạn có thể thực hiện 2 kiểm tra ngay hôm nay và sẽ thấy 1 dấu hiệu trong một session.

So sánh tuổi của từng file với tuổi của code mà file đó mô tả. %cs in ngày commit dưới dạng YYYY-MM-DD.

for f in $(git ls-files '*AGENTS.md'); do
  d=$(dirname "$f")
  printf '%s  doc:%s  code:%s\n' "$f" \
    "$(git log -1 --format=%cs -- "$f")" \
    "$(git log -1 --format=%cs -- "$d")"
done
apps/web/AGENTS.md          doc:2026-02-11  code:2026-08-07
services/worker/AGENTS.md   doc:2026-07-29  code:2026-08-09
infra/AGENTS.md             doc:2026-08-01  code:2026-08-01

Ngày của tài liệu chậm hơn ngày của code 6 tháng không chứng minh file đó sai. Nó cho bạn biết nên đọc file nào trước. Đó là tất cả những gì bạn cần từ một kiểm tra chỉ mất 1 giây.

Tìm các path không còn tồn tại. Tài liệu bị lỗi thời theo một cách rất cụ thể: vẫn mô tả code đã bị xóa. Mọi path trong các file này đều được viết trong backtick, nên dễ dàng trích xuất và kiểm tra.

grep -o '`[^`]*`' apps/web/AGENTS.md | tr -d '`' | grep '/' | while read -r p; do
  [ -e "$p" ] || [ -e "apps/web/$p" ] || echo "missing: $p"
done

Hãy đọc output thay vì đưa bước này vào CI. Lệnh cũng đánh dấu các glob như src/**/*.ts và mọi URL bạn đặt trong dấu ngoặc kép, vì cả hai đều chứa dấu gạch chéo và không cái nào là file trên disk.

Dấu hiệu trong một session. Agent đọc file, cố mở src/api/client.ts vì file yêu cầu như vậy, và tool trả về:

No such file or directory

Vì vậy, agent làm điều hợp lý và tự viết wrapper fetch. Đây mới là chi phí thực sự của một file lỗi thời. Agent không bỏ qua tài liệu của bạn. Nó làm theo tài liệu, đến một path đã bị xóa từ 3 tháng trước, rồi viết lại code mà bạn đã có. Một skill như Ponytail, buộc agent chỉ thực hiện thay đổi nhỏ nhất có hiệu quả, giúp giảm xu hướng viết lại này, nhưng không thể tìm thấy helper nếu file của bạn trỏ sai vị trí.

Claude Code có đọc các file AGENTS.md không?

Không. Cần nói rõ điều này vì layout lồng nhau phụ thuộc vào nó. Tính đến tháng 8 năm 2026, tài liệu ghi rằng: "Claude Code đọc CLAUDE.md, không đọc AGENTS.md." Mẫu này vẫn hoạt động. Bạn chỉ cần đặt một CLAUDE.md bên cạnh mỗi AGENTS.md.

Dạng import phù hợp khi bạn muốn thêm các dòng dành riêng cho tool bên trên các dòng dùng chung. Đặt nội dung sau vào services/worker/CLAUDE.md:

@AGENTS.md

## Claude Code

Use plan mode for changes under `services/worker/migrations/`.

Dạng symlink phù hợp khi không có nội dung riêng cho tool cần thêm.

git ls-files '*AGENTS.md' | while read -r f; do
  ln -s AGENTS.md "$(dirname "$f")/CLAUDE.md"
done
ls -l apps/web/CLAUDE.md

ln không in gì khi chạy thành công, nên hãy kiểm tra danh sách bằng: apps/web/CLAUDE.md -> AGENTS.md. Sau đó bắt đầu một session và chạy /context. Các file đã được nạp sẽ xuất hiện trong Memory files. Trên Windows, symlink cần quyền Administrator hoặc Developer Mode, nên dùng dạng import @AGENTS.md thay thế.

Có một điểm dễ nhầm cần nói thêm. Sau /compact, file ở root được đọc lại từ disk, nhưng các file lồng nhau trong subdirectory không được nạp lại. Chúng sẽ được nạp lại vào lần tiếp theo agent đọc một file trong directory đó. Nếu một rule theo từng directory có vẻ ngừng được áp dụng giữa chừng trong một session dài, nguyên nhân thường là vậy. Chỉ cần thay đổi bất kỳ file nào trong directory đó là rule sẽ được nạp lại.

Các settings để trỏ agent khác đến AGENTS.md

Codex đọc AGENTS.md trực tiếp. Ở mỗi cấp, nó kiểm tra AGENTS.override.md trước. Cách này cho phép một directory có override cục bộ mà không cần sửa file dùng chung. Codex dừng merge khi tổng kích thước đạt 32 KiB, là giá trị mặc định của project_doc_max_bytes. Đây cũng là một lý do nên giữ file ở root nhỏ.

Aider thực hiện việc này qua .aider.conf.yml bằng dòng read: AGENTS.md.

Gemini CLI thực hiện việc này qua .gemini/settings.json bằng { "context": { "fileName": "AGENTS.md" } }.

Tài liệu upstream mô tả một tên mới tương thích ngược cho các repository vẫn dùng tên số ít cũ: mv AGENT.md AGENTS.md && ln -s AGENTS.md AGENT.md.

Trong một monorepo rất lớn, setting claudeMdExcludes của Claude Code bỏ qua các file ở ancestor theo path hoặc glob. Điều này hữu ích khi directory của team khác nằm phía trên directory của bạn.

Điểm này khác gì với agent memory hoặc skill?

Các cơ chế này trông giống nhau nhưng có cách hỏng hoàn toàn khác nhau. Vì vậy, cần xác định chính xác bạn đang cần cơ chế nào.

AGENTS.md do bạn viết, commit vào git và review trong pull request. Nội dung này giống nhau với mọi người clone repository. Agent memory do agent ghi, được lưu bên ngoài repository và chỉ có trên một máy. Tài liệu Claude Code cũng phân biệt như vậy: CLAUDE.md chứa “Instructions and rules” do bạn viết, còn auto memory chứa “Learnings and patterns” do Claude ghi; thư mục memory không được chia sẻ giữa các máy. Cách kiểm tra rất đơn giản. Nếu một thông tin phải đúng với đồng nghiệp vừa clone repository, thông tin đó không thể nằm trong memory. Cách agent memory tồn tại giữa các phiên trình bày phần này chi tiết hơn.

Skill là cơ chế thứ ba. AGENTS.md là context được nạp trong mọi session; skill là một quy trình chỉ được nạp khi cần. Tài liệu Claude Code đưa ra một quy tắc dễ áp dụng: “Nếu một mục là quy trình nhiều bước hoặc chỉ liên quan đến một phần của codebase, hãy chuyển mục đó vào skill hoặc rule áp dụng theo path.” Vế sau của câu này chính là trường hợp nested AGENTS.md giải quyết. Vế trước là mục đích của agent skills. Khi cùng một quy trình cần dùng trong nhiều repository, hãy chia sẻ skill giữa các repo thay vì dán cùng một đoạn văn vào 10 file AGENTS.md khác nhau.

Upstream ghi rằng “tại thời điểm viết bài, repository OpenAI chính có 88 file AGENTS.md”. Con số này chính là toàn bộ lập luận. Một repository lớn không cần một file lớn hơn. Nó cần nhiều file nhỏ hơn, mỗi file nằm cạnh phần code mà nó mô tả và do người thay đổi phần code đó gần nhất phụ trách.

FAQ

Tệp AGENTS.md lồng nhau thay thế tệp ở thư mục gốc hay bổ sung vào đó?

Nó bổ sung vào tệp ở thư mục gốc. Tài liệu upstream nói rằng “tệp gần nhất được ưu tiên”. Điều này mô tả cách xử lý khi có xung đột, không phải những gì được nạp. Codex “nối các tệp từ thư mục gốc xuống dưới, phân cách bằng các dòng trống”, còn Claude Code nối mọi tệp tìm được khi đi ngược từ thư mục làm việc lên, thay vì ghi đè chúng. Tệp gần nhất chỉ được ưu tiên khi hai tệp đưa ra hướng dẫn khác nhau về cùng một vấn đề. Hãy viết các quy tắc dùng chung một lần ở tệp gốc và không lặp lại chúng trong mọi thư mục.

Tệp AGENTS.md ở thư mục gốc nên dài bao nhiêu?

Đủ ngắn để bạn không ngại dán nó vào đầu mọi request gửi trong repository đó, vì đó chính là điều xảy ra. Tài liệu của Claude Code khuyến nghị mỗi tệp dưới 200 dòng và cảnh báo rằng tệp dài hơn sẽ “giảm mức độ tuân thủ”. Theo mặc định, Codex dừng hợp nhất các tệp hướng dẫn khi tổng dung lượng đạt 32 KiB. Nếu tệp gốc của bạn ghi lại thông tin về 4 service, phần lớn nội dung sẽ không cần thiết cho một task cụ thể. Hãy chuyển phần chi tiết xuống các tệp theo từng thư mục và để lại một sơ đồ chỉ dẫn.

Làm cách nào để các tệp này không bị lỗi thời?

Đặt một quy tắc trong tệp gốc: ai thay đổi code trong một thư mục thì phải cập nhật AGENTS.md của thư mục đó trong cùng commit. Đặt tệp ngay cạnh code giúp quy tắc này được tuân thủ, vì thay đổi đó sẽ nằm trong cùng pull request diff mà người review đang đọc. Thêm một cảnh báo trong CI để ánh xạ mỗi path đã thay đổi tới AGENTS.md gần nhất ở phía trên, rồi định kỳ so sánh git log -1 --format=%cs trên từng tệp với kết quả của cùng command khi chạy trên thư mục mà tệp đó mô tả.

Claude Code có đọc các tệp AGENTS.md không?

Không. Tính đến tháng 8 năm 2026, tài liệu ghi rõ “Claude Code đọc CLAUDE.md, không đọc AGENTS.md.” Tạo một CLAUDE.md trong cùng thư mục, đặt @AGENTS.md ở dòng đầu tiên. Cách này sẽ nạp tệp dùng chung và cho phép bạn thêm hướng dẫn riêng cho Claude bên dưới. Symlink tạo bằng ln -s AGENTS.md CLAUDE.md hoạt động khi không cần thêm nội dung nào, nhưng trên Windows, thao tác này cần quyền Administrator hoặc Developer Mode. Chạy /context trong một session và xác nhận tệp xuất hiện bên dưới Memory files.

Tôi nên đặt quy tắc chỉ áp dụng đôi khi ở đâu?

Không đặt trong AGENTS.md. Tệp đó được nạp trong mọi session, nên mọi dòng trong đó đều tranh phần chú ý với request bạn thực sự nhập. Một quy trình gồm nhiều bước và chỉ thỉnh thoảng cần dùng nên đặt trong skill, vì skill được nạp theo nhu cầu. Quy tắc chỉ áp dụng cho một thư mục nên đặt trong AGENTS.md của thư mục đó. Thông tin mà agent có thể đọc trực tiếp từ code, chẳng hạn cây thư mục hoặc danh sách dependency, không nên đặt ở đâu cả.