Loading...
    • 开发者指南
    • API 参考
    • MCP
    • 资源
    • 更新日志
    Search...
    ⌘K
    入门
    Claude 简介快速开始
    模型与定价
    模型概览选择模型Claude 4.6 新特性迁移指南模型弃用定价
    使用 Claude 构建
    功能概览使用 Messages API处理停止原因提示词最佳实践
    上下文管理
    上下文窗口压缩上下文编辑
    能力
    提示缓存扩展思考自适应思考推理力度流式消息批量处理引用多语言支持Token 计数嵌入视觉PDF 支持Files API搜索结果结构化输出
    工具
    概览如何实现工具使用细粒度工具流式传输Bash 工具代码执行工具程序化工具调用计算机使用工具文本编辑器工具网页抓取工具网页搜索工具记忆工具工具搜索工具
    Agent Skills
    概览快速开始最佳实践企业级 Skills通过 API 使用 Skills
    Agent SDK
    概览快速开始TypeScript SDKTypeScript V2(预览版)Python SDK迁移指南
    流式输入实时流式响应处理停止原因处理权限用户审批与输入使用钩子控制执行会话管理文件检查点SDK 中的结构化输出托管 Agent SDK安全部署 AI 智能体修改系统提示词SDK 中的 MCP自定义工具SDK 中的子智能体SDK 中的斜杠命令SDK 中的 Agent Skills跟踪成本与用量待办事项列表SDK 中的插件
    API 中的 MCP
    MCP 连接器远程 MCP 服务器
    第三方平台上的 Claude
    Amazon BedrockMicrosoft FoundryVertex AI
    提示工程
    概览提示词生成器使用提示词模板提示词优化器清晰直接使用示例(多样本提示)让 Claude 思考(思维链)使用 XML 标签赋予 Claude 角色(系统提示词)链式复杂提示长上下文技巧扩展思考技巧
    测试与评估
    定义成功标准开发测试用例使用评估工具降低延迟
    加强安全护栏
    减少幻觉提高输出一致性防范越狱攻击流式拒绝减少提示词泄露保持 Claude 角色设定
    管理与监控
    Admin API 概览数据驻留工作空间用量与成本 APIClaude Code Analytics API零数据留存
    Console
    Log in
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...

    Solutions

    • AI agents
    • Code modernization
    • Coding
    • Customer support
    • Education
    • Financial services
    • Government
    • Life sciences

    Partners

    • Amazon Bedrock
    • Google Cloud's Vertex AI

    Learn

    • Blog
    • Catalog
    • Courses
    • Use cases
    • Connectors
    • Customer stories
    • Engineering at Anthropic
    • Events
    • Powered by Claude
    • Service partners
    • Startups program

    Company

    • Anthropic
    • Careers
    • Economic Futures
    • Research
    • News
    • Responsible Scaling Policy
    • Security and compliance
    • Transparency

    Learn

    • Blog
    • Catalog
    • Courses
    • Use cases
    • Connectors
    • Customer stories
    • Engineering at Anthropic
    • Events
    • Powered by Claude
    • Service partners
    • Startups program

    Help and security

    • Availability
    • Status
    • Support
    • Discord

    Terms and policies

    • Privacy policy
    • Responsible disclosure policy
    • Terms of service: Commercial
    • Terms of service: Consumer
    • Usage policy
    指南

    使用钩子拦截和控制代理行为

    通过钩子在关键执行点拦截和自定义代理行为

    钩子让您可以在关键点拦截代理执行,以添加验证、日志记录、安全控制或自定义逻辑。通过钩子,您可以:

    • 在危险操作执行之前阻止它们,例如破坏性的 shell 命令或未经授权的文件访问
    • 记录和审计每个工具调用,用于合规、调试或分析
    • 转换输入和输出以清理数据、注入凭据或重定向文件路径
    • 要求人工审批敏感操作,如数据库写入或 API 调用
    • 跟踪会话生命周期以管理状态、清理资源或发送通知

    钩子由两部分组成:

    1. 回调函数:钩子触发时运行的逻辑
    2. 钩子配置:告诉 SDK 要挂接到哪个事件(如 PreToolUse)以及要匹配哪些工具

    以下示例阻止代理修改 .env 文件。首先,定义一个检查文件路径的回调,然后将其传递给 query() 以在任何 Write 或 Edit 工具调用之前运行:

    import asyncio
    from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher
    
    # 定义一个接收工具调用详情的钩子回调
    async def protect_env_files(input_data, tool_use_id, context):
        # 从工具的输入参数中提取文件路径
        file_path = input_data['tool_input'].get('file_path', '')
        file_name = file_path.split('/')[-1]
    
        # 如果目标是 .env 文件则阻止操作
        if file_name == '.env':
            return {
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'deny',
                    'permissionDecisionReason': 'Cannot modify .env files'
                }
            }
    
        # 返回空对象以允许操作
        return {}
    
    async def main():
        async for message in query(
            prompt="Update the database configuration",
            options=ClaudeAgentOptions(
                hooks={
                    # 为 PreToolUse 事件注册钩子
                    # 匹配器仅过滤 Write 和 Edit 工具调用
                    'PreToolUse': [HookMatcher(matcher='Write|Edit', hooks=[protect_env_files])]
                }
            )
        ):
            print(message)
    
    asyncio.run(main())

    这是一个 PreToolUse 钩子。它在工具执行之前运行,可以根据您的逻辑阻止或允许操作。本指南的其余部分涵盖所有可用的钩子、它们的配置选项以及常见用例的模式。

    可用钩子

    SDK 为代理执行的不同阶段提供钩子。一些钩子在两个 SDK 中都可用,而另一些仅限 TypeScript,因为 Python SDK 不支持它们。

    钩子事件Python SDKTypeScript SDK触发条件示例用例
    PreToolUse是是工具调用请求(可以阻止或修改)阻止危险的 shell 命令
    PostToolUse是是工具执行结果将所有文件更改记录到审计跟踪
    PostToolUseFailure否是工具执行失败处理或记录工具错误
    UserPromptSubmit是是用户提示提交向提示中注入额外上下文
    Stop是是代理执行停止退出前保存会话状态
    SubagentStart否是子代理初始化跟踪并行任务生成
    SubagentStop是是子代理完成聚合并行任务的结果
    PreCompact是是对话压缩请求在摘要之前归档完整记录
    PermissionRequest否是将显示权限对话框自定义权限处理
    SessionStart否是会话初始化初始化日志记录和遥测
    SessionEnd否是会话终止清理临时资源
    Notification否是代理状态消息将代理状态更新发送到 Slack 或 PagerDuty

    常见用例

    钩子足够灵活,可以处理许多不同的场景。以下是按类别组织的一些最常见模式。

    配置钩子

    要为您的代理配置钩子,请在调用 query() 时通过 options.hooks 参数传递钩子:

    async for message in query(
        prompt="Your prompt",
        options=ClaudeAgentOptions(
            hooks={
                'PreToolUse': [HookMatcher(matcher='Bash', hooks=[my_callback])]
            }
        )
    ):
        print(message)

    hooks 选项是一个字典(Python)或对象(TypeScript),其中:

    • 键是钩子事件名称(例如 'PreToolUse'、'PostToolUse'、'Stop')
    • 值是匹配器数组,每个匹配器包含一个可选的过滤模式和您的回调函数

    您的钩子回调函数接收关于事件的输入数据,并返回一个响应,以便代理知道是允许、阻止还是修改操作。

    匹配器

    使用匹配器来过滤哪些工具触发您的回调:

    选项类型默认值描述
    matcherstringundefined用于匹配工具名称的正则表达式模式。内置工具包括 Bash、Read、Write、Edit、Glob、Grep、WebFetch、Task 等。MCP 工具使用 mcp__<server>__<action> 模式。
    hooksHookCallback[]-必需。当模式匹配时要执行的回调函数数组
    timeoutnumber60超时时间(秒);对于进行外部 API 调用的钩子,请增加此值

    尽可能使用 matcher 模式来定位特定工具。使用 'Bash' 的匹配器仅对 Bash 命令运行,而省略模式则会对每个工具调用运行您的回调。请注意,匹配器仅按工具名称过滤,而不是按文件路径或其他参数过滤——要按文件路径过滤,请在回调中检查 tool_input.file_path。

    匹配器仅适用于基于工具的钩子(PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest)。对于生命周期钩子如 Stop、SessionStart 和 Notification,匹配器会被忽略,钩子会对该类型的所有事件触发。

    **发现工具名称:**检查会话启动时初始系统消息中的 tools 数组,或添加一个没有匹配器的钩子来记录所有工具调用。

    **MCP 工具命名:**MCP 工具始终以 mcp__ 开头,后跟服务器名称和操作:mcp__<server>__<action>。例如,如果您配置了一个名为 playwright 的服务器,其工具将命名为 mcp__playwright__browser_screenshot、mcp__playwright__browser_click 等。服务器名称来自您在 mcpServers 配置中使用的键。

    此示例使用匹配器仅在 PreToolUse 事件触发时对文件修改工具运行钩子:

    options = ClaudeAgentOptions(
        hooks={
            'PreToolUse': [
                HookMatcher(matcher='Write|Edit', hooks=[validate_file_path])
            ]
        }
    )

    回调函数输入

    每个钩子回调接收三个参数:

    1. 输入数据(dict / HookInput):事件详情。参见输入数据了解字段
    2. 工具使用 ID(str | None / string | null):关联 PreToolUse 和 PostToolUse 事件
    3. 上下文(HookContext):在 TypeScript 中,包含一个 signal 属性(AbortSignal)用于取消。将其传递给异步操作如 fetch(),以便在钩子超时时自动取消。在 Python 中,此参数保留供将来使用。

    输入数据

    钩子回调的第一个参数包含关于事件的信息。字段名称在两个 SDK 中相同(都使用 snake_case)。

    通用字段存在于所有钩子类型中:

    字段类型描述
    hook_event_namestring钩子类型(PreToolUse、PostToolUse 等)
    session_idstring当前会话标识符
    transcript_pathstring对话记录的路径
    cwdstring当前工作目录

    钩子特定字段因钩子类型而异。标记为 TS 的项仅在 TypeScript SDK 中可用:

    字段类型描述钩子
    tool_namestring被调用的工具名称PreToolUse、PostToolUse、PostToolUseFailureTS、PermissionRequestTS
    tool_inputobject传递给工具的参数PreToolUse、PostToolUse、PostToolUseFailureTS、PermissionRequestTS
    tool_responseany工具执行返回的结果PostToolUse
    errorstring工具执行失败的错误消息PostToolUseFailureTS
    is_interruptboolean失败是否由中断引起PostToolUseFailureTS
    promptstring用户的提示文本UserPromptSubmit
    stop_hook_activeboolean停止钩子是否正在处理中Stop、SubagentStop
    agent_idstring子代理的唯一标识符SubagentStartTS、SubagentStopTS
    agent_typestring子代理的类型/角色SubagentStartTS
    agent_transcript_pathstring子代理对话记录的路径SubagentStopTS
    triggerstring触发压缩的原因:manual 或 autoPreCompact
    custom_instructionsstring为压缩提供的自定义指令PreCompact
    permission_suggestionsarray工具的建议权限更新PermissionRequestTS
    sourcestring会话如何启动:startup、resume、clear 或 compactSessionStartTS
    reasonstring会话结束原因:clear、logout、prompt_input_exit、bypass_permissions_disabled 或 otherSessionEndTS
    messagestring来自代理的状态消息NotificationTS
    notification_typestring通知类型:permission_prompt、idle_prompt、auth_success 或 elicitation_dialogNotificationTS
    titlestring代理设置的可选标题NotificationTS

    以下代码定义了一个钩子回调,使用 tool_name 和 tool_input 来记录每个工具调用的详情:

    async def log_tool_calls(input_data, tool_use_id, context):
        if input_data['hook_event_name'] == 'PreToolUse':
            print(f"Tool: {input_data['tool_name']}")
            print(f"Input: {input_data['tool_input']}")
        return {}

    回调输出

    您的回调函数返回一个对象,告诉 SDK 如何继续。返回空对象 {} 以允许操作不做更改。要阻止、修改或向操作添加上下文,请返回一个包含 hookSpecificOutput 字段的对象,其中包含您的决定。

    顶层字段(hookSpecificOutput 之外):

    字段类型描述
    continueboolean此钩子之后代理是否应继续(默认:true)
    stopReasonstring当 continue 为 false 时显示的消息
    suppressOutputboolean从记录中隐藏 stdout(默认:false)
    systemMessagestring注入到对话中供 Claude 查看的消息

    hookSpecificOutput 内部的字段:

    字段类型钩子描述
    hookEventNamestring全部必需。使用 input.hook_event_name 来匹配当前事件
    permissionDecision'allow' | 'deny' | 'ask'PreToolUse控制工具是否执行
    permissionDecisionReasonstringPreToolUse向 Claude 显示的决定说明
    updatedInputobjectPreToolUse修改后的工具输入(需要 permissionDecision: 'allow')
    additionalContextstringPreToolUse、PostToolUse、UserPromptSubmit、SessionStartTS、SubagentStartTS添加到对话中的上下文

    此示例阻止对 /etc 目录的写入操作,同时注入系统消息以提醒 Claude 安全的文件操作实践:

    async def block_etc_writes(input_data, tool_use_id, context):
        file_path = input_data['tool_input'].get('file_path', '')
    
        if file_path.startswith('/etc'):
            return {
                # 顶层字段:向对话中注入指导
                'systemMessage': 'Remember: system directories like /etc are protected.',
                # hookSpecificOutput:阻止操作
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'deny',
                    'permissionDecisionReason': 'Writing to /etc is not allowed'
                }
            }
        return {}

    权限决策流程

    当多个钩子或权限规则适用时,SDK 按以下顺序评估它们:

    1. 拒绝规则首先检查(任何匹配 = 立即拒绝)。
    2. 询问规则其次检查。
    3. 允许规则第三检查。
    4. 如果没有匹配,默认为询问。

    如果任何钩子返回 deny,操作将被阻止——其他钩子返回 allow 不会覆盖它。

    阻止工具

    返回拒绝决定以阻止工具执行:

    async def block_dangerous_commands(input_data, tool_use_id, context):
        if input_data['hook_event_name'] != 'PreToolUse':
            return {}
    
        command = input_data['tool_input'].get('command', '')
    
        if 'rm -rf /' in command:
            return {
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'deny',
                    'permissionDecisionReason': 'Dangerous command blocked: rm -rf /'
                }
            }
        return {}

    修改工具输入

    返回更新后的输入以更改工具接收的内容:

    async def redirect_to_sandbox(input_data, tool_use_id, context):
        if input_data['hook_event_name'] != 'PreToolUse':
            return {}
    
        if input_data['tool_name'] == 'Write':
            original_path = input_data['tool_input'].get('file_path', '')
            return {
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'allow',
                    'updatedInput': {
                        **input_data['tool_input'],
                        'file_path': f'/sandbox{original_path}'
                    }
                }
            }
        return {}

    使用 updatedInput 时,您还必须包含 permissionDecision。始终返回一个新对象,而不是修改原始的 tool_input。

    添加系统消息

    向对话中注入上下文:

    async def add_security_reminder(input_data, tool_use_id, context):
        return {
            'systemMessage': 'Remember to follow security best practices.'
        }

    自动批准特定工具

    绕过受信任工具的权限提示。当您希望某些操作无需用户确认即可运行时,这很有用:

    async def auto_approve_read_only(input_data, tool_use_id, context):
        if input_data['hook_event_name'] != 'PreToolUse':
            return {}
    
        read_only_tools = ['Read', 'Glob', 'Grep', 'LS']
        if input_data['tool_name'] in read_only_tools:
            return {
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'allow',
                    'permissionDecisionReason': 'Read-only tool auto-approved'
                }
            }
        return {}

    permissionDecision 字段接受三个值:'allow'(自动批准)、'deny'(阻止)或 'ask'(提示确认)。

    处理高级场景

    这些模式帮助您为复杂用例构建更复杂的钩子系统。

    链接多个钩子

    钩子按照它们在数组中出现的顺序执行。保持每个钩子专注于单一职责,并链接多个钩子以实现复杂逻辑。此示例对每个工具调用运行所有四个钩子(未指定匹配器):

    options = ClaudeAgentOptions(
        hooks={
            'PreToolUse': [
                HookMatcher(hooks=[rate_limiter]),        # 第一:检查速率限制
                HookMatcher(hooks=[authorization_check]), # 第二:验证权限
                HookMatcher(hooks=[input_sanitizer]),     # 第三:清理输入
                HookMatcher(hooks=[audit_logger])         # 最后:记录操作
            ]
        }
    )

    使用正则表达式的工具特定匹配器

    使用正则表达式模式匹配多个工具:

    options = ClaudeAgentOptions(
        hooks={
            'PreToolUse': [
                # 匹配文件修改工具
                HookMatcher(matcher='Write|Edit|Delete', hooks=[file_security_hook]),
    
                # 匹配所有 MCP 工具
                HookMatcher(matcher='^mcp__', hooks=[mcp_audit_hook]),
    
                # 匹配所有(无匹配器)
                HookMatcher(hooks=[global_logger])
            ]
        }
    )

    匹配器仅匹配工具名称,而不是文件路径或其他参数。要按文件路径过滤,请在钩子回调中检查 tool_input.file_path。

    跟踪子代理活动

    使用 SubagentStop 钩子监控子代理完成情况。tool_use_id 帮助将父代理调用与其子代理关联起来:

    async def subagent_tracker(input_data, tool_use_id, context):
        if input_data['hook_event_name'] == 'SubagentStop':
            print(f"[SUBAGENT] Completed")
            print(f"  Tool use ID: {tool_use_id}")
            print(f"  Stop hook active: {input_data.get('stop_hook_active')}")
        return {}
    
    options = ClaudeAgentOptions(
        hooks={
            'SubagentStop': [HookMatcher(hooks=[subagent_tracker])]
        }
    )

    钩子中的异步操作

    钩子可以执行异步操作,如 HTTP 请求。通过捕获异常而不是抛出异常来优雅地处理错误。在 TypeScript 中,将 signal 传递给 fetch() 以便在钩子超时时取消请求:

    import aiohttp
    from datetime import datetime
    
    async def webhook_notifier(input_data, tool_use_id, context):
        if input_data['hook_event_name'] != 'PostToolUse':
            return {}
    
        try:
            async with aiohttp.ClientSession() as session:
                await session.post(
                    'https://api.example.com/webhook',
                    json={
                        'tool': input_data['tool_name'],
                        'timestamp': datetime.now().isoformat()
                    }
                )
        except Exception as e:
            print(f'Webhook request failed: {e}')
    
        return {}

    发送通知(仅限 TypeScript)

    使用 Notification 钩子接收来自代理的状态更新,并将其转发到外部服务如 Slack 或监控仪表板:

    TypeScript
    import { query, HookCallback, NotificationHookInput } from "@anthropic-ai/claude-agent-sdk";
    
    const notificationHandler: HookCallback = async (input, toolUseID, { signal }) => {
      const notification = input as NotificationHookInput;
    
      await fetch('https://hooks.slack.com/services/YOUR/WEBHOOK/URL', {
        method: 'POST',
        body: JSON.stringify({
          text: `Agent status: ${notification.message}`
        }),
        signal
      });
    
      return {};
    };
    
    for await (const message of query({
      prompt: "Analyze this codebase",
      options: {
        hooks: {
          Notification: [{ hooks: [notificationHandler] }]
        }
      }
    })) {
      console.log(message);
    }

    修复常见问题

    本节涵盖常见问题及其解决方法。

    钩子未触发

    • 验证钩子事件名称正确且区分大小写(PreToolUse,而不是 preToolUse)
    • 检查您的匹配器模式是否与工具名称完全匹配
    • 确保钩子在 options.hooks 中位于正确的事件类型下
    • 对于 SubagentStop、Stop、SessionStart、SessionEnd 和 Notification 钩子,匹配器会被忽略。这些钩子会对该类型的所有事件触发。
    • 当代理达到 max_turns 限制时,钩子可能不会触发,因为会话在钩子执行之前就结束了

    匹配器未按预期过滤

    匹配器仅匹配工具名称,而不是文件路径或其他参数。要按文件路径过滤,请在钩子中检查 tool_input.file_path:

    const myHook: HookCallback = async (input, toolUseID, { signal }) => {
      const preInput = input as PreToolUseHookInput;
      const filePath = preInput.tool_input?.file_path as string;
      if (!filePath?.endsWith('.md')) return {};  // 跳过非 markdown 文件
      // 处理 markdown 文件...
    };

    钩子超时

    • 增加 HookMatcher 配置中的 timeout 值
    • 在 TypeScript 中使用第三个回调参数中的 AbortSignal 来优雅地处理取消

    工具被意外阻止

    • 检查所有 PreToolUse 钩子中返回 permissionDecision: 'deny' 的情况
    • 在钩子中添加日志记录,查看它们返回的 permissionDecisionReason
    • 验证匹配器模式是否过于宽泛(空匹配器匹配所有工具)

    修改后的输入未生效

    • 确保 updatedInput 在 hookSpecificOutput 内部,而不是在顶层:

      return {
        hookSpecificOutput: {
          hookEventName: input.hook_event_name,
          permissionDecision: 'allow',
          updatedInput: { command: 'new command' }
        }
      };
    • 您还必须返回 permissionDecision: 'allow' 才能使输入修改生效

    • 在 hookSpecificOutput 中包含 hookEventName 以标识输出对应的钩子类型

    会话钩子不可用

    SessionStart、SessionEnd 和 Notification 钩子仅在 TypeScript SDK 中可用。由于设置限制,Python SDK 不支持这些事件。

    子代理权限提示倍增

    当生成多个子代理时,每个子代理可能会分别请求权限。子代理不会自动继承父代理的权限。为避免重复提示,请使用 PreToolUse 钩子自动批准特定工具,或配置适用于子代理会话的权限规则。

    子代理的递归钩子循环

    生成子代理的 UserPromptSubmit 钩子如果这些子代理触发相同的钩子,可能会创建无限循环。要防止这种情况:

    • 在生成之前检查钩子输入中的子代理指示器
    • 使用 parent_tool_use_id 字段检测您是否已在子代理上下文中
    • 将钩子范围限定为仅对顶层代理会话运行

    systemMessage 未出现在输出中

    systemMessage 字段向对话中添加模型可以看到的上下文,但它可能不会出现在所有 SDK 输出模式中。如果您需要将钩子决策呈现给您的应用程序,请单独记录它们或使用专用的输出通道。

    了解更多

    • 权限:控制您的代理可以做什么
    • 自定义工具:构建工具以扩展代理能力
    • TypeScript SDK 参考
    • Python SDK 参考

    Was this page helpful?

    • 发送通知(仅限 TypeScript)
    • systemMessage 未出现在输出中