Agent Coding 101Chapter 9 of 10

Đặt hàng rào cho coding agent: cho quyền tới đâu là vừa?

Cách giới hạn quyền, dữ liệu và tác động ra bên ngoài để agent làm nhanh mà mình vẫn còn đường lui.

howznguyen

howznguyen

24 min read

Đặt hàng rào cho coding agent: cho quyền tới đâu là vừa?

Ở bài trước, mình nói về cách dừng một coding agent đang đi vòng rồi chuyển sang chẩn đoán.

Nhưng có những lỗi mà tới lúc gỡ thì đã hơi muộn.

Agent xoá nhầm file chưa commit. Migration chạy vào database thật. Một token bị dán vào log rồi đi vào commit. Script gửi email tới người dùng thật. Hay đơn giản hơn, agent tự push một diff mình chưa review.

Mấy chuyện này không nhất thiết xảy ra vì agent “xấu” hoặc cố tình bỏ qua lời dặn.

Nó xảy ra vì agent có nhiều quyền hơn thứ task cần, còn giới hạn chỉ tồn tại dưới dạng một câu nhắc trong prompt.

Bài này nói về hàng rào an toàn, hay : agent được đọc gì, viết ở đâu, chạy lệnh nào, dùng dữ liệu nào và tới bước nào phải dừng để xin phép.

Mục tiêu không phải làm agent sợ tới mức không dám làm gì.

Mục tiêu là để agent vẫn chạy nhanh, nhưng chưa nhanh tới mức mình quay lại thì căn bếp đã cháy và nó đang viết báo cáo tổng kết rất tự tin.

Lời dặn chưa phải là giới hạn quyền

Trong AGENTS.md hoặc prompt, mình có thể viết:

Plain Text

Đây là lời dặn tốt. Agent nên đọc và làm theo.

Nhưng nếu tiến trình vẫn cầm thông tin đăng nhập của môi trường thật, đọc được cả máy và có token đủ quyền push vào main, ba câu đó chưa tạo ra giới hạn thật.

Mình tách bốn lớp ra như vầy:

LớpVai tròVí dụ
Lời dặnNói agent nên và không nên làm gìQuy tắc project, phạm vi được làm và không được làm
Giới hạn quyềnKhiến hành động ngoài quyền không thể tự chạyPhạm vi file, token chỉ đọc, sandbox, tài khoản riêng
Điểm xin phépBuộc agent dừng trước hành động có rủi roPush, deploy, migration, gửi tin nhắn, xoá dữ liệu
Kiểm traXem trạng thái sau hành động có đúng khôngDiff, test, audit log, kiểm tra database, kiểm tra bản deploy

Lời dặn giúp agent chọn đúng.

Giới hạn quyền thu nhỏ hậu quả khi nó chọn sai.

Điểm xin phép giữ quyết định quan trọng ở lại với con người.

Kiểm tra giúp phát hiện khoảng cách giữa điều được phép và điều thật sự xảy ra.

Thiếu một lớp chưa chắc quy trình mất an toàn ngay. Nhưng đừng dùng một dòng hướng dẫn để giả vờ mình đã có đủ cả bốn.

📝

Hàng rào tốt không phụ thuộc vào trí nhớ của agent

Nếu ranh giới chỉ hoạt động khi agent nhớ một dòng ở đầu ngữ cảnh, nó sẽ yếu dần khi session dài, ngữ cảnh bị rút gọn hoặc task chuyển qua agent khác.

Rủi ro không chỉ nằm trong câu lệnh

Cùng một câu lệnh có thể rất bình thường ở local và rất nguy hiểm ở production.

Một migration trên database test dùng dữ liệu giả có phạm vi ảnh hưởng nhỏ. Cùng migration đó chạy bằng thông tin đăng nhập production lại là câu chuyện khác hẳn.

Mình nhìn rủi ro qua ba biến:

Plain Text

Quyền đang có là agent có thể làm loại hành động nào: đọc, viết, chạy lệnh, gọi network, push hay xoá.

Phạm vi ảnh hưởng là hành động có thể đụng tới bao nhiêu file, dữ liệu, người dùng hoặc hệ thống.

