Scheduled Agent Automation: cho vòng lặp một nhịp đập

TL;DR

  • Một vòng lặp xứng cái tên khi nó chạy theo một nhịp bạn không tự khởi động. Ba nhịp đập (heartbeat): /loop trong phiên (bạn đang ở bàn phím), cloud Routines (cron, laptop đóng), và CI (GitHub Actions). Chọn theo nơi công việc nằm và ai cần thức.
  • Quyết theo từng nhịp: do sự kiện kích hoạt (một hook bắn ở một khoảnh khắc vòng đời) hay do thời gian kích hoạt (một lịch bắn theo đồng hồ). Hook phản xạ; lịch tuần tra.
  • Kỷ luật làm một vòng lặp không người trông an toàn là hộp chờ phân loại (triage inbox): phát hiện, đẩy tới một con người, không bao giờ tự merge; không thấy gì thì lưu trữ lần chạy thật rẻ. Một lần chạy rỗng phải gần như miễn phí, không thì một nhịp hằng ngày sẽ làm bạn cháy túi.

📊 Bằng chứng kết quả. Một lần chạy phân loại buổi sáng theo lịch thật đã bắn đúng cái bước phân loại mà một GitHub Actions cron workflow đã commit và một dòng cron 5-trường cục bộ sẽ gọi. Nó đọc hai nguồn việc thật (một lần chạy pytest với hai lỗi và một danh sách issue mở), ghi mọi phát hiện vào một tệp trạng thái tried/passed/open/blocked với passed rỗng (vòng lặp không merge gì), và mở hai làn git worktree cho hai phát hiện đầu bảng, để phát hiện thứ ba lại trong hộp chờ. Một lần chạy thứ hai vào một buổi sáng sạch đã lưu trữ “nothing today” rồi thoát. Bằng chứng cơ chế (mechanism proof), không phải một bản ghi cloud-Routine trực tiếp hay một lần chạy Actions theo lịch trên cloud; bộ ra quyết định là xếp hạng đơn định, nên không có gì được đo đạc và không có con số token để báo (15/6/2026).

Bạn có một vòng lặp chạy được. Một đội worktree từ Phần 5, một /loop bạn tự bấm, một tiến trình claude -p chạy qua đêm. Nhưng bạn khởi động từng cái một, nên đó là một lần chạy bạn làm, không phải một vòng lặp tự chạy. Mảnh còn thiếu là scheduled agent automation: một nhịp đập (heartbeat) đánh thức vòng lặp mà không cần bạn, tìm việc, phân loại nó, và trao cho bạn một danh sách quyết định ngắn thay vì một prompt trống. Phản đối tự nó hiện ra: chẳng phải đây chỉ là cron thêm vài bước sao? Một câu, nói trước, từ Matt Van Horn: a loop is cron plus a decision-maker in the body. cron là nửa tầm thường; bộ ra quyết định (decision-maker) mới là vòng lặp.

Bài này cho bạn bốn thứ: ba nhịp đập một vòng lặp có thể chạy, quy tắc cho khi nào một nhịp nên bắn theo một sự kiện thay vì theo một lịch, kỷ luật hộp chờ phân loại chặn một vòng lặp không người trông khỏi merge code rác của chính nó, và một vòng lặp phân loại buổi sáng theo lịch hoàn chỉnh đấu nối tới một nguồn việc thật, một tệp trạng thái, và các làn worktree.

Yêu cầu trước:

  • Bạn đã đọc (hoặc đọc lướt được) Phần 2 tới Phần 5 của loạt bài này. Phần 6 ghép cả bốn: năm khối (Phần 2), một điều kiện dừng kiểm chứng được (Phần 3), tệp trạng thái tried/passed/open/blocked (Phần 4), và các làn worktree cho một đội (Phần 5).
  • Bạn chạy được một vòng lặp không người trông: một nhịp /loop, một cloud Routine theo lịch, một CI workflow, hay một tiến trình claude -p trần, và đọc được một bản khác biệt hợp nhất (diff).
  • Bạn biết cơ bản về Claude Code hook, hoặc sẽ đọc lướt bài hướng dẫn hook trước. Bài này không dạy lại các loại hook handler.

