mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-23 10:28:06 +00:00
v0.7.2: 根目录清理 + Agent 心跳重构 + 内嵌 ONNX 模型
- 根目录清理: branding/docs/knowledge -> assets/, package/tools/deploy -> deploy/ - meta.go: Version 0.7.2, SDKCompatibleVersion 语义改为最高兼容 - Makefile: 版本回退 0.7.2 - registry.go: 系统提示词改用 meta.Version 格式化 - Agent 心跳: reorgGraph 拆分为三个独立循环(archive/merge/review),各自可配间隔 - GraphDB: 新增 sentences 表 + 关系句子溯源 + ClearSentenceID + CleanupOrphanedSentences - Knowledge: 支持词嵌入向量化器 - NLP 四阶段流水线: Parse -> Extract -> Verify -> Fuse + SentenceRef - 移除远程 HTTP 解析器(remote_parser.go) - 新增内嵌 ONNX 模型(vocab + dep_parser.onnx): +build onnxruntime: 全量 ONNX Runtime 推理 !build onnxruntime: 内嵌词表规则式降级解析器 - config: core.agent.onnx_model_path 替代 dep_parser_url
This commit is contained in:
@ -7,8 +7,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
agentAPI "gitcode.com/JianFeeeee/HomeAgent/internal/agent/api"
|
||||
agentPkg "gitcode.com/JianFeeeee/HomeAgent/internal/agent"
|
||||
agentAPI "gitcode.com/JianFeeeee/HomeAgent/internal/agent/api"
|
||||
agentIO "gitcode.com/JianFeeeee/HomeAgent/internal/agent/io"
|
||||
"gitcode.com/JianFeeeee/HomeAgent/internal/events"
|
||||
"gitcode.com/JianFeeeee/HomeAgent/internal/knowledge"
|
||||
@ -26,19 +26,19 @@ import (
|
||||
|
||||
// Agent — 单 agent,不区分会话/实例
|
||||
type Agent struct {
|
||||
mu sync.Mutex
|
||||
id types.AgentID
|
||||
provider agentAPI.Provider
|
||||
mu sync.Mutex
|
||||
id types.AgentID
|
||||
provider agentAPI.Provider
|
||||
providerManager *agentAPI.ProviderManager
|
||||
io *agentIO.IOManager
|
||||
memory *memory.GraphDB
|
||||
indexer *memory.Indexer
|
||||
skills *skill.Manager
|
||||
tracker *tracker.Tracker
|
||||
context *RelevanceContext
|
||||
systemPrompt string
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
io *agentIO.IOManager
|
||||
memory *memory.GraphDB
|
||||
indexer *memory.Indexer
|
||||
skills *skill.Manager
|
||||
tracker *tracker.Tracker
|
||||
context *RelevanceContext
|
||||
systemPrompt string
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
||||
// 文档记忆(第二层)
|
||||
docStore *document.Store
|
||||
@ -62,6 +62,11 @@ type Agent struct {
|
||||
// 定期心跳蒸馏
|
||||
distillInterval time.Duration
|
||||
|
||||
// 三个独立心跳任务间隔
|
||||
archiveInterval time.Duration // 冷文档归档
|
||||
reviewInterval time.Duration // 关系复审
|
||||
mergeInterval time.Duration // 实体合并检测
|
||||
|
||||
// 上下文裁剪:活跃上下文最大条数,超出按相关性裁剪
|
||||
maxContextSize int
|
||||
|
||||
@ -110,30 +115,33 @@ type Agent struct {
|
||||
}
|
||||
|
||||
type AgentConfig struct {
|
||||
ID types.AgentID
|
||||
SystemPrompt string
|
||||
Provider agentAPI.Provider
|
||||
ID types.AgentID
|
||||
SystemPrompt string
|
||||
Provider agentAPI.Provider
|
||||
ProviderManager *agentAPI.ProviderManager
|
||||
IO *agentIO.IOManager
|
||||
Memory *memory.GraphDB
|
||||
Indexer *memory.Indexer
|
||||
Skills *skill.Manager
|
||||
Tracker *tracker.Tracker
|
||||
IO *agentIO.IOManager
|
||||
Memory *memory.GraphDB
|
||||
Indexer *memory.Indexer
|
||||
Skills *skill.Manager
|
||||
Tracker *tracker.Tracker
|
||||
|
||||
DocStore *document.Store
|
||||
Knowledge *knowledge.Store
|
||||
SocialStore *social.SocialStore
|
||||
TextMemory *text.Memory
|
||||
Personality *agentPkg.Personality
|
||||
PluginReg *plugin.Registry
|
||||
PluginDir string
|
||||
DistillInterval time.Duration
|
||||
MaxContextSize int // 活跃上下文最大条数,超出按相关性裁剪
|
||||
ContextSavePath string // 上下文持久化路径,空则不持久化
|
||||
EmbeddingModelPath string // 预训练词嵌入模型路径(word2vec 文本格式),空则不使用
|
||||
StageHost *StageHost
|
||||
EventBus *events.Bus
|
||||
ThinkingEnabled bool
|
||||
DocStore *document.Store
|
||||
Knowledge *knowledge.Store
|
||||
SocialStore *social.SocialStore
|
||||
TextMemory *text.Memory
|
||||
Personality *agentPkg.Personality
|
||||
PluginReg *plugin.Registry
|
||||
PluginDir string
|
||||
DistillInterval time.Duration
|
||||
ArchiveInterval time.Duration // 冷文档归档间隔(L3→L4),0 则使用 DistillInterval
|
||||
ReviewInterval time.Duration // 关系复审间隔,0 则使用 DistillInterval
|
||||
MergeInterval time.Duration // 实体合并检测间隔,0 则使用 DistillInterval
|
||||
MaxContextSize int // 活跃上下文最大条数,超出按相关性裁剪
|
||||
ContextSavePath string // 上下文持久化路径,空则不持久化
|
||||
EmbeddingModelPath string // 预训练词嵌入模型路径(word2vec 文本格式),空则不使用
|
||||
StageHost *StageHost
|
||||
EventBus *events.Bus
|
||||
ThinkingEnabled bool
|
||||
|
||||
InputProcessing types.InputProcessingConfig // 非文本输入处理配置
|
||||
}
|
||||
@ -143,6 +151,15 @@ func New(cfg AgentConfig) *Agent {
|
||||
if cfg.DistillInterval <= 0 {
|
||||
cfg.DistillInterval = 30 * time.Minute
|
||||
}
|
||||
if cfg.ArchiveInterval <= 0 {
|
||||
cfg.ArchiveInterval = cfg.DistillInterval
|
||||
}
|
||||
if cfg.ReviewInterval <= 0 {
|
||||
cfg.ReviewInterval = cfg.DistillInterval
|
||||
}
|
||||
if cfg.MergeInterval <= 0 {
|
||||
cfg.MergeInterval = cfg.DistillInterval
|
||||
}
|
||||
if cfg.MaxContextSize <= 0 {
|
||||
cfg.MaxContextSize = 30
|
||||
}
|
||||
@ -152,6 +169,10 @@ func New(cfg AgentConfig) *Agent {
|
||||
cfg.DocStore.SetVectorizer(embedder)
|
||||
cfg.DocStore.ReindexWithVectorizer(embedder)
|
||||
}
|
||||
if cfg.Knowledge != nil {
|
||||
cfg.Knowledge.SetVectorizer(embedder)
|
||||
cfg.Knowledge.ReindexWithVectorizer(embedder)
|
||||
}
|
||||
|
||||
rc := NewRelevanceContext(cfg.ContextSavePath, embedder)
|
||||
if cfg.StageHost != nil {
|
||||
@ -179,19 +200,21 @@ func New(cfg AgentConfig) *Agent {
|
||||
personality: cfg.Personality,
|
||||
pluginReg: cfg.PluginReg,
|
||||
pluginDir: cfg.PluginDir,
|
||||
distillInterval: cfg.DistillInterval,
|
||||
maxContextSize: cfg.MaxContextSize,
|
||||
stageHost: cfg.StageHost,
|
||||
eventBus: cfg.EventBus,
|
||||
selfInputCh: make(chan string, 64),
|
||||
childResults: make(map[string]string),
|
||||
interceptCh: make(chan *agentIO.InputEvent, 64),
|
||||
pluginHealth: newPluginHealthTracker(),
|
||||
thinkingEnabled: cfg.ThinkingEnabled,
|
||||
inputCfg: cfg.InputProcessing,
|
||||
embedder: embedder,
|
||||
noMergeMarkers: make(map[string]int),
|
||||
|
||||
distillInterval: cfg.DistillInterval,
|
||||
archiveInterval: cfg.ArchiveInterval,
|
||||
reviewInterval: cfg.ReviewInterval,
|
||||
mergeInterval: cfg.MergeInterval,
|
||||
maxContextSize: cfg.MaxContextSize,
|
||||
stageHost: cfg.StageHost,
|
||||
eventBus: cfg.EventBus,
|
||||
selfInputCh: make(chan string, 64),
|
||||
childResults: make(map[string]string),
|
||||
interceptCh: make(chan *agentIO.InputEvent, 64),
|
||||
pluginHealth: newPluginHealthTracker(),
|
||||
thinkingEnabled: cfg.ThinkingEnabled,
|
||||
inputCfg: cfg.InputProcessing,
|
||||
embedder: embedder,
|
||||
noMergeMarkers: make(map[string]int),
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,6 +222,9 @@ func (a *Agent) Start() {
|
||||
go a.eventLoop()
|
||||
go a.interceptLoop()
|
||||
go a.distillLoop()
|
||||
go a.archiveLoop()
|
||||
go a.mergeLoop()
|
||||
go a.reviewLoop()
|
||||
log.Printf("[agent] %s started, waiting for IO interrupts", a.id)
|
||||
}
|
||||
|
||||
|
||||
@ -28,6 +28,11 @@ func (a *Agent) enqueueConsolidationTask(task ConsolidationTask) {
|
||||
log.Printf("[agent] enqueued consolidation task: %s", task.Reason)
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// 四个独立心跳循环,各自拥有独立的 ticker 和配置
|
||||
// ──────────────────────────────────────────────
|
||||
|
||||
// distillLoop 上下文裁剪(L2 蒸馏),使用 distillInterval
|
||||
func (a *Agent) distillLoop() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@ -36,7 +41,7 @@ func (a *Agent) distillLoop() {
|
||||
go a.distillLoop()
|
||||
}
|
||||
}()
|
||||
if a.docStore == nil && a.memory == nil {
|
||||
if a.docStore == nil {
|
||||
return
|
||||
}
|
||||
ticker := time.NewTicker(a.distillInterval)
|
||||
@ -47,7 +52,6 @@ func (a *Agent) distillLoop() {
|
||||
case <-ticker.C:
|
||||
log.Printf("[agent] heartbeat distill tick")
|
||||
a.distillContext()
|
||||
a.reorgGraph()
|
||||
a.autoReloadPlugins()
|
||||
case <-a.ctx.Done():
|
||||
return
|
||||
@ -55,6 +59,88 @@ func (a *Agent) distillLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
// archiveLoop 冷文档归档(L3→L4),使用 archiveInterval
|
||||
func (a *Agent) archiveLoop() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Printf("[agent] archiveLoop panic recovered: %v\n%s", r, debug.Stack())
|
||||
time.Sleep(time.Second)
|
||||
go a.archiveLoop()
|
||||
}
|
||||
}()
|
||||
if a.memory == nil {
|
||||
return
|
||||
}
|
||||
ticker := time.NewTicker(a.archiveInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
log.Printf("[agent] heartbeat archive tick")
|
||||
a.archiveColdDocs()
|
||||
case <-a.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mergeLoop 实体合并检测(GraphDB → LLM 裁决),使用 mergeInterval
|
||||
func (a *Agent) mergeLoop() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Printf("[agent] mergeLoop panic recovered: %v\n%s", r, debug.Stack())
|
||||
time.Sleep(time.Second)
|
||||
go a.mergeLoop()
|
||||
}
|
||||
}()
|
||||
if a.memory == nil {
|
||||
return
|
||||
}
|
||||
ticker := time.NewTicker(a.mergeInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
log.Printf("[agent] heartbeat merge tick")
|
||||
a.detectEntityMerge()
|
||||
case <-a.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reviewLoop 关系复审(GraphDB → ClearSentenceID → CleanupOrphanedSentences),使用 reviewInterval
|
||||
func (a *Agent) reviewLoop() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Printf("[agent] reviewLoop panic recovered: %v\n%s", r, debug.Stack())
|
||||
time.Sleep(time.Second)
|
||||
go a.reviewLoop()
|
||||
}
|
||||
}()
|
||||
if a.memory == nil {
|
||||
return
|
||||
}
|
||||
ticker := time.NewTicker(a.reviewInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
log.Printf("[agent] heartbeat review tick")
|
||||
a.reviewRelations()
|
||||
case <-a.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// 蒸馏逻辑
|
||||
// ──────────────────────────────────────────────
|
||||
|
||||
func (a *Agent) distillContext() {
|
||||
if a.docStore == nil {
|
||||
return
|
||||
@ -68,14 +154,16 @@ func (a *Agent) distillContext() {
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// 冷文档归档:docStore → GraphDB (L3→L4)
|
||||
// ──────────────────────────────────────────────
|
||||
|
||||
|
||||
func (a *Agent) reorgGraph() {
|
||||
func (a *Agent) archiveColdDocs() {
|
||||
if a.memory == nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[agent] graph reorg start")
|
||||
log.Printf("[agent] cold doc archival start")
|
||||
|
||||
if a.indexer != nil {
|
||||
if err := a.indexer.Sync(); err != nil {
|
||||
@ -102,6 +190,18 @@ func (a *Agent) reorgGraph() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// 实体合并检测:GraphDB → LLM 裁决
|
||||
// ──────────────────────────────────────────────
|
||||
|
||||
func (a *Agent) detectEntityMerge() {
|
||||
if a.memory == nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[agent] entity merge detection start")
|
||||
|
||||
result, err := a.memory.Recall(nil, nil, 1, "")
|
||||
if err != nil || result == nil || len(result.Entities) < 2 {
|
||||
@ -167,9 +267,68 @@ func (a *Agent) reorgGraph() {
|
||||
}
|
||||
|
||||
if llmCandidates > 0 {
|
||||
log.Printf("[agent] graph reorg: %d merge candidates sent for LLM decision", llmCandidates)
|
||||
log.Printf("[agent] entity merge: %d merge candidates sent for LLM decision", llmCandidates)
|
||||
} else {
|
||||
log.Printf("[agent] graph reorg: no similar entities found")
|
||||
log.Printf("[agent] entity merge: no similar entities found")
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// 关系复审:GraphDB → ClearSentenceID → CleanupOrphanedSentences
|
||||
// ──────────────────────────────────────────────
|
||||
|
||||
func (a *Agent) reviewRelations() {
|
||||
if a.memory == nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[agent] relation review start")
|
||||
|
||||
reviewCount := 0
|
||||
const maxReviewBatch = 5
|
||||
relResult, err := a.memory.Recall(nil, nil, 1, "")
|
||||
if err != nil || relResult == nil {
|
||||
return
|
||||
}
|
||||
for _, rel := range relResult.Relations {
|
||||
if reviewCount >= maxReviewBatch {
|
||||
break
|
||||
}
|
||||
if rel.SentenceID == 0 || rel.SentenceText == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
a.enqueueConsolidationTask(ConsolidationTask{
|
||||
Type: "relation_review",
|
||||
Reason: fmt.Sprintf(
|
||||
"【关系复审】原始句子: '%s'\n当前三元组: (%s → %s → %s) 置信度 %.2f\n请判断是否需要修正(如相对引用未解析、主宾颠倒、噪音三元组等),如需修正请用 memory_edit 工具",
|
||||
rel.SentenceText, rel.SourceName, rel.RelationType, rel.TargetName, rel.Confidence,
|
||||
),
|
||||
Data: map[string]interface{}{
|
||||
"relation_id": rel.ID,
|
||||
"source": rel.SourceName,
|
||||
"relation_type": rel.RelationType,
|
||||
"target": rel.TargetName,
|
||||
"confidence": rel.Confidence,
|
||||
"sentence": rel.SentenceText,
|
||||
},
|
||||
})
|
||||
|
||||
// 清除句子引用(复审后解除关联)
|
||||
if err := a.memory.ClearSentenceID(rel.ID); err != nil {
|
||||
log.Printf("[agent] clear sentence_id for relation %d: %v", rel.ID, err)
|
||||
}
|
||||
reviewCount++
|
||||
}
|
||||
|
||||
if reviewCount > 0 {
|
||||
// 清理无引用的句子
|
||||
if deleted, err := a.memory.CleanupOrphanedSentences(); err != nil {
|
||||
log.Printf("[agent] cleanup orphaned sentences: %v", err)
|
||||
} else if deleted > 0 {
|
||||
log.Printf("[agent] cleanup %d orphaned sentences", deleted)
|
||||
}
|
||||
log.Printf("[agent] relation review: %d relations sent for review", reviewCount)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user