Độ khó phục hồi là mình có thể quay lại bằng một patch nhỏ, hay phải đổi secret, khôi phục bản sao lưu và viết một lời xin lỗi rất dài.

Vì vậy, mình không có một cấu hình “an toàn” dùng cho mọi task.

Mình nâng quyền theo từng mức rủi ro.

MứcQuyềnVí dụĐiểm chặn mặc định
0ĐọcTìm code, đọc tài liệu, xem diff, đọc log đã che dữ liệu nhạy cảmCho chạy trong phạm vi
1ViếtSửa file trong thư mục làm việc hoặc worktree riêngGiới hạn đường dẫn, review diff
2Thực thiChạy test, build, formatter, migration localDanh sách câu lệnh cho phép hoặc xin phép theo từng lệnh
3Tác động ra ngoàiPush nhánh, mở PR, gửi email test, ghi ticketXem trước nội dung và đồng ý rõ ràng
4Phá huỷXoá dữ liệu, force push, migration production, đổi thông tin đăng nhậpNgười phụ trách tự làm hoặc cấp phép đặc biệt

Mức cao hơn không mặc định chứa toàn bộ quyền của mức trước.

Ví dụ agent có quyền gọi một API không có nghĩa nó cần đọc mọi file trong thư mục cá nhân. Agent được phép push nhánh cũng không có nghĩa nó được merge.

Năm tầng quyền từ đọc, viết, thực thi tới tác động ra ngoài và phá huỷ
Quyền nên tăng theo nhu cầu, phạm vi ảnh hưởng và độ khó phục hồi, không tăng theo độ tự tin của agent.

Bắt đầu bằng quyền tối thiểu cần thiết

NIST gọi nguyên tắc này là , tức người dùng hoặc tiến trình chỉ có lượng quyền tối thiểu cần để hoàn thành việc được giao.

Áp vào coding agent, câu hỏi không phải “agent có thể dùng công cụ này không?”.

Câu hỏi là “task hiện tại cần đúng quyền gì, trên đúng tài nguyên nào và trong bao lâu?”.

Một task sửa copy trong form có thể chỉ cần:

  • Đọc repository hiện tại
  • Viết một component và test liên quan
  • Chạy formatter cùng test liên quan
  • Không cần network
  • Không cần credential
  • Không cần push

Một task tích hợp với nhà cung cấp email có thể cần mạng, nhưng chưa chắc cần khoá production. Adapter giả hoặc tài khoản thường đủ để làm và kiểm tra phần lớn công việc.

Mình cố gắng giới hạn theo bốn chiều:

Tài nguyên. Agent được chạm repository, thư mục, database, tài khoản hoặc service nào?

Hành động. Đọc, viết, thực thi hay tạo tác động ra bên ngoài?

Thời gian. Quyền tồn tại cho một câu lệnh, một session hay lâu dài?

Danh tính. Agent đang dùng tài khoản cá nhân của mình, tài khoản service riêng hay thông tin đăng nhập tạm?

Nếu một quyền chỉ cần cho đúng một bước, mình không để nó nằm sẵn trong môi trường từ đầu tới cuối task.

Đặc biệt, đừng nhầm "agent chạy trên máy mình" với "agent chỉ thấy project mình".

Thư mục cá nhân còn có khoá SSH, lịch sử shell, cấu hình cloud, hồ sơ trình duyệt và repository khác. Ranh giới file phải được cấu hình lúc chạy hoặc bằng workspace riêng, không chỉ bằng lời dặn.

Chốt đường đi trước khi bật quyền

Trước một task được sửa file hoặc chạy lệnh, mình thích có một bản chốt ngắn về đường đi.

Không cần tài liệu dài. Một khối như sau đã đủ:

Plain Text

Bản chốt này làm hai việc.

Nó giúp mình phát hiện task đang xin quá nhiều quyền trước khi bắt đầu. Nó cũng nối phần lên kế hoạch với phần thực thi bằng một thoả thuận đủ rõ.

Nếu agent đang làm mà phát hiện phải sửa file ngoài phần được phép, nó không tự mở rộng phạm vi. Nó dừng và giải thích dependency mới.

Nếu câu lệnh kiểm tra khác dự kiến, agent cho mình xem trước câu lệnh, lý do và tác động phụ trước khi chạy.