Định nghĩa chuẩn của cả loạt bài được giữ nguyên tiếng Anh, làm mỏ neo trích dẫn cho cả 9 phần:

A loop is a recursive goal: you define a purpose and a verifiable stop condition, and the system iterates agents against it until the condition holds — without you prompting each turn.

Nói cách khác, một vòng lặp là một mục tiêu đệ quy: bạn định nghĩa một mục đích và một điều kiện dừng (stop condition) kiểm chứng được, rồi để hệ thống lặp các agent cho tới khi điều kiện đó thành hiện thực, mà không cần bạn prompt ở từng lượt.

Một vòng lặp có thể chạy trên ba nhịp đập nào?

Một vòng lặp có thể đập ở ba nơi: trong phiên (/loop chạy lại một prompt theo chu kỳ khi bạn đang ở bàn phím), trên cloud (Routines chạy trên một cron 5-trường với laptop đóng), và trong CI (GitHub Actions theo một push hay một lịch). Chọn theo nơi công việc nằm và liệu một con người có cần thức khi nhịp bắn hay không.

Bắt đầu từ cái rẻ nhất. /loop trong phiên chạy lại một prompt theo chu kỳ ngay trong phiên hiện tại của bạn. Nó tức thì và không tốn gì để dựng, và nó chết ngay khi bạn đóng phiên. Đó là công cụ đúng cho một lượt quét có người trông mà bạn muốn kèm cặp, không phải cho thứ cần xảy ra lúc 6 giờ sáng khi bạn đang ngủ.

Cloud Routines mới là nhịp qua đêm thật. Chúng chạy trên hạ tầng của Anthropic với laptop đóng, trên một lịch cron 5-trường, với một giới hạn mỗi tài khoản về số lần chạy có thể khởi động trong ngày. Vì lần chạy không phụ thuộc vào máy bạn có bật hay không, đây là nhịp đập cho một vòng lặp phân loại buổi sáng buộc phải bắn dù bạn có ở bàn làm việc hay không.

CI là nhịp đập khi công việc chính là cái repo. GitHub Action của Claude Code chạy bên trong GitHub Actions theo một lịch hay một sự kiện, biến một issue thành một PR, lặp sửa-tới-khi-CI-xanh, và đi kèm các rào chắn chống chạy hoang. Dự án agentics đóng khung cùng ý này thành “Continuous AI”: các agent theo lịch sống trong các workflow Actions của bạn. Chọn CI khi team bạn vốn đã sống trong pull request và đầu ra của vòng lặp tự nhiên là một branch.

Cái ý nhịp này chẳng có gì mới, và bạn đừng giả vờ thế. Tmux-Orchestrator từng có các agent tự lên lịch các lần điểm danh của chính mình để chạy “trong lúc bạn ngủ” (một bản mẫu có sức ảnh hưởng, đã ngủ đông từ giữa 2025), và nightcrawler chạy một vòng lặp nghiên cứu qua đêm tự hành với thực thi theo từng episode. Cái lịch luôn là phần dễ. Cái biến một kịch bản theo lịch thành một vòng lặp là bộ ra quyết định bạn đặt vào thân của nó.

Điểm mấu chốt: Cái lịch là nửa dễ. Một cron job chạy một kịch bản cố định thì chỉ là cron; một cron job chạy một bộ ra quyết định thì là một vòng lặp. Chọn nhịp đập theo ai cần thức: bạn, không ai, hay máy chạy CI.

Nhịp đậpKích hoạtTrạng thái laptopHợp nhất với
/loop trong phiênMột prompt chạy lại theo chu kỳMở, bạn đang ở bàn phímMột lượt quét có người trông
Cloud Routinescron 5-trường trên hạ tầng AnthropicĐóngNhịp qua đêm hay buổi sáng thật
CI (GitHub Actions)on: schedule hay push trong một workflowTắt (chạy trên một máy chạy)Khi công việc là repo và team sống trong CI

Khi nào một nhịp nên do sự kiện kích hoạt (một hook) thay vì do thời gian kích hoạt (một lịch)?

