FDE PulseViệc làm FDE đang mở 642Mới trong 7 ngày 42Công ty đang tuyển 97Nhận làm từ xa 25%Lương trung vị (Mỹ) $204kTuyển nhiều nhất Databricks 122
EN

Tờ báo của nghề Forward Deployed Engineer

Bách khoa

AGENTS.md và PreToolUse hook: hai lớp giữ coding agent tránh xa thư mục migration của khách

Chữ "Never" trong một file Markdown chỉ là lời dặn, nên vùng cấm thật của khách cần thêm một lớp chặn.

Kỹ sư ngồi trước laptop đang xem mã nguồn trong văn phòng, hoặc một phòng máy chủ chứa cơ sở dữ liệu.
Ảnh: Emile Perron / Unsplash
Chia sẻLinkedInFacebookX

Tóm tắt nhanh

  • GitHub khuyên chia ranh giới thành ba tầng, và tầng "Never" phải nêu đường dẫn cụ thể như `db/migrations/`.
  • Agent đọc file AGENTS.md gần nhất, còn Codex mặc định nạp tối đa 32 KiB, nên hãy tách file theo thư mục và viết ngắn.
  • AGENTS.md chỉ là ngữ cảnh: muốn chặn hẳn, dùng PreToolUse hook exit 2, và nhớ hook chỉ chặn được lỗi vô ý.
Cây quyết định: nếu sửa nhầm gây sự cố thật thì xếp vào tầng Never và thêm PreToolUse hook (ví dụ db/migrations/). Nếu không, hỏi tiếp: cần người của khách duyệt trước? Có thì Ask first (services/billing/), không thì Always (make lint, make test).
Ba tầng ranh giới trong AGENTS.md của repo logistics giả định. Chỉ ghi “Never” trong file thì chưa đủ: vùng cấm thật cần hook chặn riêng.

Trong một repo lạ, coding agent không biết thư mục nào đang chạy thật trên production. Với nó, db/migrations/ trông cũng như mọi thư mục khác. GitHub đã phân tích hơn 2.500 repo có file AGENTS.md, và một lời khuyên trong đó rất thẳng: hãy nói rõ cho agent những gì nó không bao giờ được đụng vào.

Nhưng nói rõ chưa phải là chặn được. Tài liệu của Claude Code ghi rằng các file hướng dẫn này chỉ được coi là ngữ cảnh chứ không phải cấu hình bắt buộc. Muốn chặn hẳn một hành động thì phải dùng PreToolUse hook.

Bài này dành cho FDE phải vào codebase của khách và làm việc cùng agent ngay tuần đầu. Đi hết sáu bước, bạn sẽ có hai lớp bảo vệ: một AGENTS.md để agent hiểu luật, và một hook để vùng cấm thật sự bị khóa. Bạn cũng sẽ biết giới hạn của từng lớp, để không hứa với khách điều chúng không làm được.

Bạn sẽ dựng gì, và cần chuẩn bị gì?

Cả bài đi theo một ví dụ giả định: monorepo của một công ty logistics, gồm services/api, services/billing và thư mục db/migrations đang chạy thật trên production. Mọi lệnh như make test trong bài đều là lệnh minh họa cho repo giả định này. Khi làm thật, bạn thay bằng lệnh mà team của khách đang dùng.

Bạn cần quyền đọc repo và một coding agent hỗ trợ AGENTS.md. Claude Code đọc được AGENTS.md thay cho CLAUDE.md, còn Codex có cơ chế riêng để nạp file này. Quan trọng hơn cả là nửa tiếng ngồi với một kỹ sư của khách để hỏi họ build, test thế nào và thư mục nào không ai được sửa.

Bước 1: viết cho agent như viết README cho người mới

Trang agents.md mô tả file này như một README dành cho agent. Nó chỉ là Markdown thường, đặt heading thế nào cũng được, vì agent chỉ đọc chữ bạn viết. Không có schema nghĩa là cũng không có cú pháp nào bảo đảm agent tuân theo.

Nên mở file bằng những lệnh agent sẽ phải chạy, viết đủ flag:

# AGENTS.md

## Commands
- Build: `make build`
- Test (một service): `make test SERVICE=api`
- Lint trước khi commit: `make lint`