Giới hạn file phải đi cùng giới hạn trạng thái

Giới hạn đường dẫn là một hàng rào tốt, nhưng chưa đủ nếu nhiều task cùng dùng một thư mục làm việc.

Agent A chỉ sửa backend và agent B chỉ sửa frontend vẫn có thể cùng làm thay đổi lockfile, client sinh tự động hoặc schema dùng chung.

Mình thường dùng:

  • Một nhánh hoặc worktree riêng cho agent thực thi có diff lớn
  • Một người duy nhất phụ trách vùng file dùng chung
  • git statusgit diff trước lẫn sau thao tác
  • Chụp trạng thái trước migration, công cụ sinh code hoặc sửa hàng loạt
  • Review danh sách file trước khi soi từng dòng

Mốc trạng thái không nhất thiết là commit nếu repository còn thay đổi chưa sẵn sàng.

Nó có thể là một worktree tách riêng, một patch được lưu an toàn hoặc một bản sao lưu có thể kiểm chứng. Điều quan trọng là đường phục hồi có thật, không phải câu “có gì quay lại sau”.

⚠️

Đừng dọn sạch để tạo cảm giác an toàn

Agent không được tự reset, restore hoặc xoá thay đổi có sẵn chỉ để làm working tree sạch. Hàng rào phải bảo vệ trạng thái của người dùng, kể cả khi nó đang dang dở.

Giới hạn câu lệnh phải tính cả tác động ra ngoài

Tên câu lệnh không nói hết rủi ro.

test có thể ghi snapshot. build có thể chạy plugin tải code. install có thể chạy script vòng đời. migration có thể đổi schema. Một CLI ghi “preview” vẫn có thể tạo tài nguyên tạm trên cloud.

Trước câu lệnh chưa nằm trong cách làm đã biết, mình muốn agent trả lời:

Plain Text

Với câu lệnh chỉ đọc, local và quen thuộc, mình không cần xin phép từng lần.

Với câu lệnh tạo tác động bên ngoài, phần xem trước này là mức tối thiểu để mình biết mình đang cho phép chuyện gì.

Kiểu đồng ý “cho phép terminal” quá rộng.

Một lần xin phép tốt gắn với hành động, tài nguyên và khoảng thời gian cụ thể.

Đừng đưa secret qua context nếu không cần

Một lỗi khá dễ gặp là dán API key vào chat để agent “dùng tạm”.

Từ đó secret có thể xuất hiện trong đoạn chat, lịch sử shell, log, ảnh chụp, patch hoặc bản tóm tắt cuối.

Mình giữ vài quy tắc đơn giản:

  • Secret được đưa vào qua trình quản lý secret hoặc biến môi trường, không dán giá trị thẳng vào prompt
  • Agent biết tên biến, không cần biết giá trị
  • Không cho agent chạy câu lệnh in toàn bộ biến môi trường
  • Log và lỗi phải che token, cookie, email nhạy cảm hoặc dữ liệu riêng tư
  • Dùng thông tin đăng nhập ngắn hạn, phạm vi hẹp và tài khoản riêng nếu có thể
  • Không dùng thông tin đăng nhập production cho task chỉ cần dữ liệu giả hoặc sandbox
  • Nếu secret đã lộ, đổi nó trước rồi mới dọn repository hoặc lịch sử

GitHub push protection có thể chặn một số secret được phát hiện trước khi chúng tới repository. Đây là lớp phòng ngừa rất đáng dùng.

Nhưng công cụ quét secret không thay được thói quen giữ secret sạch sẽ.

Nó không đảm bảo nhận ra mọi loại thông tin đăng nhập, token riêng hoặc secret đã bị biến đổi. Và tới lúc push protection chặn, secret có thể đã nằm trong commit local, log hoặc ngữ cảnh.

Hàng rào tốt nhất vẫn là không đưa secret vào vùng agent có thể ghi ngay từ đầu.

Dữ liệu production cần một lớp quyền riêng

Nhiều quy trình chỉ tách local và production theo URL.

Với coding agent, mình tách rõ hơn:

