TungDaDev's Blog

Herdr là gì? Quản lý cả team AI Agent trong một cửa sổ

Multi agent.webp
Published on
/34 mins read/

Chạy một agent thì cái chậm là model. Chạy năm agent cùng lúc thì cái chậm là… mình.

Chiều thứ Sáu, màn hình của mình trông thế này:

  • Tab 1: Claude Code đang refactor module thanh toán.
  • Tab 2: Codex đang viết test cho API mới.
  • Tab 3: thêm một agent nữa đang lục log production tìm bug.
  • Tab 4: npm run dev.
  • Tab 5: SSH lên con VPS, có agent đang chạy migration.

Đi pha ly cà phê, quay lại mở tab 2 thì thấy Codex đứng im 20 phút chỉ để hỏi: "Cho chạy rm -rf node_modules không?". Tab 5 thì rớt SSH, agent chết luôn, làm lại từ đầu.

Lỗi không nằm ở agent. Lỗi là cái terminal không hề biết bên trong nó đang có agent. Với nó, Claude Code hay htop cũng chỉ là một process in chữ ra màn hình, vậy thôi.

Herdr làm ra để xử lý đúng chỗ này.


# tóm tắt nhanh

câu hỏitrả lời
Herdr là gì?Một terminal multiplexer kiểu tmux, nhưng biết đọc trạng thái AI agent. Viết bằng Rust, chỉ một file binary
Có thay Claude Code, Codex không?Không. Agent nào bạn đang xài thì cứ xài, Herdr chỉ quản lý cái terminal chứa nó
Hay nhất ở đâu?Thanh bên trái hiện trạng thái từng agent: working, blocked, done, idle
Tắt cửa sổ, rớt mạng thì sao?Agent vẫn chạy. Herdr tách server và client, mở lại là thấy y nguyên
Agent tự điều khiển được không?Được. Có CLI và socket API để tạo pane, bật agent khác, giao việc, chờ kết quả
Tốn tiền không?Mã nguồn mở, chạy trên máy bạn, không cần tài khoản, không gửi dữ liệu đi đâu

Bài viết dựa trên Herdr 0.8.2. Dự án ra bản mới khá dày, nên lệnh nào không chạy thì cứ herdr --help trên máy bạn là chuẩn nhất.


# mấy cái khổ khi chạy nhiều agent

Nói Herdr làm gì thì trước hết phải nói mình đang khổ chỗ nào. Mới dùng agent thì sớm muộn cũng gặp, còn dùng lâu rồi chắc đọc tới đây là gật gù.

1. agent đứng chờ mà mình không hay

Agent bây giờ đều có cơ chế xin phép: muốn sửa file, chạy lệnh shell, gọi mạng... là nó dừng lại chờ mình bấm y. Cái này tốt, nên có. Có điều nếu agent nằm ở tab mình không mở, nó cứ ngồi im đó chờ. Thời gian chờ ấy là thời gian chết, không ai làm gì cả.

2. nhảy qua nhảy lại tốn sức hơn mình tưởng

Muốn biết 5 agent đang tới đâu thì phải mở lần lượt 5 tab, đọc màn hình, nhớ lại nó đang làm bước nào. Mỗi lần nhảy tab là một lần đứt mạch. Ai code lâu đều biết, đứt mạch một lần là mất cả buổi mới vào lại được guồng.

3. đóng cửa sổ là mất trắng

Lỡ tay đóng terminal, laptop sập nguồn, Wi-Fi chập chờn làm rớt SSH... process bên trong chết theo. Một agent đã chạy 40 phút, đang nắm cả đống ngữ cảnh mà chết ngang thì mất cả thời gian lẫn tiền token.

4. tmux chỉ gỡ được một nửa

Dân terminal lâu năm sẽ bảo: "Xài tmux đi". Đúng là tmux giữ session rất ổn, chia màn hình cũng ổn. Nhưng tmux không biết pane nào có agent, agent đó đang làm hay đang chờ. Với tmux, pane nào cũng như pane nào.

Khi đã chạy nhiều agent một lúc, câu hỏi không còn là "làm sao cho AI làm giỏi hơn" nữa, mà là "làm sao để mình điều phối cho kịp". Herdr nhắm vào đúng câu hỏi thứ hai.


# vậy Herdr là gì?

README của dự án tự giới thiệu là "the runtime your coding agents live on", tạm hiểu là "chỗ ở" của các coding agent.

Nói cho dễ hình dung thì Herdr là:

  • Một terminal multiplexer: chia một cửa sổ terminal thành nhiều workspace, tab, pane, giống tmux hay zellij.
  • Biết agent là gì: tự nhận ra các agent CLI quen thuộc chạy trong pane và đọc được chúng đang làm gì.
  • Không chết theo cửa sổ: chạy theo kiểu server/client, tắt giao diện thì agent vẫn chạy nền.
  • Điều khiển được bằng lệnh: thao tác nào cũng có CLI và socket API, nên agent cũng điều khiển được layout và agent khác.
  • Nhẹ: một file binary Rust (giao diện dựng bằng Ratatui), không Electron, chạy bên trong terminal bạn đang dùng (iTerm2, WezTerm, Kitty, Ghostty, Windows Terminal...).

Cũng nên nói rõ Herdr không phải là gì, để khỏi kỳ vọng sai:

