Loading...
    • 开发者指南
    • API 参考
    • MCP
    • 资源
    • 发布说明
    Search...
    ⌘K
    快速开始
    Claude 简介快速入门
    模型与定价
    模型概览选择模型Claude 4.5 新功能迁移到 Claude 4.5模型弃用定价
    使用 Claude 构建
    功能概览使用 Messages API上下文窗口提示词最佳实践
    能力
    提示词缓存上下文编辑扩展思考工作量流式消息批量处理引用多语言支持Token 计数嵌入视觉PDF 支持Files API搜索结果结构化输出
    工具
    概览如何实现工具使用细粒度工具流式处理Bash 工具代码执行工具程序化工具调用计算机使用工具文本编辑器工具网页获取工具网页搜索工具记忆工具工具搜索工具
    Agent 技能
    概览快速入门最佳实践通过 API 使用技能
    Agent SDK
    概览快速入门TypeScript SDKTypeScript V2(预览版)Python SDK迁移指南
    流式输入处理权限使用钩子控制执行会话管理SDK 中的结构化输出托管 Agent SDK安全部署 AI Agent修改系统提示词SDK 中的 MCP自定义工具SDK 中的子 AgentSDK 中的斜杠命令SDK 中的 Agent 技能跟踪成本和使用情况待办事项列表SDK 中的插件
    API 中的 MCP
    MCP 连接器远程 MCP 服务器
    第三方平台上的 Claude
    Amazon BedrockMicrosoft FoundryVertex AI
    提示词工程
    概览提示词生成器使用提示词模板提示词改进器清晰直接使用示例(多轮提示)让 Claude 思考(CoT)使用 XML 标签给 Claude 一个角色(系统提示词)预填充 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')
    additionalContextstringPostToolUse、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 参考
    • 发送通知(仅 TypeScript)
    • systemMessage 未出现在输出中