Dùng AI

Hooks: tự kiểm tra mỗi lần AI sửa code

Hooks là các lệnh do bạn định nghĩa, được Claude Code tự chạy ở những thời điểm cố định: trước khi AI dùng công cụ, sau khi AI sửa tệp, khi AI dừng trả lời. Khác với chỉ dẫn viết bằng chữ, hook luôn chạy, nên hợp với những luật không được phép quên.

Ý chính
  • Hook là lệnh shell (hoặc lời gọi HTTP, công cụ MCP, lời nhắc cho mô hình) mà Claude Code tự chạy ở các sự kiện như PreToolUse, PostToolUse, Stop. Hook nhận dữ liệu sự kiện dạng JSON qua stdin.
  • Mã thoát quyết định kết quả: 0 là không can thiệp, 2 là chặn hoặc báo lại cho Claude kèm lý do ở stderr.
  • Dùng hook cho luật phải luôn đúng (không sửa .env, mọi tệp Python phải đúng cú pháp). Dùng tệp hướng dẫn hay skill cho những gì cần AI phán đoán.

Bài này dựa trên hướng dẫn và tài liệu tham chiếu về hooks của Claude Code, đọc ngày 11/11/2026. Tên sự kiện, mã thoát và vị trí tệp cấu hình dưới đây là điều tài liệu đang ghi. Một số bài viết trên mạng dùng mã thoát khác để chặn; hãy làm theo tài liệu chính thức.

Hook là gì và khác gì chỉ dẫn bằng chữ?

Theo tài liệu Claude Code, hook là các lệnh shell do người dùng định nghĩa, được Claude Code chạy ở những thời điểm cụ thể trong vòng đời của nó. Tài liệu nhấn mạnh hook cho bạn điều khiển có tính xác định: một việc nào đó luôn xảy ra, thay vì trông vào việc mô hình có chọn làm hay không.

Đây là điểm khác cơ bản với tệp hướng dẫn dự án và skill. Bạn có thể viết trong CLAUDE.md câu "không bao giờ sửa tệp .env", và phần lớn thời gian AI sẽ tuân theo. Nhưng đó vẫn là chữ để mô hình đọc: khi ngữ cảnh bị nén hay khi mô hình phán đoán sai, câu đó có thể bị bỏ qua. Một hook chặn sửa .env thì chạy mỗi lần, bất kể mô hình đang nghĩ gì.

Tệp hướng dẫn, skill Hook
Là gì Chữ để mô hình đọc Lệnh chạy tự động
Có luôn được làm không Tùy mô hình nhớ và hiểu Luôn chạy khi sự kiện xảy ra
Hợp với Quy ước, quy trình cần phán đoán Luật bắt buộc, kiểm tra máy làm được

Ngoài kiểu lệnh shell, tài liệu cho biết hook còn có kiểu gửi dữ liệu tới một địa chỉ HTTP, gọi công cụ của một MCP server, kiểu lời nhắc để một mô hình đánh giá một lần, và kiểu agent có dùng công cụ (đang thử nghiệm). Bài này dùng kiểu lệnh, phổ biến nhất.

Hooks quanh mỗi lần AI sửa tệp Hai hook trong bài: chặn tệp bảo vệ, kiểm cú pháp PreToolUseTrước khi sửa.env, .git/: chặnmã thoát 2 Công cụ chạyEdit, Writesửa tệp thật PostToolUseSau khi sửasai cú pháp: báo lạiClaude tự sửa Lỗi được gửi lại cho Claude ở lượt sau Hook luôn chạy, không phụ thuộc AI nhớ hay quên
Hooks quanh mỗi lần AI sửa tệp PreToolUseTrước khi sửa.env, .git/: chặnmã thoát 2 Công cụ chạyEdit, Writesửa tệp thật PostToolUseSau khi sửasai cú pháp: báo lạiClaude tự sửa Lỗi được gửi lại cho Claude ở lượt sau Hook luôn chạy, không phụ thuộc AI nhớ hay quên
Hình 1. Hai hook quanh một lần AI sửa tệp. Hook PreToolUse chạy trước, chặn sửa .env và .git/ bằng mã thoát 2. Hook PostToolUse chạy sau, nếu tệp Python sai cú pháp thì báo lỗi lại cho Claude để nó sửa ở lượt sau.

Hook chạy khi nào: các sự kiện chính

Bảng sự kiện trong tài liệu lúc đọc có 33 sự kiện. Người mới chỉ cần nắm vài sự kiện sau:

Sự kiện Chạy khi nào Chặn được không
SessionStart Phiên bắt đầu hoặc được mở lại Không
UserPromptSubmit Bạn gửi tin nhắn, trước khi Claude xử lý Có
PreToolUse Trước khi một lời gọi công cụ chạy Có
PostToolUse Sau khi lời gọi công cụ thành công Không (công cụ đã chạy), nhưng báo lại được
Notification Claude Code gửi thông báo, ví dụ đang chờ bạn Không
Stop Claude trả lời xong Có: buộc Claude làm tiếp
PreCompact Trước khi nén ngữ cảnh Có: chặn việc nén

Mỗi khối hook có thể kèm matcher để lọc. Với các sự kiện liên quan công cụ, matcher là tên công cụ, ví dụ "Edit|Write" nghĩa là chỉ chạy khi AI dùng công cụ sửa hoặc ghi tệp.

Hook nhận gì và trả lời thế nào?

Khi sự kiện xảy ra, Claude Code gửi dữ liệu dạng JSON vào stdin của lệnh. Mọi sự kiện có các trường chung như session_id và cwd (thư mục làm việc); sự kiện công cụ có thêm tool_name và tool_input (tham số của lời gọi). Ví dụ trong tài liệu khi AI định chạy lệnh npm test:

{
  "session_id": "abc123",
  "cwd": "/Users/sarah/myproject",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": { "command": "npm test" }
}

Hook trả lời bằng mã thoát và stderr:

  • Mã 0: không quyết định gì. Với PreToolUse, lời gọi vẫn đi qua luồng xin quyền bình thường, vì mã 0 không có nghĩa là cho phép.
  • Mã 2: ghi lý do ra stderr. Với PreToolUse, lời gọi bị chặn và Claude nhận lý do để đổi cách làm. Với PostToolUse, công cụ đã chạy rồi nên không chặn được, nhưng stderr được đưa cho Claude đọc. Với Stop, Claude không dừng mà làm tiếp.
  • Mã khác: ở phần lớn sự kiện, hành động vẫn tiếp tục và được ghi nhận là lỗi không chặn.

Muốn điều khiển chi tiết hơn, hook có thể thoát mã 0 và in một đối tượng JSON ra stdout, ví dụ trường additionalContext để chèn thêm thông tin cho Claude đọc. Bài này dùng mã thoát cho đơn giản.

Ví dụ: hai hook bảo vệ và tự kiểm bằng Python

Tài liệu Claude Code có ví dụ viết bằng Bash và dùng công cụ jq để đọc JSON. Hai hook dưới đây làm cùng ý tưởng nhưng viết bằng Python thư viện chuẩn, nên chạy được trên Windows, macOS và Linux mà không cần cài thêm gì.

  1. chan_tep_bao_ve.py chạy ở PreToolUse: nếu AI định sửa tệp có đường dẫn chứa .env, .git/ hoặc secrets/, hook thoát mã 2 và nói rõ lý do. Đường dẫn kiểu Windows (dấu \) được đổi sang / trước khi so, như ví dụ trong tài liệu.
  2. bao_loi_cu_phap.py chạy ở PostToolUse: nếu tệp vừa sửa là tệp Python và sai cú pháp, hook thoát mã 2 kèm số dòng và lỗi, để Claude đọc và sửa ngay. Hook dùng hàm compile có sẵn của Python nên chỉ đọc tệp, không ghi gì ra đĩa.
"""Hook PreToolUse: chặn AI sửa các tệp được bảo vệ (.env, thư mục .git/, thư mục secrets/).

Claude Code gửi dữ liệu sự kiện dạng JSON vào stdin. Thoát mã 2 kèm lý do ở stderr thì lời gọi công cụ bị chặn,
và lý do được gửi lại cho Claude để nó đổi cách làm. Thoát mã 0 thì không quyết định gì: luồng xin quyền
bình thường vẫn áp dụng.
"""
import json
import sys

MAU_BAO_VE = (".env", ".git/", "secrets/")


