Git Worktrees for Agents: chạy nhiều agent song song không hỗn loạn
TL;DR
- Khoảnh khắc một vòng lặp chạy nhiều hơn một agent, các file va vào nhau. Worktree gỡ va chạm cơ học (mỗi agent có thư mục làm việc riêng); tách agent tạo (maker) khỏi agent kiểm (checker) gỡ va chạm chất lượng (không agent nào tự merge việc của mình).
- Dùng một đội agent (fleet) ba vai: agent thăm dò (explorer), agent thực thi (implementer), agent kiểm chứng (verifier). Song song hoá các phát hiện độc lập; tuần tự hoá bất cứ thứ gì dùng chung trạng thái. Mỗi làn (lane) song song là một worktree riêng kèm tự dọn.
- Băng thông review của bạn, không phải công cụ, mới là trần. Quy tắc kinh nghiệm: nếu bạn rà soát thật được N pull request mỗi ngày, chạy nhiều nhất N làn.
📊 Bằng chứng kết quả. Một lần chạy hai phát hiện thật đã cô lập mỗi phát hiện trong một git worktree riêng trên một branch riêng, một agent tạo đơn định tạo một bản khác biệt (diff) một-file mỗi làn, và một agent kiểm
pytestthật trả về exit 0 cho cả hai. Cả hai phát hiện merge từopensangpassedtrong một tệp trạng thái, và các worktree làn-chết được thu hồi khi dọn. Bằng chứng cơ chế (mechanism proof), không phải một đội agent tự hành được chấm điểm; không có gì được đo đạc, nên không có con số token để báo (15/6/2026).
Bạn toả vòng lặp ra hai agent để xong nhanh hơn, và cả hai cùng sửa một file trong cùng một thư mục làm việc. Một lượt ghi giẫm lên lượt kia, lần chạy cho ra một merge gãy, và bạn mất cả buổi sáng gỡ rối thay vì ship. Cách sửa là cô lập: git worktrees agents không thể dùng chung, mỗi làn một thư mục làm việc riêng. Bạn sẽ trao cho mỗi agent song song một worktree riêng, lắp một đội agent ba vai kèm một agent kiểm riêng, chạy một lần phân loại (triage) hai phát hiện thật merge vào một tệp trạng thái, và học cái trần trung thực cho việc nên chạy bao nhiêu làn.
Yêu cầu trước:
- Bạn đã đọc lướt Phần 3 (điều kiện dừng) và Phần 4 (tệp trạng thái) của loạt bài này. Phần 5 xây thẳng trên cái tệp trạng thái đó.
- Bạn biết git worktree, hoặc sẽ đọc lướt bài hướng dẫn worktree của chúng tôi trước. Bài này không dạy lại cơ chế đó.
- Bạn chạy được một agent con (subagent) từ một vòng lặp và đọc được một bản khác biệt hợp nhất.
Đị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.
Vì sao hai agent trong cùng một thư mục làm việc lại va vào nhau?
Hai agent trong cùng một thư mục làm việc va vào nhau vì đúng cùng lý do hai kỹ sư cùng push lên một branch mà không có buổi họp đồng bộ (standup): chỉ có đúng một bộ file trên đĩa và không có quy ước ai được đụng cái gì. Hỏng hóc đến ở hai dạng, và chúng cần hai cách sửa khác nhau. Gọi tên chúng ra trước khi sửa.
Cái thứ nhất là va chạm cơ học. Hai agent dùng chung một thư mục làm việc, nên chỉnh sửa của agent A lên routes.ts và của agent B lên cùng file đó đan vào nhau, hoặc lệnh git add . của một agent đưa vào khu chờ phần việc dang dở của agent kia, hoặc hai lệnh git checkout giành nhau HEAD. Kết quả là một cây mã hỏng không thuộc về agent nào. Đây là va chạm bạn gặp đầu tiên, và nó chẳng liên quan gì tới chất lượng việc của hai agent.
Cái thứ hai là va chạm chất lượng. Giả sử hai agent không bao giờ đụng cùng một file. Một agent vừa viết một thay đổi vừa merge chính thay đổi đó thì không có cặp mắt thứ hai. Việc nghe-có-lý-mà-sai đáp xuống mà không ai rà soát (review), vì người duy nhất rà soát chính là tác giả. Chạy thêm nhiều agent làm chuyện này tệ hơn, không phải tốt hơn: giờ bạn có N tác giả mỗi người tự duyệt mình. Nếu bạn mới bắt đầu chạy nhiều agent cùng lúc, bài nhập môn multi-agent của chúng tôi lo phần giới thiệu; bài này là kỷ luật giữ cho một đội agent khỏi biến thành N tác giả không ai rà soát.
Phần còn lại của bài này sửa cả hai. Worktree sửa va chạm cơ học (mục kế). Tách agent tạo khỏi agent kiểm sửa va chạm chất lượng (mục sau nữa). Bạn cần cả hai, vì giải một cái không làm gì cho cái kia.
| Va chạm cơ học | Va chạm chất lượng | |
|---|---|---|
| Triệu chứng | Cây mã hỏng, merge gãy, mất chỉnh sửa | Thay đổi nghe-có-lý-mà-sai đáp xuống không ai rà soát |
| Nguyên nhân | Một thư mục làm việc, các lượt ghi giành nhau | Tác giả cũng là người rà soát duy nhất |
| Sửa bằng | Một worktree mỗi làn (Beat 2) | Tách agent tạo/agent kiểm (Beat 3) |
Điểm mấu chốt: Hai agent trong một thư mục là hai kỹ sư dùng chung một bàn phím. Vấn đề chưa bao giờ là các agent, mà là thiếu cô lập và thiếu rà soát.
Cô lập git worktrees agents bên trong một vòng lặp hoạt động ra sao?
Bạn trao cho mỗi agent một git worktree riêng, nên mỗi làn có một thư mục làm việc riêng trên một branch riêng và không gì một làn làm có thể giẫm lên làn khác. Cơ chế tạo worktree, phần hỗ trợ sẵn có, và lối git worktree add thủ công đều nằm trong bài hướng dẫn worktree của chúng tôi, nên từ đây cứ coi như bạn đã biết. Cái thay đổi bên trong một vòng lặp là ai cấp phát worktree và ai dọn nó khi không có người trông.
Trong một phiên tương tác, bạn tạo worktree bằng tay và nhớ xoá nó sau. Trong một vòng lặp, bạn khai báo cô lập trên agent con và để bộ điều phối (orchestrator) làm cả hai. Đánh dấu một agent con bằng isolation: worktree bảo bộ điều phối cấp một worktree mới cho mỗi làn trước khi agent chạy, và thu hồi nó khi agent xong hoặc chết:
# cau hinh doi agent: moi lan implementer chay co lap- role: implementer isolation: worktree # bo dieu phoi cap mot worktree + branch rieng cho moi lan on_exit: auto-cleanup # xong hoac hong, worktree cua lan chet bi xoaTự dọn (auto-cleanup) các làn chết là phần quan trọng trong một lần chạy không người trông. Một vòng lặp lặp mỗi đêm mà không bao giờ thu hồi worktree sẽ tích tụ các thư mục mồ côi cho tới khi áp lực đĩa hoặc một lỗi khoá cũ kéo sập cả lần chạy. Trong một phiên tương tác bạn sẽ để ý và chạy git worktree prune. Trong một vòng lặp không có ai ở đó, nên việc dọn phải tự động: một làn đã merge xong và một làn vừa sập đều bị xoá, và vòng lặp kế bắt đầu từ một danh sách sạch.
Cô lập cũng là thứ cho phép agent kiểm tin vào bản khác biệt nó rà soát. Vì mỗi làn là một checkout riêng, một làn chỉ bao giờ chứa thay đổi của chính nó. Trong lần chạy bên dưới, làn sửa một file vẫn giữ bản gốc, chưa đụng tới của file thuộc làn kia, đúng cái tính chất bạn muốn: không làn nào có thể ngầm phụ thuộc vào việc đang dang dở của làn khác.
Điểm mấu chốt: Trong một phiên tương tác bạn tạo một worktree; trong một vòng lặp bạn khai báo một cái rồi để bộ điều phối cấp và thu hồi. Khác biệt nằm ở chỗ ai dọn dẹp khi không ai trông.
Những hình dạng đội agent nào chạy được, và khi nào song song khi nào tuần tự?
Đội agent ngựa thồ là ba vai, và quy tắc gói trong một câu: song song hoá qua các phát hiện độc lập, tuần tự hoá bên trong chuỗi xử lý của một phát hiện. Ba vai chính là phần thưởng thực dụng của bài này, ba file agent nhỏ bạn dùng lại được:
- Agent thăm dò. Tìm việc và phát ra một danh sách các phát hiện độc lập. Nó không sửa gì; nó tạo ra cái danh sách việc mà đội agent toả ra làm.
- Agent thực thi, tức agent tạo. Nhận một phát hiện và tạo một bản khác biệt trong worktree riêng của nó. Một agent thực thi mỗi làn, một phát hiện mỗi agent thực thi.
- Agent kiểm chứng, tức agent kiểm. Rà soát bản khác biệt đó dựa trên một điều kiện dừng kiểm chứng được rồi trả về chấp nhận hoặc từ chối. Agent thực thi không bao giờ tự chấm việc của mình.
Cái tách agent tạo/agent kiểm đó không phải kiểu nhà tự nghĩ; nó là mẫu evaluator-optimizer từ Building Effective AI Agents của Anthropic, nơi một agent sinh và một agent đánh giá riêng biệt chuyền việc qua lại cho tới khi agent đánh giá hài lòng. Cái tách đó biến va chạm chất lượng ở Beat 1 thành một cú bàn giao có kiểm soát.
Khi nào song song và khi nào tuần tự? Chạy các làn song song chỉ khi các phát hiện thật sự độc lập: khác file, không dùng chung trạng thái, thứ tự không quan trọng. Tuần tự hoá ngay khi một trong các điều này đúng:
- Một bước phụ thuộc đầu ra của bước khác. Agent thăm dò phải xong trước khi các agent thực thi toả ra, nên thăm-dò-rồi-thực-thi luôn tuần tự.
- Hai làn sẽ đụng cùng một file. Độc lập là một tính chất của các phát hiện, không phải điều ước; nếu hai phát hiện cùng sửa
routes.ts, chúng là một làn tuần tự, không phải hai làn song song. - Một phát hiện sau phụ thuộc vào merge của một phát hiện trước. Nếu sửa B chỉ có nghĩa sau khi A đáp xuống, thì B chờ.
Đây không phải câu hỏi “worktree với một phiên đơn”, cái mà bài hướng dẫn worktree đã trả lời. Đây là về cấu trúc vai và phụ thuộc giữa các bước bên trong một vòng lặp. Các trình chạy đội agent mã nguồn mở hội tụ đúng về hình dạng này: claude-squad chạy N agent mỗi cái trong worktree riêng rồi merge những cái thắng, claude_code_agent_farm điều phối 20+ agent trên một danh sách vấn đề chung, và continuous-claude phơi ra một cờ --worktree kèm một bước rà soát tuỳ chọn mỗi vòng. Cách hiện thực khác nhau, cùng hai nguyên thuỷ: cô lập mỗi agent, rồi kiểm trước khi merge.
explorer ──► [ implementer A ──► verifier A ] ─┐ └─► [ implementer B ──► verifier B ] ─┴─► merge vào một tệp trạng thái (song song: các phát hiện độc lập) (tuần tự trong mỗi làn)| Tình huống | Quyết định |
|---|---|
| Các phát hiện khác file, không dùng chung trạng thái | Song song |
| Bước B cần đầu ra của bước A | Tuần tự |
| Hai phát hiện sửa cùng một file | Tuần tự (một làn) |
| Phát hiện sau phụ thuộc một merge trước đó | Tuần tự |
Điểm mấu chốt: Song song là cho các phát hiện độc lập; chuỗi xử lý bên trong một phát hiện thì luôn tuần tự. Một agent thực thi tự chấm bản khác biệt của mình không phải một đội agent, mà là một agent đội hai mũ.
Bắt tay làm: một lần phân loại hai phát hiện chạy trên các worktree song song
Đây là một lần chạy thật của cơ chế từ đầu tới cuối. Một agent thăm dò nêu ra hai phát hiện độc lập, mỗi cái nhận một worktree riêng trên một branch riêng, một agent tạo tạo một bản khác biệt một-file mỗi làn, một agent kiểm thật chạy test, và cả hai kết quả merge vào một tệp trạng thái duy nhất.
Đọc ghi chú trung thực trước: đây là một bằng chứng cơ chế, không phải một đội agent tự hành được chấm bởi Haiku. Bước agent tạo ở đây là một chỉnh sửa đơn định chứ không phải một lượt của agent LLM, và không có gì được đo đạc, nên không có con số token hay chi phí để báo. Cái nó chứng minh là phần lắp ghép điều phối (cô lập, tạo, kiểm, merge, thu hồi) chạy đúng như mô tả. Bản ghi vết đầy đủ nguyên văn nằm trong file run-trace của bản nháp.
Hai phát hiện mà agent thăm dò ghi lại:
- Phát hiện A:
mathutil.add(a, b)trả vềa - bthay vìa + b. Test gãy:test_mathutil.py::test_add. - Phát hiện B:
strutil.shout(s)trả vềs.lower()thay vìs.upper(). Test gãy:test_strutil.py::test_shout.
Chúng không dùng chung file hay trạng thái, nên theo quy tắc ở Beat 3 chúng đủ điều kiện chạy song song.
Cô lập. Một worktree mỗi phát hiện, mỗi cái trên branch riêng:
$ git worktree add ../wt-finding-a -b fix/finding-a$ git worktree add ../wt-finding-b -b fix/finding-b$ git worktree list/home/you/p5-fleet 25ee424 [master]/home/you/wt-finding-a 25ee424 [fix/finding-a]/home/you/wt-finding-b 25ee424 [fix/finding-b]Tạo. Mỗi agent tạo chỉ sửa file của làn mình. Commit của làn A đụng mathutil.py và không gì khác:
$ git diff HEAD~1 HEAD # in wt-finding-adiff --git a/mathutil.py b/mathutil.py@@ -1,3 +1,3 @@ def add(a, b):- # BUG (Finding A): returns difference instead of sum- return a - b+ # FIX (Finding A): correct sum+ return a + bCommit của làn B đụng strutil.py và không gì khác:
$ git diff HEAD~1 HEAD # in wt-finding-bdiff --git a/strutil.py b/strutil.py@@ -1,3 +1,3 @@ def shout(s):- # BUG (Finding B): lowercases instead of uppercasing- return s.lower()+ # FIX (Finding B): correct uppercase+ return s.upper()Cô lập là thật, không phải nói suông. Bên trong làn A, file của làn kia vẫn là hạt giống lỗi, vì làn A chưa bao giờ đụng nó:
$ git show HEAD:strutil.py # in wt-finding-adef shout(s): # BUG (Finding B): lowercases instead of uppercasing return s.lower()Kiểm. Một pytest thật chạy trong mỗi làn. Phán quyết là bất kỳ cái gì mã thoát nói ra, không phải một quyết định mà agent tạo được phép đưa ra:
$ pytest -q test_mathutil.py # in wt-finding-a1 passed in 0.00s # exit 0
$ pytest -q test_strutil.py # in wt-finding-b1 passed in 0.00s # exit 0Merge. Phán quyết thật của cả hai làn đáp vào một tệp trạng thái, dùng lại lược đồ tried/passed/open/blocked của Phần 4. Trước lần chạy, cả hai phát hiện nằm ở open:
## tried
## passed
## open- [finding-a] mathutil.add() returns a-b instead of a+b (fails test_mathutil.py::test_add)- [finding-b] strutil.shout() lowercases instead of uppercasing (fails test_strutil.py::test_shout)
## blockedSau lần chạy, cả hai dời sang passed, vì cả hai agent kiểm trả về exit 0:
## tried- [finding-a] worktree wt-finding-a / branch fix/finding-a: maker edited mathutil.py (a-b to a+b)- [finding-b] worktree wt-finding-b / branch fix/finding-b: maker edited strutil.py (lower to upper)
## passed- [finding-a] test_mathutil.py::test_add green in wt-finding-a (checker pytest exit 0)- [finding-b] test_strutil.py::test_shout green in wt-finding-b (checker pytest exit 0)
## open
## blockedCả hai làn đều passed trong lần chạy này, nên không có gì đáp vào blocked. Đó là kết quả trung thực; lược đồ đơn giản là có chỗ để đặt một thất bại khi nó xảy ra. Nếu pytest của một làn trả về khác 0, bước merge sẽ để phát hiện đó ở open (thử lại lần chạy sau) hoặc dời nó sang blocked (cần một người), và vòng lặp kế sẽ nhặt nó từ đó thay vì suy lại từ đầu.
Thu hồi. Dọn dẹp xoá cả hai làn chết, chỉ chừa lại cây gốc:
$ git worktree remove ../wt-finding-a$ git worktree remove ../wt-finding-b$ git worktree list/home/you/p5-fleet 25ee424 [master]Đó là trọn vòng: cô lập, tạo, kiểm, merge, thu hồi. Hai phát hiện độc lập đi qua hai làn cô lập và đáp vào một file mà lần chạy sau đọc được.
Điểm mấu chốt: Merge là khoảnh khắc của sự thật. Hai làn cô lập chỉ hữu ích nếu kết quả của chúng đáp vào một chỗ mà lần chạy sau đọc được, và chỗ đó là tệp trạng thái từ Phần 4.
Thuế điều phối là gì, và đâu là trần trung thực cho cỡ đội agent?
Mỗi làn bạn thêm tốn token và, ràng buộc hơn, tốn sự chú ý rà soát của bạn. Trần trung thực cho cỡ đội agent không phải giới hạn song song của công cụ; nó là băng thông review của bạn. Quy tắc kinh nghiệm thẳng thừng: nếu bạn rà soát thật được N pull request mỗi ngày, chạy nhiều nhất N làn. Coi đó là một quy tắc áng chừng, không phải một hằng số đo được; đây là cách đóng khung của riêng bài, hãy hiệu chỉnh theo team bạn.
Có hai thứ thuế. Thứ nhất là thuế token. N làn xấp xỉ N lần chi phí context window và lượt cho mỗi agent, cộng thêm chi phí điều phối của chính bộ điều phối. Anthropic nói rõ trong Building Effective AI Agents rằng điều phối nhiều agent nhân chi phí lên và không miễn phí; thêm nhiều agent mua cho bạn thời gian thực và bề rộng, và bạn trả cho nó bằng token. Thứ thuế đó có thật nhưng cũng là thứ bạn thấy trên hoá đơn, nên nó có xu hướng tự điều chỉnh.
Thứ hai là thuế rà soát, và đây là thứ ràng buộc. Agent kiểm nâng sàn: nó lọc những thất bại hiển nhiên để chúng không bao giờ tới tay bạn. Nhưng một con người vẫn merge, và đầu ra agent chưa rà soát chính là va chạm chất lượng ở Beat 1 hiện ra lại ở cỡ đội agent. Agent kiểm quyết định cái gì nghe có lý là đúng; bạn vẫn quyết định cái gì thật sự đúng cho kho mã của mình. Quyết định đó không song song hoá được. Mười làn cho ra mười bản khác biệt xanh là mười bản khác biệt bạn phải đọc trước khi chúng đáp xuống, và tốc độ đọc của bạn là cố định.
Vậy agent kiểm nâng sàn còn rà soát của bạn đặt trần. Worktree và cái tách agent tạo/agent kiểm cho phép bạn chạy nhiều agent một cách an toàn; chúng không cho bạn rà soát nhanh hơn. Trần của công cụ và trần của bạn là hai con số khác nhau, và của bạn thấp hơn. Định cỡ đội agent theo con số thấp hơn.
| Mỗi làn bạn thêm | Tác động |
|---|---|
| Chi phí token | Tăng (xấp xỉ N lần chi phí mỗi agent cộng chi phí điều phối) |
| Thời gian thực tới kết quả | Giảm (các làn chạy đồng thời) |
| Tải rà soát của bạn | Tăng (mỗi bản khác biệt xanh vẫn cần một người merge) |
Điểm mấu chốt: Worktree và agent kiểm cho bạn chạy nhiều agent. Băng thông review của bạn quyết định nên chạy bao nhiêu. Trần của công cụ và trần của bạn không phải cùng một con số, và của bạn thấp hơn.
Bạn có gì, và tiếp theo là gì
Giờ bạn có trọn cơ chế: một đội agent ba vai (agent thăm dò, agent thực thi, agent kiểm chứng), các làn cô lập bằng worktree kèm tự dọn, một sự tách agent tạo/agent kiểm để không agent nào tự merge việc của mình, và một lần chạy hai phát hiện đã merge vào một tệp trạng thái. Ba thứ cần để mắt khi lên production: các làn ngầm dùng chung trạng thái (tuần tự hoá chúng), worktree mồ côi nếu tắt tự dọn (thu hồi chúng), và một đội agent định cỡ theo công cụ thay vì theo băng thông review của bạn (đặt trần nó). Và nhớ rằng một agent kiểm chỉ đóng dấu cao su thì lại là một agent đội hai mũ, nên hãy buộc nó vào một điều kiện dừng kiểm chứng được thật sự.
Bạn chạy được đội agent này theo yêu cầu. Nhưng một vòng lặp chỉ xứng cái tên khi nó chạy theo một nhịp bạn không tự khởi động: thăm dò lên lịch, một nhịp tim (heartbeat), và những lần chạy tự lưu trữ chính mình. Đó là loop-scheduled-automations-heartbeat, phần kế tiếp trong loạt bài.
FAQ
Tôi thật sự chạy được bao nhiêu agent song song cùng lúc?
Về kỹ thuật, bao nhiêu tuỳ giới hạn song song của công cụ; về mặt trung thực, bao nhiêu tuỳ sức bạn rà soát. Quy tắc kinh nghiệm: nếu bạn rà soát được N pull request mỗi ngày, chạy nhiều nhất N làn. Trần của công cụ và trần rà soát của bạn là hai con số khác nhau, và của bạn thấp hơn.
Các agent con có tự được cấp worktree riêng không?
Chỉ khi bạn khai báo. Đặt isolation: worktree trên agent con và bộ điều phối cấp một thư mục làm việc riêng cho mỗi làn rồi thu hồi nó khi xong hoặc hỏng. Không có nó, mọi làn dùng chung một thư mục, đúng cái va chạm bạn đang cố tránh.
Worktree khác branch thế nào với các agent song song?
Một branch là một con trỏ trong lịch sử; một worktree là một thư mục được checkout riêng cho một branch. Hai agent có thể ở trên hai branch nhưng vẫn giành nhau một thư mục làm việc. Worktree cho mỗi agent file riêng trên đĩa, đó mới là thứ thật sự chặn va chạm ghi-giành-nhau.
Khi nào nên chạy đội agent tuần tự thay vì song song?
Tuần tự hoá bất cứ khi nào một bước phụ thuộc đầu ra của bước khác (agent thăm dò phải xong trước khi các agent thực thi toả ra), bất cứ khi nào hai làn sẽ đụng cùng một file, hoặc bất cứ khi nào một phát hiện sau phụ thuộc một merge trước đó. Chỉ song song hoá các phát hiện thật sự độc lập.
Chuyện gì xảy ra với một worktree khi một agent hỏng giữa chừng?
Với tự dọn bật, worktree của làn hỏng bị xoá để vòng lặp không tích tụ thư mục mồ côi qua các vòng. Phát hiện đó ở lại open (hoặc dời sang blocked) trong tệp trạng thái, nên lần chạy sau thử lại được mà không phải suy lại từ đầu cái đã xảy ra.
Đọc tiếp gì nữa
- Git worktree cho việc song song: cơ chế worktree mà bài này coi như đã biết (phần hỗ trợ sẵn có và lối thủ công).
- Lập trình AI multi-agent: bài nhập môn chạy nhiều hơn một agent, nếu Beat 1 đi quá nhanh.
- Điều kiện dừng và kiểm chứng: cái mà vai agent kiểm chứng thực thi; agent kiểm chỉ tốt ngang điều kiện dừng của nó.
- Mẫu tệp trạng thái: cái file tried/passed/open/blocked mà đội agent merge vào; đọc cái này trước nếu bạn bỏ lỡ Phần 4.