你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

教程:在 Azure SRE 代理中配置代理挂钩(API)

小窍门

是否更偏好门户界面? 现在可以直接在门户中创建和管理挂钩,而无需使用 REST API。 门户提供可视窗体和代码编辑器。 不需要任何 curl 命令。

在本教程中,你将创建一个具有停止挂钩的自定义代理,该挂钩强制代理向每个响应添加完成标记。 通过 REST API 配置钩子,然后在门户的沙盒环境中对其进行测试。

估计时间:15 分钟

注释

代理级别与自定义代理级挂钩: 本教程在 自定义代理 上创建自定义代理级别的挂钩。 这些挂钩仅在该特定自定义代理运行时触发。

若要创建应用于整个代理(所有线程、所有自定义代理)的 代理级挂钩 ,请在门户中使用 Builder>Hooks

级别 创建方法 Scope
代理级别 门户:生成器 > 挂钩 适用于所有线程和自定义代理
自定义代理级别 REST API(本教程)或门户:代理画布 > 自定义代理 > 管理挂钩 仅适用于一个自定义代理

本教程中,您将学习如何:

  • 使用 REST API 创建具有停止挂钩的自定义代理
  • 在门户的测试平台中测试钩子行为
  • 添加 PostToolUse 挂钩以用于审核工具使用情况
  • 使用策略钩子阻止危险指令

先决条件

  • 处于 “正在运行” 状态的 Azure SRE 代理
  • 用于调用 REST API 的 curl
  • 使用Azure CLI登录(az login)以获取访问令牌

了解挂钩 API 格式

本教程使用 REST API v2 在自定义代理上创建挂钩。 门户的 YAML 编辑器选项卡显示 v1 格式,不显示通过 API 配置的挂钩,但挂钩仍然处于活动状态。 可以在 生成器>挂钩 页或 测试操场中验证它们。

小窍门

何时使用 API 与门户:

  • 门户(生成器 > 挂钩):最适合以可视形式实现智能体级挂钩。 不需要编写代码。
  • API (本教程):最适合自定义代理级挂钩、CI/CD 管道或编程管理。

查找代理的 API URL

代理的 API 基 URL 遵循以下模式:

https://{agent-name}--{hash}.{hash}.{region}.azuresre.ai

若要查找它:

  1. 打开 sre.azure.com 并选择代理。
  2. 在左侧栏中,选择 “生成器>代理画布”。
  3. 打开浏览器的 开发人员工具 (F12 或右键单击“检查” > )。
  4. 转到“ 网络 ”选项卡,按“api”进行筛选,并查找以结尾 .azuresre.ai的 URL 的请求。
  5. 基础 URL 是 /api/... 之前的所有内容。

或者,检查“元素”选项卡中的 src 属性。查找 srchttps://{agent-name}-- 开头的 <iframe>

获取访问令牌

运行以下命令以获取 SRE 代理 API 的访问令牌:

TOKEN=$(az account get-access-token \
  --resource <RESOURCE_ID> \
  --query accessToken -o tsv)

使用 Stop 挂钩创建自定义代理

此步骤创建名为 my_hooked_agent 的自定义代理,并附带一个 Stop 挂钩,用以检查响应是否以 === RESPONSE COMPLETE === 结尾。 如果缺少标记,挂钩将拒绝响应,并告知代理添加标记。

AGENT_URL="https://your-agent--xxxxxxxx.yyyyyyyy.region.azuresre.ai"

curl -X PUT "${AGENT_URL}/api/v2/extendedAgent/agents/my_hooked_agent" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d @- << 'EOF'
{
  "name": "my_hooked_agent",
  "properties": {
    "instructions": "You are a helpful assistant. Be concise.",
    "handoffDescription": "",
    "handoffs": [],
    "enableVanillaMode": true,
    "hooks": {
      "Stop": [
        {
          "type": "prompt",
          "prompt": "Check the agent response below.\n\n$ARGUMENTS\n\nDoes it end with === RESPONSE COMPLETE ===?\nIf yes: {\"ok\": true}\nIf no: {\"ok\": false, \"reason\": \"Add === RESPONSE COMPLETE === at the end.\"}",
          "timeout": 30
        }
      ]
    }
  }
}
EOF

将收到 HTTP 202 已接受,响应正文中包含完整的智能体配置。

以下示例演示了 v2 YAML 格式的相同配置以供参考:

api_version: azuresre.ai/v2
kind: ExtendedAgent
metadata:
  name: my_hooked_agent
spec:
  instructions: |
    You are a helpful assistant. Be concise.
  handoffDescription: ""
  enableVanillaMode: true
  hooks:
    Stop:
      - type: prompt
        prompt: |
          Check the agent response below.

          $ARGUMENTS

          Does it end with === RESPONSE COMPLETE ===?
          If yes: {"ok": true}
          If no: {"ok": false, "reason": "Add === RESPONSE COMPLETE === at the end."}
        timeout: 30

停止挂钩的工作原理

停止钩子在响应返回给用户之前评估代理的回应。

  • $ARGUMENTS 替换为钩子上下文 JSON,其中包括代理的最终响应。
  • LLM 将评估提示并返回 {"ok": true}{"ok": false, "reason": "..."}
  • 如果被拒绝,代理会在将原因注入为用户消息后继续工作。
  • 三次拒绝(默认值)后,代理将停止。

在门户中测试挂钩

按照以下步骤测试停止挂钩功能:

  1. 在门户中转到智能体,选择“生成器”“智能体画布”。>

  2. 选择“测试操场”单选按钮。

  3. 选择 “子代理/工具” 下拉列表,找到 my_hooked_agent,然后选择“ 应用”。

    已选择带挂钩智能体的测试操场。

  4. 键入 What is 2+2? 聊天并选择“ 发送”。