Herdr KHÔNG phảivì sao
một AI agentKhông có model, không gọi LLM. Agent là của bạn, bạn tự mang vào
một terminal emulatorKhông thay iTerm2 hay WezTerm. Font, theme, phím tắt của terminal giữ nguyên
framework multi-agentKhác LangGraph, CrewAI. Không có graph, không có LLM nào ngầm điều phối
dịch vụ cloudChạy hoàn toàn trên máy bạn, không cần đăng nhập
lớp bọc quanh agentKhông chặn, không sửa input/output của agent, chỉ đứng ngoài nhìn màn hình

README có một câu tóm gọn khá hay: "herdr doesn't wrap or replace them; it owns their terminals." Tức là Herdr không bọc, không thay agent, nó chỉ giữ cái terminal của agent.


# bên trong Herdr: server, client và cây workspace

server và client

  • Server giữ toàn bộ pane và process. Nó chạy nền, bạn có nhìn hay không cũng vậy.
  • Client chỉ là cái giao diện gắn vào server. Mở nhiều client cùng lúc cũng được, mỗi cái xem một workspace khác nhau.
  • CLI và socket API là cửa thứ ba, để script và agent nói chuyện với server bằng JSON.

Bấm ctrl+b rồi q (detach) thì client thoát, còn server và agent vẫn chạy tiếp. Gõ lại herdr là quay về đúng chỗ cũ.

workspace, tab, pane

cấplà gìnên dùng thế nào
WorkspaceCấp to nhất, mỗi cái ứng với một dự án (UI gọi là "space")Mỗi repo, mỗi task hoặc mỗi vụ điều tra một workspace
TabMột bố cục bên trong workspaceTách riêng các góc nhìn: agents, logs, server, review
PaneMột terminal thật, chạy một processMỗi agent hoặc mỗi lệnh chạy lâu một pane

Mỗi thứ có một ID riêng, cố định, và đóng rồi thì không bao giờ dùng lại:

workspace : w1
tab       : w1:t1
pane      : w1:p1

Chi tiết này nhỏ nhưng quan trọng khi viết script: ID phải lấy từ JSON trả về, đừng đoán theo thứ tự trên thanh bên.

ba chế độ phím

Herdr học cách của tmux nhưng tách bạch hơn:

  • Terminal mode: gõ gì cũng đi thẳng vào pane đang chọn (lúc bạn đang chat với agent).
  • Prefix mode: bấm ctrl+b, Herdr chờ phím tiếp theo để làm một thao tác.
  • Navigate mode: chế độ di chuyển giữa workspace và pane bằng phím mũi tên hoặc h/j/k/l.

Không thích nhớ phím tắt cũng không sao. Herdr hỗ trợ chuột rất tốt: click để chọn pane, kéo viền để đổi kích thước, chuột phải để chia hoặc đóng pane. Người mới hoàn toàn dùng được mà không cần thuộc phím nào.


# phần đáng tiền nhất: Herdr biết agent đang làm gì

Đây là thứ làm Herdr khác tmux. Ai dùng sâu thì nên hiểu kỹ phần này, để biết lúc nào nó đúng, lúc nào nó có thể sai.

năm trạng thái

trạng tháinghĩa làbạn nên làm gì
workingAgent đang làmKệ nó
blockedTrên màn hình đang có hộp thoại xin phép hoặc câu hỏiVào duyệt ngay
doneAgent làm xong lúc bạn không nhìn, giờ đang rảnhVào đọc kết quả
idleAgent đang rảnh và bạn đã xem tab của nó rồiMuốn thì giao việc mới
unknownCó agent nhưng Herdr không chắc nó đang ở trạng thái nàoTự mở ra xem

done và idle thật ra cùng là "agent đang rảnh". Khác ở chỗ done báo thêm cho bạn biết có kết quả chưa đọc, giống cái chấm "tin nhắn mới" trên Zalo hay Slack. Bạn mở tab đó ra xem là nó chuyển thành idle.

trạng thái được "đẩy" lên cấp trên

Một agent blocked thì pane, tab và cả workspace chứa nó đều hiện là blocked. Một agent working thì workspace sáng lên là đang có việc. Vì vậy chỉ cần liếc thanh bên trái là biết dự án nào đang cần mình, kể cả khi agent đó nằm sâu trong một tab chẳng ai mở.

Cách dùng Herdr gói gọn lại chỉ có vậy: bật vài agent, cho chúng chạy song song, rồi nhìn thanh bên xem chỗ nào cần mình.

Muốn gọn hơn nữa thì đổi danh sách agent từ "xếp theo workspace" sang "xếp theo độ ưu tiên". Lúc đó thanh bên thành một hàng chờ, cái nào cần bạn nhất nằm trên cùng:

[ui]
agent_panel_sort = "priority"   # mặc định: "spaces"
status_indicators = "symbols"   # mỗi trạng thái một ký hiệu riêng thay vì chấm màu

Herdr đoán trạng thái kiểu gì?

Có hai cách. Biết hai cách này thì lúc Herdr báo sai bạn sẽ biết sửa ở đâu.

Cách 1: đọc màn hình (screen manifest). Với các agent phổ biến (Claude Code, Codex, Cursor Agent, Gemini CLI, OpenCode, Copilot CLI, Amp, Grok CLI, Kiro, Cline, Qwen Code, Droid, Pi...), Herdr có sẵn file manifest mô tả "màn hình agent X lúc đang chạy, lúc đang xin phép trông ra sao". Herdr chụp lại phần cuối màn hình rồi so với manifest.

Manifest đi kèm sẵn trong binary và được cập nhật định kỳ từ herdr.dev. Nhờ vậy khi Claude Code đổi giao diện, bạn không nhất thiết phải chờ Herdr ra bản mới. Muốn tự chỉnh thì ghi đè trong ~/.config/herdr/agent-detection/<agent>.toml.

Có một điểm mình thấy họ làm rất có tâm: trạng thái blocked được bắt cực kỳ khắt khe. Herdr chỉ báo blocked khi màn hình khớp đúng với một hộp thoại xin phép hoặc câu hỏi đã biết. Lý do dễ hiểu: báo nhầm vài lần là bạn hết tin thanh bên, lại quay về mở từng tab ra kiểm tra, vậy thì dùng Herdr làm gì nữa.

Cách 2: agent tự báo (integration). Thay vì để Herdr đoán qua màn hình, agent chủ động gọi Herdr báo "tôi đang làm", "tôi đang chờ". Cách này chính xác hơn, lại còn cho Herdr lưu session ID của agent để mở lại đúng cuộc hội thoại cũ khi server khởi động lại.

# cài integration cho agent bạn hay dùng
herdr integration install claude
herdr integration status

Khi Herdr báo sai thì dùng mấy lệnh này để soi:

herdr agent explain <pane-id-hoặc-tên-agent>
herdr server agent-manifests            # xem manifest nào đang được dùng
herdr server update-agent-manifests     # kéo manifest mới nhất về

Nói thêm cho dân kỹ thuật: Đọc màn hình nghe hơi "thủ công", nhưng là lựa chọn rất thực tế. Mỗi agent CLI một kiểu, nhiều cái còn đóng nguồn, điểm chung duy nhất là cái nào cũng vẽ lên terminal. Đọc màn hình giúp Herdr hỗ trợ hơn 20 agent mà không cần bên nào chịu hợp tác. Agent nào muốn chính xác hơn thì đi đường integration.


# cài đặt và làm quen

cài đặt

# macOS / Linux, chọn một trong các cách
brew install herdr
curl -fsSL https://herdr.dev/install.sh | sh
mise use -g herdr
 
# Windows (PowerShell)
powershell -ExecutionPolicy Bypass -c "irm https://herdr.dev/install.ps1 | iex"
 
# build từ source (cần cài Rust)
git clone https://github.com/herdrdev/herdr
cd herdr && cargo build --release

Kiểm tra lại:

herdr --version
herdr status

lần chạy đầu

herdr

Lần đầu mở, Herdr sẽ hỏi bạn muốn nhận thông báo kiểu nào: hiện ngay trong Herdr, nhờ terminal hiện, hay dùng thông báo của hệ điều hành. Chọn xong là vào màn hình chính, chia làm bốn khu:

+----------------+---------------------------------------------+
| SPACES         |  [agents]  [logs]  [review]       <- tabs   |
|                |  +-------------------+-------------------+  |
| * api          |  |                   |                   |  |
|   main         |  |  claude           |  codex            |  |
|                |  |  (working)        |  (blocked)        |  |
| ~ web          |  |                   |                   |  |
|   feat/login   |  |                   +-------------------+  |
|                |  |                   |                   |  |
+----------------+  |                   |  npm run dev      |  |
| AGENTS         |  |                   |                   |  |
| ! codex   web  |  +-------------------+-------------------+  |
| ~ claude  api  |                                             |
+----------------+---------------------------------------------+
| ctrl+b ? help  |  ctrl+b c new tab  |  ctrl+b v split        |
+--------------------------------------------------------------+
  • Spaces (góc trên bên trái): danh sách workspace, kèm branch git và trạng thái chung.
  • Agents (góc dưới bên trái): tất cả agent đang chạy, gom từ mọi workspace.
  • Khu pane (ở giữa và bên phải): terminal thật của bạn.
  • Thanh gợi ý (dưới cùng): phím tắt tuỳ theo ngữ cảnh.

bài tập đầu tiên: hai agent chạy song song

  1. Ở pane đầu tiên, cd vào repo rồi chạy claude.
  2. Bấm ctrl+b rồi v để chia đôi màn hình theo chiều dọc. Pane mới chạy codex (hoặc agent nào bạn có).
  3. Giao cho mỗi agent một việc, rồi ctrl+b c mở tab mới đi làm việc khác.
  4. Để ý thanh bên: agent nào xin quyền là chuyển sang blocked, có thêm thông báo và tiếng nếu bạn bật.
  5. Bấm ctrl+b q để detach, tắt luôn cửa sổ terminal. Mở lại, gõ herdr. Mọi thứ vẫn còn, agent vẫn đang chạy.

Phần lớn mọi người "chịu" Herdr từ bước 5.

phím tắt nên nhớ

Tất cả phím dưới đây đều bấm sau prefix ctrl+b:

