Skip to content

Feature: Event-driven Hooks System #1795

@yinwm

Description

@yinwm

Feature: Event-driven Hooks System

背景

OpenClaw 有一个很强大的 Hooks 系统,可以在特定事件(如消息发送、命令执行)时自动触发用户自定义代码。

问题:目前 PicoClaw 只支持主动调用(Skills)和定时触发(Cron),不支持事件驱动的自动化。

设计目标

  • 解耦核心代码和扩展功能
  • 支持用户自定义 Hook
  • 轻量级,符合 PicoClaw 简洁原则
  • 可插拔(启用/禁用)

最小化设计

1. 核心接口

// pkg/hooks/hooks.go
package hooks

import "time"

// Event 表示一个 Hook 事件
type Event struct {
    Type      string                 // 事件类型,如 "message:sent"
    Action    string                 // 具体动作,如 "sent"
    Timestamp time.Time              // 事件发生时间
    Context   map[string]interface{} // 事件上下文
}

// Handler Hook 处理函数
type Handler func(event Event) error

// Manager Hook 管理器
type Manager struct {
    handlers map[string][]Handler
    enabled  map[string]bool
}

// NewManager 创建 Hook 管理器
func NewManager() *Manager {
    return &Manager{
        handlers: make(map[string][]Handler),
        enabled:  make(map[string]bool),
    }
}

// On 注册 Hook 处理器
func (m *Manager) On(eventType string, handler Handler) {
    m.handlers[eventType] = append(m.handlers[eventType], handler)
}

// Emit 触发事件
func (m *Manager) Emit(event Event) {
    handlers, ok := m.handlers[event.Type]
    if !ok {
        return
    }
    
    for _, h := range handlers {
        go func(handler Handler) {
            // 异步执行,不阻塞主流程
            if err := handler(event); err != nil {
                log.Printf("[hooks] handler error: %v", err)
            }
        }(h)
    }
}

// Enable 启用 Hook
func (m *Manager) Enable(eventType string) {
    m.enabled[eventType] = true
}

// Disable 禁用 Hook
func (m *Manager) Disable(eventType string) {
    m.enabled[eventType] = false
}

2. 预定义事件类型

// pkg/hooks/events.go
package hooks

const (
    // 消息相关
    EventMessageReceived = "message:received" // 收到消息
    EventMessageSent     = "message:sent"     // 发送消息后
    
    // 命令相关
    EventCommandNew    = "command:new"    // /new 命令
    EventCommandReset  = "command:reset"  // /reset 命令
    EventCommandStop   = "command:stop"   // /stop 命令
    
    // 任务相关
    EventTaskCreated   = "task:created"   // 任务创建
    EventTaskCompleted = "task:completed" // 任务完成
    
    // Skill 相关
    EventSkillLoaded   = "skill:loaded"   // Skill 加载
    EventSkillExecuted = "skill:executed" // Skill 执行完成
    
    // 生命周期
    EventGatewayStartup = "gateway:startup" // Gateway 启动
    EventSessionStart   = "session:start"   // 会话开始
)

3. 集成示例

// 在 Agent 发送消息时触发 Hook
func (a *Agent) SendMessage(content string) error {
    // 1. 发送消息
    err := a.channel.Send(content)
    if err != nil {
        return err
    }
    
    // 2. 触发 post-response hook
    a.hooks.Emit(hooks.Event{
        Type:      hooks.EventMessageSent,
        Action:    "sent",
        Timestamp: time.Now(),
        Context: map[string]interface{}{
            "content":   content,
            "channel":   a.channel.Name(),
            "recipient": a.recipient,
        },
    })
    
    return nil
}

4. 用户自定义 Hook

// hooks/log-messages/hook.go
package main

import (
    "encoding/json"
    "os"
    
    "github.com/sipeed/picoclaw/pkg/hooks"
)

func init() {
    // 自动注册 Hook
    hooks.Register("message-logger", hooks.EventMessageSent, logMessage)
}

func logMessage(event hooks.Event) error {
    entry := map[string]interface{}{
        "timestamp": event.Timestamp,
        "content":   event.Context["content"],
        "channel":   event.Context["channel"],
    }
    
    data, _ := json.Marshal(entry)
    f, _ := os.OpenFile("messages.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    defer f.Close()
    f.WriteString(string(data) + "\n")
    
    return nil
}

配置方式

{
  "hooks": {
    "enabled": true,
    "entries": {
      "message-logger": {
        "enabled": true,
        "events": ["message:sent"]
      },
      "auto-backup": {
        "enabled": false,
        "events": ["task:completed"]
      }
    }
  }
}

实现优先级

阶段 内容 工作量
P0 核心接口 + 2-3 个事件 1-2 天
P1 配置文件支持 1 天
P2 CLI 命令 (list/enable/disable) 1 天
P3 Hook 包发现和加载 2-3 天

讨论

问题 1:是否需要 Hooks?

PicoClaw 当前定位是轻量级助手,Hooks 增加了复杂度。

支持理由

  • 解耦核心代码和扩展功能
  • 支持用户自定义自动化
  • 对标 OpenClaw 等竞品

反对理由

  • 增加代码复杂度
  • 可能用不到(YAGNI 原则)
  • Skills 系统已经够用

问题 2:如果实现,范围多大?

最小化:只支持 3-5 个核心事件,配置文件管理
完整版:参考 OpenClaw 的完整实现(自动发现、Hook 包、CLI 管理)

问题 3:与 Skills 的关系?

  • Skills:主动调用,用户显式触发
  • Hooks:被动响应,系统自动触发

是否需要?还是 Skills + Cron 就够了?

参考


请投票

  • 👍 支持实现 Hooks
  • 👎 暂时不需要
  • 🤔 需要更多讨论

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions