blog

Loop engineering: thôi prompt agent, đi thiết kế cái vòng lặp prompt nó

Dựng một vòng lặp cho agent giờ mất mười lăm phút. Phần khó là nhét vào trong đó một thứ dám nói "không". Năm nước đi của một vòng, năm kiểu hỏng, và vòng lặp thật của site này đang đi được nửa đường.

AIAgentClaude CodeCI/CD
Vòng tròn năm nút discovery, handoff, verification, persistence, scheduling, nút verification sáng màu accent và có một cổng chặn

Dựng một vòng lặp cho agent giờ mất mười lăm phút: một dòng cron, một skill, một lệnh chạy. Phần khó là nhét vào trong đó một thứ dám nói "không".

Tháng 6/2026 có ba người gọi tên cùng một kiểu làm việc trong đúng một tuần. Peter Steinberger viết là đừng prompt coding agent nữa, hãy thiết kế vòng lặp prompt nó. Boris Cherny, người dẫn Claude Code, nói anh không còn prompt Claude mà có sẵn các vòng lặp tự prompt Claude và tự tìm việc tiếp theo. Addy Osmani đặt tên và viết nó ra ngày 7/6/2026: loop engineering là thay chính mình ở vị trí người prompt agent, rồi thiết kế cái hệ thống làm việc đó thay mình.

Loop nằm một tầng trên harness, khác ở ba động từ

Bốn tầng này chồng lên nhau chứ không thay thế nhau. Prompt lo câu chữ trong một lượt. Context lo cái gì được nạp vào cửa sổ ngay lúc này. Harness lo trang bị cho một lần chạy: tool nào, hành động nào được phép, thế nào là xong. Loop lo phần còn lại: làm sao nó tự chạy lại, hết lần này tới lần khác.

Bốn tầng chồng lên nhau: prompt, context, harness, loop; mỗi tầng lo một đơn vị lớn hơn tầng dưới
Bốn tầng chồng lên nhau: prompt, context, harness, loop; mỗi tầng lo một đơn vị lớn hơn tầng dưới

Harness kết thúc ở chỗ một lần chạy xong rồi đứng đó đợi. Ba động từ tách loop khỏi harness:

  • Chạy theo giờ. Vòng lặp tự thức dậy, không cần ai bấm nút.
  • Đẻ sub-agent. Một vòng đang quay tách ra nhiều agent con, đứa viết code, đứa chỉ soi lỗi.
  • Tự ăn output của mình. Kết quả hôm nay ghi ra file, sáng mai nó đọc lại file đó rồi làm tiếp.

Động từ thứ ba là thứ khiến nó thành vòng lặp thay vì một task chạy nhiều lần.

Mỗi tầng hỏng một kiểu khác nhau, và đây mới là lý do phải phân tầng. Lấy cùng một lỗi: agent đọc sai giá trị trả về của một hàm. Ở tầng prompt, nó cho một câu trả lời sai và người ngồi đó sửa ngay. Ở tầng harness, agent sửa một file rồi kết thúc, diff nằm đó chờ review. Ở tầng loop, cái đọc sai ấy được ghi vào file state, sáng hôm sau đọc lại như một dữ kiện đã chốt, rồi mọi thứ xây tiếp lên nó. Tới lúc có người nhìn thì giả định sai đã thành chỗ chịu lực. Chi phí của một lỗi tỉ lệ với số lượt nó sống sót trước khi bị bắt, và vòng lặp về bản chất là cỗ máy tăng số lượt lên tối đa.

Năm nước đi trong một lượt: discovery, handoff, verification, persistence, scheduling

Bỏ một trong năm thì vòng không quay, hoặc quay tại chỗ.

Nước điLàm gìHiện thực bằng
DiscoveryTự tìm việc của lượt nàyMột skill đọc CI, issue, commit mới
HandoffGiao việc, mỗi việc một chỗ riênggit worktree cho từng agent
VerificationĐổi agent khác vào để nói "không"Sub-agent review, model khác
PersistenceGhi trạng thái ra ngoài cuộc hội thoạiFile markdown hoặc board, có commit
SchedulingLàm nó quay lượt saucron, GitHub Actions, automation

Discovery đặt trần cho cả vòng lặp. Tìm ra toàn việc vô giá trị thì bốn nước còn lại làm đẹp tới đâu cũng phục vụ cho hư không. Phần logic tìm việc nên nằm trong một skill có tên, đừng dán cả một khối chữ vào crontab, vì file skill còn được sửa còn khối chữ trong crontab thì mục rữa ở đó.

Osmani liệt kê năm thành phần: automations, worktrees, skills, connectors, sub-agents. Tôi thêm thành phần thứ sáu là memory, tức trạng thái nằm trên đĩa. Memory khác context: context là cái agent nhìn thấy lượt này và bị xoá khi refresh, memory sống qua nhiều ngày. Agent quên, repo thì không.

