用户提示提交Hook实战

用户提示提交的自动化处理

一、user-prompt-submit Hook概述

user-prompt-submit 是 Claude Code Hook 系统中最核心的钩子之一。它在用户向 Claude 提交提示词时触发,允许开发者在提示词发送给模型之前(before)和收到模型回复之后(after)执行自定义脚本,从而实现提示词预处理、安全检查、内容增强、对话管理等功能。

该 Hook 的工作机制分为两个阶段:

核心价值:user-prompt-submit Hook 让开发者能够以非侵入式的方式在 Claude 对话流程中注入自定义逻辑,无需修改 Claude Code 本身即可实现强大的提示工程自动化。

Hook 脚本可以是一个可执行文件(如 .sh、.bat、.py、.js 等),Claude Code 会自动检测项目中的 hook 配置并调用。配置文件通常位于项目根目录的 .claude/hooks.json 中。

// .claude/hooks.json 配置示例 { "hooks": { "user-prompt-submit": { "command": "python scripts/prompt_hook.py", "timeout": 5000 } } }

二、提示内容长度检查Hook(before)

在 before 阶段,可以读取用户输入的提示词长度,判断其是否超出模型的令牌(token)限制。当提示词过长时,可以拆分长提示、向用户发出警告或直接阻断执行,避免因上下文溢出导致生成质量下降。

2.1 读取提示内容与环境变量

Claude Code 在调用 Hook 时会通过环境变量将当前上下文信息传递给脚本。关键的环境变量包括:

2.2 令牌检查实现

以下是一个 Python 实现的令牌长度检查 Hook 示例,当用户提示词超出限制时给出警告并允许用户确认是否继续:

#!/usr/bin/env python3 # scripts/token_check_hook.py import os import sys import base64 import json import math # 读取环境变量 hook_phase = os.environ.get("CLAUDE_HOOK_PHASE", "") if hook_phase != "before": sys.exit(0) # 解码提示内容 encoded_prompt = os.environ.get("CLAUDE_PROMPT", "") try: prompt_text = base64.b64decode(encoded_prompt).decode("utf-8") except Exception: sys.exit(0) # 估算令牌数(中文约1.5字符/令牌,英文约4字符/令牌) char_count = len(prompt_text) # 简单估算:混合内容平均约2.5字符/令牌 estimated_tokens = math.ceil(char_count / 2.5) # 设置阈值 WARN_THRESHOLD = 60000 # 超过6万令牌时警告 BLOCK_THRESHOLD = 100000 # 超过10万令牌时阻断 if estimated_tokens > BLOCK_THRESHOLD: print(f"提示词过长(约{estimated_tokens}令牌),已超过最大限制。") print("请缩短提示词后重试。") sys.exit(1) # 非零退出码表示阻断 if estimated_tokens > WARN_THRESHOLD: print(f"提示词较长(约{estimated_tokens}令牌),接近上下文限制。") print("建议精简提示词以获得更好的生成效果。") # 退出码0表示允许继续,但输出会显示给用户 # 输出标准JSON格式的元数据供Claude Code参考 result = { "char_count": char_count, "estimated_tokens": estimated_tokens, "status": "ok" if estimated_tokens <= WARN_THRESHOLD else "warning" } print(json.dumps(result, ensure_ascii=False)) sys.exit(0)
重要:Hook 脚本的退出码(exit code)决定了是否阻断提示提交。退出码 0 表示允许继续,非零退出码会阻断提示词发送到模型。before 阶段脚本的输出将显示在 Claude Code 的提示确认界面中。

2.3 配置与测试

将 Hook 配置到项目中后,当提交超长提示词时,Claude Code 会先执行令牌检查脚本,在确认界面显示警告信息。用户可以根据提示决定继续提交或修改提示词。

最佳实践:令牌检查阈值应根据实际使用的模型上下文窗口设置。Claude 3.5 Sonnet 的上下文窗口为 200K 令牌,但为了保证生成质量,建议在 60K-80K 令牌时提醒用户。

三、关键词过滤和安全检查Hook(before)

在 before 阶段实施关键词过滤和安全检查,可以有效防止用户提交包含敏感内容或潜在危险指令的提示词。这不仅是安全合规的需要,也是保护系统稳定性的重要措施。

3.1 敏感关键词过滤

通过维护一个敏感词列表,在用户提交提示时进行扫描匹配。匹配到敏感词时的处理策略包括:替换为安全内容、提示用户修改、或直接阻断执行。