phímlàm gìphímlàm gì
?xem hết phím tắtqdetach (agent vẫn chạy)
cmở tab mớishift+ntạo workspace mới
n / ptab sau / tab trước1..9nhảy tới tab số N
vchia pane theo chiều dọcminuschia pane theo chiều ngang
h j k lchuyển sang pane trái/dưới/trên/phảixđóng pane
zphóng to panerchế độ đổi kích thước
wchọn workspaceggoto
shift+gtạo git worktree mớionhảy tới chỗ vừa báo thông báo
bẩn/hiện thanh bênscài đặt
emở scrollback bằng editorshift+rnạp lại config

Ai xài tmux rồi sẽ thấy quen tay. Nếu đang dùng tmux song song và sợ đụng prefix thì đổi phím khác:

[keys]
prefix = "ctrl+space"

# chỉnh Herdr theo ý mình

File config nằm ở ~/.config/herdr/config.toml (Windows thì ở %APPDATA%\herdr\config.toml). Cách dễ nhất là xuất file mặc định ra, trong đó đã có sẵn comment giải thích từng dòng:

mkdir -p ~/.config/herdr
herdr --default-config > ~/.config/herdr/config.toml

Sửa xong thì nạp lại, không cần tắt agent nào:

herdr server reload-config   # hoặc ctrl+b shift+r

Đây là bản config mình đang dùng, bạn có thể lấy làm điểm xuất phát:

# ~/.config/herdr/config.toml
 
[theme]
name = "catppuccin"
auto_switch = true              # đổi theo chế độ sáng/tối của terminal
light_name = "catppuccin-latte"
dark_name = "catppuccin"
 
[terminal]
new_cwd = "follow"              # pane mới mở ở cùng thư mục với pane hiện tại
 
[worktrees]
directory = "~/.herdr/worktrees"
 
[ui]
agent_panel_sort = "priority"   # agent cần mình nhất nằm trên cùng
status_indicators = "symbols"   # dễ phân biệt hơn chấm màu, ai yếu màu cũng đọc được
hide_tab_bar_when_single_tab = true
show_agent_labels_on_pane_borders = true
 
# báo khi agent ở workspace khác đổi trạng thái
[ui.toast]
delivery = "system"             # off | herdr | terminal | system
delay_seconds = 1
 
# chỉnh thông tin hiện trong danh sách agent
[ui.sidebar.agents]
row_gap = 0
rows = [["state_icon", "workspace", "tab"], ["agent"]]
 
# phím tắt riêng: mở lazygit dạng popup
[[keys.command]]
key = "prefix+alt+g"
type = "popup"
command = "lazygit"
width = "80%"
height = "80%"

Vài ghi chú:

  • delivery = "system" dùng thông báo của hệ điều hành, hợp với ai hay chuyển qua trình duyệt hay Slack. Ngồi suốt trong terminal thì để herdr cho đỡ ồn.
  • [ui.sound] cho đặt tiếng riêng cho "xong việc" và "cần duyệt", bật tắt theo từng agent. Nghe tiếng là biết nên chạy vào hay cứ để đó, tiện hơn mình tưởng.
  • [[keys.command]] với type = "popup" rất tiện: lazygit, btop hay script xem diff, mở đè lên trên mà không làm xáo trộn bố cục.

# làm thật: một feature, ba agent, ba worktree

Phần này cho bạn nào đã quen chạy một agent và muốn tăng lên.

luật bất di bất dịch: hai agent không sửa chung một thư mục

Cho hai agent sửa chung một thư mục là tự rước hoạ: agent A đang chạy test thì agent B sửa file, A thấy test đỏ lại đi "sửa" code của B... Cách xử lý chuẩn là git worktree: mỗi agent một bản checkout riêng, một branch riêng, dùng chung lịch sử .git.

Herdr có sẵn lệnh worktree, mỗi worktree mở ra thành một workspace riêng, thanh bên hiện luôn tên branch và trạng thái git:

herdr worktree create --cwd ~/code/shop --branch feat/payment-retry --base main --label payment
herdr worktree list --cwd ~/code/shop

Hoặc trên giao diện: ctrl+b shift+g.

cách sắp xếp

script dựng nguyên "phòng làm việc" bằng một lệnh

Sáng nào cũng tạo tay thì mệt, viết script cho nhanh. Lệnh điều khiển nào của Herdr cũng trả về JSON, ghép với jq là vừa đẹp.

Tạo file ~/bin/herdr-feature.sh:

#!/usr/bin/env bash
# dựng workspace + worktree + agent cho một feature
# dùng: herdr-feature.sh <repo-path> <branch> <agent-kind> "<prompt>"
set -euo pipefail
 
REPO="$1"
BRANCH="$2"
KIND="${3:-claude}"
PROMPT="${4:-}"
NAME="$(echo "$BRANCH" | tr '/' '-' | tr '[:upper:]' '[:lower:]' | cut -c1-32)"
 
# 1. tạo git worktree, Herdr mở nó thành workspace mới (không giành focus)
WS=$(herdr worktree create --cwd "$REPO" --branch "$BRANCH" --base main \
       --label "$NAME" --no-focus | jq -r '.result.workspace.workspace_id')
 
# 2. lấy pane đầu tiên của workspace vừa tạo
PANE=$(herdr pane list --workspace "$WS" | jq -r '.result.panes[0].pane_id')
 
