Vấn Đề: Một Agent Là Bottleneck
Nếu bạn đang dùng một AI coding assistant duy nhất, bạn đang pair programming. Điều đó ổn cho việc khám phá, debug, và học hỏi. Nhưng khi bạn có 15 ticket Linear cần ship trước thứ Sáu, pair programming với một agent là trần năng suất.
Câu hỏi không phải AI có viết code được không. Nó viết được. Câu hỏi là: bạn có thể chạy một team AI agent giống như cách bạn quản lý team developer không?
Câu trả lời là có — và nó đơn giản hơn bạn nghĩ.
Kiến Trúc: Shell Script, Không Framework
Đây là setup của tôi. Không orchestration framework. Không agent SDK. Chỉ shell script, tmux, và công cụ quản lý dự án.
Claude (Orchestrator/Reviewer)
├── Lên kế hoạch công việc
├── Viết task spec trong Linear
├── Giao việc cho worker
├── Review kết quả
│
├── MiniMax (Worker Nhẹ)
│ └── Fix nhỏ, cleanup, docs, minor refactor
│
├── Kimi (Worker Nặng)
│ └── Deep refactor, implementation, debugging
│
├── Linear (Task Pool / Source of Truth)
│ └── Todo → In Progress → Done
│
├── tmux (Control Room)
│ └── Mỗi pane là một agent, output real-time
│
└── .agent-locks/ (Chống Duplicate)
└── Mỗi task một lock file