Hãy làm một nhịp do sự kiện kích hoạt (một hook) khi nó phải phản ứng với cái gì đó vừa xảy ra trong một phiên: một công cụ vừa chạy, một lần dừng vừa được thử. Hãy làm nó do thời gian kích hoạt (một lịch) khi nó phải tuần tra theo nhịp bất kể có gì xảy ra hay không. Hook phản xạ; lịch tuần tra.

Bạn với tay tới hook handler nào, và nó gắn vào event nào, không phải việc của bài này. Còn hook event nào kích hoạt ở khoảnh khắc vòng đời nào, hãy trỏ tới hook decision guide. Phần 6 chỉ đối chiếu mô hình do-sự-kiện đó với nhịp do-thời-gian (cron/Routines), và không bao giờ giải thích lại các loại handler. Nếu bạn cần cả cơ chế viết một hook, bài hướng dẫn hook là cái sàn; từ đây cứ coi như đã biết cả hai.

Với cơ chế đã được dẫn ở trên, bản thân quyết định thì nhỏ. Một kích hoạt vòng đời (một hook) bắn bên trong một phiên, để đáp lại một hành động: đây là các phản xạ của vòng lặp. Một kích hoạt thời gian (một lịch cron, một cloud Routine, một CI on: schedule) bắn theo đồng hồ, bất kể một phiên có đang mở hay không: đây là nhịp đập của vòng lặp. Quy tắc theo thẳng từ đó. Nếu nhịp chỉ quan trọng khi có gì đó xảy ra, nó là một hook. Nếu nó phải xảy ra theo nhịp ngay cả khi không có gì xảy ra, nó là một lịch. Nhiều vòng lặp dùng cả hai, và đó không phải mâu thuẫn: một lịch đánh thức vòng lặp, còn các hook cai quản từng lượt một khi nó đang chạy.

Điểm mấu chốt: Một hook là một phản xạ; một lịch là một nhịp đập. Phản xạ bắn khi có gì đó xảy ra. Một nhịp đập bắn dù có gì xảy ra hay không, đó chính là lý do một vòng lặp theo lịch cần hộp chờ phân loại ngay sau đây.

Loại kích hoạtBắn khiVí dụHook hay lịch
Vòng đờiCó gì đó xảy ra trong một phiênMột công cụ chạy; một lần dừng được thửHook
Thời gianĐồng hồ điểm, bất kể06:00 các ngày trong tuần; mỗi pushLịch

Mẫu hộp chờ phân loại giữ một vòng lặp theo lịch an toàn ra sao?

Hộp chờ phân loại (triage inbox) là quy tắc làm một vòng lặp không người trông an toàn: mỗi lần chạy phát hiện việc và đẩy phát hiện của nó tới một hộp chờ của con người, một lần chạy không thấy gì thì tự lưu trữ thật rẻ, và một lần chạy không bao giờ tự merge phát hiện của mình. Phát hiện thì tự động; quyết định vẫn là của con người. Đây là tổng hợp của riêng bài, không phải một con số trích dẫn, nhưng nó là kỷ luật quan trọng nhất trong bài.

Ba quy tắc, theo thứ tự mức độ cắn bạn nếu bỏ qua.

Phát hiện đi vào một hộp chờ, và một con người quyết. Vòng lặp được phép làm phần phát hiện và các phần nhàm chán: mở làn, soạn nháp PR, viết một danh sách phát hiện. Nó không được phép quyết cái gì đáp xuống. Đây đúng cái ranh giới Phần 3 vạch cho các điều kiện dừng: một agent kiểm có thể buộc test phải xanh, nhưng merge là một quyết định của con người. Hộp chờ chỉ là chỗ vật lý mà cú bàn giao đó diễn ra, và nó nên nhỏ và grep được: một tệp trạng thái, một GitHub issue, một nháp PR, một tin Slack.

Không thấy gì, lưu trữ lần chạy. Một lần chạy rỗng viết một dòng “nothing today” rồi thoát. Bản thân đó là một điều kiện dừng kiểm chứng được: lần chạy đạt tới một trạng thái mà một tiến trình khác xác nhận được, và nó dừng. Nó cũng phải rẻ, đó là toàn bộ Beat 5, nên giữ ý đó trong đầu.

