# Vì sao AI agent nên thôi xin phép

Agent hỏi trước mỗi lệnh, mỗi file, mỗi API call thì autonomy nằm ở đâu? Vì sao ClarkCant coi yêu cầu của bạn là sự cho phép, và những chỗ hiếm hoi nó vẫn dừng lại để hỏi.

Duy Nguyen /zuey/ · 2026-10-05T09:34:38.823Z

Source: https://clarkcant.cc/vi/blog/why-ai-agents-should-stop-asking-for-permission

Chắc bạn đã gặp kiểu UX này. Có khi đang dùng ngay lúc này.

"Tôi muốn chạy lệnh này, cho phép không?" Ừ. "Tôi muốn đọc file kia, cho phép không?" Ừ. "Tôi muốn gọi API này, cho phép không?" Ừ. "Tôi muốn chạy test, cho phép không?" Ừ, trời ơi, thì tôi nhờ bạn làm đúng việc đó mà.

Tới prompt thứ năm là tôi bắt đầu tự hỏi một câu khác: đm vậy thì autonomy nằm đâu :))) Mình giao việc cho agent để khỏi phải canh nó, rốt cuộc vẫn ngồi canh, chỉ là thêm vài bước bấm.

Tôi đang làm ClarkCant, và bài này tôi cố tình nói một chiều về một lựa chọn của team: Clark, con agent, không xin phép để làm cái việc bạn đã nhờ nó làm. Nhưng tôi cũng sẽ nói rõ những chỗ nó vẫn hỏi, vì "không bao giờ hỏi" cũng lười y như "lúc nào cũng hỏi".

## Bấm xác nhận không phải là kiểm soát

Có một sự thật hơi khó chịu về permission prompt. Prompt đầu tiên thì nó hoạt động rất tốt: bạn đọc lệnh, suy nghĩ, rồi quyết. Tới prompt thứ hai mươi thì bạn không đọc nữa. Bạn bấm Allow y như bấm "Tôi đồng ý" ở trang điều khoản sử dụng. Tay đã quen cái nút nhanh hơn đầu kịp hiểu rủi ro.

Tới lúc đó prompt chỉ còn là diễn. Nhìn vẫn giống cơ chế an toàn, ai cũng vẫn nói được "user đã duyệt rồi", nhưng con người trong vòng lặp đã lặng lẽ rời vòng lặp. Tệ hơn, prompt dày nhất đúng lúc bạn đang ở giữa một task dài và mệt nhất, cũng là lúc một lệnh lạ dễ lọt qua nhất.

Tôi không nghĩ người thiết kế mấy flow này dở. Hỏi là cách rẻ nhất để trông có vẻ cẩn thận. Nhưng một cơ chế kiểm soát phụ thuộc vào việc con người tập trung trăm phần trăm, mọi lúc, mãi mãi, thì không phải kiểm soát. Nó là tờ miễn trừ trách nhiệm có gắn cái nút đẹp.

## Chính tôi cũng từng ship cái approval card

Công bằng mà nói: ClarkCant từng làm y vậy. Ngày 18/09/2026 team merge một thay đổi đưa mọi lệnh vào sau một approval card. Ngày hôm sau, team thay nó bằng execution policy. Commit message hôm đó nói rõ hơn tôi: "commands run under an execution policy instead of an approval card", và "asking a person is a policy, not the gate".

Khi nhìn thẳng vào vấn đề, chuyện còn tệ hơn sự phiền phức. Comment còn để lại trong packages/contracts/src/execution.ts nói cái cờ yes/no cũ đã biến approval card thành lớp chịu lực duy nhất: bỏ nó đi là giữa đề xuất của model và cái shell không còn gì cả. Cái card không phải một lớp an toàn. Nó là lớp duy nhất. Và comment đầu file apps/runtime/src/preflight.ts thừa nhận rằng giới hạn thư mục từng bị gỡ ra, nhường chỗ cho chính cái card đó.

Nên bỏ card ra khỏi đường mặc định không phải phần khó. Phần khó là đưa ranh giới thật quay lại host, chỗ mà model không thể nói khéo để lách qua.

