Hooks

借助钩子,您可以在代理在其远程沙盒中执行代码或修改文件之前或之后运行自定义脚本或外部 HTTP 请求。使用钩子通过自动化安全措施和后台工作流来扩展代理循环,例如:

  • 在执行高风险 shell 命令或受限文件读取操作之前,强制执行安全和访问权限限制
  • 在代理创建或修改文件后立即自动执行数据流水线转换
  • 在工具执行后,将企业审核遥测数据流式传输到外部监控系统。

Python

import json
from google import genai

client = genai.Client()

hooks_config = {
    "security-gate": {
        "pre_tool_execution": [
            {
                "matcher": "code_execution",
                "hooks": [
                    {
                        "type": "command",
                        "command": "python3 /.agents/hooks-scripts/gate.py",
                        "timeout": 10,
                    }
                ],
            }
        ]
    }
}

gate_script = """#!/usr/bin/env python3
import sys, json
data = json.load(sys.stdin)
cmd = str(data.get("tool_call", {}).get("args", {}))
if "rm -rf" in cmd:
    print(json.dumps({"decision": "deny", "reason": "Destructive command blocked by security gate."}))
else:
    print(json.dumps({"decision": "allow"}))
"""

interaction = client.interactions.create(
    agent="antigravity-preview-05-2026",
    input="Run `rm -rf /tmp/forbidden` using code_execution.",
    tools=[{"type": "code_execution"}],
    environment={
        "type": "remote",
        "sources": [
            {
                "type": "inline",
                "target": ".agents/hooks.json",
                "content": json.dumps(hooks_config, indent=2),
            },
            {
                "type": "inline",
                "target": ".agents/hooks-scripts/gate.py",
                "content": gate_script,
            },
        ],
    },
)
print(interaction.output_text)

JavaScript

import { GoogleGenAI } from "@google/genai";

const client = new GoogleGenAI({});

const hooksConfig = {
    "security-gate": {
        pre_tool_execution: [
            {
                matcher: "code_execution",
                hooks: [
                    {
                        type: "command",
                        command: "python3 /.agents/hooks-scripts/gate.py",
                        timeout: 10,
                    },
                ],
            },
        ],
    },
};

const gateScript = `#!/usr/bin/env python3
import sys, json
data = json.load(sys.stdin)
cmd = str(data.get("tool_call", {}).get("args", {}))
if "rm -rf" in cmd:
    print(json.dumps({"decision": "deny", "reason": "Destructive command blocked by security gate."}))
else:
    print(json.dumps({"decision": "allow"}))
`;

const interaction = await client.interactions.create({
    agent: "antigravity-preview-05-2026",
    input: "Run `rm -rf /tmp/forbidden` using code_execution.",
    tools: [{ type: "code_execution" }],
    environment: {
        type: "remote",
        sources: [
            {
                type: "inline",
                target: ".agents/hooks.json",
                content: JSON.stringify(hooksConfig, null, 2),
            },
            {
                type: "inline",
                target: ".agents/hooks-scripts/gate.py",
                content: gateScript,
            },
        ],
    },
});
console.log(interaction.output_text);

REST

curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      "agent": "antigravity-preview-05-2026",
      "input": [{"type": "text", "text": "Run `rm -rf /tmp/forbidden` using code_execution."}],
      "tools": [{"type": "code_execution"}],
      "environment": {
          "type": "remote",
          "sources": [
              {
                  "type": "inline",
                  "target": ".agents/hooks.json",
                  "content": "{\"security-gate\": {\"pre_tool_execution\": [{\"matcher\": \"code_execution\", \"hooks\": [{\"type\": \"command\", \"command\": \"python3 /.agents/hooks-scripts/gate.py\", \"timeout\": 10}]}]}}"
              },
              {
                  "type": "inline",
                  "target": ".agents/hooks-scripts/gate.py",
                  "content": "#!/usr/bin/env python3\nimport sys, json\ndata = json.load(sys.stdin)\ncmd = str(data.get(\"tool_call\", {}).get(\"args\", {}))\nif \"rm -rf\" in cmd:\n    print(json.dumps({\"decision\": \"deny\", \"reason\": \"Destructive command blocked by security gate.\"}))\nelse:\n    print(json.dumps({\"decision\": \"allow\"}))\n"
              }
          ]
      }
  }'

支持的生命周期事件

钩子支持沙盒内的 2 个事件:

活动 触发时间 作用
pre_tool_execution 工具运行前 可以在工具执行之前批准 (allow) 或屏蔽 (deny) 该工具。被屏蔽后,模型会看到您的拒绝原因并进行调整。
post_tool_execution 工具运行结束后立即执行 运行后续任务,例如格式化代码、运行单元测试或记录遥测数据。无法阻止或撤消已完成的操作。

pre_tool_execution

在工具执行之前立即触发。脚本从 stdin 读取工具调用详细信息,并将其决策 JSON(allowdeny)输出到 stdout

输入载荷 (stdin)