def kiem(du_lieu):
    """Trả về (mã thoát, thông điệp)."""
    duong_dan = (du_lieu.get("tool_input") or {}).get("file_path", "")
    duong_dan = duong_dan.replace("\\", "/")  # đường dẫn Windows
    for mau in MAU_BAO_VE:
        if mau in duong_dan:
            return 2, f"Đã chặn: {duong_dan} khớp mẫu được bảo vệ '{mau}'. Hãy sửa tệp khác hoặc hỏi người dùng."
    return 0, ""


if __name__ == "__main__":
    ma, thong_diep = kiem(json.load(sys.stdin))
    if thong_diep:
        print(thong_diep, file=sys.stderr)
    sys.exit(ma)

Khi chạy hai hook với dữ liệu giống như Claude Code gửi, kết quả như sau:

Hook Tệp trong tool_input Mã thoát Stderr
Chặn tệp /du-an/.env 2 Đã chặn: /du-an/.env khớp mẫu được bảo vệ '.env'. Hãy sửa tệp khác hoặc hỏi người dùng.
Chặn tệp C:\du-an\.git\config 2 Đã chặn: ... khớp mẫu được bảo vệ '.git/'
Chặn tệp /du-an/src/app.py 0 (trống)
Báo lỗi cú pháp tệp có def tong(a, b) thiếu dấu hai chấm 2 ... sai cú pháp sau khi sửa (dòng 1): expected ':'
Báo lỗi cú pháp tệp .md hoặc tệp Python đúng 0 (trống)

Bạn có thể tự thử cách này trước khi đăng ký hook: gõ echo '{"tool_input": {"file_path": ".env"}}' | python chan_tep_bao_ve.py rồi xem mã thoát. Tự thử với dữ liệu giả là cách nhanh nhất để chắc hook làm đúng trước khi giao cho nó canh giữ công việc thật.

Đăng ký hook trong Claude Code

Đặt hai tệp vào thư mục .claude/hooks/ của dự án, rồi thêm khối hooks vào .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "python \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/chan_tep_bao_ve.py" }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "python \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/bao_loi_cu_phap.py" }
        ]
      }
    ]
  }
}

Biến CLAUDE_PROJECT_DIR trỏ tới thư mục dự án; tài liệu dùng nó trong ví dụ để lệnh chạy đúng dù thư mục làm việc thay đổi. Nếu máy bạn gọi Python bằng tên khác (như python3 hoặc py), hãy sửa lại cho đúng.

Vị trí tệp cấu hình quyết định phạm vi của hook:

Vị trí Phạm vi Chia sẻ
~/.claude/settings.json Mọi dự án của bạn Không, chỉ trên máy bạn
.claude/settings.json Một dự án Có, commit vào kho
.claude/settings.local.json Một dự án Không
Phần đầu tệp của skill hoặc subagent Khi skill hay subagent đó chạy Có, đi cùng tệp

Trong Claude Code, lệnh /hooks cho xem mọi hook đang cấu hình, nhóm theo sự kiện. Theo tài liệu, nếu bạn sửa tệp cấu hình khi Claude Code đang chạy, thay đổi về hook thường được nhận tự động. Muốn tắt hết hook, đặt "disableAllHooks": true trong tệp cấu hình.

Để thử, nhờ Claude thêm một dòng ghi chú vào .env. Lời sửa bị chặn trước khi chạy, và Claude nhận đúng câu "Đã chặn: ..." để đổi hướng.

Thêm vài hook hay dùng

Tài liệu Claude Code có sẵn các ví dụ cho những việc sau, mỗi ví dụ kèm khối cấu hình chép được:

  • Định dạng code sau mỗi lần sửa: hook PostToolUse với matcher Edit|Write chạy Prettier trên tệp vừa sửa, để định dạng luôn thống nhất mà không cần nhắc.
  • Báo khi Claude cần bạn: hook Notification gửi thông báo trên máy tính, để bạn không phải ngồi canh terminal.
  • Nạp lại bối cảnh sau khi nén: chèn lại thông tin quan trọng sau khi cuộc trò chuyện bị nén.
  • Ghi nhật ký thay đổi cấu hình để kiểm tra lại sau.

