Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Dựng harness để AI agent làm việc đáng tin

Câu hỏi bài này trả lời: cùng một model mà lúc làm tốt lúc làm hỏng, thì phần nào của môi trường quyết định độ tin cậy, nên dựng theo thứ tự nào, và làm sao biết nó có tác dụng thật?

Cần biết trước: đã dùng một AI coding agent (CLI hoặc IDE) và biết git cơ bản. Muốn phân biệt prompt, skill, quy tắc dự án và tool thì xem AI Agent Skills. Hai lab cần Python 3 và Git; không cài thêm thư viện.

Khi agent làm hỏng việc, phản xạ thường là viết lại prompt. Nhưng những lỗi lặp lại đúng một kiểu thì prompt khó sửa tận gốc. Bài của Anthropic về agent chạy dài ghi lại các kiểu hỏng như vậy: cố làm quá nhiều một lúc rồi hết ngữ cảnh giữa chừng, thấy đã có tiến độ nên tuyên bố xong, để lại môi trường hỏng mà không ghi chú, đánh dấu tính năng xong khi chưa kiểm đầu-cuối. Bài của OpenAI về harness engineering kể vấn đề cùng gốc từ phía người làm: tiến độ ban đầu chậm không phải vì model kém mà vì “the environment was underspecified” (môi trường thiếu đặc tả), nên việc chính của kỹ sư chuyển sang làm cho agent có thể làm việc được.

Bài này gọi phần ngoài model là harness: chỉ dẫn, tri thức, quy trình, chốt chặn, trạng thái và bằng chứng. Các nguồn không dùng từ này hoàn toàn giống nhau (Anthropic gọi chính SDK chạy agent là harness, còn OpenAI dùng “harness engineering” cho việc thiết kế môi trường và vòng phản hồi), nên đây là nghĩa riêng của bài.

Ba triệu chứng, ba cơ chế

Ba triệu chứng dưới đây là ví dụ điển hình, không phải thống kê. Mỗi cái có một cơ chế xử lý và một phép thử để biết cơ chế có tác dụng.

Triệu chứngNguyên nhân gốcCơ chếPhép thử
Mỗi công cụ, mỗi người một bộ chỉ dẫn; sửa chỗ này thì lệch chỗ kiaCấu hình nằm ở nhiều nơi và được sửa tayMột nguồn chuẩn; cấu hình của từng công cụ được sinh ra từ đóSửa nguồn, chạy lại bước sinh: chỉ file sinh thay đổi
Agent làm nhiều hơn được giao, sửa cả chỗ không liên quanKhông có biên: được ghi ở đâu, được đụng file nàoPhạm vi bằng cấu trúc: mỗi task một vùng ghi riêng, vùng gốc chỉ đọcThử ghi ra ngoài vùng: phải bị chặn
Không ai biết cái gì đã được duyệt hay kiểm, chỉ có lời agent kểCổng chỉ nằm trong câu chữ, bằng chứng là lời tự khaiCổng người ở điểm không ủy quyền được, cổng máy ở điểm kiểm được; mỗi pha để lại vật chứngNgười khác đọc hồ sơ task là dựng lại được: kế hoạch, diff, kết quả kiểm

Các phần sau đi từ cấu tạo (năm lớp) tới cách dựng và cách đo.

Năm lớp của harness

LớpTrả lời câu hỏiCơ chế điển hìnhNếu thiếu
Chỉ dẫnLàm gì, cấm gì, đọc gì trước?Tệp chỉ dẫn ngắn theo định dạng AGENTS.md, luật gắn theo loại fileAgent tự đoán, hoặc bị nhồi quá nhiều chỉ dẫn
Tri thức theo nhu cầuCần kiến thức X thì tìm ở đâu?Tài liệu có cấu trúc và có version trong repo; giao thức như MCP để agent tự lấyNhồi hết vào prompt gây nhiễu; thứ agent không truy cập được thì như không tồn tại
Quy trình đóng góiViệc loại này làm thế nào?Skill theo từng loại việcMỗi lần giao việc lại giải thích từ đầu
Chốt chặn tất địnhĐiều gì không được xảy ra?Hook, linter, structural test, quyền fileLuật viết bằng chữ chỉ là lời đề nghị
Trạng thái và bằng chứngĐã làm gì, còn gì, bằng chứng đâu?Tệp tiến độ, kế hoạch và kết quả kiểm nằm trong repoPhiên sau không biết bắt đầu từ đâu; chỉ còn lời kể