#!/usr/bin/env python3 # scripts/safety_filter_hook.py import os import sys import base64 import re hook_phase = os.environ.get("CLAUDE_HOOK_PHASE", "") if hook_phase != "before": sys.exit(0) encoded_prompt = os.environ.get("CLAUDE_PROMPT", "") try: prompt_text = base64.b64decode(encoded_prompt).decode("utf-8") except Exception: sys.exit(0) # 敏感关键词列表(示例) sensitive_keywords = [ r"(?i)执行系统(命令|指令)", r"(?i)删除所有文件", r"(?i)格式化硬盘", r"(?i)关闭安全(措施|检查)", r"(?i)绕过(权限|限制|认证)", r"(?i)获取他人(密码|隐私|数据)", ] # 安全替换规则 replace_rules = { r"(?i)使用真实数据": "[使用测试数据]", } # 检查敏感词 found_sensitive = [] for pattern in sensitive_keywords: matches = re.findall(pattern, prompt_text) if matches: found_sensitive.append(pattern) if found_sensitive: print("检测到提示词中包含以下敏感内容:") for s in found_sensitive: print(f" - {s}") print("请移除相关描述后重新提交。") sys.exit(1) # 阻断 # 执行安全替换 modified_prompt = prompt_text for pattern, replacement in replace_rules.items(): modified_prompt = re.sub(pattern, replacement, modified_prompt) # 如果内容被替换,需要通知Claude Code新的提示内容 if modified_prompt != prompt_text: encoded_modified = base64.b64encode( modified_prompt.encode("utf-8") ).decode("utf-8") print(f"CLAUDE_PROMPT={encoded_modified}") print("已自动替换敏感内容,请确认后继续。") sys.exit(0)

3.2 安全审计日志

所有安全检查事件都应记录到日志文件中,以便事后审计和追溯。日志应包含时间戳、用户标识、触发规则和处理结果。

日志示例格式:每次安全过滤触发时,记录 audit.log 条目: [2026-05-08 10:00:00] 用户: user1 | 规则: 删除所有文件 | 处理: 阻断 | 会话: conv_abc123
# 日志记录辅助函数示例 import logging from datetime import datetime def setup_audit_logger(log_path="audit.log"): logger = logging.getLogger("prompt_audit") handler = logging.FileHandler(log_path) formatter = logging.Formatter( "[%(asctime)s] 用户: %(user)s | 规则: %(rule)s | 处理: %(action)s | 会话: %(conv)s" ) handler.setFormatter(formatter) logger.addHandler(handler) logger.setLevel(logging.INFO) return logger # 使用 audit_logger = setup_audit_logger() audit_logger.info("", extra={ "user": os.environ.get("USER", "unknown"), "rule": "sensitive_keyword_match", "action": "blocked", "conv": os.environ.get("CLAUDE_CONVERSATION_ID", "unknown") })

3.3 处理策略对比

策略 适用场景 退出码 用户体验
提示修改(replace) 非恶意但需规范化的内容 0 自动替换后提示用户确认
警告放行(warn) 轻微违规但可自行判断 0 显示警告但不阻止
直接阻断(block) 高危操作或敏感内容 1 显示原因并阻止提交

四、提示词增强Hook(before)

提示词增强是最强大的应用场景之一。通过在 before 阶段自动向用户提示词中添加项目级上下文、编码规范、格式要求等增强信息,可以显著提升 Claude 的回复质量和一致性。

4.1 自动附加项目上下文

根据项目类型和当前工作内容,自动向提示词中插入项目相关的上下文信息,帮助 Claude 更好地理解项目的技术栈、架构和编码规范。

