diff --git a/example/qq/plugin.go b/example/qq/plugin.go index 33c7241..18da5f7 100644 --- a/example/qq/plugin.go +++ b/example/qq/plugin.go @@ -165,7 +165,28 @@ meta JSON 格式: "reply_to": 12345 // 可选,回复指定消息 ID } type 枚举: text(文字)/ voice(语音转文字后发送)/ image(图片URL)/ file(文件URL)`, - p.handleChannelOutput) + sdk.ChannelDef{}, p.handleChannelOutput) + + // ---- 注册输入通道(记忆计算层行为) ---- + inputCleaner := func(text string) string { + // 从中断模板中提取语义内容:消息来源和发送者昵称 + // 模板: 来自「昵称」的私聊/群聊消息(message_id=N)... + // 模板: 【重要!老大消息】来自「昵称」... + cleaned := text + // 去掉模板前缀 + if strings.HasPrefix(cleaned, "【重要!老大消息】") { + cleaned = strings.TrimPrefix(cleaned, "【重要!老大消息】") + } + // 提取 "来自「XXX」" 中的昵称作为关键词 + if start := strings.Index(cleaned, "来自「"); start >= 0 { + if end := strings.Index(cleaned[start:], "」"); end >= 0 { + nick := cleaned[start+len("来自「") : start+end] + cleaned = nick + } + } + return cleaned + } + s.RegisterInputChannel("qq", sdk.ChannelDef{NoMemory: true, Cleaner: inputCleaner}) // 查询类工具输出清洗器:提取 JSON 中的 content/文本字段参与向量化 cleaner := func(output string) string { diff --git a/sdk/plugin.go b/sdk/plugin.go index 14a777c..081285c 100644 --- a/sdk/plugin.go +++ b/sdk/plugin.go @@ -35,6 +35,14 @@ const ( StageAfterOutput Stage = "after_output" ) +// ChannelDef 描述通道在记忆计算层的行为,与 ToolDef.NoMemory/Cleaner 语义一致。 +// NoMemory: 此通道输入/输出不参与记忆计算(向量化/关键词提取/蒸馏),但原文保留在上下文中 +// Cleaner: 计算层过滤函数,不改原文;仅在向量化/jieba/蒸馏/存档提取关键词时调用 +type ChannelDef struct { + NoMemory bool + Cleaner func(string) string +} + // StageContext provides context for stage handlers. type StageContext struct { mu sync.RWMutex @@ -156,8 +164,11 @@ type StageRegistrar func(stage Stage, handler StageHandler) // APIRegistrar registers a plugin API for external access. type APIRegistrar func(name string) error +// InputChannelRegistrar registers an input channel with its memory behavior. +type InputChannelRegistrar func(name string, def ChannelDef) error + // OutputChannelRegistrar registers an output channel that the output_send tool can use. -type OutputChannelRegistrar func(name string, caps int, desc string, handler ToolHandler) error +type OutputChannelRegistrar func(name string, caps int, desc string, def ChannelDef, handler ToolHandler) error // Output capability flags const ( @@ -176,6 +187,7 @@ type PluginSDK struct { regStage StageRegistrar regAPI APIRegistrar regOutput OutputChannelRegistrar + regInput InputChannelRegistrar io IOInjector mem MemoryAPI textMem TextMemoryAPI @@ -289,10 +301,21 @@ func (s *PluginSDK) RegisterPluginAPI(name string) error { // name: channel name (e.g. "qq", "webui") // caps: bitmask of supported output capabilities (CapText, CapFile, etc.) // desc: description of the channel, expected meta format, and type enum +// def: 通道在记忆计算层的行为(NoMemory/Cleaner) // handler: receives args map with keys: payload (string), type (string), meta (string|optional) -func (s *PluginSDK) RegisterOutputChannel(name string, caps int, desc string, handler ToolHandler) error { +func (s *PluginSDK) RegisterOutputChannel(name string, caps int, desc string, def ChannelDef, handler ToolHandler) error { if s.regOutput != nil { - return s.regOutput(name, caps, desc, handler) + return s.regOutput(name, caps, desc, def, handler) + } + return nil +} + +// RegisterInputChannel registers an input channel with its memory behavior. +// def.NoMemory: 此通道输入不参与记忆计算 +// def.Cleaner: 计算层对输入文本清洗后(不改原文)再向量化/提关键词 +func (s *PluginSDK) RegisterInputChannel(name string, def ChannelDef) error { + if s.regInput != nil { + return s.regInput(name, def) } return nil } @@ -300,6 +323,9 @@ func (s *PluginSDK) RegisterOutputChannel(name string, caps int, desc string, ha // SetOutputChannelRegistrar sets the output channel registrar (called by the core at startup). func (s *PluginSDK) SetOutputChannelRegistrar(r OutputChannelRegistrar) { s.regOutput = r } +// SetInputChannelRegistrar sets the input channel registrar (called by the core at startup). +func (s *PluginSDK) SetInputChannelRegistrar(r InputChannelRegistrar) { s.regInput = r } + // SetIOInjector sets the IO injector (called by the core at startup). func (s *PluginSDK) SetIOInjector(io IOInjector) { s.io = io }