一、after:tool Hook概述
after:tool Hook 是 Claude Code Hooks 系统中的一种重要扩展点,它在每个工具调用完成后被触发执行。与 before:tool Hook 不同,after:tool Hook 可以获取工具调用的执行结果和状态码,从而实现对工具调用行为的后续处理与监控。
核心特征:在工具调用完成后自动触发 | 可获取执行结果和状态($STATUS)| 不能阻断工具调用流程但可以做丰富的后续处理 | 支持按工具名称做差异化配置
after:tool Hook 的典型触发流程如下:
# 用户发起请求
# → Claude 决定调用工具
# → 执行 before:tool Hook(可阻断)
# → 执行工具调用
# → 执行 after:tool Hook(本次关注点)
# → 返回结果给 Claude
# → 生成最终回复
注意:与 before:tool 不同,after:tool Hook 不能阻断工具调用。它主要用于对已完成的调用做分析、记录、告警等后续处理工作。如果需要在调用前做校验或阻断,应使用 before:tool Hook。
after:tool Hook 在 settings.json 中的基本配置格式如下:
{
"hooks": {
"after:tool": {
"glob": "src/**",
"run": "node scripts/after-tool-handler.js",
"timeout": 30000
}
}
}
通过 Hook 上下文环境变量,脚本可以获取到丰富的调用信息:
$STATUS
工具调用的退出状态码。0 表示成功,非0 表示失败。常用于判断调用结果是否正常。
$TOOL_NAME
被调用的工具名称,可用于按工具做差异化处理。如 Read、Write、Edit、Bash 等。
$TOOL_INPUT
工具调用的输入参数,JSON 格式。可用于记录调用详情或分析调用模式。
CLAUDE_HOOK_*
其他 Hook 环境变量,包括项目路径、配置信息等。
下面将依次介绍 after:tool Hook 的五种主要实战场景:调用结果验证、执行耗时监控、错误调用自动处理、调用审计日志以及不同工具的差异化后置处理。
二、调用结果验证Hook
调用结果验证是 after:tool 最直接的应用场景。通过在工具调用后检查返回状态和输出内容,可以自动验证执行结果是否符合预期,并在发现问题时及时通知用户。
核心目标:确保每次工具调用的结果都是正确和完整的,对于关键操作(如文件修改、代码执行)进行自动化的正确性校验。
典型的验证逻辑包括:
- 状态码检查:检查 $STATUS 是否为 0。非零状态码意味着调用失败,需要记录失败原因。
- 文件一致性检测:对于 Edit 或 Write 操作,在校验脚本中再次读取目标文件,确认修改内容与预期一致。
- 差异报告输出:当检测到不一致时,生成详细的差异对比报告,通过 stderr 输出通知用户。
- 已知问题自动修复:对于常见的问题模式(如文件编码错误、缺少换行符),Hook 脚本自动执行修正逻辑。
以下是一个结果验证 Hook 脚本示例:
#!/usr/bin/env bash
# after-tool-validate.sh - 调用结果验证
if [ "$STATUS" -ne 0 ]; then
echo "[HOOK] 工具调用失败:$TOOL_NAME 退出码 $STATUS" >&2
echo "[HOOK] 输入参数:$TOOL_INPUT" >&2
# 判断是否已知的可自动修复问题
case "$TOOL_NAME" in
"Edit")
# 常见原因:文件不存在或路径错误
if [[ "$TOOL_INPUT" == *"file_path"* ]]; then
echo "[HOOK] 建议检查 file_path 是否指向正确的文件路径" >&2
fi
;;
"Bash")
echo "[HOOK] 命令执行失败,请检查命令语法和依赖环境" >&2
;;
esac
fi
最佳实践:结果验证的日志统一输出到 stderr(>&2),这样不会干扰工具调用的标准输出内容,同时用户能在终端看到验证信息。
三、执行耗时监控Hook
工具调用的执行效率直接影响用户的交互体验。通过 after:tool Hook 可以记录每次调用的耗时,检测超过阈值的慢调用,并生成性能趋势报告,帮助定位性能瓶颈。
性能数据采集方式:在 Hook 脚本中通过 date 命令记录开始和结束时间差,结合 $TOOL_NAME 和 $STATUS 构建完整的性能事件数据。
耗时监控的核心能力:
- 精确计时:记录每个工具调用的开始时间和结束时间,计算实际执行耗时(精确到毫秒级别)。
- 慢调用告警:为不同工具设置不同的耗时阈值(如 Bash 超 30 秒、Write 超 5 秒),超过阈值时输出告警信息。
- 性能趋势分析:将耗时数据追加到 CSV 文件中,定期分析趋势,发现逐渐变慢的调用模式。
- 优化建议:针对频繁出现的高耗时调用,自动给出优化建议。例如大文件读写建议拆分、复杂 Bash 命令建议简化。
以下是一个耗时监控脚本示例:
#!/usr/bin/env bash
# after-tool-monitor.sh - 执行耗时监控
declare -A THRESHOLDS
THRESHOLDS["Bash"]=30000
THRESHOLDS["Write"]=5000
THRESHOLDS["Edit"]=5000
THRESHOLDS["Read"]=3000
THRESHOLDS["Glob"]=3000
THRESHOLDS["Grep"]=5000
# 计算耗时(需要外部传入开始时间)
local now=$(date +%s%3N)
local elapsed=$(( now - HOOK_START_TIME ))
local threshold=${THRESHOLDS["$TOOL_NAME"]:-10000}
if [ "$elapsed" -gt "$threshold" ]; then
echo "[HOOK] 慢调用告警:$TOOL_NAME 耗时 ${elapsed}ms(阈值 ${threshold}ms)" >&2
if [ "$TOOL_NAME" = "Read" ] && [ "$elapsed" -gt 10000 ]; then
echo "[HOOK] 建议:大文件可分段读取(使用 offset + limit 参数)" >&2
fi
fi
# 记录性能数据到 CSV
echo "$(date +%Y-%m-%dT%H:%M:%S),$TOOL_NAME,$elapsed,$STATUS" >> .claude/perf.csv
注意:Hook 脚本本身的执行时间也会被计入工具调用的总时间里。因此 Hook 脚本应保持轻量,避免执行过重的操作(如网络请求、大文件扫描等),以免影响用户体验。
通过长期积累的性能数据,可以生成如下趋势报告:
| 时间段 |
Bash 平均耗时 |
Read 平均耗时 |
Write 平均耗时 |
慢调用占比 |
| 最近 1 小时 |
1.2s |
0.3s |
0.8s |
2.1% |
| 最近 24 小时 |
2.5s |
0.6s |
1.1s |
5.3% |
| 最近 7 天 |
3.8s |
0.9s |
1.5s |
8.7% |
四、错误调用自动处理Hook
在实际使用中,工具调用可能因为各种原因失败——文件路径错误、权限不足、网络超时、命令语法错误等。通过 after:tool Hook 可以实现智能的错误处理机制,根据错误类型自动执行修复策略。
设计理念:不是所有的工具调用失败都需要人工干预。对于已知的、可预测的错误模式,Hook 脚本可以自动进行重试或修复,减少用户被打断的频率,提升交互流畅度。
错误自动处理的核心步骤:
- 错误原因分析:解析 $TOOL_INPUT 和 $STATUS,结合工具名称判断失败的根本原因。例如 Bash 工具可能因命令不存在(127)、权限拒绝(126)、超时(143)等原因失败。
- 自动重试策略:对于可重试的错误(如网络超时、临时文件锁定),自动重新发起调用。重试次数应有限制(通常最多 3 次),避免无限循环。
- 重试失败通知:当重试达到上限仍失败时,通过 stderr 输出详细的错误摘要,包括重试次数、每次尝试的结果和最终失败原因。
- 错误日志记录:将所有失败记录持久化到日志文件,包含时间戳、工具名称、输入参数、错误码和失败原因,供后续分析。
自动重试 Hook 脚本示例:
#!/usr/bin/env bash
# after-tool-retry.sh - 错误自动重试
local MAX_RETRIES=3
local RETRY_FILE=".claude/retry-counts/$TOOL_NAME"
local retry_count=0
[ -f "$RETRY_FILE" ] && retry_count=$(cat "$RETRY_FILE")
if [ "$STATUS" -ne 0 ] && [ "$retry_count" -lt "$MAX_RETRIES" ]; then
local new_count=$(( retry_count + 1 ))
echo "$new_count" > "$RETRY_FILE"
echo "[HOOK] $TOOL_NAME 调用失败(第 ${new_count} 次),正在自动重试..." >&2
# 根据退出码分析失败原因并尝试修复
case "$STATUS" in
124|143)
echo "[HOOK] 超时错误,增加超时时间后重试" >&2
;;
127)
echo "[HOOK] 命令不存在,检查 PATH 环境变量" >&2
;;
esac
# 记录错误日志
echo "$(date +%Y-%m-%dT%H:%M:%S),RETRY,$TOOL_NAME,$STATUS,$new_count" >> .claude/error.log
elif [ "$STATUS" -ne 0 ]; then
echo "[HOOK] $TOOL_NAME 重试 $MAX_RETRIES 次均失败,请检查问题" >&2
rm -f "$RETRY_FILE"
else
# 调用成功,清理重试计数
rm -f "$RETRY_FILE"
fi
重要:自动重试必须设置合理的最大次数限制和退避策略(如每次重试间隔递增),避免因重复调用造成资源浪费或数据不一致。建议在重试间隔中加入短暂延迟(如 sleep 1),给系统恢复时间。
五、调用审计日志Hook
在开发和生产环境中,完整的调用审计日志对于问题排查、安全审计和用量分析至关重要。after:tool Hook 是实现审计日志系统的最佳位置——它能够在每次工具调用完成后捕获完整的调用信息。
审计日志价值:完整的审计日志可以帮助开发者还原现场、追踪问题根因、分析使用模式、优化 Hook 配置,以及满足合规性要求。
审计日志包含的关键信息字段:
| 字段 |
说明 |
来源 |
| timestamp |
调用完成的时间戳(ISO 8601) |
日期命令 |
| tool_name |
工具名称(如 Bash, Read, Edit) |
$TOOL_NAME |
| input_args |
调用参数的 JSON 摘要 |
$TOOL_INPUT |
| status |
退出码(0/非0) |
$STATUS |
| elapsed_ms |
执行耗时(毫秒) |
脚本计算 |
| session_id |
当前会话标识 |
环境变量 |
审计日志可以使用多种存储格式:
- JSON Lines 格式:每行一个 JSON 对象,便于程序化解析和结构化查询。适合后续导入日志分析系统(如 Elasticsearch)。
- CSV 格式:表格化存储,便于用 Excel 或命令行工具(如 cut、awk)快速分析。适合简单的用量统计。
- SQLite 数据库:功能最强大的存储方式,支持复杂查询、聚合统计和关联分析。适合长期运行的审计需求。
JSON 格式审计日志示例:
{
"timestamp": "2026-05-08T10:04:48+08:00",
"tool_name": "Edit",
"status": 0,
"elapsed_ms": 423,
"session_id": "sess_abc123",
"input": {
"file_path": "src/main.js",
"old_string": "
",
"new_string": ""
}
}
{
"timestamp": "2026-05-08T10:05:12+08:00",
"tool_name": "Bash",
"status": 0,
"elapsed_ms": 2150,
"session_id": "sess_abc123",
"input": {
"command": "npm run build"
}
}
JSON 格式的审计日志记录脚本:
#!/usr/bin/env bash
# after-tool-audit.sh - 审计日志记录
local now=$(date +%Y-%m-%dT%H:%M:%S%:z)
local audit_file=".claude/audit/$(date +%Y-%m).jsonl"
mkdir -p "$(dirname "$audit_file")"
# 构建 JSON 记录(需要 jq 或手动构造)
cat >> "$audit_file" << AUDIT_EOF
{
"timestamp": "$now",
"tool_name": "$TOOL_NAME",
"status": $STATUS,
"session_id": "${CLAUDE_SESSION_ID:-unknown}",
"project": "${CLAUDE_PROJECT:-unknown}"
}
AUDIT_EOF
# 查询统计示例:当日各工具调用次数
local today=$(date +%Y-%m-%d)
local count_today=$(grep -c "$today" "$audit_file" 2>/dev/null || echo 0)
if [ $((count_today % 50)) -eq 0 ] && [ "$count_today" -gt 0 ]; then
echo "[HOOK] 本日工具调用已达 ${count_today} 次" >&2
fi
实践建议:审计日志文件应定期轮转和压缩,避免单个文件过大。可以按月份或按文件大小(如 100MB)进行分割,配合日志清理策略保留最近 90 天的数据。同时注意审计日志可能包含敏感信息(如文件路径、命令参数),应妥善保管。
六、综合实战:完整的 after:tool 配置
以下是一个集成了上述所有功能的完整 after:tool 配置方案,展示如何通过单个 Hook 脚本实现结果验证、耗时监控、错误处理和审计日志的一体化能力。
完整的 settings.json 配置:
{
"hooks": {
"after:tool": {
"run": "bash .claude/hooks/after-tool.sh",
"timeout": 15000
}
}
}
一体化的 Hook 脚本 .claude/hooks/after-tool.sh:
#!/usr/bin/env bash
# after-tool.sh - 一体化后置处理
set -euo pipefail
local log_dir=".claude/hooks/logs"
mkdir -p "$log_dir"
local now=$(date +%Y-%m-%dT%H:%M:%S%:z)
local start_time=${HOOK_START_TIME:-0}
local end_time=$(date +%s%3N)
local elapsed=$(( end_time - start_time ))
# 1. 结果验证
if [ "$STATUS" -ne 0 ]; then
echo "[HOOK] 调用失败:$TOOL_NAME(状态码 $STATUS,耗时 ${elapsed}ms)" >&2
fi
# 2. 耗时监控
echo "$now,$TOOL_NAME,$elapsed,$STATUS" >> "$log_dir/perf.csv"
if [ "$elapsed" -gt 10000 ]; then
echo "[HOOK] 慢调用:$TOOL_NAME 耗时 ${elapsed}ms" >&2
fi
# 3. 审计日志(JSON Lines)
cat >> "$log_dir/audit.jsonl" << EOF
{"ts":"$now","tool":"$TOOL_NAME","status":$STATUS,"elapsed":$elapsed}
EOF
# 4. 错误处理
if [ "$STATUS" -ne 0 ]; then
echo "$now,$TOOL_NAME,$STATUS" >> "$log_dir/errors.csv"
fi
最佳实践:将 Hook 脚本保持在轻量快速的范围内(建议 200ms 以内完成),避免因 Hook 处理过慢影响整体体验。对于需要大量计算的分析任务,应设计为异步处理模式——Hook 仅负责将原始数据写入队列或文件,由独立的守护进程在后台处理。