你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
小窍门
是否更偏好门户界面? 现在可以直接在门户中创建和管理挂钩,而无需使用 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
若要查找它:
- 打开 sre.azure.com 并选择代理。
- 在左侧栏中,选择 “生成器>代理画布”。
- 打开浏览器的 开发人员工具 (F12 或右键单击“检查” > )。
- 转到“ 网络 ”选项卡,按“api”进行筛选,并查找以结尾
.azuresre.ai的 URL 的请求。 - 基础 URL 是
/api/...之前的所有内容。
或者,检查“元素”选项卡中的 src 属性。查找 src 以 https://{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": "..."}。 - 如果被拒绝,代理会在将原因注入为用户消息后继续工作。
- 三次拒绝(默认值)后,代理将停止。
在门户中测试挂钩
按照以下步骤测试停止挂钩功能:
在门户中转到智能体,选择“生成器”“智能体画布”。>
选择“测试操场”单选按钮。
选择 “子代理/工具” 下拉列表,找到 my_hooked_agent,然后选择“ 应用”。
键入
What is 2+2?聊天并选择“ 发送”。
观看发生的情况:
- 代理首先响应 4。
- 终止钩子会评估并拒绝响应(没有完成标记)。
- 出现思考过程步骤,智能体继续执行。
- 最终响应出现: 4 === 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 -rf、sudo 和 chmod 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 -rf和sudo的危险命令。
故障排除
下表列出了代理挂钩的常见问题和解决方案。
| 问题 | 解决方案 |
|---|---|
| 门户 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)。 |