Kiểm tra: tự chạy từng lệnh ở máy bạn. Lệnh nào báo lỗi thì agent cũng gặp lỗi đó, chỉ khác là nó có thể tự “sửa” theo cách bạn không muốn.

Bước 2: ranh giới ba tầng, có đường dẫn cụ thể

Với repo của khách, đây là phần quan trọng nhất. GitHub đề xuất ba tầng: luôn làm, hỏi trước, không bao giờ làm, và coi ranh giới là một trong sáu phần cốt lõi mà file nào cũng nên có. Ví dụ agent viết tài liệu của GitHub nhận một lệnh cấm ở mức đường dẫn: không bao giờ đụng vào src/. Bạn áp đúng mẫu đó cho migration:

## Boundaries
- Always: chạy `make lint` và `make test` trước khi đề xuất commit.
- Ask first: thêm dependency mới; sửa file trong `services/billing/`.
- Never: sửa hay xóa file trong `db/migrations/`; commit file `.env`.

Đừng bỏ tầng giữa, vì ở chỗ khách nó thường có ích nhất. Code tính tiền không phải vùng cấm, nhưng mọi thay đổi ở đó nên được người của khách gật đầu trước.

Bước 3: đặt một file riêng ngay trong thư mục migration

Theo agents.md, agent tự đọc file AGENTS.md gần nhất trong cây thư mục, và file gần nhất được ưu tiên. Codex cũng đi từ project root, thường là Git root, xuống thư mục đang làm việc để ghép chuỗi hướng dẫn. Vì thế một file đặt trong db/migrations/ sẽ được nạp khi agent làm việc ở đó.

# db/migrations/AGENTS.md
Mọi file trong thư mục này đã chạy trên production.
Không sửa, không xóa file có sẵn.
Cần thay đổi schema thì dừng lại và hỏi người của team.

Tách file còn có lý do kỹ thuật. Codex ngừng nạp thêm file khi tổng dung lượng chạm giới hạn project_doc_max_bytes, mặc định 32 KiB. Một file root phình to có thể khiến file con bị bỏ qua mà không ai hay.

Kiểm tra: mở một phiên agent trong db/migrations và hỏi nó đang thấy những quy tắc nào. Nếu quy tắc của thư mục này không xuất hiện, thứ tự file hoặc dung lượng đang có vấn đề.

Bước 4: khóa vùng cấm bằng PreToolUse hook

Một chữ “Never” trong file không phải ổ khóa. Blake Crosley, trong bài giải thích về hook của Claude Code, phân biệt rõ: file hướng dẫn cho model những chỉ dẫn mà nó có lẽ sẽ làm theo, còn hook chạy dù model có hợp tác hay không.

Cơ chế chặn khá đơn giản. Script hook ghi một thông báo ra stderr rồi exit 2: lệnh gọi tool bị từ chối, và thông báo đó được đưa lại cho Claude. Dưới đây là một script minh họa, đã đơn giản hóa: nó giả định hook nhận mô tả lệnh gọi tool qua stdin, rồi chặn mọi lệnh gọi nhắc tới db/migrations/.

#!/usr/bin/env bash
# block-migrations.sh — bản minh họa, đã đơn giản hóa
input=$(cat)

if echo "$input" | grep -q "db/migrations/"; then
  echo "Bị chặn: db/migrations/ đã chạy trên production. Không sửa file ở đây; hãy dừng lại và hỏi team của khách." >&2
  exit 2
fi

exit 0

Hãy để ý câu trong stderr. Vì nó được gửi lại cho agent, hãy viết như một lời giải thích kèm hướng đi tiếp, thay vì chỉ ghi “denied”. Bản đơn giản này còn chặn cả thao tác đọc thư mục; khi làm thật, bạn nên tra định dạng input và cách khai báo matcher trong tài liệu của agent mà khách dùng để chỉ chặn thao tác ghi.

Exit 2 không phải cách duy nhất. Hook cũng có thể trả về một quyết định có cấu trúc là permissionDecision: "deny". Hai cách cùng dẫn tới kết quả là tool bị chặn: một bên dùng mã thoát cùng stderr, bên kia trả quyết định một cách tường minh.