Môi trườngDữ liệuQuyền hợp lý
LocalDữ liệu giảTự động đọc/ghi trong phạm vi
Test/CIBộ dữ liệu tạo lại đượcThực thi qua câu lệnh đã biết
StagingDữ liệu giả nhưng service thậtMạng có giới hạn, xin phép trước tác động lớn
Bản sao production chỉ đọcDữ liệu thậtĐọc tối thiểu, che dữ liệu nhạy cảm, ghi dấu vết và thường cần xin phép
Production primaryDữ liệu thật, thay đổi trực tiếpKhông mặc định giao cho agent

Đọc dữ liệu production cũng có rủi ro.

Câu truy vấn có thể quét dữ liệu nhạy cảm, tạo tải lớn hoặc đưa thông tin định danh vào log. Vì vậy “chỉ đọc thôi” không tự động an toàn.

Nếu cần điều tra production, mình ưu tiên số liệu, trace hoặc câu truy vấn đã tổng hợp và che dữ liệu nhạy cảm. Agent chỉ thấy vừa đủ để trả lời câu hỏi hiện tại.

Nếu task cần một bản ghi mẫu, mình không đổ cả bảng dữ liệu ra.

Mọi tác động ra bên ngoài đều cần xem trước

Tác động ra bên ngoài là bất kỳ thứ gì rời khỏi thư mục làm việc:

  • Push branch hoặc tag
  • Mở, sửa hay đóng PR
  • Gửi email, tin nhắn Slack hoặc thông báo
  • Tạo issue hoặc ticket
  • Tải tài nguyên lên
  • Deploy
  • Tạo tài nguyên cloud
  • Thay DNS, secret hoặc biến môi trường

Không phải tác động phụ nào cũng nguy hiểm ngang nhau. Tải một ảnh blog lên có thể phục hồi dễ hơn gửi email cho toàn bộ người dùng.

Nhưng mình vẫn muốn một bản xem trước rõ ràng:

Plain Text

Sau khi được duyệt, agent chỉ thực hiện đúng thao tác đã cho xem trước. Nếu nội dung thay đổi đáng kể, lần đồng ý cũ hết hiệu lực.

Đây cũng là lý do mình tách commit, push, open PRmerge thành bốn quyền khác nhau.

Agent được commit không mặc định được push. Được mở PR không mặc định được merge.

Bốn lớp hàng rào gồm phạm vi, giới hạn quyền, điểm xin phép và bước kiểm tra
Một lời dặn sẽ đáng tin hơn khi được biến thành phạm vi, giới hạn quyền, điểm xin phép và bước kiểm tra thật.

Đặt điểm xin phép trước chỗ khó quay đầu

Xin phép sau khi đã làm chỉ còn là thông báo.

Mình đặt điểm xin phép ngay trước lúc phạm vi ảnh hưởng tăng mạnh:

  • Trước khi ghi ra ngoài phần đã được giao
  • Trước câu lệnh cần mạng hoặc thông tin đăng nhập mới
  • Trước migration thay schema hoặc dữ liệu
  • Trước khi ghi vào staging có người dùng chung
  • Trước push, deploy, publish hoặc gửi tin nhắn
  • Trước thao tác khó quay lại như xoá, đổi secret hoặc cập nhật cưỡng bức

Một lời xin phép tốt phải đủ cụ thể để mình trả lời mà không cần mở lại toàn bộ session.

Plain Text

Một câu kiểu “cho mình quyền push nhé” không cho người duyệt biết push cái gì và quyền đó dừng ở đâu.

Luật chặn thật tốt hơn lời hứa

Ở repository từ xa, nhánh được bảo vệ là ví dụ rõ của một hàng rào có thể thực thi thật.

GitHub cho phép yêu cầu review pull request, kiểm tra trạng thái, giải quyết hội thoại và giới hạn ai được push vào nhánh. Một số quy tắc còn có thể chặn luôn chuyện đi đường vòng.

Điểm quan trọng không nằm ở GitHub cụ thể.

Cách làm là:

  • Agent thực thi tạo thay đổi ở vùng riêng
  • Các bước kiểm tra tự động tạo bằng chứng
  • Người review có quyền đồng ý
  • Điểm đến được bảo vệ chỉ nhận thay đổi khi đủ điều kiện

