<!-- title: 文件监听造事件驱动自动化Agent -->

# 文件监听造事件驱动自动化Agent

## 概述

本教程教你把 Claude Code 从"你敲一行、它回一句"的交互式工具，改造成一个**被外部事件驱动**的常驻 Agent：文件一保存、日志一出错、定时一到点，它就自动接管任务、调用子代理、跑完闭环。核心组合是 `watchexec`/`entr` 文件监听 + `-p` 头部模式 + `--output-format stream-json` 流式输出 + `Stop`/`PostToolUse` 钩子防跑飞。适合想用 Claude Code 搭建"代码守护进程""日志自愈机器人""保存即审查"流水线的开发者和 DevOps 工程师。

## 前置条件

- 已安装 Claude Code（`claude --version` 能输出版本号），完成一次登录或配置好 `ANTHROPIC_API_KEY`
- Node.js 18+（Claude Code 运行依赖）
- 基本熟悉命令行、能读懂 shell 脚本和 JSON
- 一个用来做靶子的真实代码仓库（Python 或 TypeScript 项目均可）
- 可选但推荐：`jq`（解析 JSON 输出）、`fzf`（交互式选择）

## 核心心智模型：事件驱动 vs 交互式

传统 Claude Code 用法是**交互式**的：打开终端、输入提示、来回迭代。事件驱动自动化则把这个模型翻转过来——**没有人类在键盘前**，取而代之的是一个外部事件源（文件变化、cron、webhook、日志行），事件一发生就拉起一次 `claude -p` 调用。

这套模型有三个关键差异点：

1. **触发是无人的**：事件源决定"何时跑"，不是你决定。
2. **输出是结构化的**：人类不在场看不懂自由文本，所以必须用 `--output-format json` 或 `stream-json`，让下游脚本能解析。
3. **必须有护栏**：无人值守最大的风险是"跑飞"——无限循环、改坏文件、烧光额度。护栏由 hooks 提供。

## 第一步：把 Claude Code 装进文件监听器

事件驱动的"事件源"，最常用的是**文件系统变化**。`watchexec` 是跨平台首选，`entr` 更轻量。

先安装监听器：

```bash
# macOS
brew install watchexec entr jq

# Linux (Debian/Ubuntu)
sudo apt install entr jq
cargo install watchexec-cli   # 或从 release 下载二进制

# 验证
watchexec --version && jq --version
```

最朴素的"保存即审查"可以这样跑起来——在项目根目录执行：

```bash
# 监听所有 .py 文件变化，一变就审查
watchexec -e py -- claude -p "审查 src/ 下最近修改的 Python 文件，指出潜在 bug，输出 JSON：{file, issues:[{line, severity, message}]}"
```

**预期结果**：你随便改一个 `src/foo.py` 并保存，watchexec 检测到变化，立刻拉起一次 `claude -p` 调用，几秒后终端打印出该文件的审查结果。每保存一次就跑一次，全程无需你按回车。

**常见问题**：

- **保存后没反应**：`watchexec` 默认有约 100ms 防抖，若编辑器写入是"先删后建"可能触发不到。加 `--debounce 300` 放宽窗口，或用 `-w src` 显式锁定监听目录。
- **审查的不是刚改的文件**：`claude -p` 默认看不到"哪个文件刚变"。要精确，把文件名通过环境变量或参数传进去——下一步用脚本封装时会解决。

## 第二步：用脚本把事件和 Agent 粘合起来

裸用 `watchexec -- claude -p` 太粗。真正的事件驱动需要**把事件元数据喂给 Agent**。下面这个脚本 `monitor.sh` 是生产可用的最小骨架：

```bash
#!/usr/bin/env bash
# monitor.sh —— 文件变化驱动的自动审查守护进程
set -euo pipefail

WATCH_DIR="${1:-src}"
EVENT_FILE="$2"   # watchexec 会把变化的文件路径写进这个变量

# 关键：用 --bare 跳过 hooks/MCP 自动发现，启动更快，守护进程更稳
# 用 --output-format json 拿结构化结果，用 --add-dir 让 Agent 能读全项目上下文
claude --bare -p \
  --add-dir "$WATCH_DIR" \
  --output-format json \
  "我刚修改了文件：$EVENT_FILE。
请：
1. 读这个文件
2. 找出 bug、坏味道、安全问题
3. 如果有严重问题（severity=critical），用 Edit 工具直接修复
4. 最后用 JSON 输出摘要：{file, fixed:[bool], issues:[...], summary}" \
  | jq '.'   # 格式化输出，方便人眼和下游管道读取
```

让 `watchexec` 把变化文件路径传进来：