Theo Crosley, hook trả về deny vẫn chặn được tool kể cả ở chế độ bypassPermissions. Ở chỗ khách, nơi có thể có người chạy agent với mọi quyền mở hết, chi tiết này rất đáng giá.

Kiểm tra: yêu cầu agent thêm một dòng vào một file migration cũ. Lệnh sửa phải bị từ chối, và agent phải nhắc lại lý do bạn viết trong stderr.

Bước 5: biết hook cũng có giới hạn

Hook là hàng rào chống sai sót, không phải cơ chế ngăn một agent cố tình lách luật. Bài hướng dẫn chặn rm -rf trên dev.to cũng lưu ý rằng việc khớp theo đường dẫn có thể bị vượt qua.

Thử hình dung script ở trên: nó chỉ tìm đúng chuỗi db/migrations/. Một lệnh shell gọi tới thư mục qua đường dẫn tương đối hay một biến môi trường có thể không chứa chuỗi đó, và hàng rào dễ để lọt.

Vì thế, khi nói chuyện với khách, đừng gọi cả hai lớp này là “biện pháp bảo mật”. Chúng làm sai sót ít xảy ra hơn và dễ bị phát hiện hơn. Bảo vệ thật cho production vẫn là quyền truy cập và quy trình review mà team của khách đã có.

Bước 6: bắt đầu gọn, sửa khi agent sai

Đừng viết sẵn 300 dòng ngay ngày đầu, nhất là khi Codex có giới hạn dung lượng. Giao agent một ticket nhỏ có thật, xem nó chạy lệnh nào, sửa file nào, rồi bổ sung đúng chỗ nó vấp.

Thử hình dung agent chạy make test cho cả repo mất hai mươi phút thay vì chỉ test một service. Bạn thêm một dòng nói rõ cách test riêng bằng SERVICE. Mỗi dòng trong file nên gắn được với một lỗi đã xảy ra hoặc một rủi ro mà khách đã gọi tên.

Ở chỗ khách, hai lớp này dùng vào việc gì?

AGENTS.md thực chất là biên bản của buổi customer discovery về codebase. Câu hỏi “thư mục nào không được đụng vào?” có thể lôi ra những điều chưa từng được ghi lại: một migration từng gây sự cố, hay một module chỉ một người dám sửa.

Ghi lại những điều đó, thêm hook cho vùng nguy hiểm nhất và nhờ tech lead của khách duyệt cả hai. Đó là cách nhanh để họ thấy bạn tôn trọng hệ thống của họ.

Nếu bạn đang chuyển sang FDE, hãy đưa vào portfolio một repo mẫu có AGENTS.md ba tầng, một file con trong thư mục migration, một hook chặn ghi kèm test, và lịch sử commit cho thấy file được sửa dần sau mỗi lỗi của agent.

Khi JD nhắc đến việc triển khai công cụ AI coding cho team khách hàng, bạn có một ví dụ cụ thể để kể trong phỏng vấn thay vì chỉ nói mình “quen dùng agent”.

Bản nháp của bạn có lọt lỗi nào không?

Lấy repo bạn đang làm và soát lại. Lệnh có chạy được nguyên văn không? Ranh giới có đủ tầng “hỏi trước”, hay chỉ có “không bao giờ”? Tầng “Never” có nêu đường dẫn cụ thể không?

Tiếp theo, mở agent trong thư mục con và hỏi nó thấy quy tắc nào. Cuối cùng, với mỗi dòng “Never”, tự hỏi: nếu agent phớt lờ dòng này thì có sự cố thật không? Nếu có, dòng đó cần thêm hook.

Bản AGENTS.md tốt nhất bạn để lại ở chỗ khách là bản mà team của họ tiếp tục sửa sau khi bạn rời dự án, và hook tốt nhất là cái họ hiểu vì sao nó tồn tại.

Bài này có hữu ích không?

Dùng cùng trợ lý AIHỏi Claude ↗Hỏi ChatGPT ↗
6 nguồn
Đọc tiếp trên lộ trình · Chặng 5: Triển khaiAgent ghi vào CRM của khách: chỉ chạy bản kế hoạch đã được duyệtSửa nhầm vài trăm bản ghi khách hàng thì không có Ctrl+Z, nên agent của bạn phải viết ra một bản kế hoạch, và chỉ bản kế hoạch đã được người duyệt mới được chạy.