# 3. bật agent trong pane đó, lệnh chỉ trả về khi agent sẵn sàng nhận việc
herdr agent start "$NAME" --kind "$KIND" --pane "$PANE"
 
# 4. giao việc rồi để agent tự chạy, không đứng chờ
if [ -n "$PROMPT" ]; then
  herdr agent prompt "$NAME" "$PROMPT"
fi
 
echo "✅ $NAME đang chạy trong workspace $WS (pane $PANE)"

Chạy thử:

chmod +x ~/bin/herdr-feature.sh
 
herdr-feature.sh ~/code/shop feat/payment-retry claude \
  "Thêm cơ chế retry với exponential backoff và idempotency key cho PaymentService. Viết unit test. Không sửa file ngoài module payment."
 
herdr-feature.sh ~/code/shop feat/payment-tests codex \
  "Đọc docs/specs/payment-retry.md và viết integration test cho các kịch bản timeout, duplicate request, partial failure."

Ba chỗ cần để ý trong script:

  1. ID lấy từ JSON (.result.workspace.workspace_id, .result.panes[0].pane_id), không đoán kiểu w2:p1. ID trong Herdr không theo quy luật nào để mình tự suy ra, đừng bao giờ ghi cứng.
  2. agent start chờ agent sẵn sàng rồi mới trả về (mặc định chờ tối đa 30 giây). Nếu agent bị chặn ngay lúc mở (kiểu hỏi "có tin thư mục này không?"), lệnh báo lỗi agent_not_ready để bạn xử lý, chứ không gửi prompt vào chỗ không ai nghe.
  3. Tên agent ($NAME) phải khớp regex [a-z][a-z0-9_-]{0,31} và không được trùng. Đặt tên xong thì lệnh nào cũng gọi bằng tên được, dễ đọc hơn pane ID nhiều.

# cho agent điều khiển agent

Phần này dân AI chắc sẽ thích nhất. Vì Herdr có CLI đầy đủ, một agent chạy trong Herdr có thể tự mở pane, bật agent khác, giao việc, chờ rồi đọc kết quả.

biến môi trường Herdr gắn vào mỗi pane

HERDR_ENV=1                # đang chạy trong Herdr
HERDR_WORKSPACE_ID=w1
HERDR_TAB_ID=w1:t1
HERDR_PANE_ID=w1:p1        # pane của chính process này
HERDR_BIN_PATH=...         # đường dẫn tới binary herdr
HERDR_SOCKET_PATH=...      # socket API

Nhờ mấy biến này, agent biết mình đang đứng ở đâu và thao tác quanh vị trí của mình (--current), không lỡ tay đụng vào pane bạn đang dùng.

skill có sẵn cho agent

Herdr có sẵn một file "skill" dạy agent cách dùng CLI cho an toàn:

mkdir -p ~/.claude/skills/herdr
herdr --skill > ~/.claude/skills/herdr/SKILL.md

Với Claude Code (hoặc agent nào đọc được skill kiểu này), cài xong chỉ cần nói "dùng Herdr để..." là agent biết làm. Trong skill có vài luật an toàn mình thấy đáng học:

  • Kiểm tra HERDR_ENV=1 trước đã, không điều khiển Herdr từ bên ngoài.
  • Việc chạy nền thì dùng --no-focus, không giành màn hình của người dùng.
  • Không đóng workspace, tab, pane nào mà agent không tự tạo.
  • Gặp agent đang blocked thì đọc màn hình rồi hỏi người dùng, không tự bấm "yes".

cách 1: chạy test ở pane bên cạnh rồi đọc kết quả

# mở pane bên phải, cùng thư mục, không giành focus
NEW=$(herdr pane split --current --direction right --cwd "$PWD" --no-focus \
        | jq -r '.result.pane.pane_id')
 
herdr pane run "$NEW" "./gradlew test"
herdr pane wait-output "$NEW" --regex "BUILD (SUCCESSFUL|FAILED)" --timeout 600000
herdr pane read "$NEW" --source recent-unwrapped --lines 150

So với để agent tự chạy test trong pane của nó, cách này có hai cái lợi: bạn thấy test chạy ngay bên cạnh, và đống output dài không nhét vào ngữ cảnh của agent cho tới lúc nó cần đọc.

--source có bốn lựa chọn:

sourceđọc phần nào
visibleđúng phần đang hiện trên màn hình
recentoutput gần đây, giữ nguyên chỗ xuống dòng do tràn
recent-unwrappedoutput gần đây, nối lại các dòng bị tràn, đọc log là hợp nhất
detectionphần cuối màn hình mà Herdr dùng để đoán trạng thái

cách 2: một đứa viết, một đứa review

Một agent viết code, một agent khác model review. Dùng hai model khác nhau để đỡ dính chung "điểm mù": model nào cũng khó tự thấy loại lỗi mà chính nó hay mắc.

#!/usr/bin/env bash
# review-loop.sh, chạy trong một pane của Herdr
set -euo pipefail
test "${HERDR_ENV:-}" = 1 || { echo "không chạy trong Herdr"; exit 1; }
 
# pane cho reviewer
RP=$(herdr pane split --current --direction down --cwd "$PWD" --no-focus \
       | jq -r '.result.pane.pane_id')
herdr agent start reviewer --kind codex --pane "$RP"
 