```bash
watchexec -w src -e py \
  --shell=none \
  -- ./monitor.sh src "{$WATCHEXEC_COMMON_PATH}"
```

这里 `{$WATCHEXEC_COMMON_PATH}` 是 watchexec 的占位符，会被替换成实际变化的文件相对路径。

**预期结果**：保存 `src/api.py`，终端先打印一段 JSON，其中 `fixed: true` 且列出修复的问题——说明 Agent 不只是审查，还自动改了代码。

**常见问题**：

- **`{$WATCHEXEC_COMMON_PATH}` 没被替换、原样传入**：那是 watchexec 占位符语法，必须配合 `--shell=none` 才生效；若用默认 shell 模式，要改用环境变量 `WATCHEXEC_CHANGED_PATHS` 在脚本内 `$1` 读取。
- **每次保存触发多次**：编辑器可能触发多个事件（临时文件、保存两次）。加 `--debounce 500` 合并 500ms 内的所有事件为一次。

## 第三步：用 stream-json 做实时流水线

`json` 模式是一次性等到底才出结果，长任务（自动修 bug 可能要几十秒）期间终端一片黑。事件驱动场景下你往往想看到**实时进度**，用 `stream-json`：

```bash
claude --bare -p \
  --output-format stream-json \
  --verbose \
  "修复 src/auth.py 里的所有 lint 错误" \
  | jq --unbuffered -r 'select(.type=="tool_use") | "🔧 调用工具: \(.name) → \(.input.file_path // "")"'
```

`stream-json` 会逐行吐出 JSON 事件：`system`（初始化）、`assistant`（模型思考）、`tool_use`（要调工具）、`tool_result`（工具返回）、`result`（最终结果）。用 `jq` 过滤 `tool_use` 事件，就能实时打印"Agent 正在读哪个文件、改哪个文件"，相当于一个简易进度监控。

**预期结果**：终端像日志流一样滚动输出 Agent 的每一步动作，而不是干等几十秒。

## 第四步：装上 hooks 护栏，防止无人值守跑飞

事件驱动 Agent 最大的风险是**失控循环**：Agent 改了文件 → 触发文件监听 → 又拉起一次 Agent → 又改文件……死循环。Claude Code 的 hooks 是确定性护栏，不依赖模型"自觉"。

在 `.claude/settings.json` 配置：

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/guards/no-overwrite-guard.sh"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "ruff format \"$(jq -r '.tool_input.file_path' )\" 2>/dev/null || true"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/guards/stop-queue.sh"
          }
        ]
      }
    ]
  }
}
```

三个钩子各司其职：

- **PreToolUse（拦截护栏）**：在 Write/Edit 执行**前**跑，能让 Agent 决定继续/阻止。脚本读到模型想写的文件，校验是否符合规则。
- **PostToolUse（自动收尾）**：工具执行**后**自动跑——这里每次 Edit 完自动 `ruff format`，保证改完的代码总是格式化的。
- **Stop（防跑飞）**：Agent 想停下时触发。这里是事件驱动的命脉。

护栏脚本 `.claude/guards/no-overwrite-guard.sh`：

```bash
#!/usr/bin/env bash
# 阻止 Agent 覆盖受保护文件（如锁文件、生成产物）
INPUT="$(cat)"
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path')

# 保护模式：禁止动这些
if [[ "$FILE_PATH" =~ (package-lock\.json|\.env|dist/|build/) ]]; then
  echo "禁止修改受保护文件: $FILE_PATH" >&2
  exit 2   # exit code 2 = 阻止该工具调用，把 stderr 反馈给 Agent
fi
exit 0      # exit code 0 = 放行
```

**关于 Stop hook 的硬限制（必须知道）**：Claude Code 规定，如果 Stop hook 连续 8 次让 Agent 继续跑（即不停让模型"再做一轮"），第 9 次会被强制打断。这是为了防止无限循环。设计 Stop hook 时**不要试图用它实现"永远不停"**，而是用来排空一个有限的任务队列。

## 第五步：用 Stop hook + 任务队列实现"自驱动到完成"

真正的事件驱动不是"跑一次就停"，而是"一个事件触发后，Agent 自己把一组相关任务跑完"。这需要把待办任务持久化到文件，用 Stop hook 检查队列。

任务队列设计——`/tmp/cc-tasks.txt`，一行一个任务：

```
审查 src/auth.py 并修复 lint
审查 src/api.py 并修复 lint
跑 pytest 并修复失败的用例
```

Stop hook 脚本 `.claude/guards/stop-queue.sh`：

```bash
#!/usr/bin/env bash
# Agent 想停时，检查是否还有未完成任务
QUEUE="/tmp/cc-tasks.txt"

if [[ ! -s "$QUEUE" ]]; then
  # 队列空，真正结束
  exit 0