Chỉ dẫn. AGENTS.md được giới thiệu là “README cho agent”: một chỗ cố định, dự đoán được để đưa chỉ dẫn. Với monorepo có thể đặt nhiều file lồng nhau, file gần nhất với file đang sửa được ưu tiên (theo trang agents.md). OpenAI thử “một AGENTS.md khổng lồ” và nêu bốn lý do nó hỏng: chiếm chỗ của task, quá nhiều chỉ dẫn thành không chỉ dẫn, mục nát ngay, khó kiểm bằng máy. Họ chuyển sang coi AGENTS.md là mục lục khoảng 100 dòng, trỏ tới nguồn sâu hơn.

Tri thức. MCP là chuẩn mở để nối ứng dụng AI với hệ thống bên ngoài như nguồn dữ liệu, tool và workflow. Bài của OpenAI nói thêm: điều agent không truy cập được trong ngữ cảnh khi chạy thì coi như không tồn tại. Thảo luận trong chat hay trong đầu người không phải tri thức của hệ thống; thứ agent thấy là tài liệu nằm trong repo và có version.

Luật và năng lực là hai thứ khác nhau. Luật nói điều không được làm, skill nói cách làm, còn quyền thực thi do môi trường cấp, như đã nêu ở trang Skills.

Câu chữ và cơ chế. Chỉ dẫn viết bằng chữ chỉ có hiệu lực khi agent chịu theo. Chỉ dẫn viết thành cơ chế (hook, linter, quyền file) có hiệu lực cả khi agent không theo. OpenAI mô tả cách làm này: ràng buộc kiến trúc được kiểm bằng linter tự viết và structural test, và thông báo lỗi được viết để chèn luôn hướng dẫn sửa vào ngữ cảnh của agent.

Một nguồn chuẩn cho nhiều công cụ

Team dùng nhiều công cụ agent sẽ gặp cảnh mỗi công cụ đọc cấu hình ở một chỗ riêng. Chép tay giữa các chỗ thì sớm muộn chúng lệch nhau, và không ai biết bản nào đang đúng.

  • Giữ một thư mục nguồn chuẩn cho chỉ dẫn, luật và danh sách MCP server.
  • Một bước sinh ghi cấu hình riêng cho từng công cụ. File sinh là output build: không sửa tay.
  • Thêm một công cụ mới là thêm một bộ chuyển đổi (adapter), không sửa lõi.
  • Kiểm bằng máy: chạy lại bước sinh trong CI và thất bại nếu có diff.
  • Đồng bộ một chiều, từ nguồn ra file sinh. Nếu dùng công cụ cài đặt tự sinh cấu hình, thử trước xem chạy lại nó có ghi đè phần bạn đã sửa không; nếu có, tách phần tùy biến khỏi phần sinh.

Nguồn công khai chỉ đỡ phần nền: agents.md cho thấy nhiều công cụ đọc được cùng một định dạng, và MCP được giới thiệu với tinh thần “build once and integrate everywhere”. Cách sinh cấu hình và kiểm bằng diff là đề xuất của bài, chưa có nguồn.

Chốt chặn thật và chốt chặn giả

Hook là mã chạy ở một sự kiện trong vòng đời của agent, ví dụ ngay trước khi nó gọi một tool. Hook chỉ ngăn được việc khi nó có quyền ngăn và thật sự ngăn. Với Claude Code, tài liệu hook nêu rõ hợp đồng, và nó khác trực giác “khác 0 là lỗi, lỗi thì dừng”:

Hook kết thúc bằngVới PreToolUse
exit 2Chặn lời gọi tool; stderr được đưa lại cho Claude làm phản hồi
exit 0Không phản đối. Đây không phải phê duyệt: luồng cấp quyền thường vẫn chạy
exit khác, kể cả 1Lỗi không chặn nếu stdout không có JSON hợp lệ: tool vẫn chạy, dù 1 là mã lỗi quen thuộc của Unix
không khởi động được (sai đường dẫn, thiếu quyền thực thi)Cũng không chặn: tài liệu cảnh báo gate có thể bị tắt âm thầm

Hook cũng có thể trả JSON để quyết định (ví dụ trường permissionDecision); bài này chỉ dùng exit code cho gọn. Chọn đúng sự kiện cũng quan trọng: chặn việc chưa xảy ra phải ở PreToolUse, còn PostToolUse chỉ báo lại vì tool đã chạy xong.

Hệ quả: một gate có thể tắt mà không ai biết cho tới lúc cần nó. Cách duy nhất biết gate chặn thật là thử vi phạm: đưa vào đúng loại lời gọi mà gate phải chặn rồi xem kết quả.

Còn một bẫy nữa, riêng với hook theo đường dẫn. tool_input.file_path của Write, Edit và Read luôn là đường dẫn tuyệt đối; Claude Code mở rộng ~ và đường dẫn tương đối trước khi gọi hook. Trên Windows nó dùng dấu \, nên so sánh bằng / không bao giờ khớp và tool vẫn chạy như thể hook không có gì để chặn. Tài liệu khuyên chuẩn hóa dấu phân cách rồi khớp một đoạn đường dẫn như /baseline/ thay vì neo bằng ^.

Lab: ba hook cho cùng một luật

Luật: không ghi vào thư mục baseline. Tạo thư mục trống, ví dụ hook-lab, và bốn file sau. Hook giả lập theo hợp đồng ở trên (đọc JSON từ stdin, tool_input.file_path tuyệt đối, exit 2 để chặn) nhưng chạy bằng Python thường, không nằm trong ứng dụng agent nào. Trên Windows dùng python thay cho python3.

guard.py chuẩn hóa dấu phân cách rồi khớp đoạn /baseline/. Lý do chặn ghi ở stderr để agent đọc được:

import json
import sys

event = json.load(sys.stdin)
path = event["tool_input"]["file_path"].replace("\\", "/")
if "/baseline/" in path:
    sys.stderr.write("Chặn: baseline chỉ đọc, hãy ghi trong thư mục của task.\n")
    sys.exit(2)
sys.exit(0)

naive.py giống hệt nhưng bỏ bước chuẩn hóa. Nó chặn đường dẫn POSIX và bỏ lọt đường dẫn Windows:

import json
import sys

event = json.load(sys.stdin)
path = event["tool_input"]["file_path"]
if "/baseline/" in path:
    sys.stderr.write("Chặn: baseline chỉ đọc, hãy ghi trong thư mục của task.\n")
    sys.exit(2)
sys.exit(0)

observer.py chỉ ghi vết vào audit.log rồi luôn thoát 0:

import json
import sys

event = json.load(sys.stdin)
with open("audit.log", "a", encoding="utf-8") as log:
    log.write(event["tool_input"]["file_path"] + "\n")
sys.exit(0)

assert_blocks.py là phép thử. Nó đưa vào hai lời gọi vi phạm (đường dẫn POSIX và đường dẫn Windows) cùng một lời gọi hợp lệ, rồi so exit code với kỳ vọng:

import json
import subprocess
import sys

hook = sys.argv[1]
CALLS = {
    "ghi baseline (đường dẫn POSIX)": ("/work/baseline/app.txt", 2),
    "ghi baseline (đường dẫn Windows)": ("C:\\work\\baseline\\app.txt", 2),
    "ghi vùng task": ("/work/task/app.txt", 0),
}


def exit_code(path):
    event = {
        "hook_event_name": "PreToolUse",
        "tool_name": "Write",
        "tool_input": {"file_path": path, "content": "x"},
    }
    done = subprocess.run(
        [sys.executable, hook],
        input=json.dumps(event),
        capture_output=True,
        text=True,
        check=False,
    )
    return done.returncode


