Chạy coding agent hàng giờ mà không phải trông: ghi chép về harness engineering
Các hook guard, handoff gate, bộ phát hiện vòng lặp và file state tôi dựng quanh coding agent để việc chạy nền dài vẫn an toàn, kèm phần nào đã kiểm chứng và phần nào chưa.
Model chỉ sinh ra chữ. Mọi thứ biến nó thành agent (prompt hệ thống, tool, hook, file trạng thái, các bước kiểm tra sau mỗi lần sửa) gọi chung là harness. Tôi chạy vài agent nền cho các dự án cá nhân, và phần lớn lỗi tôi gặp đến từ harness chứ không phải model. Bài này kể các mảnh tôi đã thêm, theo thứ tự mảnh nào đáng công nhất.
Guide và sensor
Tôi nhìn harness theo hai trục. Guide dẫn agent trước khi nó làm: một CLAUDE.md ngắn, skill nạp khi cần. Sensor kiểm sau khi nó làm: lint, type check, test, reviewer agent. Chỉ có guide thì luật không ai kiểm. Chỉ có sensor thì agent lặp lại đúng lỗi cũ ở mỗi lần chạy. Cần cả hai, cộng state ghi ra file để việc sống qua nhiều phiên.
guides --> agent loop --> sensors
| ^ |
v | v
state files <-- reviewer
Bằng chứng công khai cho thấy harness có ảnh hưởng thật. LangChain giữ nguyên model, chỉ sửa harness, điểm Terminal-Bench 2.0 tăng từ 52,8 lên 66,5. Đây là báo cáo của chính họ trên benchmark công khai, nên tôi đọc là “tác động có thể lớn”, không phải con số để kỳ vọng.
Guard: chặn lệnh phá huỷ trước khi chạy
Hook đầu tiên tôi viết là guard PreToolUse. Nó đọc lệnh tool sắp chạy và từ chối một danh sách ngắn: xoá đệ quy ngoài thư mục dự án, force push, ghi vào file credential. Trong subagent chạy nền, mọi thứ lẽ ra cần tôi duyệt đều bị từ chối thẳng. Không có ai ngồi đó bấm “allow”, và một job treo còn tệ hơn một job bị từ chối.
Vai chỉ đọc cũng có allowlist tools: trong định nghĩa. Agent nghiên cứu không có tool sửa file, nên nó không thể “tiện tay” sửa thứ chỉ được nhờ xem. Mất mười phút và loại hẳn một nhóm bất ngờ.
Handoff gate: để “xong” có nghĩa thật
Thói quen tệ nhất của tôi là tin subagent khi nó báo “xong”. Giờ mỗi subagent phải kết thúc bằng một khối cố định:
Exit: ok | blocked
Tag: portfolio
Path: reports/2026-11-11-summary.md
Hook SubagentStop đọc tin nhắn cuối, kiểm đủ ba trường và Path trỏ tới file có thật. Thiếu thì nó trả quyết định block, và reason trở thành lệnh kế tiếp của agent:
{"decision": "block",
"reason": "Missing Path or file does not exist. Write the report, then end with the Exit/Tag/Path block."}
Tôi đã chạy thử end to end bằng claude -p. Lần đầu agent trả một câu trơn và bị block. Lần hai nó trả đúng khối. Rút ra hai lưu ý. reason mơ hồ thì agent đoán bừa, nên thông báo phải nói chính xác thiếu gì. Và hook phải kiểm stop_hook_active, nếu không có thể lặp vô hạn. Tôi cho chạy chế độ chỉ ghi log một tuần trước khi cho block thật.
Phát hiện vòng lặp: agent không tự thấy mình đang lặp
Agent kẹt ở một giả thuyết sai sẽ sửa đi sửa lại cùng một file, và mỗi lần sửa nhìn riêng đều hợp lý. Hook PostToolUse đếm số lần Edit và Write trên từng file trong session. Đến lần sửa thứ năm, nó chèn vào context một nhắc nhở: dừng lại, đọc lại yêu cầu gốc và lỗi đầu tiên, nêu một giả thuyết khác. Mẫu này lấy từ loop-detection middleware của LangChain.
Tôi bỏ file markdown khỏi bộ đếm vì note và todo hay được sửa nhiều lần có lý do chính đáng. Bộ đếm cũng cần dọn mỗi tuần.
Tôi đã kiểm snippet với ngưỡng 3: nhắc nhở in ra ở lần sửa thứ ba. Tôi chưa đo được nó có cải thiện kết quả không, nên chưa dám khẳng định.
State: sống sót qua compaction
Phiên dài bị compact, agent mất mạch. Tôi giữ một mục ## Đang dở ngắn trong file, lưu nó bằng hook PreCompact, rồi nạp lại bằng hook SessionStart ở sự kiện compact. Agent tiếp tục từ những gì file ghi, không phải từ trí nhớ mơ hồ. Lịch sử git và một file tiến độ lo phần còn lại.
Sensor biết im lặng
Sau mỗi lần sửa trong repo app, một hook chạy linter hoặc compiler trên file vừa đổi. Xanh thì không in gì. Đỏ thì in lỗi kèm cách sửa, vì lỗi không kèm cách sửa là mời agent đoán. Ồn hơn thế sẽ dạy agent lờ đi.
Ratchet
Khi agent lờ một quy ước lần thứ hai, tôi không viết prompt gắt hơn nữa mà đổi nó thành check: hook, lint rule, grep ở pre-commit. Một dòng văn xuôi thì không đáng tin, còn check tất định thì không quên. Mỗi lỗi chỉ sửa một lần, harness chỉ siết dần. Ngưỡng của tôi là “lặp lần hai”, vì rule sinh từ một sự cố lạ thường chỉ làm rối.
Những gì tôi chưa biết
- Tôi chưa đo gì trên hệ thống của mình: subagent trả sai khuôn bao nhiêu lần, guard kích hoạt bao nhiêu lần.
- Quyết định
asktrong subagent nền đang bypass quyền có chạy đúng không: chưa kiểm. - Còn thiếu một bộ eval (5 đến 10 task cố định chạy trước và sau mỗi lần đổi harness). Thiếu nó thì tôi vẫn đang chỉnh theo cảm giác.
Nếu bạn dựng harness, hãy bắt đầu với guard và handoff gate. Cả hai mất khoảng 30 phút, không cần hạ tầng mới, và tôi thấy khác biệt ngay trong ngày đầu.