From 4218890aed9c21ca85e16f0c03d51a20f2da9eec Mon Sep 17 00:00:00 2001 From: JianFeeeee <109188060+JianFeeeee@users.noreply.github.com> Date: Tue, 28 Jul 2026 21:56:53 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E4=BF=AE=E5=A4=8D=E8=AE=B0=E5=BF=86?= =?UTF-8?q?=E5=88=86=E5=B1=82=E5=BC=95=E7=94=A8=E9=94=99=E8=AF=AF=20+=20?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=86=85=E7=BD=AE/=E5=A4=96=E9=83=A8?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E8=AF=B4=E6=98=8E=20+=20=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E4=B8=AD=E8=8B=B1=E6=96=87=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/docs/en/ARCHITECTURE.md | 54 +++++++++++++++++++++------------- assets/docs/en/OVERVIEW.md | 2 +- assets/docs/zh/ARCHITECTURE.md | 54 +++++++++++++++++++++------------- assets/docs/zh/OVERVIEW.md | 2 +- internal/agent/core/agent.go | 2 +- internal/agent/core/distill.go | 4 +-- internal/config/registry.go | 2 +- 7 files changed, 74 insertions(+), 46 deletions(-) diff --git a/assets/docs/en/ARCHITECTURE.md b/assets/docs/en/ARCHITECTURE.md index edd0518..c14b6a4 100644 --- a/assets/docs/en/ARCHITECTURE.md +++ b/assets/docs/en/ARCHITECTURE.md @@ -120,17 +120,13 @@ Setting `ctx.Response` at any stage jumps to `after_output`. └── LLM active: memory_recall / memory_merge / memory_purge / memory_edit / memory_delete_entity Social: person_query / set_trait / relate (wraps GraphDB) -④ Distillation Pipeline (30min heartbeat) - distillContext → window > 2×maxSize → force Prune - syncGraphToDocs → Graph snapshot to Document (cross-layer searchable) - reorgGraph: - Step1: indexer.Sync — rebuild entity vector index - Step2: docStore.Reindex — rebuild document vector index - Step3: Cold docs → docToTriples → GraphDB.Commit - Step4: Entity similarity (Bigram Jaccard > 0.75) → consolidation → LLM decides merge - Step5: evaluateGraphQuality → LLM decides keep/delete +④ Four Independent Heartbeat Loops (separate tickers and config intervals) + distillLoop (distillInterval, default 30m): Context pruning — Context.Prune → Document + archiveLoop (archiveInterval, default 60m): Cold doc archival — docToTriples → GraphDB + mergeLoop (mergeInterval, default 120m): Entity merge detection — similarity → LLM decision + reviewLoop (reviewInterval, default 120m): Relation review — SentenceRef recall → LLM fix -⑤ Pipeline Rule Distiller (every heartbeat) +⑤ Pipeline Rule Distiller (every 10min heartbeat) distillOnce → regex match personal info: 我叫X / 我住在X / 我喜欢X / 我X岁 / 我的工作是X → triples → GraphDB.Commit @@ -209,16 +205,15 @@ All vectorization unified under `StaticEmbedder` (`internal/memory/static_embedd ### Context Pruning ``` -Heartbeat 30min: - ├── distillContext() — Distill current context - ├── syncGraphToDocs() — Graph → Document sync - └── reorgGraph() - ├── Indexer.Sync() - ├── DocStore.Reindex() - ├── Cold docs → Graph - └── Entity conflicts → enqueueConsolidationTask() - │ - selfInputCh → LLM decides merge/skip +Four independent heartbeat loops (each with configurable interval): + ├── distillLoop (distillInterval, default 30m) + │ └── distillContext() — Context.Prune → Document + ├── archiveLoop (archiveInterval, default 60m) + │ └── archiveColdDocs() — Cold docs → docToTriples → GraphDB + ├── mergeLoop (mergeInterval, default 120m) + │ └── detectEntityMerge() — Entity similarity detection → LLM decision + └── reviewLoop (reviewInterval, default 120m) + └── reviewRelations() — Relation review → SentenceRef recall → LLM fix ``` Entity conflict detection heuristic (bigram Jaccard > 0.75), routed through `selfInputCh` internal channel, LLM makes the final merge decision. @@ -272,6 +267,25 @@ Built-in plugin registration: `internal/plugins/all.go` blank imports → each p External plugin loading: `internal/plugin/dynamic.go` → copy to SHA256 temp path (bypass `plugin.Open` path cache) → `Open` + `Lookup("NewPlugin")`. Lua script plugin loading: `internal/lua/` → parse `main.lua` via Lua VM, call `start()` to register tools. +### Built-in vs External Plugins + +| Dimension | Built-in Plugin | External Plugin | +|-----------|----------------|-----------------| +| Registration | `init()` calls `plugin.RegisterFactory(name, factory)` | Implements `NewPluginFactory(name, config) (sdk.Plugin, error)` entry function | +| Compilation | Compiled into `homed` binary, no separate build | Compiled via `plugindev build` to `.so`/`.dll` (`-buildmode=c-shared`), loaded via C ABI bridge | +| Distribution | Bundled with kernel, not independently installable | `.hmap` package (ZIP archive), installed via WebUI or pluginmgr API | +| Metadata | `plugin.RegisterPluginMeta()` for display name | `plugin.json` manifest file (name, version, entry, platforms, etc.) | +| Plugin directory | No separate directory, compiled into binary | `plugins//` independent directory with `plugin.json` + binary | +| SDK permissions | Full PluginSDK (SocialAPI read/write, Publish events) | Restricted SDK (SocialAPI read-only, Subscribe-only events) | +| Lifecycle | Starts/stops with kernel, no individual hot-reload | Independent Start/Stop, supports hot-reload (ReloadOne) and enable/disable | +| Crash recovery | No independent recovery | Supports `SetAutoRestart(true)` for automatic crash restart | + +Common ground: +- Built-in `RegisterFactory` and external `NewPluginFactory` share the same `NativeFactory` type signature +- `Registry.Load()` handles both uniformly: checks factory table first (built-in), falls back to dynamic loading (external) +- Both use the same `Plugin` interface and `PluginSDK`; tool registration, stage hooks, and output channel APIs are identical +- Both share the same tool registry (`StageHost`); LLM invocations treat them identically + ### PluginSDK Four Channels ``` diff --git a/assets/docs/en/OVERVIEW.md b/assets/docs/en/OVERVIEW.md index bdb1500..6907de6 100644 --- a/assets/docs/en/OVERVIEW.md +++ b/assets/docs/en/OVERVIEW.md @@ -19,7 +19,7 @@ Core architecture: a long-running kernel process (`homed`) that connects to vari The significance lies in clear responsibility boundaries: the kernel focuses on orchestration and memory management, while plugins handle IO implementation — the two are not coupled. **Three-Layer Memory Architecture** — Manages information retention across long agent runtimes through a tiered storage strategy: -- **Context Layer**: In-memory local word embedding scored event window (jieba + TF-IDF + PMI → CosineSimilarity), maintains recent context in real-time, low-relevance events automatically sink to the next layer +- **Context Layer**: In-memory pretrained word embedding scored event window (StaticEmbedder word vectors → CosineSimilarity, TF-IDF fallback), maintains recent context in real-time, low-relevance events automatically sink to the next layer - **Document Layer**: JSON files + TF-IDF vector-indexed temporary memory, supports explicit submission and implicit archival, cold data distills to Graph - **Graph Layer**: SQLite graph database, persists entities and relations, BFS traversal recall, distillation pipeline extracts triples from conversations diff --git a/assets/docs/zh/ARCHITECTURE.md b/assets/docs/zh/ARCHITECTURE.md index deb6ebd..1eaf66f 100644 --- a/assets/docs/zh/ARCHITECTURE.md +++ b/assets/docs/zh/ARCHITECTURE.md @@ -120,17 +120,13 @@ eventLoop() → processTextInput() └── LLM主动: memory_recall / memory_merge / memory_purge / memory_edit / memory_delete_entity Social: person_query / set_trait / relate (包装 GraphDB) -④ 蒸馏管道 (每30min心跳) - distillContext → 窗口>2×maxSize → 强制Prune - syncGraphToDocs → Graph 快照写入 Document(跨层可搜索) - reorgGraph: - Step1: indexer.Sync — 重建实体向量索引 - Step2: docStore.Reindex — 重建文档向量索引 - Step3: 冷文档 → docToTriples → GraphDB.Commit - Step4: 实体相似度(Bigram Jaccard>0.75) → consolidation → LLM判断合并 - Step5: evaluateGraphQuality → LLM判断保留/删除 +④ 四个独立心跳循环(各自独立的 ticker 和配置间隔) + distillLoop (distillInterval, 默认30m): 上下文裁剪 — Context.Prune → Document + archiveLoop (archiveInterval, 默认60m): 冷文档归档 — docToTriples → GraphDB + mergeLoop (mergeInterval, 默认120m): 实体合并检测 — 相似度 → LLM 裁决 + reviewLoop (reviewInterval, 默认120m): 关系复审 — SentenceRef 回溯 → LLM 修正 -⑤ Pipeline 规则蒸馏器 (每心跳) +⑤ Pipeline 规则蒸馏器 (每10min心跳) distillOnce → 正则匹配个人信息: 我叫X / 我住在X / 我喜欢X / 我X岁 / 我的工作是X → 三元组 → GraphDB.Commit @@ -209,16 +205,15 @@ eventLoop() → processTextInput() ### 上下文剪枝 ``` -心跳 30min: - ├── distillContext() — 蒸馏当前上下文 - ├── syncGraphToDocs() — Graph→Document 同步 - └── reorgGraph() - ├── Indexer.Sync() - ├── DocStore.Reindex() - ├── 冷文档→Graph - └── 实体冲突 → enqueueConsolidationTask() - │ - selfInputCh → LLM 判断合并/跳过 +四个独立心跳循环(各自可配置间隔): + ├── distillLoop (distillInterval, 默认30m) + │ └── distillContext() — Context.Prune → Document + ├── archiveLoop (archiveInterval, 默认60m) + │ └── archiveColdDocs() — 冷文档 → docToTriples → GraphDB + ├── mergeLoop (mergeInterval, 默认120m) + │ └── detectEntityMerge() — 实体相似度检测 → LLM 裁决 + └── reviewLoop (reviewInterval, 默认120m) + └── reviewRelations() — 关系复审 → SentenceRef 回溯 → LLM 修正 ``` 实体冲突检测启发式(bigram Jaccard > 0.75),走 `selfInputCh` 内部通道,LLM 最终判断是否合并。 @@ -270,6 +265,25 @@ VM 内置 `json.encode` / `json.decode` / `log` / `http_get` / `http_post`。 外部插件加载:`internal/plugin/dynamic.go` → 复制到 SHA256 临时路径(绕过 `plugin.Open` 路径缓存)→ `Open` + `Lookup("NewPlugin")`。 Lua 脚本插件加载:`internal/lua/` → 通过 Lua VM 解析 `main.lua`,调用 `start()` 注册工具。 +### 内置插件 vs 外部插件 + +| 维度 | 内置插件 | 外部插件 | +|------|----------|----------| +| 注册方式 | `init()` 调用 `plugin.RegisterFactory(name, factory)` | 实现 `NewPluginFactory(name, config) (sdk.Plugin, error)` 入口函数 | +| 编译方式 | 编译进 `homed` 二进制,无需独立编译 | 通过 `plugindev build` 编译为 `.so`/`.dll`(`-buildmode=c-shared`),C ABI bridge 加载 | +| 分发方式 | 随内核分发,不可独立安装/卸载 | `.hmap` 包(ZIP 归档),通过 WebUI 或 pluginmgr API 安装 | +| 元数据 | 通过 `plugin.RegisterPluginMeta()` 注册显示名 | `plugin.json` manifest 文件(name, version, entry, platforms 等) | +| 插件目录 | 无独立目录,编译进二进制 | `plugins//` 独立目录,包含 `plugin.json` + 二进制 | +| SDK 权限 | 完整 PluginSDK(SocialAPI 读写、Publish 事件) | 受限 SDK(SocialAPI 只读、仅 Subscribe 事件) | +| 生命周期 | 随内核启动/停止,不可单独热重载 | 独立 Start/Stop,支持热重载(ReloadOne)和禁用/启用 | +| 崩溃恢复 | 无独立恢复机制 | 支持 `SetAutoRestart(true)` 崩溃自动重启 | + +两者的联系: +- 内置插件的工厂函数 `RegisterFactory` 与外部插件的 `NewPluginFactory` 共用同一个 `NativeFactory` 类型签名 +- `Registry.Load()` 统一处理两者的加载:先查工厂表(内置),无工厂则尝试动态加载(外部) +- 两者使用相同的 `Plugin` 接口和 `PluginSDK`,工具注册、阶段钩子、输出通道等 API 完全一致 +- 两者共享同一个工具注册表(`StageHost`),LLM 调用时无差别 + ### PluginSDK 四通道 ``` diff --git a/assets/docs/zh/OVERVIEW.md b/assets/docs/zh/OVERVIEW.md index dcb4f8c..082f998 100644 --- a/assets/docs/zh/OVERVIEW.md +++ b/assets/docs/zh/OVERVIEW.md @@ -19,7 +19,7 @@ HomeAgent 是一个持续运行的个人智能 Agent 框架。 这一划分的意义在于职责边界清晰:内核专注于编排与记忆管理,插件负责具体 IO 实现,二者互不耦合。 **三层记忆架构** — 通过分级存储策略管理 Agent 长周期运行中的信息留存: -- **Context 层**:内存中局部词嵌入评分的事件窗口(jieba + TF-IDF + PMI → CosineSimilarity),实时维护最近上下文,低相关性事件自动下沉到下一层 +- **Context 层**:内存中预训练词嵌入评分的事件窗口(StaticEmbedder 词向量 → CosineSimilarity,TF-IDF 回退),实时维护最近上下文,低相关性事件自动下沉到下一层 - **Document 层**:JSON 文件 + TF-IDF 向量索引的临时记忆,支持显式提交和隐式归档,冷数据蒸馏到 Graph - **Graph 层**:SQLite 图数据库,持久化实体(entities)和关系(relations),BFS 遍历召回,蒸馏管道从对话中提取三元组 diff --git a/internal/agent/core/agent.go b/internal/agent/core/agent.go index 025db64..ed59f27 100644 --- a/internal/agent/core/agent.go +++ b/internal/agent/core/agent.go @@ -133,7 +133,7 @@ type AgentConfig struct { PluginReg *plugin.Registry PluginDir string DistillInterval time.Duration - ArchiveInterval time.Duration // 冷文档归档间隔(L3→L4),0 则使用 DistillInterval + ArchiveInterval time.Duration // 冷文档归档间隔(L2→L3),0 则使用 DistillInterval ReviewInterval time.Duration // 关系复审间隔,0 则使用 DistillInterval MergeInterval time.Duration // 实体合并检测间隔,0 则使用 DistillInterval MaxContextSize int // 活跃上下文最大条数,超出按相关性裁剪 diff --git a/internal/agent/core/distill.go b/internal/agent/core/distill.go index 596f7a2..fe99d66 100644 --- a/internal/agent/core/distill.go +++ b/internal/agent/core/distill.go @@ -32,7 +32,7 @@ func (a *Agent) enqueueConsolidationTask(task ConsolidationTask) { // 四个独立心跳循环,各自拥有独立的 ticker 和配置 // ────────────────────────────────────────────── -// distillLoop 上下文裁剪(L2 蒸馏),使用 distillInterval +// distillLoop 上下文裁剪(L1→L2),使用 distillInterval func (a *Agent) distillLoop() { defer func() { if r := recover(); r != nil { @@ -59,7 +59,7 @@ func (a *Agent) distillLoop() { } } -// archiveLoop 冷文档归档(L3→L4),使用 archiveInterval +// archiveLoop 冷文档归档(L2→L3),使用 archiveInterval func (a *Agent) archiveLoop() { defer func() { if r := recover(); r != nil { diff --git a/internal/config/registry.go b/internal/config/registry.go index 6a3c9cd..7292001 100644 --- a/internal/config/registry.go +++ b/internal/config/registry.go @@ -442,7 +442,7 @@ func (r *ConfigRegistry) seedCoreDefs(dataDir string) { reg(ConfigDef{Key: "core.agent.max_tool_turns", Default: "10", Type: "int", DisplayName: "最大工具轮次", Description: "单次请求允许的最大工具调用轮数", Category: "agent"}) reg(ConfigDef{Key: "core.agent.max_context_size", Default: "30", Type: "int", DisplayName: "最大上下文", Description: "上下文窗口中保留的最大消息条数", Category: "agent"}) reg(ConfigDef{Key: "core.agent.distill_interval", Default: "30m", Type: "duration", DisplayName: "蒸馏间隔", Description: "记忆蒸馏的执行间隔", Category: "agent"}) - reg(ConfigDef{Key: "core.agent.archive_interval", Default: "60m", Type: "duration", DisplayName: "冷文档归档间隔", Description: "冷文档归档(L3→L4)的执行间隔", Category: "agent"}) + reg(ConfigDef{Key: "core.agent.archive_interval", Default: "60m", Type: "duration", DisplayName: "冷文档归档间隔", Description: "冷文档归档(L2→L3)的执行间隔", Category: "agent"}) reg(ConfigDef{Key: "core.agent.review_interval", Default: "120m", Type: "duration", DisplayName: "关系复审间隔", Description: "三元组关系复审的执行间隔", Category: "agent"}) reg(ConfigDef{Key: "core.agent.merge_interval", Default: "120m", Type: "duration", DisplayName: "实体合并检测间隔", Description: "实体合并检测(LLM 裁决)的执行间隔", Category: "agent"}) reg(ConfigDef{Key: "core.agent.workdir", Default: "", Type: "string", DisplayName: "工作目录", Description: "Agent 命令执行的默认工作目录(如 cmd_run 工具的 fallback),留空使用内核所在目录", Category: "agent"})