Một hướng mở rộng tự nhiên là chạy bộ test ở sự kiện Stop: nếu test hỏng thì thoát mã 2 để Claude chưa dừng mà sửa tiếp. Hãy cẩn thận với vòng lặp: tài liệu có mục xử lý trường hợp hook Stop chặn quá nhiều lần, nên hook cần điều kiện dừng rõ ràng và test cần chạy nhanh. Kiểm chứng việc AI làm nói chung được bàn trong kiểm chứng việc AI làm.

Giới hạn và an toàn

Theo tài liệu:

  • Hook kiểu lệnh chỉ giao tiếp qua stdout, stderr và mã thoát; nó không tự gọi lệnh gạch chéo hay công cụ được.
  • Hook PostToolUse không hoàn tác được hành động, vì công cụ đã chạy.
  • Thời gian chờ mặc định của hook kiểu lệnh là 10 phút, riêng một số sự kiện ngắn hơn; chỉnh bằng trường timeout (giây).
  • Hook không nới lỏng được quy tắc quyền: hook trả "cho phép" không vượt qua được quy tắc cấm trong cấu hình, còn hook "từ chối" thì có hiệu lực ở mọi chế độ quyền. Xem thêm giới hạn quyền khi cho AI tự chạy lệnh.

Về an toàn: hook là lệnh chạy với quyền của tài khoản bạn trên máy bạn. Một tệp .claude/settings.json trong kho của người khác có thể chứa hook chạy bất cứ lệnh gì. Trước khi làm việc với một kho lạ bằng Claude Code, hãy mở tệp đó và đọc các hook như đọc một script lạ.

Cẩn thận
  • Dùng mã thoát 1 để "chặn": theo tài liệu, ở phần lớn sự kiện hành động vẫn tiếp tục. Muốn chặn phải dùng mã 2.
  • In lý do chặn ra stdout thay vì stderr: Claude không nhận được lý do.
  • Matcher quá rộng (bỏ trống) khiến hook chạy ở mọi công cụ, làm chậm mọi bước.
  • Hook chậm hoặc có thể treo: mỗi lần AI sửa tệp đều phải chờ.
  • Hook Stop chặn mà không có điều kiện dừng, làm Claude làm mãi.
  • Commit hook dùng đường dẫn tuyệt đối của riêng máy bạn: đồng đội chạy sẽ lỗi. Dùng CLAUDE_PROJECT_DIR.

Câu hỏi thường gặp

Hook khác gì viết luật trong CLAUDE.md?

CLAUDE.md là chữ để mô hình đọc, có thể bị bỏ qua khi ngữ cảnh bị nén hoặc mô hình phán đoán sai. Hook là lệnh chạy tự động mỗi khi sự kiện xảy ra. Luật bắt buộc thì nên là hook.

Có cần biết Bash để viết hook không?

Không. Hook là bất kỳ lệnh nào đọc JSON từ stdin và trả mã thoát, nên viết bằng Python như ví dụ trong bài vẫn được, và chạy được trên cả Windows.

Làm sao biết hook có chạy không?

Gõ /hooks trong Claude Code để xem hook đã được nhận chưa. Khi hook chạy thành công, Claude Code không hiện gì trong cuộc trò chuyện; hãy kiểm kết quả mà hook tạo ra, hoặc thử bằng một yêu cầu cố tình vi phạm như sửa .env.

Hook có làm Claude chạy chậm hơn không?

Có, nếu hook chậm. Mỗi hook khớp sự kiện đều chạy trước khi bước tiếp theo diễn ra. Giữ hook nhanh và dùng matcher để chỉ chạy khi cần.

Học tiếp

  • Muốn kiểm chứng việc AI làm một cách có hệ thống, đọc kiểm chứng việc AI làm.
  • Muốn đặt quyền cho agent đúng mức, đọc giới hạn quyền khi cho AI tự chạy lệnh.
  • Muốn ghi quy ước dự án cho AI đọc, đọc viết hướng dẫn dự án cho AI.

Nếu bạn muốn học lập trình và cách làm việc với AI một cách có hệ thống, tham khảo khóa học AI của Big-O Coding.

Muốn học bài bản cùng giảng viên?

Khóa AI – Introduction to Machine Learning: nền tảng Máy học từ thuật toán cơ bản đến Neural Networks và LLM, học bằng Python. Học thử buổi 1 miễn phí.

Xem Khóa AI

Nguồn tham khảo

Chia sẻ: Facebook LinkedIn
Nhắn Zalo tư vấnZalo Gọi 0937 401 483