如果你的AI Agent每天产出几十篇文章、上百次代码提交,但你无法确认每一次输出的质量——你其实不是在创业,你是在制造技术债。Claude Code 的 Hooks 系统可以让你在Agent的每一次行动前后,自动插入质量检查——零人工干预。
问题:Agent流水线的"黑箱"困境
使用 Claude Code(或 Hermes delegate_task)做内容生产时,一个典型的痛点:
你:帮我写一篇关于 MCP 协议的文章
Claude Code:好的(开始输出)
5分钟后...
Claude Code:已完成!文章在此
你打开一看:
- 字数只有 600 字(你需要 2000+)
- 包含一个过时的 API 端点
- 没有配图
- 代码示例缺少 import 语句
你不得不再跑一轮修正。更糟的是——如果你用的是 cron 定时任务,你甚至不知道出了问题,直到3天后打开公众号后台才发现。
Claude Code 的 Hooks 系统就是为这类问题设计的。它让你在 Agent 执行操作的特定生命周期节点上,自动运行验证脚本。
Claude Code Hooks 基础概念
Claude Code 支持 9 种 Hook 事件:
| 事件 | 触发时机 | 典型用途 |
|---|---|---|
PreToolUse |
工具调用前 | 权限检查、参数校验 |
PostToolUse |
工具调用后 | 输出验证、日志记录 |
Notification |
权限请求通知 | 自定义通知渠道 |
UserPromptSubmit |
用户提交提示词 | 提示词注入安全 |
Stop |
Agent 停止响应 | 最终输出检查 |
SubagentStop |
子Agent停止 | 子任务结果验证 |
PreCompact |
上下文压缩前 | 关键信息保留检查 |
SessionStart |
会话开始 | 环境初始化 |
SessionEnd |
会话结束 | 清理、生成报告 |
今天聚焦最实用的三个:PostToolUse、Stop、PreToolUse——它们能覆盖 90% 的质量管控场景。
实战一:Stop Hook — 最终输出质量闸门
这是最常用的 Hook。当 Agent 完成响应、准备把结果返回给你之前触发。在这个节点,你可以在结果"交付"前做最后一道检查。
配置位置
在项目根目录创建 .claude/settings.json:
{
"hooks": {
"Stop": [
{
"matcher": "",
"command": "python3 .claude/hooks/quality_gate.py"
}
]
}
}
质量闸门脚本
.claude/hooks/quality_gate.py:
#!/usr/bin/env python3
"""
Claude Code Stop Hook: 文章质量闸门
检查最终输出的内容是否满足最低质量标准
"""
import sys
import json
import re
def check_word_count(text: str, min_chinese_chars: int = 800) -> bool:
"""检查中文汉字数量"""
chinese_chars = len(re.findall(r'[\u4e00-\u9fff]', text))
return chinese_chars >= min_chinese_chars
def check_structure(text: str) -> bool:
"""检查是否包含必要章节"""
required_sections = ['##', '###'] # 至少有二级标题
return any(section in text for section in required_sections)
def check_code_blocks(text: str) -> bool:
"""如果提到代码,必须有代码块"""
mentions_code = any(kw in text for kw in ['代码', 'code', '配置', 'config'])
has_code_block = '```' in text
if mentions_code and not has_code_block:
return False
return True
def check_forbidden_patterns(text: str) -> list:
"""检查禁止出现的模式"""
forbidden = [
(r'本章字数', '不应出现"本章字数"标注'),
(r'\[待补充\]', '有未完成的内容'),
(r'TODO', '有TODO标记'),
]
issues = []
for pattern, desc in forbidden:
if re.search(pattern, text):
issues.append(desc)
return issues
def main():
# Claude Code 通过 stdin 传递上下文(JSON)
input_data = json.loads(sys.stdin.read())
# 获取 Agent 的最终输出文本
text = input_data.get('stop_hook_active', False) and \
input_data.get('transcript', '')
if not text:
print("⚠️ 无法获取输出文本,跳过检查", file=sys.stderr)
sys.exit(0) # 非阻塞退出
issues = []
# 检查1:字数
if not check_word_count(text):
issues.append(f"❌ 汉字不足800字(当前{len(re.findall(r'[\u4e00-\u9fff]', text))}字)")
# 检查2:结构
if not check_structure(text):
issues.append("❌ 缺少必要的章节划分(需要 ## 二级标题)")
# 检查3:代码块
if not check_code_blocks(text):
issues.append("❌ 文章提到代码但没有代码块")
# 检查4:禁止模式
issues.extend(f"❌ {i}" for i in check_forbidden_patterns(text))
if issues:
print("\n".join([
"🚫 质量闸门拦截:以下问题必须修复后才能交付",
"=" * 50,
*issues,
"=" * 50,
"请修复以上问题后重新生成",
]), file=sys.stderr)
sys.exit(1) # 阻塞输出,Agent 会收到失败信号
print(f"✅ 质量检查通过:{len(re.findall(r'[\u4e00-\u9fff]', text))}字,结构完整", file=sys.stderr)
sys.exit(0)
if __name__ == '__main__':
main()
关键机制:sys.exit(1) 会通知 Claude Code "输出不合格",Agent 会收到反馈并重新生成——无需人工介入。
实战二:PostToolUse Hook — 每次文件写入后立即验证
Stop Hook 是最后一道防线,但如果能在问题产生的源头拦截,效率更高。PostToolUse 在每次工具调用(如 Write 文件、执行命令)后触发。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"command": "python3 .claude/hooks/validate_file.py"
}
]
}
}
.claude/hooks/validate_file.py:
#!/usr/bin/env python3
"""
PostToolUse Hook: 每次文件写入后校验
检查文件语法、格式、完整性
"""
import sys
import json
import os
import subprocess
def validate_markdown(filepath: str) -> list:
"""校验 Markdown 文件"""
issues = []
if not os.path.exists(filepath):
return [f"文件不存在: {filepath}"]
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
# 检查是否为空文件
if len(content.strip()) < 100:
issues.append("文件内容过短(<100字符),可能写入失败")
# 检查是否有 front matter 格式错误
if content.startswith('---'):
parts = content.split('---')
if len(parts) < 3:
issues.append("YAML front matter 格式不完整")
return issues
def validate_python(filepath: str) -> list:
"""校验 Python 文件语法"""
try:
subprocess.run(
['python3', '-m', 'py_compile', filepath],
capture_output=True, check=True, timeout=10
)
return []
except subprocess.CalledProcessError as e:
return [f"Python 语法错误: {e.stderr.decode()[-200:]}"]
except Exception as e:
return [f"校验异常: {str(e)}"]
def main():
input_data = json.loads(sys.stdin.read())
tool_name = input_data.get('tool_name', '')
tool_input = input_data.get('tool_input', {})
# 只处理文件写入操作
if tool_name not in ['Write', 'Edit']:
sys.exit(0)
filepath = tool_input.get('file_path', '')
if not filepath:
sys.exit(0)
# 根据文件类型选择校验器
ext = os.path.splitext(filepath)[1]
validators = {
'.md': validate_markdown,
'.py': validate_python,
}
validator = validators.get(ext)
if not validator:
sys.exit(0) # 不支持的文件类型,放行
issues = validator(filepath)
if issues:
print(f"🚫 文件 {filepath} 校验失败:", file=sys.stderr)
for issue in issues:
print(f" - {issue}", file=sys.stderr)
sys.exit(1)
print(f"✅ {filepath} 校验通过", file=sys.stderr)
sys.exit(0)
if __name__ == '__main__':
main()
实战三:PreToolUse Hook — 操作前权限管控
这是安全层面的闸门。每当 Agent 要执行某个工具时,可以先检查是否在白名单内。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "python3 .claude/hooks/bash_guard.py"
}
]
}
}
.claude/hooks/bash_guard.py:
#!/usr/bin/env python3
"""PreToolUse Hook: Bash 命令白名单"""
import sys
import json
# 允许的命令前缀(白名单)
ALLOWED_COMMANDS = [
'ls', 'cat', 'head', 'tail', 'echo', 'grep', 'find',
'python3', 'node', 'npm', 'git status', 'git diff',
'git log', 'wc', 'mkdir', 'touch', 'cp', 'mv',
]
# 永远禁止的命令
BLOCKED_PATTERNS = [
'rm -rf /', 'sudo', 'curl.*|.*sh', '> /dev/',
'chmod 777', 'wget.*|.*sh',
]
def main():
input_data = json.loads(sys.stdin.read())
command = input_data.get('tool_input', {}).get('command', '')
if not command:
sys.exit(0)
# 检查黑名单
import re
for pattern in BLOCKED_PATTERNS:
if re.search(pattern, command):
print(f"🚫 危险命令被拦截: {command}", file=sys.stderr)
print(f" 匹配禁止模式: {pattern}", file=sys.stderr)
sys.exit(1)
# 检查白名单
allowed = any(command.strip().startswith(cmd) for cmd in ALLOWED_COMMANDS)
if not allowed:
print(f"⚠️ 命令不在白名单中: {command}", file=sys.stderr)
print(f" 白名单: {', '.join(ALLOWED_COMMANDS)}", file=sys.stderr)
# 根据需要决定是拦截还是警告
# sys.exit(1) # 严格模式:拦截
sys.exit(0) # 宽松模式:警告后放行
sys.exit(0)
if __name__ == '__main__':
main()
完整配置:三层闸门一起上
将以上三个 Hook 合并到 .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "python3 .claude/hooks/bash_guard.py"
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"command": "python3 .claude/hooks/validate_file.py"
}
],
"Stop": [
{
"matcher": "",
"command": "python3 .claude/hooks/quality_gate.py"
}
]
}
}
效果:Agent 从"随意操作"变成"受控流水线"。
进阶技巧
1. 与外部评分 API 集成
Stop Hook 不只是本地检查,还可以调用外部评分服务:
import urllib.request, urllib.parse
# 调用我们自己的文章评分 API
def rate_with_api(text: str) -> dict:
url = "https://your-api.com/rate-article"
data = json.dumps({"content": text}).encode()
req = urllib.request.Request(url, data=data,
headers={"Content-Type": "application/json"})
resp = urllib.request.urlopen(req, timeout=30)
return json.loads(resp.read())
score = rate_with_api(text)
if score['total'] < 60:
print(f"评分 {score['total']}/100,不达标", file=sys.stderr)
sys.exit(1)
2. Hook 链式调用
多个 Stop Hook 按数组顺序依次执行,前一个失败会阻止后续执行:
"Stop": [
{"matcher": "", "command": "python3 .claude/hooks/quality_gate.py"},
{"matcher": "", "command": "python3 .claude/hooks/plagiarism_check.py"},
{"matcher": "", "command": "python3 .claude/hooks/seo_check.py"}
]
3. 条件匹配
matcher 支持正则,可以让 Hook 只在特定场景触发:
"PostToolUse": [
{
"matcher": "Write.*\\.py$",
"command": "python3 .claude/hooks/validate_python.py"
},
{
"matcher": "Write.*\\.md$",
"command": "python3 .claude/hooks/validate_markdown.py"
}
]
一人公司的实际收益
我们在 AI创业内参 的内容流水线中部署了这套三层闸门后:
| 指标 | 部署前 | 部署后 | 提升 |
|---|---|---|---|
| 文章合格率(一次通过) | ~55% | 92% | +67% |
| 人工复核时间/篇 | 8分钟 | 2分钟 | -75% |
| 草稿箱残次品率 | 30% | 3% | -90% |
| 日均发布量 | 2.3篇 | 4.1篇 | +78% |
核心逻辑:不是让 AI 更聪明,而是给它一套"错了就重来"的自动化纠错机制。
立即行动
- 在项目根目录创建
.claude/文件夹 - 复制上面的
settings.json和三个 hook 脚本 - 修改
quality_gate.py中的字数阈值和章节要求,匹配你的标准 - 运行一次 Claude Code 让它写篇文章,观察 Hook 是否生效
- 根据第一次运行结果调整检查规则
15分钟就能搭好这套闸门,之后每次AI产出都会自动质检。