Dòng thời gian các commit thật của ClarkCant, giờ Sài Gòn. 18/09/2026, 22:35: đưa lệnh vào sau approval card (#16). 19/09, 18:07: lệnh chạy theo execution policy thay cho approval card. 18:44: hỏi con người là một policy, không phải cửa chặn. 18:49: có emergency stop và audit trail tồn tại qua nó. 19:10: một execution policy cho mọi chỗ có thể gây hiệu lực (#23). 20/09, 16:15: các điều khiển tự chủ chuyển vào tab Control trong Settings.



## Ý định của bạn chính là sự cho phép

Khi bạn gõ "dọn thư mục build rồi chạy lại test", bạn đã cho phép dọn thư mục build và chạy test. Câu đó chính là quyền. Hỏi lại "cho tôi chạy rm -rf dist nhé?" là bắt bạn duyệt lại chính câu mình vừa nói, mà còn ở dạng khó đọc hơn câu bạn viết.

Đó là nguyên tắc trong DESIGN.md của ClarkCant, và tôi trích nguyên văn vì nó được viết thẳng một cách có chủ đích: "Security must not be used as a reason to add a duplicate confirmation after the user has already expressed clear intent." Mức thực thi mặc định là Tự chủ (Autonomous), khai báo đúng một chỗ trong code, là mặc định của execution policy. Nguyên tắc thiết kế cho onboarding cũng rõ y vậy: không chặn bạn bằng bảng câu hỏi xin quyền, chỉ một dòng ghi chú kèm link để đổi mức.

Đây không phải "tin model đi". Đây là "tin vào yêu cầu của người dùng, còn mọi thứ khác thì host phải tự cưỡng chế".

Thẻ trạng thái: mức thực thi mặc định của ClarkCant là Tự chủ, khai báo một lần trong DEFAULT\_EXECUTION\_POLICY\_CONFIG, guardrail Jev bật cho mọi loại trừ việc đọc. Đối chiếu với code ngày 05/10/2026.



So sánh hai cách. Hỏi từng bước: bạn cấp quyền lại ở mỗi bước, mỗi lệnh, file, API call tốn một prompt, tới prompt thứ hai mươi thì bấm Allow không đọc, thứ duy nhất chặn lệnh nguy hiểm là ai đó còn tỉnh táo, ý kiến thứ hai là đôi mắt đã mỏi, việc gửi ra ngoài cũng bị hỏi như mọi thứ, muốn biết chuyện gì xảy ra thì lướt lại prompt, và khi sai thì lỗi của người đã bấm duyệt. Ý định cộng policy, audit và undo: bạn cấp quyền một lần lúc nhờ, không tốn sự chú ý cho tới khi có chuyện đáng hỏi, không có prompt thứ hai mươi, preflight của host cưỡng chế thư mục được giao, deadline và trần output, guardrail Jev chỉ được thu hẹp, tự ý gửi, xoá, tiêu tiền mà bạn không nhờ thì vẫn hỏi, audit trail chỉ ghi thêm và danh sách "Việc đã chạy không hỏi" cho biết chuyện gì đã xảy ra, còn Stop và Undo ở chỗ làm được thật thì xử lý sai sót.

```json
[
  {
    "id": "who",
    "concern": "Ai cấp quyền cho việc này",
    "ask": "Bạn, lại lần nữa, ở mỗi bước",
    "intent": "Bạn, một lần, lúc bạn nhờ"
  },
  {
    "id": "attention",
    "concern": "Tốn bao nhiêu sự chú ý",
    "ask": "Một prompt cho mỗi lệnh, file, API call",
    "intent": "Không gì cả, cho tới khi có chuyện đáng hỏi"
  },
  {
    "id": "fatigue",
    "concern": "Tới prompt thứ hai mươi",
    "ask": "Bấm Allow mà không đọc",
    "intent": "Không có prompt thứ hai mươi"
  },
  {
    "id": "boundary",
    "concern": "Cái gì thật sự chặn lệnh nguy hiểm",
    "ask": "Ai đó còn tỉnh táo",
    "intent": "Preflight của host: thư mục được giao, deadline, trần output"
  },
  {
    "id": "judgment",
    "concern": "Ý kiến thứ hai",
    "ask": "Đôi mắt đã mỏi của bạn",
    "intent": "Guardrail Jev, chỉ được thu hẹp"
  },
  {
    "id": "outward",
    "concern": "Tự ý gửi, xoá, tiêu tiền",
    "ask": "Hỏi, như mọi thứ khác",
    "intent": "Vẫn hỏi, vì bạn đâu có nhờ"
  },
  {
    "id": "after",
    "concern": "Biết chuyện gì đã xảy ra",
    "ask": "Lướt lại đống prompt",
    "intent": "Audit trail chỉ ghi thêm, danh sách Việc đã chạy không hỏi"
  },
  {
    "id": "mistake",
    "concern": "Khi có sai sót",
    "ask": "Bạn đã bấm duyệt, lỗi của bạn",
    "intent": "Stop, và Undo ở chỗ làm được thật"
  }
]
```

## Vậy cái gì giữ nó khỏi làm chuyện ngu?

Không phải một hộp thoại. Mà là một pipeline. Mọi hiệu lực Clark muốn gây ra đều đi đúng một thứ tự: preflight của host, rồi execution policy, rồi guardrail, rồi mới chạy, rồi ghi evidence và audit.

Preflight là tất định và chạy trước khi bất kỳ model nào được hỏi. Lệnh phải nằm trong một thư mục node này sở hữu, thư mục đó phải tồn tại, và lệnh mang theo deadline (hai phút) cùng trần output (8.000 byte mỗi stream). Process con nhận một môi trường theo allowlist, nên SSH\_AUTH\_SOCK, mấy biến AWS\_\*, GITHUB\_TOKEN và key của provider không lọt vào thứ đang chạy. Với agent, secret chỉ là metadata: nó biết có github\_token, dùng để làm gì, còn giá trị thì đi thẳng vào đúng một lần gọi, không bao giờ quay về model.

Sau đó policy quyết, theo thứ tự cố định: "từ chối tất cả" ở cấp node thắng mọi thứ, tiếp theo là ranh giới của hệ điều hành và tài khoản, rồi tới quy tắc theo loại việc bạn đặt, cuối cùng mới là mức tự chủ. Rồi Jev, lớp guardrail, có thể xét lệnh theo luật bạn tự viết ("không bao giờ xoá kho git"). Jev được từ chối, hỏi lại cho rõ, hoặc thu hẹp lệnh. Nếu nó cố nới bất cứ thứ gì, deadline dài hơn, output lớn hơn, thư mục ngoài chỗ host đã chọn, thì cả câu trả lời bị từ chối.

Dòng tôi thích nhất trong codebase là một comment trong packages/core/src/execution-policy.ts: "Autonomy changes who is asked, never what is checked." Tự chủ chỉ đổi ai được hỏi, không bao giờ đổi cái gì được kiểm tra.

Xong việc, bạn không phải đoán. Mọi hiệu lực đều vào một audit trail chỉ ghi thêm, theo tên và kết quả, không bao giờ theo giá trị. Trong Settings có danh sách "Việc đã chạy không hỏi", tồn tại chính vì ở mức Tự chủ có những việc chạy mà không có card nào. Stop là thật: POST /stop giết cả process group của mọi process con, ngắt các lượt đang chạy và huỷ việc nền đang xếp hàng, và lệnh bị dừng được ghi là stopped chứ không phải failed. Undo có ở những chỗ làm được một cách trung thực: đổi một preference thì quay về đúng giá trị trước đó, thông báo đã ẩn thì khôi phục được trong năm phút, rollback một package thì bản generation trước vẫn tiếp tục chạy.

Các con số lấy từ code ClarkCant: 4 mức thực thi trong Settings (Tự chủ, Có rào, Hỏi mỗi lần, Từ chối tất cả); 7 loại hiệu lực (read, local-write, external-write, destructive, financial, communication, media-capture); 5 loại trong đó là rủi ro và vẫn được hỏi khi bạn không nhờ (external-write, destructive, financial, communication, media-capture); 4 loại ranh giới cứng được hỏi ở mọi mức (quyền hệ điều hành, OAuth, quyền trình duyệt, consent của nhà cung cấp).

```json
[
  {
    "id": "levels",
    "label": "Mức thực thi trong Settings",
    "value": 4,
    "hint": "Tự chủ, Có rào, Hỏi mỗi lần, Từ chối tất cả"
  },
  {
    "id": "categories",
    "label": "Loại hiệu lực",
    "value": 7,
    "hint": "read, local-write, external-write, destructive, financial, communication, media-capture"
  },
  {
    "id": "risky",
    "label": "Loại rủi ro, vẫn hỏi nếu bạn không nhờ",
    "value": 5,
    "hint": "external-write, destructive, financial, communication, media-capture"
  },
  {
    "id": "hard",
    "label": "Ranh giới cứng, hỏi ở mọi mức",
    "value": 4,
    "hint": "quyền OS, OAuth, quyền trình duyệt, consent của nhà cung cấp"
  }
]
```

## Khi nào hỏi vẫn là đúng

Giờ tới phần công bằng. Clark vẫn hỏi, và tôi nghĩ đây đúng là những chỗ nên hỏi.

Khi việc đó nằm ngoài ý định của bạn. Mức Tự chủ có một cổng rủi ro: nếu Clark tự quyết làm một việc thuộc nhóm rủi ro, ghi ra thứ gì đó ngoài máy này, xoá dữ liệu, tiêu tiền, gửi tin nhắn, ghi âm hay ghi hình, nó sẽ hỏi, vì bạn có nhờ đâu. Comment trong code gọi đây là khác biệt giữa một trợ lý làm đúng việc được giao và một trợ lý tự đi gửi mail mà chẳng ai nhờ.

Khi đó không phải quyết định của mình. Prompt quyền của hệ điều hành, OAuth consent, quyền camera và micro của trình duyệt, màn hình xác nhận của chính nhà cung cấp, đều được hỏi ở mọi mức, kể cả Tự chủ. ClarkCant không giả vờ cấp được mấy quyền đó thay bạn, và điều khiển trình duyệt hay máy tính không bao giờ tự bấm màn hình consent.

Khi việc đến từ người khác. Một task do máy đã ghép nối giao sang chỉ được chạy trong phạm vi chủ máy này cho phép peer đó. Ngoài phạm vi thì chờ chủ máy, ở mọi mức.

Khi thật sự có nhiều hơn một đáp án đúng. "Xoá project nào?" không phải permission prompt. Đó là một câu hỏi, và nó kết thúc lượt hiện tại, nên chờ bạn trả lời không tốn gì cả.

Trích mã nguồn packages/core/src/execution-policy.ts, dòng 347 tới 380, nhánh autonomous của decideExecution. Quy tắc nói hỏi thì vẫn hỏi. Sau đó là cổng rủi ro: nếu hiệu lực thuộc loại rủi ro và không có chỉ dẫn nào của con người bao trùm, Clark hỏi, với lý do mức tự chủ không hành động ra ngoài máy theo sáng kiến riêng của agent. Còn lại thì chạy và được ghi audit.



Còn nếu bạn đơn giản là thích được hỏi, đó là lựa chọn của bạn, không phải của tôi. Settings có bốn mức: Tự chủ, Có rào (việc trong máy chạy ngay, còn gửi ra ngoài, xoá, chi tiêu, liên lạc, ghi âm ghi hình thì mở card), Hỏi mỗi lần, và Từ chối tất cả. Bạn đặt được quy tắc theo từng loại việc, và "Từ chối" luôn thắng. Bạn cũng có thể bảo Clark đối xử với yêu cầu từ chương trình khác, một AI client qua MCP hay một script gọi API, khác với tin nhắn của chính bạn. Ý tôi không phải hỏi là xấu. Ý tôi là hỏi nên là một policy bạn tự chọn, chứ không phải thứ duy nhất đứng giữa model và cái shell của bạn.

## Chỗ tôi có thể sai

Tôi muốn nói thẳng mấy điểm yếu, vì không thì bài này rẻ tiền quá.

Guardrail là lớp phán đoán, không phải ranh giới bảo mật. Nếu không gọi được Jev, mặc định là vẫn chạy tiếp, chỉ bỏ bước phán đoán. Preflight và vùng cách ly vẫn giữ, bạn cũng chuyển được sang dừng lại, nhưng mặc định là fail-open và bạn nên biết điều đó. Prompt injection từ một trang web vẫn có thể lái model đi sai; cách ly giảm thiệt hại chứ không chứng minh được là an toàn. Không có Undo cho cái email đã gửi đi, và team từ chối làm giả một cái. Và ClarkCant đang là bản bootstrap, chưa phải bản release. Sổ conformance trong repo ghi rõ như vậy.

Có những người rất giỏi tin rằng mọi hành động đều phải được xác nhận, và họ không sai về rủi ro. Tôi chỉ nghĩ xác nhận là công cụ sai cho rủi ro đó. Có thể tôi sai. Chỉ những người thật sự sống chung với agent mỗi ngày mới trả lời được, nên nói tôi nghe.

Khi agent đang làm việc bạn nhờ, bạn thật sự muốn gì?

- Cứ làm đi. Cho tôi xem đã làm gì, cho tôi dừng và undo
- Chỉ hỏi khi không đảo ngược được, gửi ra ngoài, hoặc dính tới tiền
- Hỏi trước mỗi lệnh. Tôi đọc thật mà
- Nói thật, tôi bấm Allow mà không đọc
