ทำงานหลาย agent ขนาน + แยกพื้นที่ด้วย worktree
จนถึงตอนนี้เราคุยกันเรื่อง "agent เดียวทำงานเดียว" — วน core loop, ใช้ skill, ทำ TDD/debug ให้ครบก่อนค่อยส่งต่อ แต่พองานใหญ่ขึ้น (เช่น implement plan ที่มีหลาย task หรือต้องรีวิวโค้ดหลายมุมพร้อมกัน) การนั่งไล่ทำทีละอย่างในเซสชันเดียวจะช้าและ context บวมเร็ว บทนี้จะพาไปดู 3 สกิลที่ทำงานคู่กันเพื่อแก้ปัญหานี้: subagent-driven-development (แตกงานให้ subagent ทำทีละ task พร้อมรีวิว), dispatching-parallel-agents (ตัดสินใจว่าเมื่อไรควรยิงงานขนานได้จริง) และ using-git-worktrees (แยกพื้นที่ทำงานไม่ให้ agent ชนกัน) — สามอย่างนี้คือฐานของการทำงานแบบ "หัวหน้าทีมสั่งงานลูกทีม" ที่เอาไปใช้กับงาน dev จริงได้ทันที
Subagent-Driven Development — orchestrator แตกงานให้ subagent
ไอเดียหลักตามสเปกใน skills/subagent-driven-development/SKILL.md คือ:
เมื่อมี implementation plan
ที่ task ส่วนใหญ่ independent กัน (ไม่ต้องพึ่งผลลัพธ์ของกันและกันมาก)
และอยากอยู่ใน session เดิม (ไม่ย้ายไป session คู่ขนานแบบ executing-plans)
ให้ agent หลัก (ทำหน้าที่เป็น orchestrator)
ยิง subagent
ใหม่เอี่ยมทีละ task ไม่ใช่ทำเองทั้งหมด
เหตุผลตามต้นฉบับ: "You delegate tasks to specialized agents with isolated context... They should never inherit your session's context or history — you construct exactly what they need. This also preserves your own context for coordination work." พูดง่าย ๆ คือ subagent แต่ละตัวเห็นแค่สิ่งที่ orchestrator ป้อนให้ (task brief) เท่านั้น ไม่เห็นประวัติสนทนาทั้งเซสชัน — ทำให้ subagent โฟกัสงานตัวเองได้เต็มที่ และ context ของ orchestrator เองก็ไม่บวมจากรายละเอียดที่ subagent คุยกับตัวเอง
จุดที่ต้องเน้น: subagent ไม่ได้อ่าน plan ทั้งไฟล์ — orchestrator จะสกัด
"task brief" เฉพาะ task นั้นให้ (สคริปต์ task-brief ในสกิลจริง) แล้วส่งไฟล์ path
ให้ subagent อ่านเอง เพื่อไม่ให้ prompt ยาวจนบวม context ของ orchestrator เอง
เช่นเดียวกับตอนรีวิว — reviewer จะได้รับ "diff package" เป็นไฟล์ ไม่ใช่ paste diff ทั้งดุ้นเข้า prompt
Implementer รายงานสถานะ 4 แบบ — orchestrator ต้องรู้ว่าจะจัดการยังไง
| สถานะ | ความหมาย | orchestrator ทำอะไรต่อ |
|---|---|---|
DONE |
ทำเสร็จ ทดสอบผ่าน commit แล้ว | สร้าง review package แล้วส่งให้ task reviewer |
DONE_WITH_CONCERNS |
ทำเสร็จ แต่มีข้อกังวล | อ่านข้อกังวลก่อน — ถ้าเรื่อง correctness/scope ต้องแก้ก่อนรีวิว |
NEEDS_CONTEXT |
ขาดข้อมูลที่จำเป็น | ให้ context เพิ่ม แล้ว dispatch ใหม่ |
BLOCKED |
ทำต่อไม่ได้ | วิเคราะห์ว่าเป็น context ไม่พอ / งานยากเกิน model / plan ผิด แล้วจัดการตามนั้น |
เลือก model ตามความยากของ task: งานกลไกล้วน ๆ (แก้ 1-2 ไฟล์ ที่ spec ชัดเจน) ใช้โมเดลถูก/เร็ว งานที่ต้อง integrate หลายไฟล์หรือ debug ใช้โมเดลมาตรฐาน ส่วนงานออกแบบสถาปัตยกรรม รวมถึง final whole-branch review ให้ใช้โมเดลที่แรงที่สุดที่มี — ตามต้นฉบับ: "Always specify the model explicitly when dispatching a subagent" ห้ามปล่อยให้ subagent สืบทอด model ของ session หลักไปเฉย ๆ เพราะมักแพงเกินความจำเป็น
Dispatching Parallel Agents — เมื่อไรควรกระจายงานขนาน
สกิลนี้ตอบคำถามที่ subagent-driven-development ไม่ได้ตอบตรง ๆ: "แล้วเมื่อไรที่ยิง subagent หลายตัว พร้อมกัน ได้จริง (ไม่ใช่ทีละตัวเรียงคิว)?" เงื่อนไขตามสเปกคือ ต้องมีปัญหา/งาน 2 อย่างขึ้นไป ที่:
- Independent — แต่ละงานเข้าใจและแก้ได้โดยไม่ต้องรู้บริบทของงานอื่น
- ไม่มี shared state — ไม่แก้ไฟล์เดียวกัน ไม่ใช้ resource เดียวกัน
- ไม่มีลำดับก่อนหลัง — แก้ปัญหา A ไม่ทำให้ปัญหา B หายหรือเปลี่ยนไป
ถ้าเข้าเงื่อนไขครบ → ยิง dispatch หลายตัวในคำตอบเดียวกัน (ต้นฉบับ: "Multiple dispatch calls in one response = parallel execution. One per response = sequential.") ถ้าปัญหาเกี่ยวโยงกัน (แก้ตัวหนึ่งอาจแก้อีกตัวไปด้วย) หรือมี shared state → ใช้ agent เดียวสืบทั้งหมด หรือยิงแบบ sequential แทน
ตัวอย่างจากต้นฉบับ (สถานการณ์จริง): มี test fail 6 เคสกระจายอยู่ 3 ไฟล์ที่ไม่เกี่ยวกัน
(agent-tool-abort.test.ts, batch-completion-behavior.test.ts,
tool-approval-race-conditions.test.ts) — แต่ละไฟล์เป็นปัญหาคนละ root cause
จึงยิง subagent 3 ตัวพร้อมกัน ตัวละไฟล์ แต่ละตัวได้ scope ชัดเจน + constraint ("ห้ามแก้ไฟล์อื่น")
+ บอกชัดว่าให้ return อะไรกลับมา (สรุป root cause + สิ่งที่แก้) ผลคือ 3 ปัญหาแก้เสร็จพร้อมกัน
ไม่มี conflict แล้วค่อยรัน full test suite รวมเพื่อ verify ทีเดียว
using-git-worktrees — แยกพื้นที่ทำงานไม่ให้ชนกัน
พอเริ่มมีหลาย agent ทำงานพร้อมกันจริง (โดยเฉพาะถ้าแต่ละงานต้องแก้โค้ดจริง ไม่ใช่แค่รีวิว)
ปัญหาที่ตามมาคือทุกคนแก้ไฟล์ในโฟลเดอร์เดียวกัน branch เดียวกัน — ชนกันแน่นอน
using-git-worktrees แก้ตรงนี้ด้วยการให้แต่ละงาน (หรือแต่ละ session) มี
worktree
ของตัวเอง — เหมือน clone repo แยก แต่ประหยัดพื้นที่กว่าเพราะแชร์ .git object เดียวกัน
ลำดับที่สกิลนี้ทำจริง (สำคัญ อย่าข้าม step):
-
Step 0 — เช็กก่อนว่าอยู่ในพื้นที่แยกอยู่แล้วหรือยัง
เทียบ
git rev-parse --git-dirกับ--git-common-dirถ้าไม่เท่ากัน (และไม่ใช่ submodule) แปลว่าอยู่ใน worktree แยกอยู่แล้ว — ข้ามไป step 2 ทันที ห้ามสร้างซ้อนอีกชั้น -
Step 1 — สร้าง worktree ถ้ายังไม่มี ให้ลองใช้ native tool ของ harness ก่อน
(เช่นเครื่องมือ
EnterWorktree/คำสั่ง/worktreeถ้ามี) ถ้าไม่มีจริง ๆ ค่อย fallback มาใช้git worktree addเอง — ลำดับการเลือกโฟลเดอร์คือ เช็ก preference ที่ผู้ใช้ระบุไว้ก่อน → เช็กว่ามี.worktrees/หรือworktrees/อยู่แล้วไหม (ถ้ามีทั้งคู่.worktrees/ชนะ) → ถ้าไม่มีเลย default เป็น.worktrees/ที่ root โปรเจกต์ พร้อมต้องตรวจสอบว่าโฟลเดอร์นี้ถูก .gitignore แล้ว ก่อนสร้างเสมอ (กัน worktree หลุดเข้าไปอยู่ใน commit) -
Step 2-3 — setup โปรเจกต์ + รัน test baseline
เช่นถ้ามี
go.modรันgo mod download, ถ้ามีpackage.jsonรันnpm installแล้วต้องรัน test suite ให้ผ่าน ก่อนเริ่มงานจริง — ถ้า test แดงตั้งแต่ baseline ต้องรายงานและถามก่อน จะได้แยกได้ว่า bug ใหม่ที่ agent ทำ กับ bug เก่าที่มีอยู่แล้ว
# ตัวอย่าง fallback แบบ manual (เมื่อไม่มี native worktree tool)
git check-ignore -q .worktrees || # ต้องถูก ignore ก่อนเสมอ
(echo ".worktrees/" >> .gitignore && git add .gitignore && git commit -m "chore: ignore worktrees")
git worktree add .worktrees/feature-security-review -b feature-security-review
cd .worktrees/feature-security-review
# NestJS: setup + baseline
npm install
npm test
ทำไมต้องแยก worktree แทนแค่แยก branch เฉย ๆ — เพราะการ checkout branch ในโฟลเดอร์เดียวกัน ทำให้ทำงานสองอย่างพร้อมกันไม่ได้ (checkout ทับกัน ไฟล์ที่แก้ค้างอยู่หาย) worktree ทำให้ แต่ละ branch มีโฟลเดอร์เป็นของตัวเอง agent หลายตัวจึงรันคู่ขนานจริงได้ โดยไม่ต้องกลัวไฟล์ที่ยังไม่ commit ของงานหนึ่งไปกระทบอีกงานหนึ่ง
ผูกกับงานจริง — รีวิว 3 มุมพร้อมกันบนโมดูล NestJS
ตัวอย่างที่ใช้ 3 สกิลนี้ร่วมกันแบบเป็นรูปธรรม: สมมติมีโมดูล
UsersModule ใน NestJS ที่เพิ่ง merge ฟีเจอร์ใหม่เข้ามา แล้วอยากรีวิว 3 มุมพร้อมกัน
— security, performance, และ type safety การรีวิวทั้ง 3 มุมนี้ independent กันจริง
(อ่านโค้ดเดิม ไม่มีใครแก้ไฟล์ ไม่มี shared state) จึงเข้าเงื่อนไข dispatch แบบขนานได้เลย
ไม่ต้องแยก worktree ด้วยซ้ำ (เพราะเป็น read-only review ไม่ได้แก้โค้ด)
// ยิงพร้อมกันในคำตอบเดียว — 3 dispatch = parallel
Subagent 1 (security): "รีวิว security ของ src/users/*.ts — เน้น input validation,
guard/decorator, การจัดการ password/token ห้ามแก้โค้ด คืนเป็นลิสต์ finding พร้อม severity"
Subagent 2 (performance): "รีวิว performance ของ src/users/*.ts — เน้น N+1 query
ใน UsersService, index ที่ควรมีใน UsersEntity, การ cache ที่ควรเพิ่ม"
Subagent 3 (types): "รีวิว type safety ของ src/users/*.ts — เน้น any ที่หลุดมา,
DTO validation ตรงกับ entity ไหม, return type ของทุก public method ชัดเจนไหม"
แต่ถ้าโจทย์เปลี่ยนเป็น "ให้ 3 agent แก้บั๊ก ใน UsersService พร้อมกัน"
— อันนี้เข้าเงื่อนไข parallel ไม่ได้แล้ว เพราะทั้ง 3 คนต้องแก้ไฟล์เดียวกัน (shared state)
ถ้ายิงพร้อมกันจริงจะเกิด conflict ทันที ต้องแยก worktree ให้แต่ละคนก่อน หรือไม่ก็ทำแบบ
sequential (แก้ทีละคนในโฟลเดอร์เดียวกัน) — นี่คือเหตุผลที่ subagent-driven-development เขียนไว้
ใน Red Flags ชัดเจนว่า "Never dispatch multiple implementation subagents in parallel
(conflicts)" การ implement task ต้องทำทีละตัวเสมอ ต่างจากการรีวิว/สืบสวนที่ทำขนานได้
Pitfall ที่พบบ่อยที่สุด: เห็นว่ามีหลาย task ในแผนงาน แล้วรีบยิง implementer subagent หลายตัวพร้อมกันเพื่อ "ให้เร็วขึ้น" ทั้งที่ task พวกนั้น dependent กัน (เช่น task 2 ต้องใช้ interface ที่ task 1 เพิ่งสร้าง) ผลคือโค้ดชนกัน หรือ task 2 เขียนโดยเดา interface ผิด ๆ เพราะ task 1 ยังไม่เสร็จ — ก่อนกระจายงานขนานทุกครั้ง ให้ถามตัวเองก่อนว่า "งานพวกนี้ไม่มีใครรอผลใครจริง ๆ ใช่ไหม" ถ้าไม่แน่ใจ ให้ถือว่า dependent แล้วทำ sequential ไว้ก่อน ปลอดภัยกว่าเสมอ
สรุปเทียบ 3 สกิลนี้แบบไว ๆ
| สกิล | ตอบคำถามอะไร | ใช้เมื่อ |
|---|---|---|
| subagent-driven-development | "จะ execute plan หลาย task ในเซสชันนี้ยังไง" | มี plan, task ส่วนใหญ่ independent, อยู่ session เดิม |
| dispatching-parallel-agents | "ยิง agent พร้อมกันได้ไหม" | 2+ ปัญหา independent ไม่มี shared state ไม่มีลำดับก่อนหลัง |
| using-git-worktrees | "แยกพื้นที่ทำงานยังไงไม่ให้ชนกัน" | เริ่มงานใหม่ที่ต้อง isolate จาก workspace ปัจจุบัน โดยเฉพาะก่อน execute plan |
-
สมมติคุณมี Go service ที่มี 3 endpoint พังพร้อมกันหลัง refactor:
/users,/orders,/payments— เขียน prompt สำหรับ dispatch 3 subagent (ระบุ scope + constraint + expected output ของแต่ละตัว) แล้วบอกว่าทำไมกรณีนี้ถึง dispatch พร้อมกันได้ - ลองคิดสถานการณ์ 1 อย่างที่ "ดูเหมือน" กระจายงานขนานได้ แต่จริง ๆ แล้ว dependent กัน (เช่น เกี่ยวกับ migration ฐานข้อมูล PostgreSQL) อธิบายว่าอะไรทำให้มันไม่ independent และควรจัดการยังไงแทน
-
ลองรัน
git rev-parse --git-dirกับ--git-common-dirในโปรเจกต์จริงของคุณตอนนี้ (ไม่ต้องสร้าง worktree จริงก็ได้) แล้วดูว่าค่าที่ได้เท่ากันไหม — ถ้าเท่ากันแปลว่าตอนนี้คุณอยู่ใน checkout ปกติ ไม่ใช่ worktree แยก