Đừng đưa token có quyền đi đường vòng vào một automation rồi kỳ vọng lời dặn “không được đi đường vòng” sẽ mạnh bằng quy tắc bảo vệ nhánh.

Nếu agent cần mở PR, token chỉ cần quyền mở PR. Merge vẫn nằm ở một danh tính hoặc điểm xin phép khác.

Nghĩ đường lui trước khi hành động

Kế hoạch viết sau khi lỗi xảy ra thường rất lạc quan. Lúc chưa có chuyện thì ai cũng thấy nút “undo” ở rất gần.

Trước thao tác làm đổi trạng thái, mình hỏi:

  • Trạng thái trước thao tác có ảnh chụp không?
  • Có bản sao lưu và đã từng thử khôi phục chưa?
  • Quay lại có làm mất thay đổi hợp lệ khác không?
  • Tin nhắn đã gửi có thể thu hồi hay chỉ có thể gửi đính chính?
  • Đổi thông tin đăng nhập có ảnh hưởng service nào khác?
  • Ai phụ trách nếu việc phục hồi vượt quá task?

Với migration, “có down migration” chưa chắc đồng nghĩa quay lại an toàn. Nếu migration đã biến đổi hoặc xoá dữ liệu, chạy ngược schema không tự đem dữ liệu quay lại.

Với Git, “có thể revert” cũng không bảo vệ file chưa commit hoặc secret đã push công khai.

Mình thích phân loại:

Plain Text

Thao tác càng gần nhóm cuối, lời xin phép càng phải tới đúng người phụ trách.

Ví dụ hàng rào cho tính năng mời vào workspace

Giả sử bài tổng kết tiếp theo sẽ làm một lát cắt: admin tạo lời mời vào workspace.

Trước khi giao code, mình chia quyền theo từng giai đoạn.

Giai đoạn tìm hiểu:

  • Đọc repository, sổ tay project và đặc tả
  • Không sửa file
  • Không dùng mạng ngoài tài liệu đã cho phép
  • Không cần secret

Giai đoạn thực thi:

  • Sửa service, route, migration và test trong worktree riêng
  • Chạy test liên quan, typecheck và migration local trên database tạo lại được
  • Dùng email adapter giả
  • Không push
  • Dừng nếu cần đổi thoả thuận công khai hoặc mô hình phân quyền

Giai đoạn ghép:

  • Review migration và đầu ra sinh tự động
  • Chạy test đồng thời trên trạng thái cuối
  • Không dùng thông tin đăng nhập staging trước khi được duyệt

Giai đoạn kiểm tra bên ngoài:

  • Đưa thông tin đăng nhập email sandbox với phạm vi hẹp vào lúc chạy
  • Chỉ gửi tới test inbox đã chỉ định
  • Xem trước người nhận và nội dung
  • Không chạm tới người dùng production

Giai đoạn bàn giao:

  • Commit sau review
  • Push nhánh sau khi được đồng ý rõ ràng
  • Mở PR với gói bằng chứng
  • Không merge hoặc deploy trong cùng một lần xin phép

Bản chốt trước khi chạy có thể viết:

Plain Text

Hàng rào này không tự làm phần triển khai đúng.

Nó bảo đảm nếu phần thực thi sai, hậu quả vẫn nằm trong một vùng nhỏ và bằng chứng còn đủ để review.

Prompt cho một lượt thực thi có hàng rào

Mình có thể đưa thoả thuận vào prompt theo mẫu sau:

Plain Text

Prompt vẫn chỉ là lời hướng dẫn.

Mình cần cấu hình môi trường chạy, token, tài khoản và quy tắc repository để ranh giới quan trọng không chỉ nằm trên giấy.

Khi agent xin thêm quyền

Xin thêm quyền không phải một dấu hiệu xấu.

Một agent tốt nên dừng khi task vượt quyền hiện tại.

Mình đánh giá lời xin qua năm câu hỏi:

  1. Quyền mới có thật sự cần cho kết quả không?
  2. Có cách lấy cùng bằng chứng bằng quyền thấp hơn không?
  3. Tài nguyên và thời gian có thể thu hẹp thêm không?
  4. Thao tác đã được xem trước và có đường phục hồi chưa?
  5. Ai là người đúng để duyệt?

