Skip to content

Latest commit

 

History

History
627 lines (529 loc) · 11.3 KB

File metadata and controls

627 lines (529 loc) · 11.3 KB

DeepVCode Hooks Examples

本文档提供实际可用的Hook脚本示例,可直接集成到项目中。

示例1:安全网关 (BeforeTool Hook)

文件: hooks/security-gate.sh

#!/bin/bash
set -euo pipefail

# 从stdin读取JSON输入
read INPUT

# 提取工具名称
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty')

if [[ -z "$TOOL" ]]; then
  # 如果无法解析,允许操作
  echo '{"decision":"allow","systemMessage":"Warning: Could not parse tool name"}' >&2
  exit 0
fi

# 黑名单:禁止危险操作
case "$TOOL" in
  "delete_file"|"remove_directory"|"shell")
    # 这些操作被禁止
    echo '{
      "decision": "deny",
      "reason": "Tool '"$TOOL"' is blocked by security policy"
    }'
    exit 0
    ;;
esac

# 允许其他操作
echo '{"decision":"allow"}'
exit 0

配置:

{
  "hooks": {
    "BeforeTool": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ./hooks/security-gate.sh",
            "timeout": 5000
          }
        ]
      }
    ]
  }
}

示例2:提示增强 (BeforeAgent Hook)

文件: hooks/enhance-prompt.sh

#!/bin/bash
set -euo pipefail

read INPUT

# 添加系统指导
SYSTEM_INSTRUCTION="[SYSTEM GUIDELINES]
- Always verify operations before proceeding
- Never modify system-critical files
- Log all important actions
- Ask for confirmation for dangerous operations
- Provide explanations for all recommendations
[END GUIDELINES]"

echo "{
  \"decision\": \"allow\",
  \"hookSpecificOutput\": {
    \"hookEventName\": \"BeforeAgent\",
    \"additionalContext\": \"$SYSTEM_INSTRUCTION\"
  }
}"
exit 0

配置:

{
  "hooks": {
    "BeforeAgent": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ./hooks/enhance-prompt.sh",
            "timeout": 2000
          }
        ]
      }
    ]
  }
}

示例3:审计日志 (AfterTool Hook)

文件: hooks/audit-log.sh

#!/bin/bash
set -euo pipefail

read INPUT

TOOL=$(echo "$INPUT" | jq -r '.tool_name // "unknown"')
TIMESTAMP=$(echo "$INPUT" | jq -r '.timestamp')
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id')

# 写入审计日志
LOG_FILE="logs/audit-$(date +%Y%m%d).log"
mkdir -p logs

# 记录到文件(不要让失败阻止操作)
echo "[$TIMESTAMP] AUDIT: Tool=$TOOL Session=$SESSION_ID" >> "$LOG_FILE" 2>/dev/null || true

# 总是允许
echo '{"decision":"allow"}'
exit 0

配置:

{
  "hooks": {
    "AfterTool": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ./hooks/audit-log.sh"
          }
        ]
      }
    ]
  }
}

示例4:权限控制 (BeforeToolSelection Hook)

文件: hooks/role-based-access.sh

#!/bin/bash
set -euo pipefail

read INPUT

# 从环境变量或配置获取用户角色
USER_ROLE="${USER_ROLE:-viewer}"

case "$USER_ROLE" in
  "admin")
    # 管理员:允许所有工具
    echo '{"decision":"allow"}'
    ;;
  "developer")
    # 开发者:禁止删除操作
    echo '{
      "hookSpecificOutput": {
        "toolConfig": {
          "mode": "ANY",
          "allowedFunctionNames": [
            "read_file",
            "list_directory",
            "write_file",
            "edit_file",
            "web_fetch",
            "web_search",
            "shell"
          ]
        }
      }
    }'
    ;;
  "viewer")
    # 查看者:仅读取权限
    echo '{
      "hookSpecificOutput": {
        "toolConfig": {
          "mode": "ANY",
          "allowedFunctionNames": [
            "read_file",
            "list_directory",
            "web_fetch",
            "web_search"
          ]
        }
      }
    }'
    ;;
  *)
    # 未知角色:拒绝所有
    echo '{
      "decision": "deny",
      "reason": "Unknown user role: '$USER_ROLE'"
    }'
    ;;
