Files
JARVIS/development-doc/plan/agent-update/phase-7-hook-interception-layer.md
WIN-JHFT4D3SIVT\caoxiaozhu a3fe4d24fc feat(agents): Phase 7-10 hook system, plugins, skills, orchestration
Phase 7: Built-in Hooks (audit_log, dangerous_confirmation, security_scan)
Phase 8: Plugin system (PluginManager, PluginSandbox, PluginManifest)
Phase 9: Skills registry (SkillRegistry, local/plugin/MCP loaders)
Phase 10: TeamLeader, RemoteTransport, BackgroundTaskManager
2026-04-04 22:56:27 +08:00

537 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 7Hook 拦截层Hook Interception Layer
日期2026-04-04
状态:待开始
前置依赖Phase 6工具系统重构
Demo参考claw-code-main — toolHooks.ts, hook pipeline
---
## 1. 阶段目标
实现完整的 **PreTool/PostTool Hook 拦截机制**,允许在工具执行前、执行后、异常时进行拦截、修改、拒绝和审计。
**Hook 是工具系统的基础设施**,不是独立功能,必须依赖 Phase 6 的注册表。
---
## 2. Hook 类型定义
### 2.1 Hook 类型
```python
# backend/app/agents/tools/hooks/types.py
class HookType(Enum):
"""Hook 类型"""
PRE_TOOL_USE = "pre_tool_use" # 工具执行前
POST_TOOL_USE = "post_tool_use" # 工具执行后
TOOL_ERROR = "tool_error" # 工具执行异常
TOOL_SKIP = "tool_skip" # 工具被跳过
class HookAction(Enum):
"""Hook 执行动作"""
ALLOW = "allow" # 允许执行
DENY = "deny" # 拒绝执行
MODIFY = "modify" # 修改参数
MODIFY_RESULT = "modify_result" # 修改结果
CONTINUE = "continue" # 继续后置Hook用
@dataclass
class HookContext:
"""Hook 执行上下文"""
tool_name: str
arguments: dict
phase: HookType
result: Any = None
error: Exception | None = None
session_id: str | None = None
user_id: str | None = None
metadata: dict = field(default_factory=dict)
@dataclass
class HookResult:
"""Hook 执行结果"""
action: HookAction
message: str | None = None
modified_args: dict | None = None
modified_result: Any = None
```
---
## 3. 内置 Hook 实现
### 3.1 危险操作确认 Hook
```python
# backend/app/agents/tools/hooks/dangerous_confirmation.py
class DangerousConfirmationHook:
"""
危险操作确认 Hook
对危险操作WRITE/EXTERNAL 权限)执行前确认
"""
DANGEROUS_PATTERNS = [
"delete",
"remove",
"drop",
"truncate",
"rm ",
"rmdir",
"shutdown",
"reboot",
"format",
"fdisk",
]
def __init__(self, confirmation_service: ConfirmationService):
self.confirmation_service = confirmation_service
async def pre_execute(self, ctx: HookContext) -> HookResult:
"""检查是否危险操作"""
manifest = tool_registry.get(ctx.tool_name)
# 只对 WRITE/EXTERNAL 权限的工具生效
if manifest.permission_class not in [PermissionClass.WRITE, PermissionClass.EXTERNAL]:
return HookResult(action=HookAction.ALLOW)
# 检查是否包含危险模式
args_str = str(ctx.arguments).lower()
for pattern in self.DANGEROUS_PATTERNS:
if pattern in args_str:
# 请求用户确认
confirmed = await self.confirmation_service.request_confirmation(
user_id=ctx.user_id,
message=f"危险操作:{ctx.tool_name}\n参数:{ctx.arguments}",
timeout_seconds=300
)
if not confirmed:
return HookResult(
action=HookAction.DENY,
message="用户拒绝执行危险操作"
)
return HookResult(action=HookAction.ALLOW)
```
### 3.2 安全扫描 Hook
```python
# backend/app/agents/tools/hooks/security_scan.py
class SecurityScanHook:
"""
安全扫描 Hook
执行后扫描结果中的敏感信息
"""
SENSITIVE_PATTERNS = [
r"password\s*=\s*['\"][^'\"]+['\"]",
r"api[_-]?key\s*=\s*['\"][^'\"]+['\"]",
r"secret\s*=\s*['\"][^'\"]+['\"]",
r"-----BEGIN (RSA |EC |DSA )?PRIVATE KEY-----",
r"sk-[a-zA-Z0-9]{20,}",
]
async def post_execute(self, ctx: HookContext) -> HookResult:
"""扫描结果中的敏感信息"""
if ctx.result is None:
return HookResult(action=HookAction.CONTINUE)
result_str = str(ctx.result)
for pattern in self.SENSITIVE_PATTERNS:
if re.search(pattern, result_str, re.IGNORECASE):
# 脱敏处理
sanitized = re.sub(pattern, "[REDACTED]", result_str, flags=re.IGNORECASE)
return HookResult(
action=HookAction.MODIFY_RESULT,
message="检测到敏感信息,已脱敏",
modified_result=sanitized
)
return HookResult(action=HookAction.CONTINUE)
```
### 3.3 日志记录 Hook
```python
# backend/app/agents/tools/hooks/audit_log.py
class AuditLogHook:
"""
审计日志 Hook
记录所有工具调用
"""
async def pre_execute(self, ctx: HookContext) -> HookResult:
"""记录调用前状态"""
logger.info(
"tool_call_start",
extra={
"tool": ctx.tool_name,
"args": ctx.arguments,
"session_id": ctx.session_id,
"user_id": ctx.user_id,
"timestamp": datetime.now().isoformat(),
}
)
return HookResult(action=HookAction.ALLOW)
async def post_execute(self, ctx: HookContext) -> HookResult:
"""记录调用后状态"""
logger.info(
"tool_call_end",
extra={
"tool": ctx.tool_name,
"success": ctx.error is None,
"duration_ms": ctx.metadata.get("duration_ms", 0),
"session_id": ctx.session_id,
"user_id": ctx.user_id,
"timestamp": datetime.now().isoformat(),
}
)
return HookResult(action=HookAction.CONTINUE)
async def on_error(self, ctx: HookContext) -> HookResult:
"""记录错误"""
logger.error(
"tool_call_error",
extra={
"tool": ctx.tool_name,
"error": str(ctx.error),
"error_type": type(ctx.error).__name__,
"session_id": ctx.session_id,
"user_id": ctx.user_id,
"timestamp": datetime.now().isoformat(),
}
)
return HookResult(action=HookAction.CONTINUE)
```
---
## 4. Hook 配置系统
### 4.1 Hook 配置格式
```json
{
"hooks": {
"pre_tool_use": {
"enabled": true,
"handlers": [
{
"type": "audit_log",
"filter": null
},
{
"type": "dangerous_confirmation",
"filter": {
"permissions": ["WRITE", "EXTERNAL"]
}
}
]
},
"post_tool_use": {
"enabled": true,
"handlers": [
{
"type": "security_scan",
"filter": null
},
{
"type": "audit_log",
"filter": null
}
]
},
"tool_error": {
"enabled": true,
"handlers": [
{
"type": "audit_log",
"filter": null
}
]
}
}
}
```
### 4.2 HookManager
```python
# backend/app/agents/tools/hooks/manager.py
class HookManager:
"""Hook 管理器"""
def __init__(self):
self._builtin_hooks: dict[str, type] = {
"audit_log": AuditLogHook,
"dangerous_confirmation": DangerousConfirmationHook,
"security_scan": SecurityScanHook,
}
self._config: HookConfig = {}
self._enabled = False
def load_config(self, config: dict):
"""加载 Hook 配置"""
self._config = config
self._enabled = config.get("enabled", True)
async def execute_pre_hooks(
self,
tool_name: str,
arguments: dict,
context: dict
) -> HookResult:
"""执行前置 Hook"""
if not self._enabled:
return HookResult(action=HookAction.ALLOW)
pre_config = self._config.get("pre_tool_use", {})
if not pre_config.get("enabled", True):
return HookResult(action=HookAction.ALLOW)
ctx = HookContext(
tool_name=tool_name,
arguments=arguments,
phase=HookType.PRE_TOOL_USE,
**context
)
for handler_config in pre_config.get("handlers", []):
handler = self._create_handler(handler_config)
if not self._should_apply(handler_config, tool_name):
continue
result = await handler.pre_execute(ctx)
if result.action == HookAction.DENY:
return result
elif result.action == HookAction.MODIFY:
ctx.arguments = result.modified_args
return HookResult(action=HookAction.ALLOW, modified_args=ctx.arguments)
async def execute_post_hooks(
self,
tool_name: str,
arguments: dict,
result: Any,
context: dict
) -> HookResult:
"""执行后置 Hook"""
if not self._enabled:
return HookResult(action=HookAction.CONTINUE, modified_result=result)
post_config = self._config.get("post_tool_use", {})
if not post_config.get("enabled", True):
return HookResult(action=HookAction.CONTINUE, modified_result=result)
ctx = HookContext(
tool_name=tool_name,
arguments=arguments,
phase=HookType.POST_TOOL_USE,
result=result,
**context
)
for handler_config in post_config.get("handlers", []):
handler = self._create_handler(handler_config)
if not self._should_apply(handler_config, tool_name):
continue
hook_result = await handler.post_execute(ctx)
if hook_result.action == HookAction.MODIFY_RESULT:
ctx.result = hook_result.modified_result
return HookResult(action=HookAction.CONTINUE, modified_result=ctx.result)
```
---
## 5. 用户自定义 Hook
### 5.1 Hook API
```python
# backend/app/routers/hooks.py
@router.post("/api/hooks/config")
async def update_hook_config(
config: HookConfigUpdate,
current_user: User = Depends(get_current_user)
):
"""更新 Hook 配置"""
# 验证配置格式
await hook_manager.validate_config(config)
# 保存配置
await hook_config_service.save_config(
user_id=current_user.id,
config=config
)
# 重新加载
hook_manager.load_config(config)
return {"status": "ok"}
@router.get("/api/hooks/config")
async def get_hook_config(
current_user: User = Depends(get_current_user)
):
"""获取 Hook 配置"""
config = await hook_config_service.get_config(current_user.id)
return config
@router.get("/api/hooks/available")
async def list_available_hooks():
"""列出所有可用的 Hook"""
return {
"builtin": [
{"name": "audit_log", "description": "审计日志"},
{"name": "dangerous_confirmation", "description": "危险操作确认"},
{"name": "security_scan", "description": "安全扫描"},
],
"custom": hook_manager.list_custom_hooks()
}
```
---
## 6. 与工具系统集成
### 6.1 修改 ToolExecutor
```python
# backend/app/agents/tools/executor.py
class ToolExecutor:
"""工具执行器(集成 Hook"""
def __init__(
self,
registry: ToolRegistry,
hook_manager: HookManager
):
self.registry = registry
self.hook_manager = hook_manager
async def execute(
self,
tool_name: str,
arguments: dict,
context: dict
) -> ToolResult:
"""执行工具(带 Hook"""
# 1. Pre Hook
pre_result = await self.hook_manager.execute_pre_hooks(
tool_name, arguments, context
)
if pre_result.action == HookAction.DENY:
return ToolResult(
success=False,
error=pre_result.message,
tool_name=tool_name
)
if pre_result.modified_args:
arguments = pre_result.modified_args
# 2. 执行工具
try:
manifest = self.registry.get(tool_name)
executor = self.registry.get_executor(tool_name)
start_time = datetime.now()
result = await executor(**arguments)
duration_ms = (datetime.now() - start_time).total_seconds() * 1000
# 3. Post Hook
post_result = await self.hook_manager.execute_post_hooks(
tool_name, arguments, result, context
)
if post_result.action == HookAction.MODIFY_RESULT:
result = post_result.modified_result
return ToolResult(
success=True,
result=result,
tool_name=tool_name,
duration_ms=duration_ms
)
except Exception as e:
# 4. Error Hook
error_result = await self.hook_manager.execute_error_hook(
tool_name, arguments, e, context
)
return ToolResult(
success=False,
error=str(e),
tool_name=tool_name
)
```
---
## 7. 文件结构
```
backend/app/agents/tools/hooks/
├── __init__.py
├── types.py # Hook 类型定义
├── manager.py # Hook 管理器
├── builtins/ # 内置 Hook
│ ├── __init__.py
│ ├── audit_log.py
│ ├── dangerous_confirmation.py
│ └── security_scan.py
└── custom/ # 用户自定义 Hook
├── __init__.py
└── loader.py
```
---
## 8. 验收标准
| 检查点 | 标准 |
|--------|------|
| Pre Hook 执行 | 危险操作被正确拦截 |
| Post Hook 执行 | 结果中的敏感信息被正确脱敏 |
| Error Hook 执行 | 异常被正确记录 |
| 配置持久化 | Hook 配置可保存和加载 |
| API 可用 | 用户可通过 API 配置 Hook |
| 向后兼容 | 不配置 Hook 时系统正常运行 |
---
## 9. Demo 借鉴
| claw-code | Jarvis 对应 |
|-----------|------------|
| `toolHooks.ts` | `hooks/types.py`, `hooks/manager.py` |
| `PreToolUse` | `HookType.PRE_TOOL_USE` |
| `PostToolUse` | `HookType.POST_TOOL_USE` |
| Hook 配置解析 | `hooks/config.py` |