graph TB
subgraph "Layout 层"
L["Layout<br/>空间结构"]
end
subgraph "Pane 层"
P1["Pane<br/>终端窗格"]
P2["Pane<br/>终端窗格"]
P3["Pane<br/>终端窗格"]
end
subgraph "Agent 层"
A1["Agent<br/>AI 代理"]
A2["Agent<br/>AI 代理"]
end
L --> P1
L --> P2
L --> P3
P1 --> A1
P2 --> A2
P3 -.->|"无 Agent<br/>普通 Shell"| N[普通进程]
13 第 11 章 Agent 自动化
13.1 概述
前面几章我们一直在手动操作 Herdr——用键盘切窗格、用鼠标点 Agent、用对话框输入指令。这很好,但远不是 Herdr 的全部实力。
Herdr 的真正威力在于可编程:你可以用脚本控制 Agent,甚至让 Agent 控制 Agent。本章将深入 Herdr 的自动化层,涵盖三大原语、控制面选择、Agent 生命周期管理和完整的编排 Recipes。
读完本章,你将能够:
- 理解 Layout / Pane / Agent 三大原语在自动化中的角色
- 精准选择
pane run、agent start、agent prompt等控制原语 - 编写可靠的多 Agent 编排脚本
- 处理超时、阻塞、异常等边界情况
13.2 三大原语回顾
在进入自动化之前,我们需要重新审视 Herdr 的三大核心原语。在日常交互中,它们的边界很模糊;但在自动化场景中,理解它们的分工至关重要。
13.2.1 Layout(布局)
Layout 是对空间结构的描述——窗格怎么分割、比例多少、谁在左谁在右。它是声明式的:你定义「长什么样」,而不是「怎么做到」。
# 导出当前布局
herdr layout export > my-layout.json
# 应用布局
herdr layout apply my-layout.json在自动化中,Layout 用于初始化工作空间的结构。
13.2.2 Pane(窗格)
Pane 是终端进程的容器。每个 Pane 有唯一的 pane_id(如 p1、p2),可以运行任何东西——Shell、TUI 程序、AI Agent。
Pane 层的控制原语:
| 原语 | 作用 | 示例 |
|---|---|---|
pane run |
在窗格中执行命令(阻塞直到完成) | herdr pane run p1 -- "git status" |
pane send-text |
向窗格发送文本(不回车) | herdr pane send-text p1 -- "npm test" |
pane send-keys |
发送特殊按键 | herdr pane send-keys p1 -- Enter |
pane wait-output |
等待输出匹配模式 | herdr pane wait-output p1 --pattern "done" |
pane read |
读取窗格当前可见内容 | herdr pane read p1 |
13.2.3 Agent(代理)
Agent 是 Pane 中运行的 AI 代理实例。它有名字、有状态、有生命周期。Agent 层的控制原语更高级——它理解 Agent 的语义,知道什么时候在「工作」、什么时候在「等待」。
| 原语 | 作用 | 示例 |
|---|---|---|
agent start |
在指定窗格启动 Agent | herdr agent start --pane p2 --kind claude my-helper |
agent prompt |
向 Agent 发送提示 | herdr agent prompt my-helper -- "修复这个 bug" |
agent send-keys |
向 Agent 窗格发送按键 | herdr agent send-keys my-helper -- Escape |
agent wait |
等待 Agent 进入指定状态 | herdr agent wait my-helper --until done |
agent read |
读取 Agent 输出 | herdr agent read my-helper --source recent |
关键区别:Pane 层原语操作的是「终端」——它不关心里面跑的是什么。Agent 层原语操作的是「AI 代理」——它理解 idle/working/blocked/done 等状态。选择哪一层取决于你需要什么粒度的控制。
13.3 Agent 身份与启动
13.3.1 Pane ID vs Agent Name
每个 Agent 有两种标识方式:
- Pane ID:空间位置标识(
p1、p2),随布局变化可能改变 - Agent Name:语义名称(
my-helper、reviewer),全局唯一,与位置无关
# 用 Pane ID 操作
herdr agent prompt --pane p2 -- "hello"
# 用 Agent Name 操作(推荐)
herdr agent prompt my-helper -- "hello"最佳实践:在自动化脚本中,始终使用 Agent Name 而非 Pane ID。布局变化时 Pane ID 可能重新编号,但 Agent Name 保持稳定。
13.3.2 命名规则
Agent 名称必须符合以下正则表达式:
^[a-z][a-z0-9_-]{0,31}$
即:
- 以小写字母开头
- 只包含小写字母、数字、下划线和连字符
- 长度 1-32 个字符
✅ 合法名称:helper、my-helper、code-reviewer-2、a
❌ 非法名称:Helper(大写)、1helper(数字开头)、my.helper(点号)、超长名称超过三十二个字符的名称(中文 + 超长)
13.3.3 agent start 语法
herdr agent start [--pane <pane_id>] [--kind <kind>] [--timeout <seconds>] <name> [-- <agent-args>...]13.3.3.1 选项详解
| 选项 | 说明 | 默认值 |
|---|---|---|
--pane |
在哪个窗格启动 Agent。省略则使用当前焦点窗格 | 当前焦点 |
--kind |
Agent 类型(决定底层模型) | 配置文件默认值 |
--timeout |
Agent 空闲超时(秒),超时后自动停止 | 配置文件默认值 |
-- <args> |
透传给 Agent 的额外参数 | 无 |
13.3.3.2 支持的 Kind 完整列表
| Kind | 说明 | 典型用途 |
|---|---|---|
claude |
Claude Code (Anthropic) | 通用编程、架构设计 |
codex |
Codex CLI (OpenAI) | 推理、数学密集任务 |
gemini |
Gemini CLI (Google) | 长上下文任务 |
kimi |
Kimi Code (Moonshot) | 中文场景 |
grok |
Grok CLI (xAI) | 快速原型 |
qwen |
Qwen Code (Alibaba) | 混合中英文 |
custom |
自定义 Agent | 任何符合 Herdr Agent 协议的程序 |
# 在 p2 窗格启动名为 helper 的 Claude Agent
herdr agent start --pane p2 --kind claude helper
# 启动 Codex Agent,并透传参数
herdr agent start --pane p3 --kind codex reviewer -- --model o4-mini
# 在当前窗格启动,5 分钟空闲超时
herdr agent start --kind claude --timeout 300 my-agent透传参数的 --:-- 之后的所有参数会原样传递给 Agent 程序。如果忘了加 --,参数会被 Herdr 解析,可能导致意外的行为。
13.4 控制面选择指南
面对如此多的控制原语,初学者常困惑「该用哪个」。下面的决策表帮你快速选择。
13.4.1 决策流程图
flowchart TD
A[需要控制什么?] -->|普通进程/Shell| B{需要等待输出吗?}
A -->|AI Agent| C{需要语义理解吗?}
B -->|否,只发送| D[pane send-text]
B -->|是,等输出匹配| E[pane wait-output]
B -->|是,执行并等待| F[pane run]
C -->|否,模拟按键| G[agent send-keys]
C -->|是,发送提示| H[agent prompt]
C -->|是,等待状态| I[agent wait]
H --> J{需要同步等待回复吗?}
J -->|是| K[agent prompt --wait]
J -->|否| L[agent prompt 异步]
13.4.2 完整对照表
| 场景 | 推荐原语 | 替代方案 | 原因 |
|---|---|---|---|
| 在窗格执行命令并获取结果 | pane run |
pane send-text + pane wait-output |
一步到位 |
| 向窗格发送命令但不等待 | pane send-text |
— | 非阻塞 |
| 发送特殊按键(Ctrl+C 等) | pane send-keys |
— | send-text 不处理特殊键 |
| 等待窗格出现特定输出 | pane wait-output |
轮询 pane read |
原子操作更可靠 |
| 启动 AI Agent | agent start |
— | 初始化 Agent 生命周期 |
| 向 Agent 发送提示 | agent prompt |
pane send-text |
prompt 理解 Agent 语义 |
| 同步等待 Agent 回复 | agent prompt --wait |
agent prompt + agent wait |
减少 TOCTOU 窗口 |
| 等待 Agent 完成工作 | agent wait --until done |
轮询 agent get |
原子等待 |
| 打断 Agent(Escape) | agent send-keys |
pane send-keys |
语义一致 |
| 读取 Agent 当前输出 | agent read |
pane read |
支持 source 参数 |
一般原则:如果操作对象是 Agent,优先用 Agent 层原语。它们理解 Agent 的生命周期,能在合适的时机做合适的事。
13.5 agent prompt –wait 详解
agent prompt --wait 是自动化中最常用、也最容易出问题的原语之一。它向 Agent 发送提示,并阻塞直到 Agent 回复(回到 idle 状态)。
13.5.1 工作原理
sequenceDiagram
participant Script as 你的脚本
participant H as Herdr
participant A as Agent
Script->>H: agent prompt --wait my-agent "修复 bug"
H->>A: 发送提示文本
A->>A: 处理中(working)
Note over A: Agent 思考、写代码
A->>H: 回复完成(idle)
H->>Script: 返回结果
13.5.2 生命周期观察
如果 Agent 当前不在工作状态(idle),--wait 会先发送提示,然后等待 Agent 进入 working 状态再回到 idle。Herdr 要求这个状态转换在 5 秒内发生,否则触发 agent_prompt_stalled 错误。
正常流程:idle → working → idle ✅
超时情况:idle → (5s 仍在 idle) ❌ agent_prompt_stalled
13.5.3 错误处理
# 可能超时的场景
herdr agent prompt --wait my-agent "分析这个 1000 行的文件" --timeout 120
# 处理 stalled 错误
result=$(herdr agent prompt --wait my-agent "hello" 2>&1)
if echo "$result" | grep -q "agent_prompt_stalled"; then
echo "Agent 未响应,可能已挂起"
# 重试或检查 Agent 状态
herdr agent get my-agent
fiagent_prompt_stalled 常见原因: 1. Agent 进程已崩溃但 Pane 仍存在 2. Agent 正在等待交互式输入(如确认对话框) 3. Agent 模型 API 限流或网络超时 4. Agent 尚未完全启动(需要更长预热时间)
解决方案:先检查 Agent 状态(agent get),必要时重启。
13.5.4 –timeout 参数
# 设置 60 秒超时
herdr agent prompt --wait my-agent "修复 bug" --timeout 60
# 不设置 timeout 时,使用默认值(通常 300 秒)
herdr agent prompt --wait my-agent "hello"超时后命令返回非零退出码,但 Agent 仍在后台运行——它可能只是需要更多时间。
13.6 agent wait –until 详解
agent wait 用于等待 Agent 进入指定状态,不发送任何提示。这在你已经用 agent prompt(不带 –wait)异步发送了任务后特别有用。
13.6.1 支持的状态
| 状态 | 说明 | 典型场景 |
|---|---|---|
idle |
Agent 空闲,等待指令 | 等待 Agent 处理完毕 |
done |
Agent 标记任务完成 | 等待 Agent 明确报告完成 |
blocked |
Agent 被阻塞,需要人类介入 | 等待需要审批的场景 |
13.6.2 使用示例
# 异步发送任务
herdr agent prompt my-helper -- "重构 auth 模块"
# 等待 Agent 完成(最多 10 分钟)
herdr agent wait my-helper --until done --timeout 600
# 等待 Agent 被阻塞(需要审批时)
herdr agent wait my-helper --until blocked --timeout 30013.6.3 多状态等待
你可以同时等待多个状态:
# 等待完成或阻塞(任一满足即返回)
herdr agent wait my-helper --until done --until blocked13.6.4 超时处理
# 超时返回退出码 124
if ! herdr agent wait my-helper --until done --timeout 300; then
echo "Agent 超时未完成"
# 检查当前状态
status=$(herdr agent get my-helper --format json | jq -r '.state')
echo "当前状态: $status"
if [ "$status" = "working" ]; then
echo "Agent 仍在工作,继续等待..."
herdr agent wait my-helper --until done --timeout 300
elif [ "$status" = "blocked" ]; then
echo "Agent 被阻塞,需要介入"
herdr agent read my-helper --source recent
fi
fi组合模式:先 agent prompt(异步)→ agent wait(等状态),比 agent prompt --wait 更灵活。你可以在等待期间做其他事情——读取中间输出、检查进度等。
13.7 输出读取策略
正确读取 Agent(或 Pane)的输出是自动化的关键。Herdr 提供四种读取源(source),各有适用场景。
13.7.1 四种 Source
| Source | 说明 | 包含内容 | 典型用途 |
|---|---|---|---|
visible |
当前屏幕可见内容 | 视口范围内的文本 | 快速检查当前状态 |
recent |
最近的输出 | 滚动缓冲区的最近部分 | 读取 Agent 的回复 |
recent-unwrapped |
最近的输出(不去除换行) | 原始 ANSI 文本 | 调试终端控制序列 |
detection |
Agent 检测用文本 | 去除噪声的纯净文本 | 程序化判断 Agent 状态 |
# 读取可见区域(人眼看到的)
herdr agent read my-helper --source visible
# 读取最近输出(最常用)
herdr agent read my-helper --source recent
# 调试用:原始未处理输出
herdr agent read my-helper --source recent-unwrapped
# 程序判断用
herdr agent read my-helper --source detection13.7.2 –lines 参数
--lines 参数控制读取的行数,行为因 source 不同而略有差异:
# recent + --lines 50:读取最近 50 行
herdr agent read my-helper --source recent --lines 50
# visible + --lines 50:读取可见区域,最多 50 行
herdr agent read my-helper --source visible --lines 50
# 不指定 --lines:使用默认值(recent 默认 80 行,visible 默认视口高度)
herdr agent read my-helper --source recent13.7.3 alternate-screen 限制
许多 TUI 程序(vim、less、Claude Code 的全屏界面)使用 alternate screen。当程序在 alternate screen 中运行时:
alternate-screen 限制: - visible 能看到 alternate screen 的内容 - recent 可能无法捕获 alternate screen 中的交互——因为切换回主屏幕时 alternate screen 内容会被丢弃 - 如果 Agent 使用了 alternate screen,recent 可能只包含切换前的旧内容
建议:对于 Claude Code 等使用全屏界面的 Agent,优先使用 visible source,或让 Agent 以非交互模式运行(减少 alternate screen 的使用)。
13.8 完整编排 Recipes
以下三个实战示例展示了从简单到复杂的多 Agent 编排模式。
13.8.1 Recipe 1:启动 Helper + 分配任务 + 等待 + 读取结果
这是最经典的编排模式:主脚本启动一个 Agent,给它任务,等它完成,读取结果。
#!/bin/bash
set -euo pipefail
# ============================================
# Recipe 1: 单 Agent 任务编排
# 场景:让 helper Agent 修复一个 bug
# ============================================
AGENT_NAME="bug-fixer"
BUG_DESCRIPTION="用户登录时偶发 500 错误,怀疑是 race condition"
echo "→ 启动 Agent..."
herdr agent start \
--pane p2 \
--kind claude \
"$AGENT_NAME"
echo "→ 等待 Agent 就绪..."
# 新启动的 Agent 需要几秒初始化
sleep 3
echo "→ 发送任务..."
herdr agent prompt "$AGENT_NAME" -- "$BUG_DESCRIPTION"
echo "→ 等待完成..."
# 等待 Agent 进入 done 或 idle 状态
herdr agent wait "$AGENT_NAME" --until done --timeout 600
echo "→ 读取结果..."
output=$(herdr agent read "$AGENT_NAME" --source recent --lines 100)
echo "$output"
echo "→ 清理..."
herdr agent send-keys "$AGENT_NAME" -- q # 退出 Agent
echo "✅ 完成"13.8.2 Recipe 2:等待 Agent 请求输入 + 检查 + 交互
Agent 在工作过程中可能需要人类确认(如「是否执行这个命令?」)。这个 Recipe 展示了如何检测并响应这种场景。
#!/bin/bash
set -euo pipefail
# ============================================
# Recipe 2: 响应 Agent 的输入请求
# 场景:Agent 需要确认才能执行危险操作
# ============================================
AGENT_NAME="code-reviewer"
# 发送代码审查任务
herdr agent prompt "$AGENT_NAME" -- "审查 src/auth/ 目录的安全性"
# 主循环:持续监控 Agent 状态
while true; do
# 等待 Agent 进入 blocked 或 done
if herdr agent wait "$AGENT_NAME" \
--until blocked --until done \
--timeout 300; then
# 检查当前状态
status=$(herdr agent get "$AGENT_NAME" --format json | jq -r '.state')
if [ "$status" = "done" ]; then
echo "✅ Agent 已完成"
herdr agent read "$AGENT_NAME" --source recent --lines 80
break
fi
if [ "$status" = "blocked" ]; then
echo "⚠️ Agent 请求输入"
# 读取 Agent 的问题
question=$(herdr agent read "$AGENT_NAME" --source recent --lines 20)
echo "Agent 问: $question"
# 自动批准安全操作
if echo "$question" | grep -qiE "run.*(test|lint|format)"; then
echo "→ 自动批准测试/lint 操作"
herdr agent send-keys "$AGENT_NAME" -- y
elif echo "$question" | grep -qiE "run.*(deploy|delete|drop)"; then
echo "→ 危险操作,拒绝"
herdr agent send-keys "$AGENT_NAME" -- n
else
# 交给人类决定
echo "→ 需要人工确认,查看 Agent 输出:"
herdr agent read "$AGENT_NAME" --source visible
read -p "批准?(y/n) " answer
herdr agent send-keys "$AGENT_NAME" -- "$answer"
fi
fi
else
echo "❌ 超时"
break
fi
done13.8.3 Recipe 3:运行普通进程 + 等待输出(非 Agent)
并非所有自动化都涉及 Agent。有时你只需要在窗格中运行普通命令并等待结果。
#!/bin/bash
set -euo pipefail
# ============================================
# Recipe 3: 运行普通进程并等待输出
# 场景:在指定窗格运行测试,等待结果
# ============================================
PANE_ID="p3"
TEST_PATTERN="PASS|FAIL|Error"
echo "→ 在 $PANE_ID 中运行测试..."
# 方法 1:pane run(阻塞执行,获取 stdout)
echo "--- 方法 1: pane run ---"
result=$(herdr pane run "$PANE_ID" -- "npm test 2>&1")
echo "$result"
# 方法 2:send-text + wait-output(异步模式,适合长时间任务)
echo "--- 方法 2: send-text + wait-output ---"
herdr pane send-text "$PANE_ID" -- "npm test"
herdr pane send-keys "$PANE_ID" -- Enter
# 等待测试结果出现
echo "→ 等待测试完成..."
if herdr pane wait-output "$PANE_ID" \
--pattern "$TEST_PATTERN" \
--timeout 300; then
output=$(herdr pane read "$PANE_ID" --source recent --lines 30)
if echo "$output" | grep -q "PASS"; then
echo "✅ 测试通过"
elif echo "$output" | grep -q "FAIL"; then
echo "❌ 测试失败"
echo "$output"
exit 1
fi
else
echo "❌ 测试超时"
exit 124
fi13.9 自动化脚本最佳实践
13.9.1 1. 始终设置超时
# ❌ 危险:没有超时,可能永远挂起
herdr agent wait my-helper --until done
# ✅ 安全:设置合理超时
herdr agent wait my-helper --until done --timeout 60013.9.2 2. 使用 set -euo pipefail
#!/bin/bash
set -euo pipefail
# 任何命令失败立即退出
# 未定义变量报错
# 管道中任何环节失败都算失败13.9.3 3. 清理策略
# 定义清理函数
cleanup() {
echo "→ 清理资源..."
herdr agent send-keys "$AGENT_NAME" -- q 2>/dev/null || true
# 关闭临时窗格
herdr pane close p3 2>/dev/null || true
}
# 注册退出时清理
trap cleanup EXIT ERR INT TERM13.9.4 4. 结构化输出处理
# 使用 JSON 格式便于解析
status=$(herdr agent get my-helper --format json)
state=$(echo "$status" | jq -r '.state')
kind=$(echo "$status" | jq -r '.kind')
pane=$(echo "$status" | jq -r '.pane_id')
echo "Agent: $AGENT_NAME | State: $state | Kind: $kind | Pane: $pane"13.9.5 5. 日志记录
LOG_FILE="/tmp/herdr-automation-$(date +%Y%m%d-%H%M%S).log"
log() { echo "[$(date '+%H:%M:%S')] $*" | tee -a "$LOG_FILE"; }
log "启动 Agent $AGENT_NAME"
herdr agent start --pane p2 --kind claude "$AGENT_NAME"
log "发送任务"
herdr agent prompt "$AGENT_NAME" -- "$TASK"
herdr agent wait "$AGENT_NAME" --until done --timeout 600
log "Agent 完成"13.9.6 6. 幂等性设计
# 检查 Agent 是否已存在,避免重复创建
if ! herdr agent get "$AGENT_NAME" 2>/dev/null; then
herdr agent start --pane p2 --kind claude "$AGENT_NAME"
sleep 3 # 等待初始化
fi13.9.7 7. 组合使用 Layout 和 Agent
```bash # 先应用布局,再在对应窗格中启动 Agent herdr layout apply layouts/code-review.json herdr agent start –pane p1 –kind claude orchestrator herdr agent start –pane p2 –kind codex reviewer herdr agent start –pane p3 –kind claude fixer sleep 5 # 等待就绪 herdr agent prompt orchestrator – “分析代码库并分配审查任务” herdr agent wait orchestrator –until idle –timeout 120