Điều đáng lo nhất trong một tác vụ AI kéo dài không nhất thiết là thất bại. Đó là sự im lặng.
Con người có thể làm việc nhà, đi mua đồ, ăn xong rồi quay lại mà AI vẫn hiện “đang làm việc”. Khi đó có hai khả năng rất khác nhau:
- Nó đang nghiêm túc chạy một bộ test rất lớn.
- Nó đã mắc kẹt ở đâu đó từ vài giờ trước.
Spinner không phân biệt được hai trường hợp này. Không có commit trong một thời gian cũng không chứng minh nó đã dừng, vì testing, điều tra, so sánh, sinh nội dung hoặc chờ hệ thống bên ngoài có thể không tạo artefact mới trong một khoảng dài.
Vì vậy AI chạy lâu cần thêm một thứ ngoài trí thông minh: observability.
1. Kết luận――nếu tác vụ có thể kéo dài, hãy công khai “hiện đang ở đâu”
Agent chạy lâu không nên chỉ báo cáo khi hoàn tất. Trạng thái trung gian cũng nên đọc được.
Không cần mỗi phút nói “tôi vẫn sống”. Điều cần biết là:
- hiện đang làm gì
- đã xong gì
- còn gì
- có đang test không
- có blocker không
- lần cuối xác nhận còn hoạt động là khi nào
- lần cuối có thay đổi vật chất thực sự là khi nào
Một ví dụ cấu hình thực tế là cập nhật Heartbeat sau khoảng 10 phút không có artefact nhìn thấy, và nếu Heartbeat cũ hơn 20 phút thì đọc là STALE_UNKNOWN, không tự động kết luận “đã dừng”.
Im lặng trước hết nghĩa là “chưa biết”, không phải “đã thất bại”.
2. Vì sao chỉ nhìn lịch sử commit là không đủ
Git commit rất tốt để chứng minh thay đổi thực sự, nhưng yếu nếu dùng làm dấu hiệu duy nhất của hoạt động hiện tại.
Một regression test kéo dài một giờ có thể hoàn toàn khỏe mạnh mà không tạo commit nào. Ngược lại, commit một file Heartbeat vô nghĩa mỗi mười phút chỉ làm lịch sử bẩn đi.
Nên tách:
Heartbeat = trạng thái hiện tại Material change = bằng chứng rằng code, contract, artefact hoặc verification thực sự thay đổi
Tách lastHeartbeatAt và lastMaterialChangeAt giúp hiểu ngay trạng thái “30 phút chưa có commit nhưng 5 phút trước vẫn là TESTING”.
Mục tiêu không phải có nhiều commit hơn. Mục tiêu là làm cho sự im lặng có thể giải thích được.
3. Tối thiểu nên ghi những gì?
Một run dài nên có:
runIdstate: RUNNING / BLOCKED / COMPLETED / STALE_UNKNOWN, v.v.phase: READING / IMPLEMENTING / TESTING / VERIFYING, v.v.workingOnstartedAtlastHeartbeatAtlastMaterialChangeAtbranch / baseSha / headSha / PRcompletedMilestones / remainingMilestonesblockerstestsnextCheckpoint
Nhờ vậy một công việc sáu giờ không còn là “hộp đen sáu giờ”, mà có thể đọc thành:
IMPLEMENTING → TESTING → INTEGRATING → VERIFYING
Một hộp đen rất thông minh vẫn là hộp đen. Qua vài giờ, người dùng sẽ bắt đầu lo.
4. Trong lúc testing, Heartbeat đặc biệt quan trọng
Test dài nhìn từ bên ngoài gần như giống hệt Agent bị treo.
Trước test dài, hãy ghi:
phase = TESTING- tên suite
- phạm vi
- số đếm khách quan như
84 / 127nếu tổng số cố định - đã có failure hay chưa
Tránh tỷ lệ bịa như “82% hoàn thành” cho thiết kế hoặc debugging. Không ai thật sự biết 18% còn lại gồm những gì.
Tỷ lệ chỉ có ý nghĩa khi mẫu số cố định:
- 84 / 127 tests
- 3 / 5 acceptance gates
“Triển khai xong 82%” là phiên bản AI của “sắp tới rồi” từ một người có thể vẫn đang ở nhà.
5. Hãy đọc câu hỏi “nó dừng chưa?” theo từng tầng
Một policy Heartbeat thực tế có thể phân loại độ cũ như sau:
| Độ cũ | Cách hiểu |
|---|---|
| 0–10 phút | CURRENT |
| 10–20 phút | HEARTBEAT_OVERDUE |
| Hơn 20 phút | STALE_UNKNOWN |
STALE_UNKNOWN không phải FAILED.
Sau đó kiểm tra theo thứ tự:
- record tiến độ hiện tại
- Check / Status
- branch / PR
- commit mới nhất
- suy đoán chỉ ở bước cuối
Nếu Heartbeat cũ nhưng sau đó có PR hoặc commit mới, thường nghĩa là Agent vẫn làm việc nhưng quên cập nhật trạng thái.
AI cũng có chuyện: làm xong việc nhưng quên ghi timesheet.
6. Các lỗi quản lý tiến độ cần tránh
Không commit Heartbeat-only lên main
Nó làm bẩn lịch sử, tăng conflict và có thể kích hoạt CI/deploy không cần thiết. Nên dùng surface nhẹ có thể cập nhật như Issue comment, Check, Status hoặc khu ops không production.
Không tạo comment mới cho mỗi Heartbeat
Một record có thể cập nhật cho mỗi run dễ đọc hơn. Comment mới mỗi 10 phút sẽ biến việc kiểm tra tiến độ thành khảo cổ học.
Không đồng nhất im lặng với thất bại
Nếu Heartbeat chỉ cũ, dùng STALE_UNKNOWN. Chỉ dùng failed khi có bằng chứng thất bại rõ ràng.
Không đưa bí mật vào log tiến độ
Không API key, token, password, private URL, raw private chat, dữ liệu cá nhân hoặc nội dung file mật.
Không dừng công việc an toàn chỉ vì kênh observability bị lỗi
Nếu progress API hỏng, fallback sang sink khác và tiếp tục phần việc độc lập vẫn an toàn.
7. Template thực dụng
State: RUNNING
Phase: TESTING
Run ID: agent-20260916-long-task
Started: 10:00
Last heartbeat: 14:05
Last material change: 13:42
Branch: feat/long-task
Head SHA: abc1234
PR: #123
Working on:
- regression tests
Completed:
- runtime implementation
- contract update
Remaining:
- regression completion
- merge verification
- production readback
Blockers:
- none
Tests:
- 84 / 127 passed so far
- no failure observed
Next checkpoint:
- finish regression, then integration
Chỉ cần vậy, “nó dừng rồi à?” sẽ thành “à, đang test”.
Mục đích của visibility không phải để thúc ép AI. Nó giúp con người không restart, gián đoạn hoặc gửi lặp cùng một chỉ thị chỉ vì không biết tình trạng.
8. Tổng kết――thiết kế “thông minh” và “nhìn thấy được” riêng biệt
Với AI chạy lâu, cần phân biệt:
- không có commit ≠ đã dừng
- có Heartbeat ≠ có tiến bộ vật chất
- Heartbeat cũ ≠ thất bại
- một blocker ≠ dừng toàn bộ
- im lặng trong test ≠ ngủ
Nếu AI có thể làm việc sáu giờ, con người không nên phải nhìn nó sáu giờ.
Thiết kế tốt hơn là: quay lại lúc nào cũng biết ngay nó đang ở bước nào.
Agent dài hạn lý tưởng không phải Agent nói liên tục.
Nó làm việc yên lặng, nhưng khi nhìn vào, vị trí hiện tại luôn rõ.

