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:
root
2026-07-29 14:48:14 +08:00
parent 51cce51352
commit 3621ac309e
29 changed files with 1265 additions and 94 deletions

View File

@ -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,

View File

@ -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)

View File

@ -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)
}

View File

@ -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)
}
}