工具调用后Hook实战(after:tool)

工具调用后的自动化处理

一、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 最直接的应用场景。通过在工具调用后检查返回状态和输出内容,可以自动验证执行结果是否符合预期,并在发现问题时及时通知用户。

核心目标:确保每次工具调用的结果都是正确和完整的,对于关键操作(如文件修改、代码执行)进行自动化的正确性校验。

典型的验证逻辑包括:

以下是一个结果验证 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 构建完整的性能事件数据。

耗时监控的核心能力:

以下是一个耗时监控脚本示例:

#!/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 脚本可以自动进行重试或修复,减少用户被打断的频率,提升交互流畅度。

错误自动处理的核心步骤:

自动重试 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 格式审计日志示例:

{ "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 仅负责将原始数据写入队列或文件,由独立的守护进程在后台处理。