Nếu câu trả lời chưa rõ, mình giữ quyền cũ và chuyển về bước tìm hiểu.

Không nên thưởng cho một agent đang kẹt bằng cách đưa toàn quyền để nó “tự xử”. Càng chưa chắc chắn, phạm vi ảnh hưởng càng nên nhỏ lại.

Với Knowns, quyền nên đi cùng task

Trong Knowns, mình có thể ghi phần được phép, không được phép, cần xin phép và điều kiện dừng ngay trong ghi chú task hoặc đặc tả.

Agent nhận task mới không cần đoán lại ranh giới từ đoạn chat cũ.

Khi quyền thay đổi, ghi chú phải nói rõ ai duyệt, thao tác nào được mở và quyền đó kết thúc ở đâu.

Bằng chứng sau khi chạy quay lại task: câu lệnh chính xác, phạm vi diff, URL hoặc đầu ra đã tạo, cùng phần chưa kiểm tra.

Quyết định lâu dài như “migration production luôn cần người phụ trách đồng ý” nên nằm trong tài liệu project. Nó không nên biến mất khi task lời mời đóng lại.

Knowns không tự thực thi giới hạn file, token hoặc bảo vệ nhánh.

Nó giữ thoả thuận ở chỗ dễ tìm. Môi trường chạy và nền tảng mới là nơi thực thi quyền thật.

Checklist trước khi giao quyền cho agent

  • Mục tiêu và nguồn chuẩn đã rõ chưa?
  • Task cần quyền đọc, viết, thực thi, tác động ra ngoài hay phá huỷ?
  • File đã được giới hạn đúng workspace chưa?
  • Đường dẫn được sửa và vùng dùng chung có người phụ trách rõ không?
  • Câu lệnh nào có tác động phụ, dùng mạng hoặc thông tin đăng nhập?
  • Có thể dùng dữ liệu giả, adapter giả hoặc sandbox thay production không?
  • Secret có được đưa vào mà không đi qua prompt và log không?
  • Dữ liệu nhạy cảm đã được thu nhỏ và che bớt chưa?
  • Điểm xin phép nằm trước push, deploy, publish hoặc migration chưa?
  • Lời xin phép có đích đến, nội dung, rủi ro và cách phục hồi cụ thể không?
  • Nhánh hoặc điểm đến quan trọng có quy tắc thực thi thật không?
  • Ảnh chụp trạng thái và cách quay lại đã có trước thao tác chưa?
  • Thao tác có thể quay lại, có thể phục hồi hay không thể thu hồi hoàn toàn?
  • Agent biết dừng khi phạm vi hoặc rủi ro thay đổi chưa?
  • Bước kiểm tra sẽ quan sát trạng thái cuối nào?

Không phải task nào cũng cần mười lăm dòng.

Một lỗi chính tả không cần nghi lễ bảo mật. Nhưng nếu task chạm secret, dữ liệu production hoặc người dùng bên ngoài, vài phút viết hàng rào rẻ hơn rất nhiều so với một buổi đổi thông tin đăng nhập và dọn hậu quả.

Kết

Mình không đánh giá độ an toàn bằng câu agent nói “đã cẩn thận”.

Mình nhìn quyền nó đang có, phạm vi ảnh hưởng của hành động và đường phục hồi nếu giả định ban đầu sai.

Lời hướng dẫn vẫn quan trọng. Nhưng nó nên đứng cùng giới hạn quyền, điểm xin phép và bước kiểm tra.

Quyền nên tăng dần từ đọc, sửa trong phạm vi, thực thi tới tạo tác động ra bên ngoài. Thao tác phá huỷ hoặc đụng production không bao giờ trở thành bước phụ chỉ vì agent làm các bước trước nhanh.

Hàng rào tốt không làm coding agent chậm đi ở mọi chỗ.

Nó giữ các bước rẻ được tự động, còn quyết định đắt và khó quay lại thì dừng đúng chỗ.

Bài cuối của series sẽ ghép mọi thứ lại thành một bài tổng kết: từ một ý tưởng mơ hồ, mình tạo nguồn chuẩn, chia task, giao agent làm, xử lý lỗi, review bằng chứng và chuẩn bị một pull request sẵn sàng cho con người quyết định.

See yah.

Tài liệu tham khảo