Không bao giờ để phát hiện đi thẳng tới merge. Đây là tội lớn nhất của tự động hoá không người trông, và đúng là kịch bản thất bại mà playbook agent chạy dài của Anthropic cảnh báo: chiến thắng non, giả-xong, một vòng lặp tuyên bố một thành công nó chưa kiếm được. Cái phản mẫu nghe rất bùi tai (“chạy ngày chạy đêm tới khi tính năng xong, tự merge dọc đường”) và đó là cách bạn thức dậy với một nhánh main đầy các commit nghe-có-lý-mà-sai. Hộp chờ là bức tường lửa.

Điểm mấu chốt: Một vòng lặp không người trông được phép tìm việc và được phép làm các phần nhàm chán. Nó không bao giờ được phép quyết cái gì ship. Hộp chờ là nơi vòng lặp dừng và bạn bắt đầu.

lần chạy theo lịch
|
|-- có phát hiện? --> hộp chờ (tệp trạng thái / issue / PR) --> con người quyết --> merge
| (vòng lặp không bao giờ tự merge phát hiện của mình)
|
'-- không có gì? --> lưu trữ "nothing today" --> thoát rẻ

Bắt tay làm: một vòng lặp scheduled agent automation cho phân loại buổi sáng

Một kích hoạt theo lịch đánh thức một bước phân loại, nó đọc hai nguồn việc thật (CI hôm qua và một danh sách issue mở), ghi mọi phát hiện vào một tệp trạng thái tried/passed/open/blocked duy nhất, và mở các làn worktree cho hai phát hiện đầu bảng để phần còn lại trong hộp chờ. Mục passed vẫn rỗng, vì vòng lặp đẩy đi chứ không bao giờ merge. Đây là lần chạy, trung thực về giới hạn của nó trước đã.

Đọc ghi chú trung thực trước khi xem code. Đây là một bằng chứng cơ chế (mechanism proof), không phải một cloud-Routine qua đêm trực tiếp hay một lần chạy GitHub Actions theo lịch trên cloud. Cả hai đều không bắt được sạch sẽ ở chế độ headless, đúng cái giới hạn đã ghi trong các trace Phần 3, 4, và 5. File workflow Actions bên dưới được trưng ra, không chạy trên cloud; dòng cron là cái artifact thật bạn sẽ cài, nhưng sandbox không có cron daemon, nên bản thân cái lịch không bị cron bắn. Thay vào đó cái bước phân loại đúng-y mà dòng cron gọi đã được chạy trực tiếp, đó là cái nhịp đập thực thi dù theo cách nào. Bộ ra quyết định là logic xếp hạng đơn định, không phải một lượt của agent LLM, nên không có gì được đo đạc và không có con số token hay chi phí để báo. Đừng đọc ra một con số nào.

Hai nguồn việc. Vòng lặp buổi sáng đọc hai thứ. Cái thứ nhất là CI hôm qua: một bộ pytest thật với hai lỗi thật, độc lập (một slugify để sót một gạch nối ở đuôi, và một load_port trả về một chuỗi thay vì một int). Cái thứ hai là một danh sách issue mở với ba mục: #14#15 mô tả đúng hai khiếm khuyết đó, và #19 là một yêu cầu tính năng không có test gãy. Bộ ra quyết định khử trùng một lỗi CI với cái issue đại diện nó (nên #14#15 không bị đếm hai lần), xếp các bug CI-đỏ trên các mục chỉ-có-issue và bug trên tính năng, rồi chọn hai cái đầu bảng cho các làn.

Bước 0, CI hôm qua (kỳ vọng 2 RED). Trước mọi phân loại, một lần chạy thật xác nhận hai phát hiện đỏ:

$ python3 -m pytest -q
tests/test_config.py:3: AssertionError
_____________________________ test_trailing_hyphen _____________________________
def test_trailing_hyphen():
> assert slugify("Hello World!") == "hello-world"
E AssertionError: assert 'hello-world-' == 'hello-world'
E
E - hello-world
E + hello-world-
E ? +
tests/test_slugify.py:3: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_port_is_int - AssertionError: assert '8080'...
FAILED tests/test_slugify.py::test_trailing_hyphen - AssertionError: assert '...
2 failed in 0.01s
PYTEST_EXIT=1