{
  "tool_call": {
    "name": "code_execution",
    "args": {
      "code": "rm -rf /tmp/forbidden",
      "language": "bash"
    }
  },
  "environment_id": "env_xyz789"
}

输出响应 (stdout)

批准工具调用:

{
  "decision": "allow"
}

如需阻止工具调用并向模型返回反馈,请执行以下操作:

{
  "decision": "deny",
  "reason": "Destructive command blocked by security gate."
}

如果钩子拒绝了命令,则会立即跳过工具调用。代理会在当前对话轮次中看到包含拒绝原因的错误结果。然后,模型可以通过选择替代命令或向用户说明屏蔽原因来进行自我修正。

如果脚本输出无法识别的 JSON、纯文本或除 {"decision": "deny"} 之外的任何内容,运行时会将该响应视为审批 (allow)。

post_tool_execution

在工具完成时立即触发。您的脚本会从 stdin 读取执行详情和任何错误状态。

输入载荷 (stdin)

{
  "tool_call": {
    "name": "code_execution",
    "args": {
      "code": "python3 /workspace/app.py",
      "language": "bash"
    }
  },
  "environment_id": "env_xyz789"
}

如果 shell 命令将错误输出到标准错误 (stderr) 或文件系统操作失败,则载荷中会包含一个包含错误文本的 "error" 字段。如果命令成功执行且未出现错误,则系统会完全省略 "error" 字段。

输出响应 (stdout)

{}

由于后工具钩子仅针对代码格式设置或日志记录等后台任务运行,因此运行时会忽略在 stdout 上返回的任何决策值。

配置发现

运行时会自动从沙盒环境中的 .agents/hooks.json/.agents/hooks.json 中发现钩子定义。您可以使用任何受支持的环境来源,在自定义脚本旁边提供 hooks.json

  • 代码库装载:包含 .agents/hooks.jsonAGENTS.md 的 Git 代码库。
  • Cloud Storage (gcs):包含已复制到环境中的 hooks.json 的 GCS 存储分区。
  • 内嵌来源:调用 client.interactions.create 时在 environment.sources 中传递的原始 JSON 字符串和脚本内容。

hooks.json 个架构

hooks.json 文件用于将事件定义(pre_tool_executionpost_tool_execution)归入自定义名称下。您可以单独启用或停用每个群组:

{
  "security-gate": {
    "enabled": true,
    "pre_tool_execution": [
      {
        "matcher": "code_execution",
        "hooks": [
          {
            "type": "command",
            "command": "python3 /.agents/hooks-scripts/gate.py",
            "timeout": 10
          }
        ]
      }
    ]
  },
  "auto-format": {
    "post_tool_execution": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "python3 /.agents/hooks-scripts/auto_lint.py",
            "timeout": 15
          }
        ]
      }
    ]
  }
}

匹配器语法和规则

hooks.json 中的每个规则组都使用 matcherhooks 属性定义了处理程序的触发时间和方式:

字段 类型 说明
enabled boolean 可选。设置为 false 可停用相应群组(默认为 true)。
matcher string 用于匹配容器内目标工具名称的正则表达式模式。
hooks array 处理程序定义(commandhttp)的有序列表。处理程序按声明顺序依次运行。

正则表达式评估的运作方式

当代理在沙盒内调用工具时,运行时会使用标准 RE2 正则表达式针对您的 matcher 模式评估工具的容器名称。如果正则表达式与工具名称匹配,则 hooks 数组中的所有处理程序将按顺序执行。如果多个规则组与同一工具匹配,则所有相应的处理程序数组都会运行。

您可以指定任何内置容器工具名称:代码执行 (code_execution) 或文件系统操作(read_filewrite_filelist_filesdelete_file)。

常见的匹配器表达式

  • "code_execution":针对 shell 命令和脚本执行的精确字符串匹配。
  • "write_file":文件系统文件创建和磁盘写入的完全匹配。
  • "read_file|write_file":竖线分隔符可在单个规则中匹配多个特定工具名称。
  • ".*_file":与任何以 _file 结尾的工具(例如 read_filewrite_filedelete_file)匹配的正则表达式通配符。标准 RE2 正则表达式需要 .*;简单的 shell glob(例如 *_file)是无效的正则表达式语法,将无法匹配。
  • ".*""*""":捕获容器内每个工具调用的全能型模式。

处理程序类型

命令钩子

命令钩子在沙盒内执行 shell 命令或脚本。该脚本在 stdin 上接收事件 JSON,并在 stdout 上输出决策 JSON。

字段 类型 说明
type string 必须为 "command"
command string 要在沙盒内运行的命令行(例如 python3 /.agents/hooks-scripts/gate.py)。
timeout integer 超时时间(以秒为单位)。默认值:30

HTTP 钩子

HTTP 钩子会直接从沙盒网络内部将事件 JSON 作为 POST 请求发送到外部 HTTPS 网址。目标服务器使用完全相同的 JSON 格式({"decision": "allow"}{"decision": "deny", "reason": "..."})在 HTTP 响应正文中返回其决策。