Agent chấm bài của chính nó thì luôn cho điểm cao

Đây là chỗ mọi vòng lặp chết. Bảo agent tự đánh giá thứ nó vừa viết ra thì nó khen, khen chắc nịch, kể cả khi người ngồi cạnh nhìn phát biết là tầm thường. Kỹ sư Anthropic Prithvi Rajasekaran gặp đúng chuyện đó khi dựng các ứng dụng agent chạy dài.

Nguyên nhân nằm ở context. Cửa sổ lúc đó đã đầy ắp lý do vì sao code được viết như vậy, nên khi agent nhìn lại output của mình, nó không nhìn thấy kết quả, nó nhìn thấy chuỗi lập luận đã dẫn nó tới đó.

Cách sửa không nằm ở câu chữ. Bắt generator tự khắt khe hơn thì gần như vô ích, còn tinh chỉnh một evaluator độc lập cho nó đa nghi lại dễ hơn nhiều. Không ai bước ra khỏi góc nhìn của chính mình được, nhưng đổi hẳn một agent khác vào, với bộ chỉ dẫn khác, nhìn code từ con số không thì được.

md
# .claude/agents/reviewer.md
ROLE: Adversarial code reviewer.
ASSUME: this code is BROKEN until proven otherwise.
DO NOT praise. Find what fails.