# writer: giả sử đã có agent tên "writer" đang chạy
herdr agent prompt writer \
  "Implement TODO trong src/payment/RetryPolicy.java. Khi xong, commit với message rõ ràng." \
  --wait --timeout 1800000
 
# xem writer dừng ở trạng thái nào
STATE=$(herdr agent get writer | jq -r '.result.agent.agent_status // empty')
if [ "$STATE" = "blocked" ]; then
  herdr notification show "writer cần duyệt" --sound request
  exit 0
fi
 
# reviewer đọc commit cuối và báo cáo
herdr agent prompt reviewer \
  "Review commit HEAD. Chỉ báo các lỗi thực sự có thể gây bug hoặc lỗ hổng bảo mật, kèm file:line. Ghi kết quả vào /tmp/review.md." \
  --wait --timeout 900000
 
herdr notification show "Review xong" --body "Xem /tmp/review.md" --sound done

Vài điều nên biết về agent prompt:

  • Agent đang blocked thì lệnh không gửi mà trả về agent_blocked. Nhờ vậy prompt của bạn không bị gõ nhầm vào hộp thoại xin phép (rồi vô tình thành câu trả lời "yes").
  • --wait chờ tới khi agent dừng lại ở một trạng thái: idle, done hoặc blocked.
  • Sau 5 giây mà agent không nhúc nhích, lệnh trả về agent_prompt_stalled thay vì treo mãi.

cách 3: hỏi một câu, ba agent cùng trả lời

Gặp bug khó thì hỏi cùng một câu cho ba agent khác nhau rồi so đáp án, giống đi khám mà hỏi thêm ý kiến bác sĩ khác:

#!/usr/bin/env bash
set -euo pipefail
Q="Đọc logs/incident-1003.log và src/order/. Tìm nguyên nhân gốc của lỗi duplicate order. Trả lời ngắn gọn, có dẫn chứng file:line."
 
for KIND in claude codex gemini; do
  P=$(herdr pane split --current --direction right --cwd "$PWD" --no-focus \
        | jq -r '.result.pane.pane_id')
  herdr agent start "inv-$KIND" --kind "$KIND" --pane "$P"
  herdr agent prompt "inv-$KIND" "$Q"
done
 
# chờ cả ba
for KIND in claude codex gemini; do
  herdr agent wait "inv-$KIND" --timeout 1200000 || true
done
 
herdr notification show "3 agent đã điều tra xong" --sound done

Ba đứa cùng chỉ một chỗ thì khá chắc. Mỗi đứa nói một kiểu thì đó chính là chỗ bạn nên tự đào sâu.

socket API cho ai muốn tích hợp sâu

CLI thật ra chỉ là lớp mỏng bọc ngoài socket API dạng JSON. Muốn làm dashboard, bot Slack hay nối vào hệ thống riêng thì nói chuyện thẳng với socket ở $HERDR_SOCKET_PATH:

{
  "id": "req_1",
  "method": "pane.split",
  "params": { "pane_id": "w1:p1", "direction": "right", "ratio": 0.333 }
}

Đăng ký nhận sự kiện mỗi khi agent đổi trạng thái, có cái này là tự làm được hệ thống cảnh báo riêng:

{
  "id": "sub_1",
  "method": "events.subscribe",
  "params": {
    "subscriptions": [{ "type": "pane.agent_status_changed", "pane_id": "w1:p1" }]
  }
}

Xem toàn bộ schema:

herdr api schema --json   # mọi method, request, response, event
herdr api snapshot        # trạng thái hiện tại của cả session

tự viết agent thì báo trạng thái cho Herdr

Nếu bạn đang tự làm agent (bằng Claude Agent SDK, LangGraph, hay chỉ một vòng lặp Python), thêm vài lệnh là nó hiện lên Herdr đẹp như agent xịn:

# lúc bắt đầu làm
"$HERDR_BIN_PATH" pane report-agent "$HERDR_PANE_ID" \
  --source my-agent --agent my-agent --state working --seq 1
 
# lúc chờ người dùng duyệt
"$HERDR_BIN_PATH" pane report-agent "$HERDR_PANE_ID" \
  --source my-agent --agent my-agent --state blocked --seq 2
 
# lúc thoát
"$HERDR_BIN_PATH" pane release-agent "$HERDR_PANE_ID" \
  --source my-agent --agent my-agent --seq 3

--seq phải tăng dần để Herdr bỏ qua những báo cáo tới trễ, sai thứ tự. Đây là chiêu quen thuộc trong hệ thống phân tán, giống sequence number của TCP hay cột version trong optimistic locking. Nhớ kiểm tra HERDR_ENV=1 trước, để agent của bạn vẫn chạy bình thường khi không nằm trong Herdr.


# agent chạy trên server, mình ngồi laptop xem

Trường hợp rất hay gặp: cho agent chạy trên VPS hoặc máy build mạnh (gần database staging, không hao pin laptop, chạy xuyên đêm được).

herdr --remote user@build-box
herdr --remote user@build-box --session agents

Với --remote, server Herdr chạy trên máy từ xa, còn client chạy trên laptop của bạn. Rớt mạng thì agent trên server vẫn chạy, nối lại là thấy ngay. Phím tắt và config là của máy bạn (--remote-keybindings local là mặc định), nên cảm giác y như làm trên máy mình.