esac
exit 0

配置:

{
  "hooks": {
    "BeforeToolSelection": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ./hooks/role-based-access.sh",
            "timeout": 3000
          }
        ]
      }
    ]
  }
}

示例5:LLM参数优化 (BeforeModel Hook)

文件: hooks/tune-llm-params.sh

#!/bin/bash
set -euo pipefail

read INPUT

# 获取第一条消息内容
FIRST_MESSAGE=$(echo "$INPUT" | jq -r '.llm_request.messages[0].content // ""')
MSG_LENGTH=${#FIRST_MESSAGE}

# 根据内容长度调整温度
if [[ $MSG_LENGTH -gt 1000 ]]; then
  # 长提示:复杂问题,增加创意
  TEMPERATURE="0.8"
  MAX_TOKENS="4096"
elif [[ $MSG_LENGTH -gt 500 ]]; then
  # 中等长度
  TEMPERATURE="0.7"
  MAX_TOKENS="2048"
else
  # 短提示:简单问题,降低创意
  TEMPERATURE="0.3"
  MAX_TOKENS="1024"
fi

# 检查是否涉及代码
if echo "$FIRST_MESSAGE" | grep -qiE "(code|script|function|class|def|import|export)"; then
  # 代码问题:精确性更重要
  TEMPERATURE="0.1"
  MAX_TOKENS="4096"
fi

echo "{
  \"hookSpecificOutput\": {
    \"llm_request\": {
      \"config\": {
        \"temperature\": $TEMPERATURE,
        \"maxOutputTokens\": $MAX_TOKENS
      }
    }
  }
}"
exit 0

配置:

{
  "hooks": {
    "BeforeModel": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ./hooks/tune-llm-params.sh",
            "timeout": 2000
          }
        ]
      }
    ]
  }
}

示例6:文件操作白名单 (BeforeTool Hook with Matcher)

文件: hooks/file-whitelist.sh

#!/bin/bash
set -euo pipefail

read INPUT

TOOL=$(echo "$INPUT" | jq -r '.tool_name')
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.path // ""')

# 如果不是文件操作,允许
if [[ ! "$TOOL" =~ ^(read_file|write_file|edit_file|delete_file)$ ]]; then
  echo '{"decision":"allow"}'
  exit 0
fi

# 检查文件路径是否在允许的目录中
ALLOWED_DIRS=(
  "/home/user/projects"
  "/tmp"
  "./src"
  "./docs"
)

ALLOWED=false
for DIR in "${ALLOWED_DIRS[@]}"; do
  if [[ "$FILE_PATH" == "$DIR"* ]]; then
    ALLOWED=true
    break
  fi
done

if [[ "$ALLOWED" == "true" ]]; then
  echo '{"decision":"allow"}'
else
  echo "{
    \"decision\": \"deny\",
    \"reason\": \"File path '$FILE_PATH' is not in allowed directories\"
  }"
fi
exit 0

配置:

{
  "hooks": {
    "BeforeTool": [
      {
        "matcher": "read_file|write_file|edit_file|delete_file",
        "hooks": [
          {
            "type": "command",
            "command": "bash ./hooks/file-whitelist.sh",
            "timeout": 3000
          }
        ]
      }
    ]
  }
}

示例7:会话生命周期跟踪

文件: hooks/session-tracker.sh

#!/bin/bash
set -euo pipefail

read INPUT

EVENT=$(echo "$INPUT" | jq -r '.hook_event_name')
SOURCE=$(echo "$INPUT" | jq -r '.source // .reason // "unknown"')
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id')

LOG_FILE="logs/sessions-$(date +%Y%m%d).log"
mkdir -p logs

TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

# 记录会话事件
echo "[$TIMESTAMP] Event=$EVENT Source=$SOURCE SessionID=$SESSION_ID" >> "$LOG_FILE" 2>/dev/null || true