results = {name: (exit_code(path), want) for name, (path, want) in CALLS.items()}
wrong = [name for name, (got, want) in results.items() if got != want]
if wrong:
    print(f"{hook}: SAI ở {len(wrong)} lời gọi: " + "; ".join(wrong))
    sys.exit(1)
print(f"{hook}: đạt cả {len(CALLS)} lời gọi")

Thông điệp agent nhận được khi bị chặn, và exit code thật:

echo '{"tool_input": {"file_path": "/work/baseline/app.txt"}}' | python3 guard.py || echo "exit $?"
Chặn: baseline chỉ đọc, hãy ghi trong thư mục của task.
exit 2

Chạy phép thử trên cả ba hook. Hook đúng thì đạt:

python3 assert_blocks.py guard.py
guard.py: đạt cả 3 lời gọi

observer.py thất bại ở cả hai lời gọi vi phạm, vì ghi vết không phải là chặn:

python3 assert_blocks.py observer.py
observer.py: SAI ở 2 lời gọi: ghi baseline (đường dẫn POSIX); ghi baseline (đường dẫn Windows)

naive.py thất bại ở đúng một lời gọi, cái mà một phép thử chỉ có đường dẫn POSIX sẽ không thấy:

python3 assert_blocks.py naive.py
naive.py: SAI ở 1 lời gọi: ghi baseline (đường dẫn Windows)

Đọc kết quả:

  • observer.py có hook, có log, có exit code, và không chặn gì. Nếu chỉ nhìn cấu hình, nó giống một gate.
  • naive.py chặn đúng trong ca người viết nghĩ tới và hở trong ca còn lại. Phép thử chỉ có một lời gọi vi phạm sẽ cho nó qua; đó là lý do assert_blocks.py có hai lời gọi vi phạm. Phép thử cũng cần được thử bằng bản lỗi như thế này.
  • Suy luận của bài, chưa có nguồn: một hook chỉ thấy những sự kiện nó được đăng ký cho. Nếu nó chỉ gắn với tool ghi file thì lệnh shell ghi cùng file có thể không đi qua nó; hãy thử đúng loại lời gọi đó thay vì giả định. Lớp độc lập bên dưới là quyền của hệ thống file: thư mục chỉ đọc thì ghi thất bại dù hook bị bỏ qua.

Cổng người duyệt

Máy chạy được phép kiểm nhưng không quyết được cái gì đáng kiểm. Nếu agent vừa viết tiêu chí vừa viết phép thử cho nó, “đạt” có thể chỉ có nghĩa là hai thứ cùng sai giống nhau. Vì vậy chỗ cần người là điểm quyết định tiêu chí và kế hoạch, không phải từng dòng code.

Bài Building effective agents mô tả agent có thể dừng chờ phản hồi của người ở các checkpoint hoặc khi gặp vướng. Ba mức triển khai cổng, theo cách phân loại của bài:

  1. Agent dừng và hỏi trong hội thoại. Rẻ nhất, nhưng dựa vào việc agent tuân theo.
  2. Dấu duyệt nằm ở nơi hook cấm agent ghi, nên agent không tự duyệt được. Đây là ràng buộc bằng cấu trúc.
  3. Người review ở bước merge. Độc lập với agent, nhưng chậm.

Đừng nhầm ràng buộc bằng câu chữ với ràng buộc bằng cơ chế. Bài của Anthropic về agent chạy dài yêu cầu agent chỉ được đổi trường passes trong danh sách tính năng và nhắc mạnh rằng xóa hay sửa test là không chấp nhận được; họ cũng ghi lý do chọn JSON: model ít sửa nhầm file JSON hơn Markdown. Đó là giảm xác suất sửa nhầm chứ chưa phải chặn.

Độ chặt của cổng phải khớp với tốc độ và rủi ro, không có mức đúng chung. OpenAI vận hành với rất ít cổng merge chặn vì throughput của agent vượt xa sự chú ý của người, và chính họ nói cách đó “would be irresponsible in a low-throughput environment” (sẽ thiếu trách nhiệm ở nơi throughput thấp). Cách viết tiêu chí để máy kiểm được nằm ở bài Tiêu chí hoàn tất có thể kiểm chứng.