Herdr còn cho gom nhiều máy vào một cửa sổ: workspace trên máy mình và workspace trên các máy SSH đã lưu nằm chung một thanh bên, danh sách agent gộp lại, máy nào rớt thì máy đó tự nối lại, không ảnh hưởng máy khác. Phần đăng nhập vẫn do OpenSSH lo, Herdr không lưu mật khẩu hay private key. Key có passphrase thì nhớ ssh-add trước.


# cái gì còn, cái gì mất?

Chỗ này nhiều người hiểu nhầm nên nói cho rõ:

chuyện gì xảy raprocess agentbố cục (workspace/tab/pane/thư mục)cuộc hội thoại của agent
detach, tắt cửa sổ terminal✅ vẫn chạy✅ y nguyên✅ y nguyên, vì process chưa hề dừng
rớt SSH (khi dùng --remote)✅ vẫn chạy✅ y nguyên✅ y nguyên
khởi động lại Herdr server❌ dừng✅ được khôi phục⚠️ chỉ khi agent có integration hỗ trợ
khởi động lại máy❌ dừng✅ khôi phục từ bản lưu⚠️ như trên

Thêm vài cơ chế:

  • Lưu bố cục: Herdr giữ tối đa 48 bản lưu bố cục để bạn khôi phục bằng tay.
  • Mở lại đúng cuộc hội thoại: agent có integration sẽ báo session ID cho Herdr, nên sau khi server khởi động lại, Herdr mở agent lên đúng cuộc hội thoại cũ (ví dụ dùng --resume của agent).
  • Phát lại nội dung pane: phải tự bật, chỉ chạy khi bố cục khớp chính xác bản lưu.
  • Chuyển giao khi cập nhật (đang thử nghiệm): herdr update --handoff cố gắng chuyển các pane đang chạy sang server mới khi cập nhật Herdr, để process khỏi phải dừng.

Rút ra: detach thoải mái, nhưng đừng khởi động lại server khi agent đang làm dở. Và cài integration cho mấy agent chính để có đường lui.


# so với các lựa chọn khác

tiêu chíHerdrtmux / zellijterminal "AI-native" (Warp...)mở nhiều tab thường
biết trạng thái agent✅ thanh bên + thông báo❌một phần, thường chỉ agent nhà làm❌
giữ session khi tắt✅✅tuỳ sản phẩm❌
dùng agent CLI nào cũng được✅ hơn 20 agent✅ (nhưng không biết gì về agent)thường ưu tiên agent tích hợp sẵn✅
giữ terminal đang dùng✅ chạy bên trong✅❌ phải đổi terminal✅
có API cho agent điều khiển✅ CLI + socket, có skill⚠️ tự chế bằng tmux send-keystuỳ sản phẩm❌
dùng chuột✅ rất tốt⚠️ phải cấu hình✅✅
độ chín⚠️ còn mới (0.x)✅ rất chíntuỳ sản phẩm—

So với subagent hay agent team có sẵn trong agent (ví dụ subagent của Claude Code): hai thứ này nằm ở hai tầng khác nhau, dùng chung được chứ không loại nhau.

  • Subagent trong một agent: do model tự chia việc, chạy ngầm, hợp để chẻ nhỏ một task.
  • Herdr: do người (hoặc script) điều phối, agent nào cũng là một terminal bạn nhìn thấy và nhảy vào được, trộn nhiều hãng model cũng được.

Ví von cho dễ nhớ: subagent giống mấy bạn trong nhóm tự chia việc với nhau, còn Herdr là cái văn phòng mở, ngồi một chỗ là thấy cả team.


# khi nào không cần Herdr

Nói ưu điểm rồi thì cũng phải nói thẳng mấy chỗ chưa ổn:

  • Bạn chỉ chạy một agent. Một cửa sổ terminal là đủ rồi. Herdr chỉ đáng khi chạy từ hai agent trở lên, hoặc khi cần giữ session qua SSH.
  • Bạn cần thứ gì thật ổn định. Herdr mới ở bản 0.x, ra bản liên tục, API và phím tắt còn có thể đổi. Theo mấy bài review, dự án chủ yếu do một người phát triển chính. Định đưa vào quy trình bắt buộc cho cả công ty thì nên cân nhắc.
  • Agent của bạn không nằm trong danh sách hỗ trợ và cũng không tự báo trạng thái. Herdr vẫn chạy nó như một terminal bình thường, nhưng bạn mất cái hay nhất là thanh trạng thái.
  • Đọc màn hình thì có lúc trật, nhất là khi agent vừa đổi giao diện. Manifest cập nhật từ xa đỡ được phần lớn, nhưng thỉnh thoảng bạn vẫn sẽ thấy unknown.
  • Herdr không giúp bạn đỡ tốn tiền. Năm agent chạy song song là đốt token gấp năm. Herdr giúp điều phối, không giúp tiết kiệm.

# kinh nghiệm khi "quản lý" một team agent

Có công cụ rồi thì cách mình làm việc mới là thứ quyết định. Mấy điều dưới đây mình rút ra sau một thời gian dùng, đa số áp dụng được cả khi không dùng Herdr.

1. bắt đầu với 2 đến 3 agent thôi