CHECK, in order:
1. Does it run? (execute, don't read)
2. Tests: run them, paste real output.
3. Edge cases the author skipped.
4. Does behavior match the ticket?

USE Playwright MCP: open the page, click, screenshot, inspect the DOM.
Judge behavior, not intent.

VERDICT: PASS only if every check holds. Otherwise REJECT + list each reason.

Hai chi tiết trong file trên đáng chú ý hơn phần còn lại. Evaluator phải hành động, không chỉ đọc. Chỉ đọc thì nó đánh giá "trông có vẻ đúng" chứ không phải "chạy có đúng không"; nối nó vào Playwright MCP để mở trang, bấm nút, chụp màn hình thì căn cứ chấm đổi từ "JSX này nhìn ổn" thành "tôi bấm nút, trang chuyển, ảnh đây". Nên đổi cả model bên dưới, vì cùng một model với chỉ dẫn mới thường giữ nguyên điểm mù cũ.

Điều kiện dừng cũng nên giao cho một model khác. Tài liệu Claude Code mô tả /goal là "Claude keeps working across turns until the condition is met". Sau mỗi lượt có một model nhỏ và nhanh kiểm xem điều kiện đã đạt chưa; chưa đạt thì chạy tiếp lượt nữa thay vì trả quyền về. Đây là nguyên tắc maker-checker của ngành ngân hàng, người nhập lệnh chuyển tiền lớn và người duyệt phải là hai người, đem áp cho câu hỏi "xong chưa".

text
/goal all tests in test/auth pass and the lint step is clean

Trình độ generator quyết định vòng lặp làm ra được cái gì, còn trình độ evaluator quyết định nó không cho cái gì lọt ra, nên sàn chất lượng của cả vòng nằm ở evaluator.

Vòng lặp của site này mới đi được nửa đường

Repo của nguyentrungkhuong.com đang chạy một vòng lặp thật, nhỏ, và tôi biết chính xác nó thiếu gì.

Nước đi có sẵn: persistence là 10 file .sources.yml nằm cạnh 10 bài blog, tổng 102 claim, mỗi claim ghi số nào lấy từ nguồn nào. Verificationcheck-sources.js, 280 dòng, chạy ở prebuild, đối chiếu mọi số có hình dạng dữ liệu trong bài với sidecar và thoát khác 0 nếu lệch:

json
"prebuild": "npm run check:sources && node scripts/generate-search-index.js && ..."

Đặt nó ở prebuild là cố ý. Nó không phải một cảnh báo trong log, nó chặn npm run build, nên deploy đỏ và site giữ nguyên bản cũ. Đây đúng là "thứ dám nói không", và giá của nó là 280 dòng JavaScript không phụ thuộc gì.

Scheduling là một job cron chạy 08:00 thứ hai hàng tuần:

yaml
on:
  schedule:
    - cron: '0 1 * * 1'

Discovery nằm trong job đó: nó gom mọi link ngoài trong content/, curl từng cái xem còn sống không, rồi soi những claim đánh dấu volatile có ngày kiểm quá 90 ngày.

Thiếu handoff. Job cron tìm ra vấn đề rồi đổ vào ::warning:: của Actions, chờ tôi đọc. Không có worktree nào mở ra, không agent nào được giao việc, không PR nào được tạo. Nó tự tìm việc và tự chặn việc sai, nhưng không tự làm, nên mới là nửa vòng lặp.

Đợt rà tháng 7/2026 chạy tay qua 10 bài tìm ra 7 lỗi thật, trong đó có một câu gán cho tài liệu Chrome một phát biểu mà tài liệu đó không hề nói. Cái sidecar không ngăn được việc viết sai câu ấy. Nó làm việc viết sai kiểm được, và đó là toàn bộ giá trị của nó.

Bước tiếp theo đã rõ: cho job thứ hai mở worktree cho từng phát hiện, giao một agent soạn bản sửa, và giữ nguyên chỗ dừng ở PR. Không auto-merge.

Năm kiểu vòng lặp hỏng, mỗi kiểu là một nước đi bị bỏ

KiểuBỏ nước điTriệu chứng
Gật gùVerificationChạy hàng trăm lượt chưa từng nói "không" lần nào
Mất tríPersistenceSáng nào cũng bắt đầu lại từ chỗ cũ
Thủ côngSchedulingLần chạy gần nhất là hôm đem đi demo
DiscoveryNgười vẫn mất buổi sáng để quyết xem vòng lặp nên làm gì
RốiHandoffMột agent thì ổn, sáng nào chạy năm agent là merge không gỡ nổi

Kiểu gật gù phổ biến nhất. Một vòng lặp chạy hàng trăm lượt mà chưa lần nào tự bác bỏ chính nó là chuyện không thể có với bất kỳ khối lượng công việc thật nào, nên nó là bằng chứng rằng không có cái kiểm nào tồn tại ở đó.

Năm kiểu này hay đi cùng nhau. Vòng lặp làm vội thường cài đúng hai nước tạo ra kết quả nhìn thấy được, discovery và handoff, rồi bỏ ba nước tạo ra an toàn.

Bốn khoản nợ không kêu tiếng nào

Vòng lặp tự chạy cũng là vòng lặp tự sai. Không khoản nợ nào dưới đây phát ra tiếng trong lúc nó đang chạy.

Verification debt. Mỗi PR mở ra và merge vào đều tiết kiệm thời gian, và thời gian tiết kiệm được biến thành output chưa ai kiểm, nằm đợi ngày trả. Nó trốn ở đúng chỗ test không phủ, trong khoảng cách giữa "chạy được" và "đúng".

Comprehension rot. Codebase phình ra trong khi bản đồ trong đầu đứng yên. Đọc code chán hơn viết code, mà phần viết thì vòng lặp lấy mất rồi.

Cognitive surrender. Vòng lặp càng đáng tin thì càng dễ thôi có ý kiến, nhận cái nó đưa rồi đi. Đây là phiên bản thái độ của hai khoản trên: hết muốn bận tâm, trong khi thời gian vẫn còn nguyên.

Token blowout. Khoản duy nhất đánh thẳng vào hoá đơn. Một con bug quay không tải cả đêm thì sáng ra có hoá đơn lạ chứ không có code đã sửa.

Bốn khoản này nuôi nhau. Một vòng lặp mở 20 PR qua đêm, test xanh hết, nhìn qua là thắng lớn. Giả sử ba trong hai mươi PR chứa lỗi tinh vi mà test không chạm tới: không evaluator độc lập, ba cái đó merge, đấy là verification debt. Người merge hai mươi PR không đọc, bản đồ trong đầu lùi lại hai mươi thay đổi, đấy là comprehension rot. Vòng chạy êm quá nên sáng hôm sau thôi đọc luôn, đấy là cognitive surrender. Và vì nó thoải mái đẻ helper với retry suốt đêm, hoá đơn gấp ba dự tính, đấy là token blowout.

Ba cái chốt tương ứng, đặt trước khi chạy chứ không phải sau hoá đơn đầu tiên:

  • Đọc mẫu mỗi ngày, không đọc hết, nhưng buộc mình giải thích lại vài thay đổi đã đọc. Giải thích không nổi nghĩa là bản đồ trong đầu đã tụt lại.
  • Đặt trần cứng: ngân sách mỗi lần chạy, ngân sách mỗi ngày, số retry tối đa. Mấy con số này là cầu chì, không phải mẹo tiết kiệm.
  • Chừa một cửa mở, tức ít nhất một điểm vòng lặp dừng lại chờ người. Không phải vì lần nào cũng cần can thiệp, mà vì cái cửa đó giữ cho người còn ở tư thế can thiệp được.

Stripe merge hơn 1.300 PR mỗi tuần bằng gate cứng, không bằng model to hơn

Hệ thống Minions của Stripe merge hơn 1.300 pull request mỗi tuần, không dòng nào gõ tay, theo lời kỹ sư Steve Kaliski trên podcast How I AI. Trigger nhẹ tới mức buồn cười: tag bot trong Slack, hoặc thả một emoji reaction.

Phần làm nên độ tin cậy nằm ở đoạn trước khi model thức dậy. Một orchestrator xác định gom context trước: quét link, kéo Jira, tìm docs, dùng Sourcegraph và MCP để định vị code liên quan. Để LLM tự đi tìm context của nó là phần khó kiểm soát nhất, nên phần việc đó, vốn có thể viết thành luật, bị lấy ra khỏi tay model.

Chi tiết ngược đời nhất: Minions không dựng trên một model mạnh hơn. Nó là bản fork của Goose, một tool nguồn mở, và luận điểm của nó là độ tin cậy đến từ chất lượng các ràng buộc chứ không từ kích thước model. Kiến trúc cài xen kẽ gate xác định với bước sáng tạo của LLM: agent viết code, một pipeline cứng chạy linter mà agent không có cách nào bỏ qua, agent sửa lint, rồi một bước cứng nữa chạy commit.

1.300 PR đó vẫn do người review, tức người không rời đi mà đổi từ bàn viết sang bàn duyệt.

Dựng vòng lặp đầu tiên: chọn local hay cloud trước đã

Câu hỏi duy nhất cần trả lời: việc của vòng lặp có dính vào máy local không?

/loop trên máyLịch cloud
Máy phải bậtKhông
Session phải mởKhông
Chu kỳ ngắn nhấtphútgiờ
Thấy file localKhông

Vòng lặp phải ping dev server mỗi phút thì chỉ chạy local được, vì cloud không nhìn thấy process trên laptop. Vòng lặp quét issue lúc ba giờ sáng thì đừng buộc vào laptop, vì laptop bị gập nắp, hết pin và bị xách ra khỏi nhà.

Chỗ hay nhầm là coi việc chạy lại tại chỗ là "chạy trong lúc mình ngủ". /loop chạy trong session, theo mô tả trong tài liệu là "Run a prompt repeatedly while the session stays open". Nó nghĩa là "chạy thêm mấy vòng nữa trong lúc tôi còn ngồi đây". Lịch cloud mới nghĩa là "chạy cả khi tôi không ở đây".

Với người làm web ở Việt Nam thì phần này rẻ hơn nghe tưởng. Ai đang thuê VPS chạy site là đã có sẵn một máy không bao giờ tắt. Repo này cài GitHub Actions self-hosted runner ngay trên server đó cho pipeline deploy, và cùng cái runner ấy chạy luôn job cron kiểm nguồn hàng tuần. Không phát sinh thêm hạ tầng, không tốn phút Actions.

Ba lệnh đủ để bắt đầu, kiểm trên bản Claude Code 2.1.196:

bash
# chạy lặp trong session, tự giãn nhịp nếu bỏ trống khoảng thời gian
/loop 5m check if the deploy finished

# chạy tới khi điều kiện đạt, có model khác chấm điều kiện
/goal all tests in test/auth pass and the lint step is clean

# mỗi việc một worktree riêng, agent song song không giẫm chân nhau
claude --worktree fix/auth-test "draft the fix"

Sáu dòng checklist trước khi thả cho nó chạy một mình:

  • Discovery: nó đọc gì theo lịch? CI, issue, commit hay inbox
  • State: file nào trên đĩa giữ trí nhớ giữa các lượt
  • Evaluator: có cái kiểm độc lập nào nói "không" được không
  • Isolation: mỗi agent song song có worktree riêng chưa
  • Token cap: đã đặt trần chi chưa, ai dừng nó khi nó chạy hoang
  • Human review: bước nào dừng lại chờ người nhìn

Hai dòng đầu quyết định vòng lặp chạy được hay không. Bốn dòng sau quyết định nó có gây hoạ khi đã chạy hay không. Người mới thường ship với đúng hai dòng đầu.

Đánh đổi thật khi giao việc cho vòng lặp

Vòng lặp là một dấu nhân trung thành: mang hiểu biết vào thì nó nhân hiểu biết, mang lười vào thì nó nhân lười lên đúng chừng ấy lần. Hai người dựng cùng một vòng lặp, code giống nhau tới chín mươi phần trăm, khác một hai chỗ dừng, và sáu tháng sau một người đứng trên vòng lặp còn người kia thành người gác cổng cho một cỗ máy họ không đọc được nữa.

Chỗ site này chưa làm được, nói thẳng: gate của tôi chỉ đối chiếu số. Ba loại sai nguy hiểm nhất vẫn phải đọc bằng mắt, gồm claim định tính sai, gán số đúng cho sai nguồn, và trích một tài liệu có thật những câu nó không nói. Cả ba đều nằm trong 7 lỗi đợt rà vừa rồi, và không cái nào máy bắt được.

Vòng lặp làm phần sinh ra gần như miễn phí. Thứ còn khan hiếm là biết cái nào đúng, và đó là lý do vẫn cần một kỹ sư ngồi đây.

Bài viết liên quan

tất cả bài viết