Vòng đời task: mỗi pha một vật chứng

Bài của Anthropic mở đầu bằng hình ảnh một dự án có kỹ sư làm theo ca, mỗi ca mới đến không nhớ gì về ca trước. Agent chạy qua nhiều cửa sổ ngữ cảnh ở đúng tình trạng đó. Thứ bắc cầu giữa các phiên là vật chứng đọc lại được, không phải trí nhớ.

PhaCâu hỏiVật chứng đọc lại đượcAi hoặc cái gì quyết
Chốt phạm viLàm gì, không làm gì, giả định nào?Ghi chú phạm vi và giả địnhNgười xác nhận
Lập kế hoạchChia lát nào, rủi ro gì, kiểm bằng gì?Kế hoạch, rủi ro, tiêu chí hoàn tấtNgười duyệt (cổng người)
Làm từng lát nhỏLát này đạt chưa?Commit nhỏ, nhật ký tiến độMáy: test, lint, kiểm kiểu
ReviewCòn lỗi nào test không bắt được?Kết quả review của người hoặc agent khác tác giảReviewer khác tác giả
Nghiệm thuMọi tiêu chí đạt chưa, bằng chứng đâu?Tệp bằng chứng: lệnh, exit code, output, môi trườngMáy chạy, người đọc
Bàn giaoNgười sau cần biết gì?Tóm tắt, tài liệu đã cập nhật, trạng thái sạchNgười

Sáu pha trên là cách chia của bài, không phải chuẩn; tên pha không quan trọng. Ba điều mới quan trọng: mỗi pha để lại vật chứng đọc lại được; chỉ có một hai điểm cần người; mọi chỗ còn lại kiểm bằng máy.

Đầu mỗi phiên, agent đọc vật chứng thay vì được hỏi là nó nhớ gì. Bài của Anthropic mô tả trình tự khởi động: xác định thư mục làm việc, đọc lịch sử git và tệp tiến độ, rồi đọc danh sách tính năng để chọn việc ưu tiên cao nhất chưa xong. Cuối phiên, họ yêu cầu để lại “clean state”: code ở mức có thể merge vào nhánh chính, không còn lỗi lớn, có ghi chú đủ để người sau bắt đầu mà không phải dọn trước.

Cô lập vùng ghi bằng git worktree

Chạy nhiều agent trên cùng một thư mục thì chúng đạp lên nhau. git worktree cho mỗi agent một thư mục làm việc riêng trên cùng một repository: theo tài liệu git-worktree, một repository có thể có nhiều working tree, cho phép checkout nhiều nhánh cùng lúc. Bài này gọi một worktree cộng với nhánh của nó và tài nguyên chạy riêng (cổng mạng, schema database, thư mục dữ liệu) là một vùng làm việc. OpenAI mô tả ứng dụng của họ khởi động được theo từng git worktree, cùng một stack quan sát tạm thời cho từng worktree, bỏ đi khi task xong.

Worktree cô lập file, không cô lập mọi thứ. Theo tài liệu git, worktree liên kết chia sẻ mọi thứ trừ các file riêng như HEAD và index: ref dưới refs/ dùng chung, và config dùng chung theo mặc định. Hệ quả: một vùng đọc được commit của vùng khác; xóa nhánh hay đổi config ở một nơi tác động cả repository. Muốn config riêng phải bật extensions.worktreeConfig.

Hai quy ước giúp vùng làm việc hữu ích:

  • Một nhánh một vùng. git worktree add từ chối checkout một nhánh đang nằm ở worktree khác, trừ khi dùng --force. Đây là chốt chặn miễn phí: hai agent không thể cùng giữ một nhánh.
  • Baseline chỉ đọc. Giữ worktree chính làm mốc, không agent nào ghi vào. Bằng chứng của một vùng là git diff giữa nhánh của nó và nhánh baseline.

Lab: hai worktree trên một repository

Chạy từ thư mục trống. repo giữ baseline; wt-a và wt-b là hai worktree, mỗi cái một nhánh. Danh tính git đặt một lần ở repo và cả hai worktree dùng chung, đúng như “config dùng chung theo mặc định”:

git --version
git init -q repo
git -C repo symbolic-ref HEAD refs/heads/main
git -C repo config user.name Lab
git -C repo config user.email lab@example.org
echo "v1" > repo/app.txt
git -C repo add app.txt
git -C repo commit -q -m "baseline"
git -C repo worktree add -q ../wt-a -b feature/a
git -C repo worktree add -q ../wt-b -b feature/b
git -C repo worktree list --porcelain | grep '^branch'
branch refs/heads/main
branch refs/heads/feature/a
branch refs/heads/feature/b

wt-a thêm một commit. wt-b không đổi gì, nhưng đọc được commit của wt-a vì nhánh và object dùng chung:

echo "change from a" >> wt-a/app.txt
git -C wt-a commit -q -am "change from a"
echo "--- wt-a so với main"
git -C wt-a diff --stat main
echo "--- wt-b so với main"
git -C wt-b diff --stat main
echo "--- wt-b đọc được commit của wt-a"
git -C wt-b log --format=%s main..feature/a
--- wt-a so với main
app.txt | 1 +
1 file changed, 1 insertion(+)
--- wt-b so với main
--- wt-b đọc được commit của wt-a
change from a

Một nhánh chỉ ở một worktree. Thêm worktree thứ ba trên nhánh feature/a bị từ chối; thông báo lỗi khác nhau giữa các phiên bản Git nên lab chỉ kiểm exit code:

git -C repo worktree add ../wt-c feature/a

Dọn dẹp. remove chỉ xóa worktree sạch và nhánh vẫn còn; prune dọn metadata của worktree đã bị xóa tay:

git -C repo worktree remove ../wt-a
git -C repo worktree list --porcelain | grep '^branch'
echo "--- nhánh sau khi xóa wt-a"
git -C repo branch --list feature/a
branch refs/heads/main
branch refs/heads/feature/b
--- nhánh sau khi xóa wt-a
feature/a

Các vùng làm việc vẫn dễ đạp lên nhau ở những thứ worktree không cô lập: cổng mạng, database dùng chung, cache, container. Mỗi vùng cần cổng và namespace riêng; nếu không, hai vùng khác thư mục vẫn tranh cùng một tài nguyên.

Đo harness bằng đối chứng

Cảm giác “có harness thì tốt hơn” không phải bằng chứng. Phép đo gọn nhất là thí nghiệm đối chứng: hai worktree xuất phát từ cùng một commit, cùng model, cùng đề, cùng ngân sách, chỉ khác harness. Bên A chỉ có prompt, bên B có harness. Kết quả chấm bằng đáp án chuẩn (ground truth) mà agent không được thấy, và chấm cả diff lẫn quá trình: lệnh nào đã chạy, test thật hay chỉ tuyên bố.

Rủi ro của thí nghiệmHậu quảCách giảm
Chỉ chạy một lầnModel có tính ngẫu nhiên nên một lần chỉ là giai thoạiChạy lặp, báo phân bố thay vì một con số
Đáp án chuẩn lọt vào worktree (trong repo hoặc lịch sử git)Agent chép đáp ánCắt lịch sử hoặc ẩn nhánh chứa đáp án trước khi dựng worktree
Harness được chỉnh theo đúng bộ đề đoThước đo thành mục tiêu và hết đáng tinGiữ một bộ đề chưa dùng để chỉnh; thay đề định kỳ
Đổi nhiều thành phần cùng lúcKhông biết thành phần nào có tác dụngMỗi lần đổi một thành phần
Chấm bằng lời agent kểTự khai không phải bằng chứngChấm bằng diff và kết quả test chạy lại

Cũng dùng chính phép đo này để bớt. Anthropic khuyên tìm giải pháp đơn giản nhất có thể và chỉ tăng độ phức tạp khi cần (Building effective agents, tháng 12/2024; chính bài ghi chú rằng nhiều chi tiết công cụ trong đó đã đổi, nên chỉ dùng ý nguyên lý). Suy luận của bài này, chưa có nguồn: mỗi thành phần harness mã hóa một giả định về điều model chưa tự làm được, và giả định đó cũ dần khi model tiến bộ. Tắt thử một thành phần, đo lại, nếu kết quả không tệ đi thì gỡ hẳn.