Giống Kanban giới hạn số việc đang làm: số agent hợp lý không phải số nhiều nhất máy chạy nổi, mà là số nhiều nhất mình review kịp. Năm agent đẻ ra năm cái diff, đọc không xuể thì chỉ là đang sản xuất nợ kỹ thuật nhanh hơn thôi.

2. xử lý theo thứ tự blocked, rồi done, rồi tới phần còn lại

blocked là thời gian chết, xử lý trước. done là kết quả đang chờ, xử lý sau. Bật agent_panel_sort = "priority" là thanh bên tự xếp đúng thứ tự này.

3. một agent, một worktree, một branch

Với task có sửa code thì không có ngoại lệ. Agent chỉ đọc (review, đọc log) thì dùng chung repo chính cũng được.

4. đặt tên agent cho dễ hiểu

payment-retry, reviewer, inv-claude dễ nhìn hơn nhiều so với claude, claude, claude. Mà tên cũng là thứ script dùng để gọi agent.

5. giao việc phải có giới hạn rõ ràng

Prompt cho agent chạy nền nên có đủ: làm gì, được sửa chỗ nào ("không sửa file ngoài module payment"), xong là khi nào ("test pass thì commit"). Agent càng chạy lâu không ai trông thì giới hạn càng phải rõ.

6. có Herdr rồi cũng đừng tắt chế độ xin phép

Herdr giúp mình duyệt nhanh hơn, vậy thì càng nên giữ bước duyệt chứ không phải bỏ nó đi. Muốn agent tự chủ hơn thì dùng danh sách cho phép cụ thể (cho chạy test, cấm rm, cấm git push) thay vì bật "cho phép hết".

7. quyền điều khiển Herdr là quyền rất lớn

Agent nào gọi được herdr pane run hay herdr agent send-keys là agent đó chạy được lệnh ở mọi pane, và bấm duyệt thay cho agent khác được luôn. Đây là lỗ hổng có thật, nhất là khi agent đọc nội dung không đáng tin (issue GitHub, trang web, log) mà bên trong có thể cài prompt injection. Chỉ cài Herdr skill cho agent khi thật sự cần điều phối, và luôn giữ nguyên tắc "gặp blocked thì hỏi người".

8. cuối ngày nhớ dọn dẹp

herdr agent list                         # còn agent nào đang chạy?
herdr worktree list --cwd ~/code/shop    # worktree nào merge rồi?
herdr worktree remove --workspace <id>   # xoá worktree đã xong

Ngày nào cũng tạo vài cái thì worktree với workspace bỏ quên chất đống nhanh lắm.


# checklist bắt đầu

ngày 1: làm quen

  • cài Herdr, chạy herdr, chọn kiểu thông báo
  • chạy 2 agent ở 2 pane, xem thanh bên đổi trạng thái khi agent xin quyền
  • thử detach (ctrl+b q), tắt terminal, mở lại bằng herdr
  • xem hết phím tắt bằng ctrl+b ?

tuần 1: cấu hình

  • herdr --default-config > ~/.config/herdr/config.toml
  • bật agent_panel_sort = "priority" và thông báo hệ thống
  • herdr integration install <agent> cho mấy agent dùng chính
  • tạo worktree đầu tiên bằng ctrl+b shift+g

tuần 2: tự động hoá

  • viết script dựng workspace + worktree + agent cho một feature
  • thử kiểu một đứa viết, một đứa review với hai model khác nhau
  • có VPS thì thử herdr --remote cho mấy task chạy qua đêm

hàng tuần

  • agent nào hay blocked vì cùng một lệnh → cân nhắc thêm lệnh đó vào danh sách cho phép
  • số agent có vượt sức review của mình không? → giảm bớt
  • dọn worktree và workspace đã xong

# tổng kết

cái khổ khi chạy nhiều agentHerdr xử lý bằng
không biết agent nào đang chờ duyệttrạng thái blocked nổi lên thanh bên, kèm thông báo
không biết agent nào đã xongtrạng thái done, kiểu "tin nhắn chưa đọc"
mở 5 tab để xem 5 agentmột thanh bên, gom agent từ mọi workspace
lỡ tắt terminal, rớt SSHserver/client, detach, --remote
agent sửa đè code của nhauherdr worktree, mỗi agent một worktree, một workspace
điều phối tay lặp đi lặp lạiCLI, socket API và agent skill để viết script

Herdr không làm agent của bạn thông minh hơn. Nó làm một việc nhỏ hơn nhưng giai đoạn này lại rất cần: giúp mình không trở thành cái cổ chai khi làm cùng nhiều agent.

Agent ngày càng giống đồng nghiệp ngồi làm song song với mình. Kỹ năng quan trọng của dev vì thế cũng dần chuyển từ "code nhanh" sang "giao việc rõ, review nhanh, điều phối khéo". Có một công cụ nhìn được cả team là bước đầu cho chuyện đó.

Cứ bắt đầu với hai agent. Nhìn thanh bên. Gặp blocked thì xử lý trước. Quen tay rồi hẵng tăng lên.

Tài liệu tham khảo:


Chỉ là những ghi chép cá nhân với hy vọng mang lại chút giá trị. Nếu thấy hữu ích, đừng ngại chia sẻ cho bạn bè & đồng nghiệp nhé!

Happy coding 😎 👍🏻 🚀 🔥.