fi

# 弹出队首任务，喂回给 Agent 让它继续
NEXT_TASK=$(sed -n '1p' "$QUEUE")
sed -i '1d' "$QUEUE"   # 删掉队首

# 通过 JSON decision 让 Agent 继续，并注入新任务
jq -n --arg t "$NEXT_TASK" '{decision: "block", reason: "队列未空，继续处理下一个任务：\($t)"}'
```

这里 `decision: "block"` 配合 `reason` 是 Stop hook 的约定：告诉 Claude Code"别停，这是原因，继续做"。Agent 读到 reason 后会接着干下一个任务。配合前述 8 次硬上限，单个事件最多驱动 8 轮——足够处理一个小队列，又不会失控。

## 实战案例：代码守护进程 `codewatch`

把前五步组装成一个完整的、可运行的守护进程——**监听 `src/`，文件一变就让子代理审查并修复，跨文件问题丢进队列串行处理**。

目录结构：

```
my-project/
├── src/
│   ├── auth.py
│   └── api.py
├── .claude/
│   ├── settings.json
│   ├── agents/
│   │   └── code-reviewer.md      # 子代理：专门做审查
│   └── guards/
│       ├── no-overwrite-guard.sh
│       └── stop-queue.sh
└── codewatch.sh                   # 守护进程入口
```

子代理定义 `.claude/agents/code-reviewer.md`（事件驱动常配子代理，让主 Agent 专注编排）：

```markdown
---
name: code-reviewer
description: 审查单个 Python 文件，找出 bug 和安全问题，必要时直接修复。当需要审查代码改动时调用。
tools: Read, Edit, Grep
---

你是资深 Python 代码审查员。审查时：
1. 先通读整个文件，理解意图
2. 按 severity(critical/high/medium/low) 标注问题
3. critical 和 high 直接用 Edit 修复
4. 输出 JSON：{file, fixed_issues, remaining_issues, summary}
```

守护进程入口 `codewatch.sh`：

```bash
#!/usr/bin/env bash
# codewatch —— 文件变化驱动的自愈代码守护进程
set -euo pipefail

WATCH_DIR="${1:-src}"
QUEUE="/tmp/cc-tasks-$(basename "$PWD").txt"
: > "$QUEUE"   # 清空队列

echo "👁  守护进程启动，监听 $WATCH_DIR （Ctrl+C 退出）"

# 初始化队列 + 关键：把队列路径告诉 Stop hook
export CC_QUEUE="$QUEUE"

watchexec -w "$WATCH_DIR" -e py --debounce 600 --shell=none -- \
  bash -c '
    FILE="{$WATCHEXEC_COMMON_PATH}"
    # 把任务压入队列（保持单一 Python 审查任务串行，避免并发抢同一文件）
    echo "用 code-reviewer 子代理审查并修复 $FILE" >> "'"$QUEUE"'"
    # 拉起 Agent，它会读到队列里的任务，靠 Stop hook 逐个消化
    claude --bare -p \
      --add-dir "'"$WATCH_DIR"'" \
      --permission-mode acceptEdits \
      "处理 /tmp 队列里的代码审查任务。每个任务委托 code-reviewer 子代理完成。"
  '