Bài này mô tả phương pháp, không nêu kết quả đối chứng nào.

Dựng theo thứ tự nào

Thứ tự dưới đây là đề xuất của bài, rút từ lập luận phụ thuộc giữa các bước chứ chưa đo. Mỗi bước dựa vào bước trước: không có phép chạy và kiểm được thì chốt chặn và đối chứng không có gì để đo; không có cô lập thì đối chứng nhiễu.

  1. Làm cho dự án chạy và kiểm được bằng một lệnh. Vòng phản hồi của agent chính là vòng kiểm của bạn. Anthropic cho agent chạy một kịch bản khởi động rồi một phép thử đầu-cuối cơ bản trước khi làm tính năng mới; OpenAI làm ứng dụng khởi động được theo từng worktree.
  2. Viết chỉ dẫn ngắn như mục lục, trỏ tới nguồn sâu hơn.
  3. Chọn rủi ro lớn nhất, dựng một chốt chặn thật cho nó và thử vi phạm.
  4. Thêm tiến độ và checkpoint để task sống qua nhiều phiên.
  5. Cô lập vùng ghi khi bắt đầu chạy song song.
  6. Đo bằng đối chứng và bớt thành phần không có tác dụng.

Giới hạn và lỗi thường gặp

Giới hạn của bài

  • Lab giả lập: hook chạy bằng Python thường, chưa chạy trong ứng dụng agent thật. Hợp đồng exit code theo Claude Code, đọc ngày 2026-10-02; công cụ khác có hợp đồng riêng, cần đọc tài liệu từng công cụ.
  • Các bài của Anthropic và OpenAI mô tả tình huống riêng của họ. OpenAI ghi rằng hành vi làm tính năng từ đầu đến cuối “depends heavily on the specific structure and tooling of this repository and should not be assumed to generalize without similar investment”; bài của Anthropic tối ưu cho web app full-stack và nói chưa rõ một agent tổng quát hay nhiều agent chuyên biệt tốt hơn.
  • Bài không đo hiệu quả của harness bằng số.
  • Lab chạy ngày 2026-10-02 trên macOS 27.0.1 (arm64) với Python 3.14.8 và Git 2.54.0 (Apple Git-157). Linux và Windows chưa thử, các lệnh viết cho bash. Riêng Windows, bẫy dấu phân cách trong bài lấy từ tài liệu; assert_blocks.py chỉ giả lập đường dẫn kiểu Windows bằng một chuỗi chứ chưa chạy trên máy Windows.

Lỗi thường gặp

LỗiHậu quảCách tránh
Tệp chỉ dẫn dài như bách khoaChiếm chỗ của task, thành không chỉ dẫn, mục nátGiữ như mục lục, trỏ tới nguồn sâu hơn
Hook chỉ ghi log mà tưởng là chặnVi phạm vẫn xảy ra, chỉ có dấu vếtPhép thử vi phạm cho từng hook chặn
Hook thoát exit 1 hoặc không khởi động đượcGate tắt âm thầmDùng exit 2; chạy phép thử ngay sau khi cấu hình
So đường dẫn bằng / trên WindowsBỏ lọt mọi lời gọiChuẩn hóa dấu phân cách; thử cả hai kiểu đường dẫn
Agent tự duyệt kế hoạch của chính nóCổng chỉ còn là hình thứcĐể dấu duyệt ở nơi agent không ghi được
Sửa tay file sinhLần sinh sau ghi đè, hoặc hai nơi lệch nhauSửa nguồn; CI sinh lại và so diff
Các worktree dùng chung cổng hoặc databaseVùng khác thư mục vẫn đạp lên nhauCổng và namespace riêng cho từng vùng
Đo một lần rồi kết luậnChỉ là giai thoạiChạy lặp, đổi một thành phần mỗi lần
Giữ thành phần harness thừaChi phí bảo trì mà không đổi lấy gìTắt thử từng thành phần và đo lại

Học tiếp

Nguồn tham khảo