Đó là tất cả. Mỗi component có đúng một nhiệm vụ. Claude nghĩ. MiniMax và Kimi thực thi. Linear theo dõi trạng thái. tmux hiển thị đang xảy ra gì. Lock file ngăn xung đột.
Cách Hoạt Động: Hai Script
Toàn bộ hệ thống chạy trên hai shell script.
dwh-launch-grid.sh — Grid Launcher
Đây là script setup control room. Nó đọc hàng đợi task hiện tại, tính toán layout grid, và spin up một tmux pane cho mỗi task. Đây là cấu trúc file đầy đủ mà script sử dụng:
project-root/
│
├── dwh-launch-grid.sh # ← BẠN ĐANG Ở ĐÂY — grid launcher
├── dwh-auto-agent.sh # agent loop (một bản chạy mỗi pane)
│
├── .env # API key và cấu hình model
│ ├─ LINEAR_API_KEY
│ ├─ ANTHROPIC_API_KEY
│ ├─ MINIMAX_API_KEY
│ ├─ KIMI_API_KEY
│ ├─ LINEAR_TEAM_ID
│ ├─ WORKER_MINIMAX="minimax-text-01"
│ └─ WORKER_KIMI="moonshot-v1-32k"
│
├── .agent-locks/ # advisory lock file (tự tạo lúc runtime)
│ ├─ DWH-168.lock
│ ├─ DWH-169.lock
│ └─ DWH-172.lock
│
├── logs/ # log thực thi từng pane (tự tạo lúc runtime)
│ ├─ agent-pane-0.log
│ ├─ agent-pane-1.log
│ └─ agent-pane-2.log
│
├── vault/ # knowledge base kiểu Obsidian
│ ├── architecture/
│ ├── decisions/
│ └── agents/task-log.md # log chia sẻ mà tất cả agent đều ghi vào
│
└── .grid-state # snapshot runtime: session, số task, thời gian start
Đây là script đầy đủ có chú thích:
#!/usr/bin/env bash
# dwh-launch-grid.sh — Khởi chạy multi-agent coding grid
set -euo pipefail
source .env
SESSION="dwh-grid"
LOCK_DIR=".agent-locks"
LOG_DIR="logs"
MAX_AGENTS=${MAX_AGENTS:-6} # giới hạn: không bao giờ chạy quá 6 agent
LOCK_TTL_MINUTES=${LOCK_TTL_MINUTES:-60}
# ── 1. DỌN DẸP: xóa lock hết hạn và log cũ ──────────────────────────────────
mkdir -p "$LOCK_DIR" "$LOG_DIR"
find "$LOCK_DIR" -name "*.lock" -mmin +"$LOCK_TTL_MINUTES" -delete 2>/dev/null
find "$LOG_DIR" -name "*.log" -mtime +7 -delete 2>/dev/null
echo "[grid] Đã xóa lock hết hạn. Lock đang active: $(ls $LOCK_DIR | wc -l)"
# ── 2. QUERY LINEAR: đếm bao nhiêu task Todo đang sẵn sàng ───────────────────
TASK_COUNT=$(curl -sf -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "{ issues(filter: {
state: { name: { eq: \"Todo\" } },
team: { id: { eq: \"'"$LINEAR_TEAM_ID"'\" } }
}) { nodes { id identifier title priority } } }"
}' | jq '.data.issues.nodes | length')
echo "[grid] Task sẵn sàng trong Linear: $TASK_COUNT"
if [ "$TASK_COUNT" -eq 0 ]; then
echo "[grid] Không có gì để làm. Thoát."
exit 0
fi
# ── 3. TÍNH LAYOUT GRID ───────────────────────────────────────────────────────
PANES=$(( TASK_COUNT < MAX_AGENTS ? TASK_COUNT : MAX_AGENTS ))
# Tạo grid gần vuông (ví dụ: 6 pane → 2 hàng × 3 cột)
COLS=$(echo "sqrt($PANES)" | bc)
[ "$COLS" -eq 0 ] && COLS=1
ROWS=$(( (PANES + COLS - 1) / COLS ))
echo "[grid] Khởi chạy grid ${PANES} pane (${ROWS} hàng × ${COLS} cột)"
# ── 4. TẠO TMUX SESSION ───────────────────────────────────────────────────────
tmux kill-session -t "$SESSION" 2>/dev/null || true
tmux new-session -d -s "$SESSION" -x 220 -y 50
# ── 5. CHIA PANE VÀ SẮP XẾP ──────────────────────────────────────────────────
for ((i = 1; i < PANES; i++)); do
if (( i % COLS == 0 )); then
tmux split-window -t "$SESSION" -v # bắt đầu hàng mới
else
tmux split-window -t "$SESSION" -h # mở rộng hàng hiện tại
fi
done
tmux select-layout -t "$SESSION" tiled
# ── 6. START MỘT AGENT MỖI PANE ──────────────────────────────────────────────
for ((i = 0; i < PANES; i++)); do
# Chiến lược giao model:
# Pane 0–1 → MiniMax (nhẹ: docs, cleanup, fix nhỏ)
# Pane 2+ → Kimi (nặng: refactor nhiều file, impl phức tạp)
if (( i < 2 )); then
MODEL="$WORKER_MINIMAX"
WORKER_NAME="minimax"
else
MODEL="$WORKER_KIMI"
WORKER_NAME="kimi"
fi
LOG_FILE="$LOG_DIR/agent-pane-${i}.log"
tmux select-pane -t "$SESSION:0.$i" -T "agent-${i}-${WORKER_NAME}"
tmux send-keys -t "$SESSION:0.$i" \
"AGENT_MODEL=$MODEL PANE_ID=$i bash dwh-auto-agent.sh 2>&1 | tee $LOG_FILE" Enter
done
# ── 7. LƯU TRẠNG THÁI GRID ───────────────────────────────────────────────────
cat > .grid-state <<EOF
SESSION=$SESSION
PANES=$PANES
STARTED=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
TASK_COUNT=$TASK_COUNT
EOF
echo "[grid] Grid đang chạy. Kết nối bằng: tmux attach -t $SESSION"
tmux attach -t "$SESSION"
Các quyết định thiết kế quan trọng trong script:
| Quyết định | Lý do |
|---|---|
Giới hạn MAX_AGENTS=6 | Vượt 6 pane, tmux cell quá nhỏ để đọc và API rate limit trở thành bottleneck |
| Giao model theo index pane | Pane 0–1 chạy MiniMax (nhanh/rẻ), pane 2+ chạy Kimi (mạnh hơn). Claude viết task đơn giản hơn trước trong Linear để MiniMax tự nhiên chọn chúng |
tee $LOG_FILE | Output của mỗi pane vừa hiển thị live vừa ghi vào logs/agent-pane-N.log — hữu ích cho post-mortem sau khi grid đóng |
File .grid-state | Snapshot nhẹ ghi lại thời điểm grid start, số pane, số task đang xếp hàng. dwh-launch-grid.sh --status đọc file này để hiển thị tình trạng grid hiện tại |
| Kill session trước khi tạo mới | Ngăn lỗi thường gặp khi chạy script hai lần — tạo ra hai grid cạnh tranh nhau claim cùng Linear task |
set -euo pipefail | Bất kỳ lỗi chưa xử lý nào (phản hồi API sai, jq parse fail, tmux lỗi) đều thoát ngay thay vì âm thầm tiếp tục trong trạng thái lỗi |
dwh-auto-agent.sh — Agent Loop
Mỗi pane chạy cùng một vòng lặp:
chọn task từ Linear
→ tạo lock file
→ chuyển task sang "In Progress"
→ thu thập context
→ chạy AI coding agent
→ test + commit
→ parse kết quả
→ cập nhật trạng thái Linear
→ giải phóng lock
→ lấy task tiếp theo
Điều đẹp ở đây là mọi agent chạy cùng script. Sự khác biệt nằm ở cấp task — Claude giao task nhẹ cho MiniMax và task nặng cho Kimi dựa trên độ phức tạp.

Bí Quyết: Chất Lượng Task Specification
Đây là điều hầu hết mọi người làm sai với multi-agent: họ tập trung vào model mà bỏ qua task spec.
Model không phải bottleneck. Chất lượng task brief mới là. Claude viết task spec trông như thế này:
## Task: DWH-168 — Refactor payment retry logic
**Goal**: Thay thế retry mechanism hardcoded trong `src/payments/retry.ts`
bằng exponential backoff sử dụng `@utils/backoff` utility có sẵn.
**Context**: Retry logic hiện tại dùng fixed 3-second delay, gây
thundering herd problem khi load cao (xem incident INC-042).
**Constraints**:
- Phải giữ backward compatibility với PaymentProcessor interface
- Tối đa 5 retry, initial delay 500ms, max delay 30s
- Thêm jitter để tránh synchronized retry
**Files cần check**:
- `src/payments/retry.ts` (chính)
- `src/payments/__tests__/retry.test.ts` (cập nhật test)
- `src/utils/backoff.ts` (utility có sẵn để dùng)
**KHÔNG động vào**:
- PaymentProcessor class
- Bất kỳ database migration nào
- Module billing
**Definition of Done**:
- [ ] Tất cả test hiện tại pass
- [ ] Test mới cho exponential backoff
- [ ] Không TypeScript error
- [ ] Manual test: simulate 10 concurrent failure, verify không thundering herd
Đây là sự khác biệt giữa “sửa retry logic” và một brief mà junior developer — hoặc model AI nhỏ hơn — có thể thực thi mà không cần đoán.
Khi task spec chính xác, ngay cả model nhẹ cũng trở nên hiệu quả đáng ngạc nhiên. Chúng không cần hiểu toàn bộ kiến trúc. Chúng chỉ cần thực thi một scope được định nghĩa rõ ràng.
Tại Sao Lock File Quan Trọng
Vấn đề thực tế lớn nhất với parallel agent không phải chất lượng model — mà là duplicate work. Nếu không có coordination, hai agent sẽ grab cùng task, sửa cùng file, và tạo merge conflict.
Giải pháp đơn giản đến xấu hổ: lock file.
# Khi agent grab task
touch .agent-locks/DWH-168.lock
# Agent khác kiểm tra trước khi claim
if [ -f .agent-locks/DWH-168.lock ]; then
skip_task # Lấy task khác
fi
# Khi hoàn thành hoặc crash (auto-expire sau 1 giờ)
find .agent-locks -name "*.lock" -mmin +60 -delete
Nếu agent crash giữa chừng, lock tự expire sau 1 giờ và task tự động chuyển từ “In Progress” về “Todo” trong Linear. Không cần can thiệp thủ công.
Đây là pattern mà distributed system đã dùng hàng thập kỷ — advisory lock với TTL. Không glamorous, nhưng hoạt động lúc 2 giờ sáng khi bạn không ngồi canh.
Mô Hình Worker Phân Tầng
Không phải task nào cũng bằng nhau, và không phải model nào cũng bằng nhau. Insight then chốt là ghép độ phức tạp task với năng lực model:
MiniMax — Worker Nhẹ
MiniMax giỏi:
- Bug fix nhỏ (< 50 dòng thay đổi)
- Code cleanup và formatting
- Cập nhật documentation
- Sinh script đơn giản
- Minor refactor với pattern rõ ràng
Chi phí: Thấp. Tốc độ: Nhanh. Context cần: Ít.
Kimi — Worker Nặng
Kimi xử lý:
- Deep refactor nhiều file
- Task implementation phức tạp
- Debug với context nặng
- Thay đổi cấp kiến trúc
- Task cần hiểu cross-file
Chi phí: Cao hơn. Tốc độ: Vừa. Context cần: Đáng kể.
Claude — Orchestrator
Claude không viết production code trong setup này. Claude:
- Phân tách feature thành atomic task
- Viết task specification chi tiết
- Giao task cho đúng worker
- Review work đã hoàn thành
- Đưa ra quyết định kiến trúc
- Xử lý integration và conflict resolution
Cách tiếp cận phân tầng này nghĩa là bạn không đốt token model đắt tiền cho documentation update, và bạn không yêu cầu model nhẹ refactor hệ thống authentication.

Knowledge Layer: Obsidian Vault Làm Context
Một bổ sung cải thiện đáng kể chất lượng output: agent tạo và đọc documentation trong Obsidian Vault.
vault/
├── architecture/
│ ├── system-overview.md
│ ├── payment-flow.md
│ └── auth-design.md
├── decisions/
│ ├── ADR-001-database-choice.md
│ └── ADR-002-retry-strategy.md
├── agents/
│ ├── task-log.md
│ └── common-patterns.md
└── context/
├── api-contracts.md
└── env-setup.md
Trước khi thực thi task, mỗi agent đọc vault doc liên quan. Sau khi hoàn thành task, nó cập nhật log. Điều này tạo knowledge base chia sẻ tồn tại xuyên session — giải quyết vấn đề “context amnesia” thường gặp với single-agent workflow.
Thực Tế Trông Như Thế Nào
Một cycle điển hình:
-
Claude lên kế hoạch: “Có 8 ticket cho payment module refactor. 3 đơn giản (docs, test, cleanup), 3 trung bình (refactor từng function), 2 phức tạp (cross-module integration).”
-
Claude giao việc: MiniMax nhận 3 task đơn giản. Kimi nhận 3 task trung bình. 2 task phức tạp xếp hàng cho Kimi sau batch đầu.
-
Grid khởi chạy: 6 pane tmux mở. 6 agent start đồng thời.
-
Agent thực thi: Mỗi agent chọn task được giao, tạo lock, làm việc, chạy test, commit, cập nhật Linear.
-
Claude review: Check từng PR. Approve cái sạch, request changes cái khác.
-
Cycle lặp lại: Task chưa xong quay về pool. Task mới được tạo nếu Claude phát hiện issue khi review.
Tổng thời gian wall-clock cho 8 task: ~45 phút thay vì ~4 giờ chạy tuần tự.
Con Số: Parallel Execution Tiết Kiệm Bao Nhiêu
Dựa trên usage thực tế trong tháng qua:
| Metric | Tuần tự (1 agent) | Grid (3-6 agent) |
|---|---|---|
| Task mỗi giờ | 2-3 | 8-12 |
| Trung bình hoàn thành task | 18 phút | 20 phút (tương đương) |
| Wall-clock cho 8 task | ~4 giờ | ~45 phút |
| Review pass lần đầu | 70% | 82% (spec tốt hơn!) |
| Merge conflict | 0 | ~1 mỗi 10 task |
Phát hiện ngược trực giác: review pass rate cao hơn với grid. Tại sao? Vì Claude viết task spec tốt hơn khi biết model nhỏ hơn sẽ thực thi. Kỷ luật viết spec chính xác cải thiện chất lượng output nhiều hơn cả trí tuệ model.
Đây KHÔNG Phải Gì
Hãy rõ ràng về giới hạn:
- Không thay thế tư duy. Claude vẫn cần đưa ra quyết định kiến trúc. Grid thực thi — nó không thiết kế kiến trúc.
- Không zero-maintenance. Lock file expire, agent crash, merge conflict xảy ra. Bạn cần monitoring.
- Không cho mọi task. Exploratory work, debug vấn đề phức tạp, và design decision vẫn là hoạt động single-agent.
- Không rẻ khi scale. Chạy 6 agent song song ngốn API token nhanh. Budget phù hợp.
- Không phải framework. Đây là shell script và convention. Đó là feature, không phải bug.
Bắt Đầu: Grid Tối Thiểu
Nếu muốn thử pattern này, bắt đầu nhỏ:
- Chọn 3 task độc lập từ backlog
- Viết task spec rõ ràng (goal, context, constraint, file, DoD)
- Mở 3 tmux pane — mỗi pane một task
- Chạy một agent mỗi pane với task spec làm prompt
- Review output thủ công
Đó là grid đầu tiên của bạn. Không cần script. Không cần lock file. Chỉ parallel execution với spec rõ ràng.
Khi bạn cảm nhận được sự khác biệt throughput, bạn sẽ muốn automate. Lúc đó mới viết script.
Bức Tranh Lớn: AI Team Management
Những gì tôi mô tả không thực sự về AI model. Nó về project management áp dụng cho AI agent.
Cùng những nguyên tắc làm team dev con người hiệu quả cũng áp dụng cho team AI agent:
- Task decomposition rõ ràng tốt hơn instruction mơ hồ
- Đúng người (model) cho đúng việc
- Coordination ngăn duplicate work
- Review gate bắt vấn đề chất lượng
- Documentation tạo institutional knowledge
Agent ngày càng thông minh hơn mỗi quý. Nhưng orchestration — lên kế hoạch, phân tách, giao việc, review — đó vẫn là kỹ năng con người. Và hiện tại, đó là kỹ năng có đòn bẩy cao nhất mà Technical Lead có thể phát triển.
Tương lai không phải một AI làm mọi thứ. Mà là một team AI, được quản lý bởi con người biết cách phân tách vấn đề và giữ agent chịu trách nhiệm với specification rõ ràng.
Nếu bạn đang chạy setup tương tự, tôi rất muốn nghe cái gì hiệu quả với bạn. Tooling đang phát triển nhanh — điều quan trọng là pattern, không phải tool cụ thể.