```

启动它：

```bash
chmod +x codewatch.sh .claude/guards/*.sh
./codewatch.sh src
```

然后去编辑器里改坏一个文件（比如在 `src/auth.py` 故意留个未关闭的文件句柄、加个 `eval(user_input)`），保存。你会观察到：

1. `watchexec` 检测到 `.py` 变化，压一个任务进队列
2. `claude --bare -p` 启动，读到队列，派 `code-reviewer` 子代理审查
3. 子代理发现 `eval` 漏洞，用 Edit 修复；PostToolUse hook 自动 `ruff format`
4. 主 Agent 想停 → Stop hook 看到队列还有任务 → 弹出下一个 → 继续
5. 队列清空 → Stop hook 返回放行 → Agent 真正退出，等下一次文件变化

**这就是事件驱动的完整闭环**：人类只负责"保存文件"这一个动作，审查、修复、格式化、串行调度全自动化。

```mermaid
flowchart TD
    A[人类保存 src/auth.py] --> B[watchexec 检测文件变化]
    B --> C[任务压入 /tmp 队列]
    C --> D["claude --bare -p 启动<br/>读取队列"]
    D --> E[PreToolUse 护栏: 拦截危险文件]
    E --> F[委托 code-reviewer 子代理]
    F --> G[子代理审查+Edit修复]
    G --> H[PostToolUse: ruff 自动格式化]
    H --> I{Agent 想停}
    I -->|Stop hook 查队列| J{队列还有任务?}
    J -->|是| K[弹出队首 注入继续] --> F
    J -->|否| L[真正退出 等待下次事件]
    K -.8次上限.-> M[强制熔断 防跑飞]

    style A fill:#e1f5e1
    style L fill:#e1f5e1
    style M fill:#ffe1e1
    style E fill:#fff3e0
    style H fill:#fff3e0
```

## 进阶：多事件源与并发隔离

单个文件监听器只是起点。真实场景会接多个事件源：

- **cron 定时事件**：`0 2 * * * /run-audit.sh` 每天凌晨跑全仓库安全扫描，结果写进队列，Agent 醒来消化。
- **webhook 事件**：用 `netcat` 或轻量 HTTP server 收 GitHub PR webhook，落盘成任务文件，触发 `claude -p`。
- **日志监听**：`tail -f app.log | grep --line-buffered ERROR | while read line; do echo "调查日志错误: $line" >> "$QUEUE"; done`——日志一出错，Agent 自动来查。

并发隔离用 **git worktree**：多个事件同时到达时，给每个任务开一个临时 worktree，避免改同一文件打架（这正是"worktree 并行扇出批量跑任务"的思路，事件驱动里把它作为并发后端）。注意 worktree 模式下 hooks 配置和 `.claude/agents` 仍从主仓库读取，护栏一致。

## 调试与可观测性

无人值守最怕"不知道它刚才干了啥"。三个排查手段：

1. **看流式事件**：开发期始终用 `--output-format stream-json | jq`，每个 `tool_use` 都可见。
2. **审计日志**：在 `codewatch.sh` 里把每次 `claude -p` 的输出 tee 到带时间戳的日志：
   ```bash
   claude --bare -p ... | tee -a "logs/$(date +%Y%m%d-%H%M%S).json"
   ```
3. **hook 失败定位**：PreToolUse hook 的 `exit code 2` 才是"阻止并反馈"，其他非零退出码只是"hook 出错但放行"——别搞混，否则护栏形同虚设。

## 延伸阅读

- [Run Claude Code programmatically（官方头部模式文档）](https://code.claude.com/docs/en/headless) —— `-p`、`--bare`、`--output-format` 全量参数。
- [Automate actions with hooks（官方 hooks 指南）](https://code.claude.com/docs/en/hooks-guide) —— 从 PreToolUse 到 Stop 的实战配置。
- [Hooks reference（官方 hooks 参考）](https://code.claude.com/docs/en/hooks) —— 事件 JSON schema、退出码语义。
- [Orchestrate subagents at scale with dynamic workflows](https://code.claude.com/docs/en/workflows) —— 子代理编排进阶。
- [watchexec 官方文档](https://github.com/watchexec/watchexec) —— 占位符、防抖、shell 模式详解。

## 参考来源

1. [Run Claude Code programmatically — Claude Code Docs](https://code.claude.com/docs/en/headless) — 头部/`-p`/`--bare`/JSON 输出格式的权威参数说明。
2. [Hooks reference — Claude Code Docs](https://code.claude.com/docs/en/hooks) — 全部 hook 事件、JSON 输入输出 schema、退出码与 Stop hook 8 次熔断机制。
3. [Event-Driven Agent Automation — Claude Code Guide (cc.bruniaux.com)](https://cc.bruniaux.com/guide/workflows/event-driven-agents/) — 把交互式翻转为事件驱动的完整方法论。
4. [Claude Code in CI/CD and Headless Automation — hidekazu-konishi](https://hidekazu-konichi.com/entry/claude_code_cicd_and_headless_automation.html) — 无人值守管道的工程实现细节。
5. [Claude Code Hooks: Complete Guide with 20+ Examples (2026) — aiorg](https://aiorg.dev/blog/claude-code-hooks) — PreToolUse 拦截、PostToolUse 自动格式化、Stop 队列的即用型示例。

## 训练营适用性评估

- **适用类型**：Claude Code 进阶/DevOps 自动化训练营、AI 工程师流水线训练营。不适合零基础营——学员需先掌握交互式用法和 hooks 基础。
- **前置模块**：建议放在"hooks 入门""头部模式 `-p`"两节课之后，作为把二者组装成系统的综合实战。
- **预计教学时长**：4–5 小时（含动手）。其中监听器+脚本封装 1.5h、hooks 护栏 1.5h、守护进程实战组装与调试 1.5–2h。
- **考核点**：学员能独立搭出一个监听代码目录、文件变化自动审查修复、且不会无限循环的守护进程；能解释 Stop hook 8 次熔断为什么是事件驱动安全性的关键。
- **延伸挑战**：把单文件监听升级为多事件源（cron+日志+webhook），并用 git worktree 做并发隔离——可作为营期毕业项目。

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