echo '{"decision":"allow"}'
exit 0

配置:

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ./hooks/session-tracker.sh"
          }
        ]
      }
    ],
    "SessionEnd": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ./hooks/session-tracker.sh"
          }
        ]
      }
    ]
  }
}

配置完整示例

将所有hooks集合到.gemini/settings.json

{
  "tools": {
    "enableHooks": true
  },
  "hooks": {
    "BeforeTool": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ./hooks/security-gate.sh",
            "timeout": 5000
          }
        ]
      },
      {
        "matcher": "read_file|write_file|edit_file|delete_file",
        "hooks": [
          {
            "type": "command",
            "command": "bash ./hooks/file-whitelist.sh",
            "timeout": 3000
          }
        ]
      }
    ],
    "AfterTool": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ./hooks/audit-log.sh"
          }
        ]
      }
    ],
    "BeforeAgent": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ./hooks/enhance-prompt.sh",
            "timeout": 2000
          }
        ]
      }
    ],
    "BeforeModel": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ./hooks/tune-llm-params.sh",
            "timeout": 2000
          }
        ]
      }
    ],
    "BeforeToolSelection": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ./hooks/role-based-access.sh",
            "timeout": 3000
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ./hooks/session-tracker.sh"
          }
        ]
      }
    ],
    "SessionEnd": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ./hooks/session-tracker.sh"
          }
        ]
      }
    ]
  }
}

测试Hooks

创建测试脚本

文件: hooks/test-hooks.sh

#!/bin/bash
# 测试hooks是否正确响应

test_hook() {
  local hook_script=$1
  local test_input=$2
  local expected_decision=$3

  echo "Testing $hook_script..."

  RESULT=$(echo "$test_input" | bash "$hook_script" 2>/dev/null)
  DECISION=$(echo "$RESULT" | jq -r '.decision // empty')

  if [[ "$DECISION" == "$expected_decision" ]]; then
    echo "  ✓ PASS"
  else
    echo "  ✗ FAIL: Expected '$expected_decision', got '$DECISION'"
    echo "  Output: $RESULT"
  fi
}

# 测试安全网关
TEST_INPUT='{
  "session_id": "test",
  "cwd": "/tmp",
  "hook_event_name": "BeforeTool",
  "timestamp": "2025-01-15T10:00:00Z",
  "tool_name": "delete_file",
  "tool_input": {"path": "/tmp/test"}
}'

test_hook "./hooks/security-gate.sh" "$TEST_INPUT" "deny"

# 测试允许工具
TEST_INPUT='{
  "session_id": "test",
  "cwd": "/tmp",
  "hook_event_name": "BeforeTool",
  "timestamp": "2025-01-15T10:00:00Z",
  "tool_name": "read_file",
  "tool_input": {"path": "/tmp/test"}
}'

test_hook "./hooks/security-gate.sh" "$TEST_INPUT" "allow"

运行测试

bash hooks/test-hooks.sh

调试Hooks

启用调试模式

在hook脚本中添加DEBUG变量:

#!/bin/bash
DEBUG=${DEBUG:-0}

read INPUT

if [[ $DEBUG -eq 1 ]]; then
  echo "[DEBUG] Input: $INPUT" >&2
fi

# ... hook逻辑

运行:

DEBUG=1 bash ./hooks/security-gate.sh < test_input.json

性能考虑

  • 序列执行:使用sequential: true时,后续hook看到前一个的修改
  • 并行执行:默认行为,所有hooks并发运行
  • 超时:设置合理的超时避免长期阻塞(默认60秒)
  • 轻量化:保持hooks简洁,复杂逻辑调用外部API

最佳实践

  1. 验证输入 - 检查JSON有效性
  2. 优雅降级 - Hook失败不应阻止系统
  3. 记录日志 - 用于审计和调试
  4. 版本控制 - Hooks代码应受版本控制
  5. 文档 - 记录每个hook的作用
  6. 测试 - 创建测试用例
  7. 监控 - 跟踪hook执行情况