#!/usr/bin/env python3 # scripts/prompt_enhancer.py import os import sys import base64 import json hook_phase = os.environ.get("CLAUDE_HOOK_PHASE", "") if hook_phase != "before": sys.exit(0) encoded_prompt = os.environ.get("CLAUDE_PROMPT", "") try: prompt_text = base64.b64decode(encoded_prompt).decode("utf-8") except Exception: sys.exit(0) # 项目增强配置 enhancements = { "python": { "header": "【项目规范】本项目使用 Python 3.11+,遵循 PEP 8 编码规范。" "类型注解是必须的。使用 pytest 进行测试。", "keywords": ["python", "django", "flask", "fastapi", "pip"] }, "javascript": { "header": "【项目规范】本项目使用 TypeScript + React 18。" "使用 ESLint + Prettier 规范代码风格。", "keywords": ["javascript", "typescript", "react", "vue", "node"] }, "default": { "header": "【通用规范】请提供清晰、完整、可维护的代码解决方案。" "包含必要的注释和错误处理。" } } # 检测项目类型(简化示例) project_type = "default" for ptype, config in enhancements.items(): if ptype == "default": continue for kw in config["keywords"]: if kw in prompt_text.lower(): project_type = ptype break # 获取当前工作目录中的配置文件信息 config_files = { "pyproject.toml": "python", "package.json": "javascript", "Cargo.toml": "rust", "go.mod": "go" } for cfg_file, ptype in config_files.items(): if os.path.exists(cfg_file): project_type = ptype break # 构建增强后的提示词 enhanced_prompt = enhancements[project_type]["header"] + "\n\n" + prompt_text # 编码并输出 encoded_enhanced = base64.b64encode( enhanced_prompt.encode("utf-8") ).decode("utf-8") # 告知Claude Code使用修改后的提示词 print(f"CLAUDE_PROMPT={encoded_enhanced}") print(f"已自动附加 {project_type} 项目规范上下文") sys.exit(0)

4.2 增强策略配置化

将增强规则抽取为独立的 JSON 配置文件,避免硬编码。这样不同项目可以共享相同的 Hook 脚本,只需更换配置文件即可。

{ "version": "1.0", "rules": [ { "name": "中文学术增强", "match_type": "always", "append_header": "请使用中文回答,术语首次出现时标注英文原文。" "引用来源需注明出处。" }, { "name": "代码审查模式", "match_type": "keyword", "keywords": ["review", "code review", "审查代码", "CR"], "append_header": "请从以下几个方面进行代码审查:" "1. 安全漏洞和潜在风险" "2. 性能瓶颈和优化建议" "3. 代码风格和可维护性" "4. 测试覆盖率和边界情况" "请给出具体的代码行号和修改建议。" }, { "name": "调试模式", "match_type": "keyword", "keywords": ["debug", "调试", "bug", "error", "报错"], "append_header": "请按以下步骤分析问题:" "1. 复现问题步骤" "2. 分析错误信息和堆栈" "3. 定位根因" "4. 提供修复方案" "5. 添加预防措施" } ] }
提示:提示词增强应保持克制。过度增强会消耗宝贵的上下文窗口,反而降低模型对用户核心问题的关注度。建议每次增强附加的内容控制在 200 令牌以内。

4.3 多规则组合策略

实际应用中,同一个提示词可能同时匹配多个增强规则。需要设计合理的优先级和组合策略:

实战经验:为每种命令类型(如 debug、review、implement、refactor)编写专门的增强提示词。通过提示词中出现的动词自动判断用户意图类型,匹配最合适的增强策略。这样可以实现零配置的智能提示增强。

五、多轮对话管理Hook(after)

在 after 阶段,可以在模型回复返回给用户后执行对话管理逻辑。这对于维护对话历史、管理上下文窗口、记录会话日志等场景非常有用。

5.1 对话历史记录

自动记录每一轮对话的提示词和回复内容,保存为结构化的日志文件,便于后续查阅、分析和调试。

#!/usr/bin/env python3 # scripts/conversation_logger.py import os import sys import base64 import json from datetime import datetime from pathlib import Path hook_phase = os.environ.get("CLAUDE_HOOK_PHASE", "") if hook_phase != "after": sys.exit(0) # 读取环境变量 conv_id = os.environ.get("CLAUDE_CONVERSATION_ID", "unknown") encoded_prompt = os.environ.get("CLAUDE_PROMPT", "") try: prompt_text = base64.b64decode(encoded_prompt).decode("utf-8") except Exception: prompt_text = "[解码失败]" # 从stdin读取模型回复(after阶段可用) response_text = "" try: response_length = int(os.environ.get("CLAUDE_RESPONSE_BYTES", "0")) if response_length > 0: response_text = sys.stdin.read(response_length) except Exception: pass # 构建日志记录 log_entry = { "timestamp": datetime.now().isoformat(), "conversation_id": conv_id, "prompt_preview": prompt_text[:200] + "..." if len(prompt_text) > 200 else prompt_text, "prompt_length": len(prompt_text), "response_length": len(response_text), } # 保存到对话日志 log_dir = Path(".claude/conversations") log_dir.mkdir(parents=True, exist_ok=True) log_file = log_dir / f"{conv_id}.jsonl" with open(log_file, "a", encoding="utf-8") as f: f.write(json.dumps(log_entry, ensure_ascii=False) + "\n") print(f"对话记录已保存: {log_file}") sys.exit(0)

