mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-10-02 15:23:57 +00:00
sdk: embed non-toolchain SDK in third_party, add NoMemory/Cleaner support
- Embed sdk/, example/, meta/, go.mod from homeagent-sdk (no .git)
- Core .gitignore excludes SDK toolchain: bin/, tools/, package/
- RegisterInputChannel + ChannelDef(NoMemory, Cleaner) in SDK
- IOManager input channel registry with GetInputChannelDef
- eventloop: apply channel Cleaner/NoMemory to interrupt text
- context engine: channelDefLookup applied in textForVector
- document store: ChannelCleaner param for archive functions
- All callers/adapters updated with ChannelDef{} default
This commit is contained in:
@ -178,6 +178,9 @@ func New(cfg AgentConfig) *Agent {
|
||||
if cfg.StageHost != nil {
|
||||
rc.SetToolDefLookup(cfg.StageHost.ToolDef)
|
||||
}
|
||||
if cfg.IO != nil {
|
||||
rc.SetChannelDefLookup(cfg.IO.GetInputChannelDef)
|
||||
}
|
||||
|
||||
return &Agent{
|
||||
id: cfg.ID,
|
||||
|
||||
@ -22,6 +22,7 @@ func (d *mockOutputDevice) Tools() []agentIO.ToolDef { return d.too
|
||||
func (d *mockOutputDevice) Start() error { return nil }
|
||||
func (d *mockOutputDevice) Stop() error { return nil }
|
||||
func (d *mockOutputDevice) OutputCapabilities() agentIO.OutputCapability { return d.caps }
|
||||
func (d *mockOutputDevice) ChannelDef() agentIO.ChannelDef { return agentIO.ChannelDef{} }
|
||||
func (d *mockOutputDevice) Execute(tool string, args map[string]interface{}) (interface{}, error) {
|
||||
if d.toolFn != nil {
|
||||
return d.toolFn(tool, args)
|
||||
|
||||
@ -34,13 +34,14 @@ type ContextEvent struct {
|
||||
const contextFlushInterval = 5 * time.Second
|
||||
|
||||
type RelevanceContext struct {
|
||||
mu sync.Mutex
|
||||
events []*ContextEvent
|
||||
embedder *memory.StaticEmbedder
|
||||
savePath string
|
||||
saveTimer *time.Timer
|
||||
dirty bool
|
||||
toolDefLookup func(name string) *sdk.ToolDef
|
||||
mu sync.Mutex
|
||||
events []*ContextEvent
|
||||
embedder *memory.StaticEmbedder
|
||||
savePath string
|
||||
saveTimer *time.Timer
|
||||
dirty bool
|
||||
toolDefLookup func(name string) *sdk.ToolDef
|
||||
channelDefLookup func(name string) (sdk.ChannelDef, bool)
|
||||
}
|
||||
|
||||
func NewRelevanceContext(savePath string, embedder *memory.StaticEmbedder) *RelevanceContext {
|
||||
@ -60,6 +61,12 @@ func (c *RelevanceContext) SetToolDefLookup(fn func(name string) *sdk.ToolDef) {
|
||||
c.toolDefLookup = fn
|
||||
}
|
||||
|
||||
func (c *RelevanceContext) SetChannelDefLookup(fn func(name string) (sdk.ChannelDef, bool)) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.channelDefLookup = fn
|
||||
}
|
||||
|
||||
func (c *RelevanceContext) load() {
|
||||
data, err := os.ReadFile(c.savePath)
|
||||
if err != nil {
|
||||
@ -75,7 +82,7 @@ func (c *RelevanceContext) load() {
|
||||
c.events = events
|
||||
}
|
||||
|
||||
func textForVector(evt *ContextEvent, toolDefLookup func(name string) *sdk.ToolDef) string {
|
||||
func textForVector(evt *ContextEvent, toolDefLookup func(name string) *sdk.ToolDef, channelDefLookup func(name string) (sdk.ChannelDef, bool)) string {
|
||||
var text string
|
||||
switch {
|
||||
case evt.Source == "agent" && evt.Response != "":
|
||||
@ -86,6 +93,16 @@ func textForVector(evt *ContextEvent, toolDefLookup func(name string) *sdk.ToolD
|
||||
text = evt.Input
|
||||
}
|
||||
|
||||
// 计算层:应用输入通道的 Cleaner(不改原文,仅在计算层清洗)
|
||||
if channelDefLookup != nil {
|
||||
if chDef, ok := channelDefLookup(evt.Source); ok && chDef.Cleaner != nil {
|
||||
text = chDef.Cleaner(text)
|
||||
}
|
||||
if chDef, ok := channelDefLookup(evt.Source); ok && chDef.NoMemory {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// 计算层:附加工具输出,NoMemory 跳过,其余经 Cleaner 过滤
|
||||
if toolDefLookup != nil {
|
||||
noMemory := make(map[string]bool)
|
||||
@ -130,8 +147,41 @@ func (c *RelevanceContext) toolOutputClean(name, output string) string {
|
||||
return output
|
||||
}
|
||||
|
||||
// inputChannelClean 根据输入通道的 Def 清洗输入文本,用于计算层。
|
||||
func (c *RelevanceContext) inputChannelClean(source, input string) string {
|
||||
if c.channelDefLookup == nil {
|
||||
return input
|
||||
}
|
||||
chDef, ok := c.channelDefLookup(source)
|
||||
if !ok {
|
||||
return input
|
||||
}
|
||||
if chDef.Cleaner != nil {
|
||||
return chDef.Cleaner(input)
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
// channelCleanerForDoc 返回 ChannelCleaner,使 document 包在存档时能按来源查找 Cleaner。
|
||||
func (c *RelevanceContext) channelCleanerForDoc() document.ChannelCleaner {
|
||||
if c.channelDefLookup == nil {
|
||||
return nil
|
||||
}
|
||||
return func(source string) func(string) string {
|
||||
chDef, ok := c.channelDefLookup(source)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return chDef.Cleaner
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RelevanceContext) computeVector(evt *ContextEvent) vector.Vector {
|
||||
return c.embedder.Vectorize(textForVector(evt, c.toolDefLookup))
|
||||
text := textForVector(evt, c.toolDefLookup, c.channelDefLookup)
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
return c.embedder.Vectorize(text)
|
||||
}
|
||||
|
||||
func (c *RelevanceContext) Save() error {
|
||||
@ -274,7 +324,7 @@ func (c *RelevanceContext) Prune(currentInput string, topK int, docStore *docume
|
||||
ToolResults: convertToolResults(s.event.ToolResults),
|
||||
}
|
||||
}
|
||||
doc, err := docStore.ContextToDoc("context_archived", entries, c.embedder, nil, c.toolOutputClean)
|
||||
doc, err := docStore.ContextToDoc("context_archived", entries, c.embedder, nil, c.toolOutputClean, c.channelCleanerForDoc())
|
||||
if err == nil && doc != nil {
|
||||
archived = len(entries)
|
||||
}
|
||||
|
||||
@ -281,6 +281,11 @@ func (a *Agent) processTextInput(evt *agentIO.InputEvent, input string) {
|
||||
if v, ok := evt.Payload["no_memory"].(bool); ok {
|
||||
noMemory = v
|
||||
}
|
||||
if !noMemory && a.io != nil {
|
||||
if chDef, ok := a.io.GetInputChannelDef(evt.Source); ok && chDef.NoMemory {
|
||||
noMemory = true
|
||||
}
|
||||
}
|
||||
|
||||
stageCtx := a.stageCtxFromInput(input, evt.Source, "")
|
||||
stageCtx.Extra["input_source"] = evt.Source
|
||||
@ -297,12 +302,20 @@ func (a *Agent) processTextInput(evt *agentIO.InputEvent, input string) {
|
||||
|
||||
input = stageCtx.RawMessage
|
||||
|
||||
// 计算层用的清洗文本(不改原文):通道 Cleaner 提取语义内容后用于向量化/提关键词
|
||||
cleanInput := input
|
||||
if a.io != nil {
|
||||
if chDef, ok := a.io.GetInputChannelDef(evt.Source); ok && chDef.Cleaner != nil {
|
||||
cleanInput = chDef.Cleaner(input)
|
||||
}
|
||||
}
|
||||
|
||||
a.publishEvent(events.EventRawInput, map[string]interface{}{
|
||||
"content": input,
|
||||
"source": evt.Source,
|
||||
})
|
||||
|
||||
archived := a.context.Prune(input, a.maxContextSize-1, a.docStore)
|
||||
archived := a.context.Prune(cleanInput, a.maxContextSize-1, a.docStore)
|
||||
if archived > 0 {
|
||||
log.Printf("[agent] pruned %d low-relevance events to document memory", archived)
|
||||
}
|
||||
@ -328,7 +341,7 @@ func (a *Agent) processTextInput(evt *agentIO.InputEvent, input string) {
|
||||
a.context.Append(ContextEvent{
|
||||
Timestamp: time.Now(),
|
||||
Source: "agent",
|
||||
Input: input,
|
||||
Input: cleanInput,
|
||||
Response: response,
|
||||
ToolsUsed: toolsUsed,
|
||||
ToolResults: toolResults,
|
||||
@ -337,7 +350,7 @@ func (a *Agent) processTextInput(evt *agentIO.InputEvent, input string) {
|
||||
a.emitResponse(evt, response)
|
||||
|
||||
if !stageCtx.NoMemory {
|
||||
a.emitMemoryCandidate(evt.Source, input, response, toolResults, toolsUsed)
|
||||
a.emitMemoryCandidate(evt.Source, cleanInput, response, toolResults, toolsUsed)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,8 +6,13 @@ import (
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
pubsdk "gitcode.com/JianFeeeee/homeagent-sdk/sdk"
|
||||
)
|
||||
|
||||
// ChannelDef 描述通道在记忆计算层的行为,与 ToolDef.NoMemory/Cleaner 语义一致。
|
||||
type ChannelDef = pubsdk.ChannelDef
|
||||
|
||||
type DeviceType int
|
||||
|
||||
const (
|
||||
@ -60,6 +65,7 @@ type Device interface {
|
||||
Start() error
|
||||
Stop() error
|
||||
OutputCapabilities() OutputCapability
|
||||
ChannelDef() ChannelDef
|
||||
}
|
||||
|
||||
type ToolHandler func(args map[string]interface{}) (interface{}, error)
|
||||
@ -90,20 +96,22 @@ type OutputEvent struct {
|
||||
}
|
||||
|
||||
type IOManager struct {
|
||||
mu sync.RWMutex
|
||||
devices map[string]Device
|
||||
inputCh chan *InputEvent
|
||||
interruptCh chan *InputEvent
|
||||
outputCh chan *OutputEvent
|
||||
nextReqID int64
|
||||
mu sync.RWMutex
|
||||
devices map[string]Device
|
||||
inputCh chan *InputEvent
|
||||
interruptCh chan *InputEvent
|
||||
outputCh chan *OutputEvent
|
||||
nextReqID int64
|
||||
inputChannels map[string]ChannelDef
|
||||
}
|
||||
|
||||
func NewIOManager() *IOManager {
|
||||
return &IOManager{
|
||||
devices: make(map[string]Device),
|
||||
inputCh: make(chan *InputEvent, 256),
|
||||
interruptCh: make(chan *InputEvent, 64),
|
||||
outputCh: make(chan *OutputEvent, 256),
|
||||
devices: make(map[string]Device),
|
||||
inputCh: make(chan *InputEvent, 256),
|
||||
interruptCh: make(chan *InputEvent, 64),
|
||||
outputCh: make(chan *OutputEvent, 256),
|
||||
inputChannels: make(map[string]ChannelDef),
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,6 +341,28 @@ func (m *IOManager) EmitTextTo(target, outputChannel, text string) {
|
||||
func (m *IOManager) InputChan() <-chan *InputEvent { return m.inputCh }
|
||||
func (m *IOManager) OutputChan() <-chan *OutputEvent { return m.outputCh }
|
||||
|
||||
// RegisterInputChannel 注册输入通道的记忆行为
|
||||
func (m *IOManager) RegisterInputChannel(name string, def ChannelDef) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.inputChannels[name] = def
|
||||
}
|
||||
|
||||
// UnregisterInputChannel 注销输入通道
|
||||
func (m *IOManager) UnregisterInputChannel(name string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
delete(m.inputChannels, name)
|
||||
}
|
||||
|
||||
// GetInputChannelDef 查询输入通道的记忆行为定义
|
||||
func (m *IOManager) GetInputChannelDef(name string) (ChannelDef, bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
def, ok := m.inputChannels[name]
|
||||
return def, ok
|
||||
}
|
||||
|
||||
func (m *IOManager) GetAllTools() []ToolDef {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
@ -434,6 +464,7 @@ func (d *Microphone) OutputCapabilities() OutputCapability { return 0 } // 纯
|
||||
func (d *Microphone) Description() string { return fmt.Sprintf("麦克风 (%s, %dHz)", d.name, d.sampleRate) }
|
||||
func (d *Microphone) Start() error { return nil }
|
||||
func (d *Microphone) Stop() error { return nil }
|
||||
func (d *Microphone) ChannelDef() ChannelDef { return ChannelDef{} }
|
||||
|
||||
func (d *Microphone) Tools() []ToolDef {
|
||||
return []ToolDef{{
|
||||
@ -468,6 +499,7 @@ func (d *Speaker) OutputCapabilities() OutputCapability { return CapText | CapAu
|
||||
func (d *Speaker) Description() string { return fmt.Sprintf("扬声器 (%s)", d.name) }
|
||||
func (d *Speaker) Start() error { return nil }
|
||||
func (d *Speaker) Stop() error { return nil }
|
||||
func (d *Speaker) ChannelDef() ChannelDef { return ChannelDef{} }
|
||||
|
||||
func (d *Speaker) Tools() []ToolDef {
|
||||
return []ToolDef{{
|
||||
@ -504,6 +536,7 @@ func (d *Camera) OutputCapabilities() OutputCapability { return CapImage } //
|
||||
func (d *Camera) Description() string { return fmt.Sprintf("摄像头 (%s)", d.name) }
|
||||
func (d *Camera) Start() error { return nil }
|
||||
func (d *Camera) Stop() error { return nil }
|
||||
func (d *Camera) ChannelDef() ChannelDef { return ChannelDef{} }
|
||||
|
||||
func (d *Camera) Tools() []ToolDef {
|
||||
return []ToolDef{
|
||||
@ -551,6 +584,7 @@ func (d *RobotArm) OutputCapabilities() OutputCapability { return CapStructured
|
||||
func (d *RobotArm) Description() string { return fmt.Sprintf("机械臂 (%s)", d.name) }
|
||||
func (d *RobotArm) Start() error { return nil }
|
||||
func (d *RobotArm) Stop() error { return nil }
|
||||
func (d *RobotArm) ChannelDef() ChannelDef { return ChannelDef{} }
|
||||
|
||||
func (d *RobotArm) Tools() []ToolDef {
|
||||
return []ToolDef{
|
||||
@ -602,6 +636,7 @@ func (d *GPIODevice) OutputCapabilities() OutputCapability { return CapStructure
|
||||
func (d *GPIODevice) Description() string { return "GPIO 通用引脚" }
|
||||
func (d *GPIODevice) Start() error { return nil }
|
||||
func (d *GPIODevice) Stop() error { return nil }
|
||||
func (d *GPIODevice) ChannelDef() ChannelDef { return ChannelDef{} }
|
||||
|
||||
func (d *GPIODevice) Tools() []ToolDef {
|
||||
return []ToolDef{
|
||||
|
||||
@ -43,6 +43,7 @@ func (d *mockDevice) Stop() error {
|
||||
return nil
|
||||
}
|
||||
func (d *mockDevice) OutputCapabilities() OutputCapability { return d.caps }
|
||||
func (d *mockDevice) ChannelDef() ChannelDef { return ChannelDef{} }
|
||||
func (d *mockDevice) Execute(tool string, args map[string]interface{}) (interface{}, error) {
|
||||
if d.executeFn != nil {
|
||||
return d.executeFn(tool, args)
|
||||
|
||||
Reference in New Issue
Block a user