观看发生的情况:

  • 代理首先响应 4
  • 终止钩子会评估并拒绝响应(没有完成标记)。
  • 出现思考过程步骤,智能体继续执行。
  • 最终响应出现: 4 === RESPONSE COMPLETE ===

停止挂钩结果显示智能体在初始拒绝后添加了 RESPONSE COMPLETE 标记。

挂钩起作用了。 它强制代理在停止之前添加标记。

添加 PostToolUse 挂钩以进行审计

添加一个 PostToolUse 挂钩,用于记录代理使用的每个工具。 通过发送包含两个钩子的新 PUT 请求来更新同一代理:

curl -X PUT "${AGENT_URL}/api/v2/extendedAgent/agents/my_hooked_agent" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d @- << 'EOF'
{
  "name": "my_hooked_agent",
  "properties": {
    "instructions": "You are a helpful assistant. Be concise.",
    "handoffDescription": "",
    "handoffs": [],
    "enableVanillaMode": true,
    "hooks": {
      "Stop": [
        {
          "type": "prompt",
          "prompt": "Check the agent response below.\n\n$ARGUMENTS\n\nDoes it end with === RESPONSE COMPLETE ===?\nIf yes: {\"ok\": true}\nIf no: {\"ok\": false, \"reason\": \"Add === RESPONSE COMPLETE === at the end.\"}",
          "timeout": 30
        }
      ],
      "PostToolUse": [
        {
          "type": "command",
          "matcher": "*",
          "timeout": 30,
          "failMode": "allow",
          "script": "#!/usr/bin/env python3\nimport sys, json\ncontext = json.load(sys.stdin)\ntool = context.get('tool_name', 'unknown')\nprint(json.dumps({'decision': 'allow', 'hookSpecificOutput': {'additionalContext': f'[AUDIT] {tool} executed.'}}))"
        }
      ]
    }
  }
}
EOF

matcher: "*" 表示此挂钩针对每个工具调用运行。 该脚本记录工具名称,并将消息 [AUDIT] 注入到会话中。

若要测试挂钩,请向代理询问触发工具的问题(例如“运行 echo hello”)。

阻止危险命令

添加一个第二 PostToolUse 挂钩以阻止 rm -rfsudochmod 777

PostToolUse:
  # Audit hook (runs for all tools)
  - type: command
    matcher: "*"
    timeout: 30
    failMode: allow
    script: |
      #!/usr/bin/env python3
      import sys, json
      context = json.load(sys.stdin)
      tool = context.get('tool_name', 'unknown')
      print(json.dumps({"decision": "allow",
        "hookSpecificOutput": {"additionalContext": f"[AUDIT] {tool} executed."}}))

  # Policy hook (only for shell tools)
  - type: command
    matcher: "Bash|ExecuteShellCommand"
    timeout: 30
    failMode: block
    script: |
      #!/usr/bin/env python3
      import sys, json, re
      context = json.load(sys.stdin)
      command = context.get('tool_input', {}).get('command', '')
      for pattern in [r'\brm\s+-rf\b', r'\bsudo\b', r'\bchmod\s+777\b']:
          if re.search(pattern, command):
              print(json.dumps({"decision": "block", "reason": f"Blocked: {pattern}"}))
              sys.exit(0)
      print(json.dumps({"decision": "allow"}))

与审计钩子的重要区别:

  • matcher: "Bash|ExecuteShellCommand" 仅针对 shell 工具运行(模式已定位为 ^(Bash|ExecuteShellCommand)$)。
  • failMode: block 如果脚本本身崩溃(严格模式),则阻止工具结果。
  • 当发现危险模式时,返回 "block" 并附带原因。

挂钩响应格式

提示挂钩和命令挂钩使用不同的响应格式。

提示挂钩

提示钩子返回简单的 JSON:

{"ok": true}
{"ok": false, "reason": "Please fix X."}

命令挂钩

命令挂钩返回扩展的 JSON:

{"decision": "allow"}
{"decision": "block", "reason": "Dangerous command."}
{"decision": "allow", "hookSpecificOutput": {"additionalContext": "Audit note."}}

命令挂钩还可以使用退出代码,而不是 JSON:

退出代码 行为
0 无输出 允许
0 采用 JSON 解析 JSON
2 阻止(标准错误成为原因)
其他 回退到 failMode

注意

没有理由的拒绝被视为批准。 拒绝时始终包含 reason

验证

配置并测试挂钩后,请确认以下条件:

  • 使用 REST API v2 配置 自定义代理级挂钩 。 它们仅适用于该自定义代理。
  • 可以在生成器 > 挂钩中创建智能体级挂钩。 它们适用于整个智能体。
  • 停止挂钩会导致代理在停止之前添加 === RESPONSE COMPLETE === 标记。
  • PostToolUse 审核挂钩记录工具调用的 [AUDIT] 消息。
  • 策略钩子会阻止类似于 rm -rfsudo 的危险命令。

故障排除

下表列出了代理挂钩的常见问题和解决方案。

问题 解决方案
门户 YAML 选项卡中不可见的挂钩 预期 - YAML 选项卡仅显示 v1。 通过 API 创建的自定义代理级挂钩在 生成器>挂钩 或操场中处于活动状态且可见。
Unsupported kind: ExtendedAgent 使用 v2 终结点: PUT /api/v2/extendedAgent/agents/{name}
Handoffs cannot be null 添加 "handoffs": [] 到 JSON 有效负载。
钩子不起作用 拒绝时请包括 reason 字段。 如果没有,拒绝被视为批准。
智能体无限循环 较低 maxRejections (默认值:3,范围:1-25)。

后续步骤