5.2 上下文窗口管理

多轮对话中,随着对话轮次增加,上下文窗口可能被填满。可以在 after 阶段检查当前对话的令牌使用量,并在接近上限时发出警告或自动触发摘要压缩。

# 上下文窗口管理逻辑(after阶段) import math def check_context_usage(prompt_text, response_text, conv_id): total_chars = len(prompt_text) + len(response_text) estimated_tokens = total_chars / 2.5 # 读取已积累的上下文统计 quota_file = f".claude/quota/{conv_id}.json" quota_data = {"total_tokens": 0, "turn_count": 0} if os.path.exists(quota_file): with open(quota_file, "r") as f: quota_data = json.load(f) # 更新统计 quota_data["total_tokens"] += estimated_tokens quota_data["turn_count"] += 1 # 检查阈值 MAX_TOKENS = 200000 # 模型最大上下文 WARN_AT = 160000 # 警告阈值 os.makedirs(os.path.dirname(quota_file), exist_ok=True) with open(quota_file, "w") as f: json.dump(quota_data, f) if quota_data["total_tokens"] > WARN_AT: remaining = MAX_TOKENS - quota_data["total_tokens"] print(f"对话已累积约 {int(quota_data['total_tokens'])} 令牌," f"剩余约 {int(remaining)} 令牌。建议开始新对话。") if remaining < 10000: print("严重警告:上下文即将耗尽,请立即创建新对话!") else: print(f"当前对话第 {quota_data['turn_count']} 轮," f"已使用约 {int(quota_data['total_tokens'])} / {MAX_TOKENS} 令牌")

5.3 自动清理过期对话

在每次 after Hook 执行时,可以顺便检查并清理过期的对话记录,避免历史日志无限增长占用磁盘空间。

# 过期对话清理逻辑 import time from pathlib import Path CLEANUP_INTERVAL = 86400 # 每天执行一次清理 RETENTION_DAYS = 30 # 保留30天的对话记录 # 使用标记文件控制清理频率 last_cleanup_file = Path(".claude/last_cleanup") last_cleanup = 0 if last_cleanup_file.exists(): try: last_cleanup = int(last_cleanup_file.read_text().strip()) except ValueError: pass now = time.time() if now - last_cleanup > CLEANUP_INTERVAL: cutoff = now - (RETENTION_DAYS * 86400) conv_dir = Path(".claude/conversations") if conv_dir.exists(): cleaned = 0 for f in conv_dir.iterdir(): if f.is_file() and f.stat().st_mtime < cutoff: f.unlink() cleaned += 1 last_cleanup_file.write_text(str(int(now))) if cleaned > 0: print(f"已清理 {cleaned} 个过期对话记录(超过 {RETENTION_DAYS} 天)")

5.4 对话统计与洞察

利用积累的对话日志,可以分析使用模式,生成有价值的统计信息:

指标 描述 用途
平均提示词长度 每轮对话用户输入的平均字符数 了解用户使用习惯,优化提示工程
平均对话轮次 每次会话的平均交互次数 评估任务复杂度,改进上下文管理
高频触发规则 哪些安全或增强规则被频繁触发 优化规则配置,减少误报
阻断率 提示词被阻断的比例 评估安全策略的严格程度
日志管理建议:对话日志可能包含敏感信息,建议:1) 对日志文件设置严格的读写权限;2) 不要在版本控制中提交日志目录;3) 定期轮转和归档日志;4) 考虑对提示词内容进行脱敏处理后再记录。

核心要点总结:

1. user-prompt-submit Hook 在用户提交提示词时触发,分为 before(预处理)和 after(后处理)两个阶段。

2. before 阶段的退出码决定是否阻断提示词提交:0 允许继续,非零阻断。

3. before 阶段可以通过输出 CLAUDE_PROMPT=... 来修改即将发送给模型的提示词内容。

4. 令牌检查、关键词过滤、提示词增强是 before 阶段的三大典型应用。

5. after 阶段更适合对话日志记录、上下文窗口管理和统计分析。

6. Hook 脚本支持任何可执行语言(Python、Bash、Node.js 等),通过环境变量与 Claude Code 交互。

7. 退出码是控制流程的核心机制,合理利用可以实现精细化的提示词处理流水线。