Kích hoạt theo lịch A, một GitHub Actions workflow đã commit. Đây là nhịp đập CI (Beat 1) như một file thật, đã commit. Nó được trưng ra để tái lập, không chạy trên cloud ở đây:

.github/workflows/morning-triage.yml
name: morning-triage
on:
schedule:
- cron: "0 6 * * 1-5" # 06:00 on weekdays, the heartbeat
workflow_dispatch: {}
jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run morning triage (discover, write inbox, open lanes)
run: python3 triage/triage.py --budget-runs 1 --max-lanes 2

Kích hoạt theo lịch B, một dòng cron 5-trường cục bộ thật. Đây là mục bạn sẽ cài bằng crontab -e:

0 6 * * 1-5 cd /home/you/p6-triage && /usr/bin/python3 triage/triage.py --max-lanes 2 --budget-runs 1 >> $HOME/.morning-triage.log 2>&1

Phân loại, bộ ra quyết định trong thân vòng lặp. Chạy đúng cái bước cron gọi: nó đọc cả hai nguồn, xếp hạng, mở hai làn worktree cho hai cái đầu bảng, và để phát hiện thứ ba trong hộp chờ. --max-lanes 2--budget-runs 1 là các công tắc ngắt (kill switch) từ Beat 5 (giới hạn số làn mỗi lần chạy, giới hạn số lần chạy mỗi lần gọi):

$ python3 triage/triage.py --max-lanes 2 --budget-runs 1
TRIAGE: 3 findings; opened 2 lanes; 1 left in inbox; passed=0 (never auto-merge)
$ git worktree list
/home/you/p6-triage 242b6dc [master]
/home/you/wt-finding-14 242b6dc [triage/finding-14]
/home/you/wt-finding-15 242b6dc [triage/finding-15]

Hộp chờ, một tệp trạng thái. Các phát hiện đáp vào một LOOP_STATE.md duy nhất dùng lại lược đồ tried/passed/open/blocked của Phần 4. Các làn mở ra dùng lại cơ chế worktree của Phần 5:

# Loop State: morning triage (Part 4 schema)
## tried
- read CI: 2 failing test(s); read issues: 3 open
- opened 2 worktree lane(s) for the top 2 finding(s)
## passed
- (none; triage never merges; a human decides what ships)
## open
- [#14 CI-red: tests/test_slugify.py::test_trailing_hyphen] lane opened: /home/you/wt-finding-14 on triage/finding-14 (awaiting human)
- [#15 CI-red: tests/test_config.py::test_port_is_int] lane opened: /home/you/wt-finding-15 on triage/finding-15 (awaiting human)
- [#19 [feature] add a --version flag to the CLI] in inbox, no lane (below top-2)
## blocked

Tiêu đề của cả lần chạy là một dòng trong file đó: ## passed rỗng. Vòng lặp phát hiện việc, ghi nó vào hộp chờ, và mở các làn cho một con người nhặt. Nó không merge gì.

Lần chạy rỗng, lưu trữ thật rẻ. Sửa cả hai bug, dọn danh sách issue, và chạy lại để mô phỏng một buổi sáng sạch. Bước phân loại đọc tối thiểu rồi thoát:

$ python3 -m pytest -q
.. [100%]
2 passed in 0.00s
$ python3 triage/triage.py --max-lanes 2 --budget-runs 1
EMPTY RUN: 0 findings -> archived 'nothing today', exit cheap
# Loop State: morning triage (Part 4 schema)
## tried
- [2026-06-15] read CI (0 failing) + issues (0 open) -> nothing to triage; archived
## passed
## open
## blocked

Thu hồi các làn. Dọn dẹp xoá cả hai làn để nhịp đập không tích tụ worktree mồ côi qua các lần chạy:

$ git worktree remove ../wt-finding-14
$ git worktree remove ../wt-finding-15
$ git worktree list
/home/you/p6-triage 0bea45a [master]

Đó là trọn vòng lặp buổi sáng. Một lịch đánh thức nó, nó đọc hai nguồn thật, nó ghi phát hiện vào một tệp trạng thái mà không merge gì, nó mở làn cho hai cái đầu bảng, và một lần chạy lại sạch tự lưu trữ gần như miễn phí.

Điểm mấu chốt: Vòng lặp buổi sáng là cả loạt bài trong một lần chạy: một lịch đánh thức nó (Phần 6), một điều kiện dừng giới hạn nó (Phần 3), các phát hiện đáp vào tệp trạng thái (Phần 4), và hai cái đầu bảng nhận các làn worktree (Phần 5). Cái nhịp là phần mới duy nhất.

Chạy theo nhịp tốn bao nhiêu, và làm sao giữ các lần chạy rỗng rẻ?

Một nhịp hằng ngày nhân chi phí lên theo tần suất, nên cái ràng buộc đáng kể là mỗi-lần-chạy: một trần ngân sách (budget) và các công tắc ngắt trên mỗi lần chạy, và một lần chạy rỗng thoát gần-như-miễn-phí. Nếu một lần chạy không thấy gì vẫn tốn tiền thật, thì một lịch hằng ngày là một chỗ rò chậm bạn không nhận ra cho tới khi nhận hoá đơn.

Kỷ luật chi phí phiên nói chung nằm trong cost-optimization guide; cứ trỏ tới nó. Phần 6 chỉ bàn cái riêng của nhịp không người trông: một trần ngân sách mỗi lần chạy, các công tắc ngắt, và vì sao một lần chạy theo lịch mà rỗng phải gần như miễn phí. Đừng tái chế quy tắc 15-tin-nhắn hay các chiến lược chống phình context window ở đây; đó là việc của bài kia.

Ba thứ riêng cho việc chạy theo lịch. Thứ nhất, ngân sách mỗi lần chạy và các công tắc ngắt. continuous-claude phơi ra bề mặt công tắc ngắt cho một vòng lặp không người trông một cách trực tiếp: --max-runs, --max-cost, --max-duration, --stall-threshold, và các rào chắn chống chạy hoang của claude-code-action là cùng ý đó trong CI. Một vòng lặp theo lịch thiếu chúng chính là cái thứ đốt một tháng ngân sách qua đêm khi không ai trông.

Thứ hai, các lần chạy rỗng phải rẻ. Lối “không có gì, lưu trữ” của hộp chờ phân loại từ Beat 3 cũng là lối chi phí. Trên một nhịp hằng ngày, lần chạy rỗng là trường hợp thường, không phải ngoại lệ, vì hầu hết các buổi sáng chẳng có gì đỏ. Ngân sách mỗi episode của nightcrawler cũng cùng bản năng đó: giới hạn cái một lần chạy đơn được tiêu trước khi nó bắt đầu. Thứ ba, cái đánh đổi nhịp-chi-phí. Một nhịp đập dày hơn nghĩa là phát hiện nhanh hơn và nhiều lần chạy rỗng phải trả tiền hơn. Chỉnh tần suất theo việc thật sự xuất hiện thường ra sao, không phải theo việc bạn ước mình đang nhìn thường ra sao.

Điểm mấu chốt: Trên một nhịp hằng ngày, lần chạy rỗng là trường hợp thường, nên lần chạy rỗng mới là chi phí thật của bạn. Giới hạn mỗi lần chạy và làm “nothing today” gần như miễn phí, không thì nhịp đập lặng lẽ rút cạn ngân sách khi bạn ngủ.

Công tắc ngắtCái nó giới hạnLỗi nó chặn
--max-runsSố lượt mỗi lần gọiMột vòng lặp chạy lại vô hạn
--max-costSố đô mỗi lần chạyMột hoá đơn chạy hoang qua đêm
--max-durationThời gian thực mỗi lần chạyMột lần chạy treo không bao giờ thoát
--stall-thresholdSố lượt không tiến triểnQuay vòng mà không hội tụ

Quy tắc một dòng dưới bảng đó: một lần chạy rỗng là một lần đọc tối thiểu cộng một lần thoát. Hơn thế nữa thì trường hợp thường thành trường hợp đắt.

Bạn có gì, và tiếp theo là gì

Giờ bạn có lớp nhịp: ba nhịp đập (/loop trong phiên, cloud Routines, CI) và một quy tắc hợp cho mỗi cái, quyết định hook-hay-lịch (phản xạ so với nhịp đập), kỷ luật hộp chờ phân loại (đẩy tới một con người, không bao giờ tự merge, lưu trữ thật rẻ), một vòng lặp phân loại buổi sáng theo lịch hoàn chỉnh đấu nối tới một nguồn việc thật, một tệp trạng thái, và các làn worktree, và một ngân sách mỗi lần chạy với các công tắc ngắt để lần chạy rỗng giữ gần-như-miễn-phí.

Bốn thứ cần để mắt khi lên production: một lịch không có bộ ra quyết định trong thân (đó chỉ là cron); một vòng lặp tự merge phát hiện của mình (tội lớn nhất); một lần chạy rỗng mà không thật sự rẻ (một chỗ rò hằng ngày); và một nhịp nhanh hơn việc xuất hiện (trả tiền cho các lần chạy rỗng). Một nhịp đập không có hộp chờ phân loại chỉ là một agent không người trông merge code rác lúc 3 giờ sáng, nên hãy buộc mọi lần chạy vào một điều kiện dừng kiểm chứng được và giữ merge là một quyết định của con người.

Giờ bạn có đủ mọi mảnh: các khối, các điểm dừng, bộ nhớ, đội agent, và một nhịp đập. Cái còn lại là phần trung thực. Tiếp theo là nhật ký hành trình cải tạo (retrofit) tất cả những thứ này lên một vòng lặp đầu tiên thật, lộn xộn, loop-journey-retrofitting-first-loop, mở màn cho giai đoạn thực hành.

FAQ

Một vòng lặp agent theo lịch có phải chỉ là một cron job không?

Không. Một cron job chạy một kịch bản cố định; một vòng lặp theo lịch chạy một bộ ra quyết định trong thân. Cái lịch là nửa tầm thường. Cái làm nó thành một vòng lặp là mỗi lần chạy đọc trạng thái, quyết làm gì, và cập nhật trạng thái đó, nên lần chạy kế tiếp tục từ chỗ lần này dừng thay vì lặp lại một lệnh tĩnh.

Tôi nên chạy vòng lặp bằng cron, cloud Routines, hay CI?

Chọn theo nơi công việc nằm và ai cần thức. /loop trong phiên là cho các lượt quét có người trông khi bạn ở bàn phím. Cloud Routines chạy trên một cron 5-trường với laptop đóng (giới hạn số lần chạy mỗi ngày theo tài khoản), nên chúng là nhịp qua đêm thật. CI (GitHub Actions) hợp nhất khi công việc là repo và team bạn vốn đã sống trong CI.

Khi nào tôi nên dùng một hook thay vì một lịch?

Dùng một hook khi nhịp phải phản ứng với cái gì đó vừa xảy ra trong một phiên (một công cụ chạy, một lần dừng được thử); hook là phản xạ. Dùng một lịch khi nhịp phải tuần tra theo nhịp dù có gì xảy ra hay không; lịch là nhịp đập. Nhiều vòng lặp dùng cả hai: một lịch đánh thức vòng lặp, các hook cai quản từng lượt bên trong nó.

Làm sao tôi chặn một vòng lặp theo lịch khỏi tự merge code tồi?

Dùng mẫu hộp chờ phân loại: vòng lặp phát hiện việc và đẩy phát hiện tới một con người (một tệp trạng thái, một issue, một nháp PR), nhưng một người quyết cái gì merge. Một lần chạy không thấy gì thì tự lưu trữ. Vòng lặp không bao giờ được phép tự merge phát hiện của mình, nên phát hiện thì tự động còn quyết định vẫn là của con người.

Làm sao giữ một vòng lặp theo lịch hằng ngày khỏi đốt ngân sách của tôi?

Giới hạn mọi lần chạy, không chỉ cả công việc: đặt một ngân sách mỗi lần chạy và các công tắc ngắt (--max-cost, --max-duration, --max-runs, --stall-threshold), và làm lần chạy rỗng rẻ để một lần chạy “nothing today” đọc tối thiểu rồi thoát. Trên một nhịp hằng ngày lần chạy rỗng là trường hợp thường, nên chi phí của nó là chi phí thật của bạn.

Đọc tiếp gì nữa

” / ”|” (không em-dash). Mọi dash khác trong prose = hyphen-minus.

Gate compliance:

  • Frontmatter: locale vi, level senior, persona [“senior-dev”], category+archetype tutorial, 6 series-SEO fields (targetKeyword GIỮ TIẾNG ANH “scheduled agent automation”), pubDate 2026-06-27 (LOCKED), overrides length_norm 2200, tags có “vietnamese” (KHÔNG “english”), image path reserved. KHÔNG có seoTitle.
  • H1 dẫn bằng keyword tiếng Anh “Scheduled Agent Automation” + đuôi VI “cho vòng lặp một nhịp đập” (53 ký tự); suy từ H1.
  • description dẫn bằng “Scheduled agent automation” (~146 ký tự, <=155).
  • Exact phrase “scheduled agent automation” (tiếng Anh) ở: H1, description, đoạn body ĐẦU TIÊN, VÀ H2 Beat 4 (“Bắt tay làm: một vòng lặp scheduled agent automation cho phân loại buổi sáng”) -> thoả gate keyword-in-H2.
  • 5 beat khoá thứ tự, câu hỏi VI; beat 4 giữ form “Bắt tay làm:”. Mỗi beat: Answer-First + 1 capsule blockquote VI (”> Điểm mấu chốt:”) + visual. + Close + FAQ + “Đọc tiếp gì nữa”.
  • FAQ: 5 heading ”### ” câu hỏi VI (FAQPage-derivable).
  • 2 boundary line (link-don’t-reteach) render bằng VI: Beat 2 -> claude-code-hook-decision-guide (không dạy lại handler types); Beat 5 -> claude-code-cost-optimization (chỉ kinh tế nhịp không người trông).
  • Beat 4: mọi code block (pytest/yaml/cron/triage/state file/empty/reap) BYTE-IDENTICAL với EN draft (code/log không dịch); scratch paths anonymized sang /home/you/… (mapping ground truth ở RUN-TRACE, không lặp lại đây để file ship sạch); hashes 242b6dc + 0bea45a giữ nguyên; không còn literal scratch-temp path nào trong file. Sơ đồ Beat 3 (visual khái niệm, KHÔNG phải block trace) được Việt hoá nhãn, khớp tiền lệ Part 5 VI. Khung bằng-chứng-cơ-chế trung thực; token/cost honestly unavailable.
  • Softened claims carried từ EN fact-check: (a) Routines = “giới hạn mỗi tài khoản về số lần chạy” (KHÔNG cite Pro 5 / Max 15); (b) nightcrawler = “vòng lặp nghiên cứu qua đêm tự hành với thực thi theo từng episode” (KHÔNG nói launchd).
  • Internal links VI routes (/vi/blog/…): loop-anatomy-five-building-blocks, claude-code-hook-decision-guide, claude-code-hooks-guide, loop-stop-conditions-verification, loop-memory-state-files, loop-parallel-worktrees-subagents, claude-code-cost-optimization. Cliffhanger loop-journey-retrofitting-first-loop CHỈ TÊN (no href; chưa ship trên develop VI per /content-idea CHECK 5).
  • BANNED cross-part numbers: KHÔNG dùng. Chỉ báo dữ liệu beat-4 run của chính bài.

TODO trước publish:

  • Fact-checker: re-verify liveness + redirect 8 URL ngoài + wording quote mvanhorn (X chưa re-fetch).
  • Cover image /images/blog/loop-scheduled-automations-heartbeat-cover.png chưa tạo (path reserved, dùng chung EN).
  • Internal VI link targets resolve trên develop /vi/blog/ routes; verify khi publish.
  • Next: /content-review (CHƯA chạy theo yêu cầu). —>