人工在环(Human-in-the-Loop,简称 HITL)中间件允许在 Agent 执行敏感操作前进行人工审核、批准或修改,确保 AI 系统的安全性和可控性。
import (
"github.com/astercloud/aster/pkg/middleware"
)
// 创建 HITL 中间件
hitlMW, err := middleware.NewHumanInTheLoopMiddleware(&middleware.HumanInTheLoopMiddlewareConfig{
// 配置需要审核的工具
InterruptOn: map[string]interface{}{
"Bash": true, // Shell 命令需要审核
"fs_delete": true, // 文件删除需要审核
"HttpRequest": true, // HTTP 请求需要审核
},
// 审核处理器
ApprovalHandler: func(ctx context.Context, req *middleware.ReviewRequest) ([]middleware.Decision, error) {
for _, action := range req.ActionRequests {
fmt.Printf("工具: %s\n", action.ToolName)
fmt.Printf("参数: %+v\n", action.Input)
fmt.Print("批准? (y/n): ")
var answer string
fmt.Scanln(&answer)
if answer == "y" {
return []middleware.Decision{{
Type: middleware.DecisionApprove,
}}, nil
}
return []middleware.Decision{{
Type: middleware.DecisionReject,
}}, nil
}
return nil, fmt.Errorf("no decision")
},
})
// 注册到中间件栈
stack := middleware.NewStack()
stack.Use(hitlMW)
支持三种配置方式:
InterruptOn: map[string]interface{}{
"Bash": true, // 启用默认审核
"Read": false, // 不需要审核
}
InterruptOn: map[string]interface{}{
"Write": map[string]interface{}{
"message": "文件写入需要审核",
"allowed_decisions": []string{"approve", "reject", "edit"},
},
}
InterruptOn: map[string]interface{}{
"database_update": &middleware.InterruptConfig{
Enabled: true,
Message: "数据库更新需要审核",
AllowedDecisions: []middleware.DecisionType{
middleware.DecisionApprove,
middleware.DecisionReject,
},
},
}
| 类型 | 说明 | 使用场景 |
|---|---|---|
DecisionApprove | 批准执行 | 操作安全,可以执行 |
DecisionReject | 拒绝执行 | 操作不安全或不合理 |
DecisionEdit | 编辑参数后执行 | 参数需要调整 |
审核处理器负责获取人工决策,可以通过多种方式实现:
ApprovalHandler: func(ctx context.Context, req *middleware.ReviewRequest) ([]middleware.Decision, error) {
action := req.ActionRequests[0]
fmt.Printf("工具: %s\n参数: %+v\n", action.ToolName, action.Input)
fmt.Print("选择 (approve/reject/edit): ")
var choice string
fmt.Scanln(&choice)
switch choice {
case "approve":
return []middleware.Decision{{Type: middleware.DecisionApprove}}, nil
case "reject":
return []middleware.Decision{{Type: middleware.DecisionReject}}, nil
case "edit":
// 编辑参数...
return []middleware.Decision{{
Type: middleware.DecisionEdit,
EditedInput: editedParams,
}}, nil
}
return nil, fmt.Errorf("invalid choice")
}
ApprovalHandler: func(ctx context.Context, req *middleware.ReviewRequest) ([]middleware.Decision, error) {
action := req.ActionRequests[0]
risk := assessRisk(action)
switch risk {
case RiskLow:
// 低风险自动批准
return []middleware.Decision{{Type: middleware.DecisionApprove}}, nil
case RiskMedium:
// 中风险需要确认
return promptForConfirmation(action)
case RiskHigh:
// 高风险需要明确确认
return promptForExplicitConfirmation(action)
}
return nil, nil
}
hitlMW, _ := middleware.NewHumanInTheLoopMiddleware(&middleware.HumanInTheLoopMiddlewareConfig{
InterruptOn: map[string]interface{}{
"fs_delete": map[string]interface{}{
"message": "⚠️ 文件删除操作需要审核",
"allowed_decisions": []string{"approve", "reject"},
},
"Write": map[string]interface{}{
"message": "📝 文件写入操作需要审核",
"allowed_decisions": []string{"approve", "reject", "edit"},
},
},
ApprovalHandler: fileOperationHandler,
})
hitlMW, _ := middleware.NewHumanInTheLoopMiddleware(&middleware.HumanInTheLoopMiddlewareConfig{
InterruptOn: map[string]interface{}{
"Bash": true,
},
ApprovalHandler: func(ctx context.Context, req *middleware.ReviewRequest) ([]middleware.Decision, error) {
action := req.ActionRequests[0]
cmd := action.Input["command"].(string)
// 检测危险命令
if strings.Contains(cmd, "rm -rf") {
fmt.Println("🚨 检测到危险命令!")
fmt.Printf("命令: %s\n", cmd)
fmt.Print("输入 'CONFIRM' 确认执行: ")
var confirm string
fmt.Scanln(&confirm)
if confirm == "CONFIRM" {
return []middleware.Decision{{Type: middleware.DecisionApprove}}, nil
}
return []middleware.Decision{{Type: middleware.DecisionReject}}, nil
}
// 普通命令简单确认
fmt.Printf("命令: %s\n批准? (y/n): ", cmd)
var answer string
fmt.Scanln(&answer)
if answer == "y" {
return []middleware.Decision{{Type: middleware.DecisionApprove}}, nil
}
return []middleware.Decision{{Type: middleware.DecisionReject}}, nil
},
})
ApprovalHandler: func(ctx context.Context, req *middleware.ReviewRequest) ([]middleware.Decision, error) {
action := req.ActionRequests[0]
fmt.Printf("工具: %s\n", action.ToolName)
fmt.Println("当前参数:")
for key, value := range action.Input {
fmt.Printf(" %s: %v\n", key, value)
}
fmt.Print("\n选择 (approve/reject/edit): ")
var choice string
fmt.Scanln(&choice)
if choice == "edit" {
editedInput := make(map[string]interface{})
for key, value := range action.Input {
fmt.Printf("编辑 %s (当前: %v, 回车保持): ", key, value)
var newValue string
fmt.Scanln(&newValue)
if newValue != "" {
editedInput[key] = newValue
} else {
editedInput[key] = value
}
}
return []middleware.Decision{{
Type: middleware.DecisionEdit,
EditedInput: editedInput,
Reason: "参数已编辑",
}}, nil
}
// ... 处理其他选择
}
type HumanInTheLoopMiddlewareConfig struct {
// InterruptOn 配置哪些工具需要审核
InterruptOn map[string]interface{}
// ApprovalHandler 审核处理器
ApprovalHandler ApprovalHandler
// DefaultAllowedDecisions 默认允许的决策类型
DefaultAllowedDecisions []DecisionType
}
type InterruptConfig struct {
Enabled bool // 是否启用审核
AllowedDecisions []DecisionType // 允许的决策类型
Message string // 审核提示信息
}
type ReviewRequest struct {
ActionRequests []ActionRequest // 待审核的操作列表
ReviewConfigs []InterruptConfig // 每个操作的审核配置
}
type ActionRequest struct {
ToolCallID string // 工具调用 ID
ToolName string // 工具名称
Input map[string]interface{} // 工具输入参数
Message string // 审核提示信息
}
type Decision struct {
Type DecisionType // 决策类型
EditedInput map[string]interface{} // 编辑后的参数
Reason string // 决策理由
}
sequenceDiagram
participant Agent
participant HITL as HITL 中间件
participant Handler as 审核处理器
participant Human as 人类审核员
participant Tool as 工具
Agent->>HITL: 调用工具
HITL->>HITL: 检查是否需要审核
alt 需要审核
HITL->>Handler: 发送审核请求
Handler->>Human: 显示操作信息
Human->>Handler: 做出决策
Handler->>HITL: 返回决策
alt 批准
HITL->>Tool: 执行工具
Tool->>HITL: 返回结果
else 拒绝
HITL->>Agent: 返回拒绝信息
else 编辑
HITL->>Tool: 使用编辑后的参数执行
Tool->>HITL: 返回结果
end
else 不需要审核
HITL->>Tool: 直接执行
Tool->>HITL: 返回结果
end
HITL->>Agent: 返回最终结果
// ✅ 推荐:只审核敏感操作
InterruptOn: map[string]interface{}{
"Bash": true, // Shell 命令
"fs_delete": true, // 文件删除
"api_payment": true, // 付费 API
}
// ❌ 不推荐:审核所有操作(影响效率)
InterruptOn: map[string]interface{}{
"Read": true, // 读取通常安全
"calculate": true, // 计算无副作用
}
InterruptOn: map[string]interface{}{
"email_send": map[string]interface{}{
"message": `📧 邮件发送审核
请确认:
- 收件人地址是否正确
- 邮件内容是否合适`,
},
}
ApprovalHandler: func(ctx context.Context, req *middleware.ReviewRequest) ([]middleware.Decision, error) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Minute)
defer cancel()
select {
case decision := <-getDecisionAsync(req):
return []middleware.Decision{decision}, nil
case <-ctx.Done():
return nil, fmt.Errorf("审核超时")
}
}
func logApproval(action middleware.ActionRequest, decision middleware.Decision) {
log := AuditLog{
Timestamp: time.Now(),
Tool: action.ToolName,
Input: action.Input,
Decision: string(decision.Type),
Reason: decision.Reason,
}
saveToAuditLog(log)
}
可以在 System Prompt 中告知 Agent HITL 机制:
const HITL_SYSTEM_PROMPT = `## Human-in-the-Loop (HITL)
某些敏感操作需要人工批准才能执行。当你调用这些工具时:
1. 系统会暂停执行,等待人工审核
2. 人工审核员可以:批准、拒绝或编辑参数
3. 如果操作被拒绝,请尝试其他方法或向用户说明
4. 清楚解释为什么需要执行该操作
5. 不要重复尝试被拒绝的操作`
// 使用方式
config.SystemPrompt += "\n\n" + middleware.HITL_SYSTEM_PROMPT
A: 是的,HITL 会引入延迟。建议只对敏感操作启用审核,并实现超时机制。
A: Agent 会收到拒绝信息,可以尝试其他方法或向用户说明情况。
A: 支持。ReviewRequest.ActionRequests 可以包含多个待审核操作。
A: 在 ApprovalHandler 中发送请求到 Web 服务,等待用户在浏览器中做出决策。参见 完整指南。