fix(plugins): 内置清单终审 15 → 13 —— kbtree/multimodal 翻案加回

## 翻案依据(逐方法对照公开 SDK,非方法名 grep)

初判把 kbtree/multimodal 归入"只用公开 SDK",错了。两边的
s.Knowledge() / s.LLM() **同名但接口面不同**:

- kbtree 需要 KnowledgeAPI.Subtree/CategoryCounts/SearchIn/Categories
  与 KnowledgeTreeView/KnowledgeCategoryCount 类型 —— 公开 KnowledgeAPI
  只有 Search/Add/List,树视图类型公开面根本没有
- multimodal 需要 LLMAPI.Chat + LLMMessage/LLMContentBlock ——
  公开 LLMAPI 只有 ListSources/SetSource/CurrentSource,没有 Chat

外置它们 = OCR/看图/转写、知识树浏览静默失效。

## 最终内置 13 个

  cli webui agentcli healthcheck pluginmgr seq remotedevice
  clawhubadapter kbtree multimodal cfgmgr skillmgr seemedia

外置 5 个(SDK 仓 example/ 已提供工程并已部署验证):
  cmd localuse mcp(本轮)+ ai_image files(此前)+ timer(更早)

## 文档同步(中英 6 份)

数量 11 → 13;判定标准小节补「终审翻案」与教训:
**判外置与否必须逐方法对照公开接口,同名方法不等于同面。**

验证:go build/vet/test 全绿;生产实例已部署新内核 + 新插件包,
四个按需插件经 plugin_start 拉起、工具全部注册(cmd_run /
local_screensee / mcp_* / files_edit_line 实核)。
This commit is contained in:
root
2026-10-19 22:09:09 +08:00
parent 835688c94a
commit 2aab90c508
7 changed files with 50 additions and 17 deletions

View File

@ -235,7 +235,7 @@ internal/
├── memory/ 三层记忆:Graph(SQLite) / Document(JSON+TF-IDF) / Text(JSONL) + StaticEmbedder(预训练词嵌入/TF-IDF回退) + CleanTemplateText(去模版) ├── memory/ 三层记忆:Graph(SQLite) / Document(JSON+TF-IDF) / Text(JSONL) + StaticEmbedder(预训练词嵌入/TF-IDF回退) + CleanTemplateText(去模版)
├── knowledge/ 知识库(文件系统 + TF-IDF) ├── knowledge/ 知识库(文件系统 + TF-IDF)
├── plugin/ 插件注册表 + 子进程加载器(stdio RPC + 共享内存段 + 事件环) ├── plugin/ 插件注册表 + 子进程加载器(stdio RPC + 共享内存段 + 事件环)
├── plugins/ 内置 11 个插件(webui/cli/agentcli/cfgmgr/healthcheck/pluginmgr/clawhubadapter/remotedevice/skillmgr/seemedia/seq)—— 判定标准见 ARCHITECTURE「内置插件的判定标准」 ├── plugins/ 内置 13 个插件(webui/cli/agentcli/cfgmgr/healthcheck/kbtree/multimodal/pluginmgr/clawhubadapter/remotedevice/skillmgr/seemedia/seq)—— 判定标准见 ARCHITECTURE「内置插件的判定标准」
├── sdk/ PluginSDK(Tool/Stage/Event 三通道) ├── sdk/ PluginSDK(Tool/Stage/Event 三通道)
├── config/ SQLite 配置中心 ├── config/ SQLite 配置中心
├── events/ 事件总线 ├── events/ 事件总线
@ -390,7 +390,7 @@ make install # 安装到系统
本项目的 AGPL。因此**外部插件不是本项目的衍生作品**,作者可自行选择许可(含闭源、商业、私有), 本项目的 AGPL。因此**外部插件不是本项目的衍生作品**,作者可自行选择许可(含闭源、商业、私有),
既不必同许可、也不受 §13 网络条款约束。第三方插件生态的安全与活跃正建立在这条之上。 既不必同许可、也不受 §13 网络条款约束。第三方插件生态的安全与活跃正建立在这条之上。
边界很清楚:**AGPL 覆盖内核与随包内置插件**(`homed`、`internal/`、`internal/plugins/` 下 11 个内置插件); 边界很清楚:**AGPL 覆盖内核与随包内置插件**(`homed`、`internal/`、`internal/plugins/` 下 13 个内置插件);
**MIT 覆盖公开 SDK**(`sdk/`,`go.mod` 零外部依赖、只依赖 Go 标准库,不引用内核任何代码)。 **MIT 覆盖公开 SDK**(`sdk/`,`go.mod` 零外部依赖、只依赖 Go 标准库,不引用内核任何代码)。
子进程隔离在这里不重要了——决定许可的是被链接的 SDK 代码本身,而它是 MIT。 子进程隔离在这里不重要了——决定许可的是被链接的 SDK 代码本身,而它是 MIT。

View File

@ -216,7 +216,7 @@ internal/
├── memory/ Three-layer memory: Graph(SQLite) / Document(JSON+TF-IDF) / Text(JSONL) + StaticEmbedder(pretrained word embedding/TF-IDF fallback) + CleanTemplateText(de-template) ├── memory/ Three-layer memory: Graph(SQLite) / Document(JSON+TF-IDF) / Text(JSONL) + StaticEmbedder(pretrained word embedding/TF-IDF fallback) + CleanTemplateText(de-template)
├── knowledge/ Knowledge base (filesystem + TF-IDF) ├── knowledge/ Knowledge base (filesystem + TF-IDF)
├── plugin/ Plugin registry + subprocess loader (stdio RPC + shared memory segment + event ring) ├── plugin/ Plugin registry + subprocess loader (stdio RPC + shared memory segment + event ring)
├── plugins/ 11 built-in plugins (webui/cli/agentcli/cfgmgr/healthcheck/pluginmgr/clawhubadapter/remotedevice/skillmgr/seemedia/seq) — see ARCHITECTURE "which plugins stay built-in" ├── plugins/ 13 built-in plugins (webui/cli/agentcli/cfgmgr/healthcheck/kbtree/multimodal/pluginmgr/clawhubadapter/remotedevice/skillmgr/seemedia/seq) — see ARCHITECTURE "Which Plugins Stay Built-in"
├── sdk/ PluginSDK (Tool/Stage/Event three channels) ├── sdk/ PluginSDK (Tool/Stage/Event three channels)
├── config/ SQLite config center ├── config/ SQLite config center
├── events/ Event bus ├── events/ Event bus
@ -416,7 +416,7 @@ private included), with no same-license obligation and no §13 network clause. T
vitality of the third-party plugin ecosystem rest on this. vitality of the third-party plugin ecosystem rest on this.
The boundary is clean: **AGPL covers the kernel and the bundled plugins** (`homed`, `internal/`, The boundary is clean: **AGPL covers the kernel and the bundled plugins** (`homed`, `internal/`,
the 11 built-in plugins under `internal/plugins/`); **MIT covers the public SDK** (`sdk/`, whose the 13 built-in plugins under `internal/plugins/`); **MIT covers the public SDK** (`sdk/`, whose
`go.mod` has zero external dependencies and imports only the Go standard library — it never `go.mod` has zero external dependencies and imports only the Go standard library — it never
references any kernel code). Process isolation is beside the point here — what decides the references any kernel code). Process isolation is beside the point here — what decides the
license is the linked SDK code itself, and that code is MIT. license is the linked SDK code itself, and that code is MIT.

View File

@ -318,7 +318,7 @@ VM built-ins: `json.encode` / `json.decode` / `log` / `http_get` / `http_post`.
| Method | Registration Mechanism | Compilation | Usage | | Method | Registration Mechanism | Compilation | Usage |
|--------|----------------------|-------------|-------| |--------|----------------------|-------------|-------|
| Built-in | `init()` → `RegisterFactory` | `internal/plugins/` compiled into kernel | webui/cli/agentcli etc. (11 total; see the criteria section below) | | Built-in | `init()` → `RegisterFactory` | `internal/plugins/` compiled into kernel | webui/cli/agentcli etc. (13 total; see the criteria section below) |
| External subprocess plugin | Handshake + stdio JSON-RPC reverse registration | `hmapdev build` → `plugin.bin` (ordinary Go binary) | qq/browser/files etc. | | External subprocess plugin | Handshake + stdio JSON-RPC reverse registration | `hmapdev build` → `plugin.bin` (ordinary Go binary) | qq/browser/files etc. |
| Lua script plugin | Execute `main.lua` to register tools | No compilation, takes effect after restart/reload | luademo etc. | | Lua script plugin | Execute `main.lua` to register tools | No compilation, takes effect after restart/reload | luademo etc. |
| SKILL plugin | Parse `SKILL.md` | Markdown definition | Loaded via clawhubadapter | | SKILL plugin | Parse `SKILL.md` | Markdown definition | Loaded via clawhubadapter |
@ -372,7 +372,19 @@ Counter-examples (externalized, because public capabilities suffice):
| Plugin | Actual dependencies | | Plugin | Actual dependencies |
|---|---| |---|---|
| timer | RegisterTool + Settings + InjectText (public) | | timer | RegisterTool + Settings + InjectText (public) |
| ai_image / files / cmd / kbtree / localuse / mcp / multimodal | RegisterTool + Settings (+ public APIs such as s.Knowledge()) | | ai_image / files / cmd / localuse / mcp | RegisterTool + Settings (+ public RegisterStage) |
> ★ Reversal on final review (2026-10-19, method-by-method comparison against
> the public SDK brought two back): **kbtree** needs
> `KnowledgeAPI.Subtree/CategoryCounts/SearchIn/Categories` — the public
> `KnowledgeAPI` only has `Search/Add/List`, and the public SDK lacks the
> `KnowledgeTreeView`/`KnowledgeCategoryCount` types. **multimodal** needs
> `LLMAPI.Chat` + `LLMMessage`/`LLMContentBlock` — the public `LLMAPI` only
> has `ListSources/SetSource/CurrentSource`. Both were initially misjudged as
> "public-only" because `s.Knowledge()`/`s.LLM()` exist on both sides **with
> the same names but different surfaces**. Lesson: externalization decisions
> require method-by-method comparison against the public interface, not name
> matching.
> ⚠️ Both copies existing at once is forbidden: `loadOne` checks the built-in > ⚠️ Both copies existing at once is forbidden: `loadOne` checks the built-in
> factory table first and never looks at the external `plugin.bin` if it hits > factory table first and never looks at the external `plugin.bin` if it hits
@ -641,7 +653,7 @@ internal/
│ ├── registry.go — Registry + lifecycle │ ├── registry.go — Registry + lifecycle
│ ├── dynamic.go — Load-channel dispatch (proc / lua / skill); .so only remains to error loudly │ ├── dynamic.go — Load-channel dispatch (proc / lua / skill); .so only remains to error loudly
│ └── manifest.go — plugin.json metadata │ └── manifest.go — plugin.json metadata
├── plugins/ — Built-in plugin implementations (11; see "Which Plugins Stay Built-in") ├── plugins/ — Built-in plugin implementations (13; see "Which Plugins Stay Built-in")
│ ├── all.go — Blank imports (**the single authoritative list**; edit only this) │ ├── all.go — Blank imports (**the single authoritative list**; edit only this)
│ ├── webui/ — HTTP server + embedded SPA │ ├── webui/ — HTTP server + embedded SPA
│ ├── cli/ — Unix socket CLI │ ├── cli/ — Unix socket CLI
@ -655,7 +667,7 @@ internal/
│ ├── seemedia/ — Direct media view │ ├── seemedia/ — Direct media view
│ ├── seq/ — Sequence orchestration │ ├── seq/ — Sequence orchestration
│ ├── data/ plugin_data/ — Plugin static assets (non-Go, shipped with the kernel) │ ├── data/ plugin_data/ — Plugin static assets (non-Go, shipped with the kernel)
│ ├── kbtree/ cmd/ localuse/ mcp/ multimodal/ ai_image/ files/ │ ├── cmd/ localuse/ mcp/ ai_image/ files/
│ │ — Source remains but **not listed in all.go**: externalized │ │ — Source remains but **not listed in all.go**: externalized
│ │ on 2026-10-19 as .hmap packages (projects in the SDK repo's │ │ on 2026-10-19 as .hmap packages (projects in the SDK repo's
│ │ example/); directories to be removed once external projects stabilize │ │ example/); directories to be removed once external projects stabilize

View File

@ -100,7 +100,7 @@ Code is in the project root, implemented in Go.
Core functionality is operational. Plugin system and SDK are ready for independent external plugin development. Core functionality is operational. Plugin system and SDK are ready for independent external plugin development.
- Built-in plugins (11): webui / cli / agentcli / cfgmgr / healthcheck / pluginmgr / clawhubadapter / remotedevice / skillmgr / seemedia / seq - Built-in plugins (13): webui / cli / agentcli / cfgmgr / healthcheck / kbtree / multimodal / pluginmgr / clawhubadapter / remotedevice / skillmgr / seemedia / seq
- Criterion: **a plugin stays built-in only if it requires internal-SDK-only capabilities**; everything else ships as an .hmap external plugin (see ARCHITECTURE) - Criterion: **a plugin stays built-in only if it requires internal-SDK-only capabilities**; everything else ships as an .hmap external plugin (see ARCHITECTURE)
- External plugin examples ([homeagent-sdk](https://github.com/JianFeeeee/homeagentsdk) repo `example/`, both Go and Lua types): qq / files / a2a / ai_image / bili / browser / calendar / editdoc / memo / music / ocr / rss / sanitizer / weather / luademo - External plugin examples ([homeagent-sdk](https://github.com/JianFeeeee/homeagentsdk) repo `example/`, both Go and Lua types): qq / files / a2a / ai_image / bili / browser / calendar / editdoc / memo / music / ocr / rss / sanitizer / weather / luademo
- Distribution: `.hmap` plugin package format, installable via WebUI - Distribution: `.hmap` plugin package format, installable via WebUI

View File

@ -307,7 +307,7 @@ VM 内置 `json.encode` / `json.decode` / `log` / `http_get` / `http_post`。
| 方式 | 注册机制 | 编译 | 用途 | | 方式 | 注册机制 | 编译 | 用途 |
|------|----------|------|------| |------|----------|------|------|
| 内置插件 | `init()` → `RegisterFactory` | `internal/plugins/` 编译进内核 | webui/cli/agentcli 等 11 个(判定标准见下) | | 内置插件 | `init()` → `RegisterFactory` | `internal/plugins/` 编译进内核 | webui/cli/agentcli 等 13 个(判定标准见下) |
| 外部子进程插件 | 握手 + stdio JSON-RPC 反向注册 | `hmapdev build` → `plugin.bin`(普通 Go 二进制) | qq/browser/files 等 | | 外部子进程插件 | 握手 + stdio JSON-RPC 反向注册 | `hmapdev build` → `plugin.bin`(普通 Go 二进制) | qq/browser/files 等 |
| Lua 脚本插件 | 执行 `main.lua` 注册工具 | 无需编译,重启/重载生效 | luademo 等 | | Lua 脚本插件 | 执行 `main.lua` 注册工具 | 无需编译,重启/重载生效 | luademo 等 |
| SKILL 插件 | 解析 `SKILL.md` | Markdown 定义 | clawhubadapter 兼容加载 | | SKILL 插件 | 解析 `SKILL.md` | Markdown 定义 | clawhubadapter 兼容加载 |
@ -356,7 +356,16 @@ Lua 脚本插件加载:`internal/plugin/` → gopher-lua 解释器执行 `main
| 插件 | 实际依赖 | | 插件 | 实际依赖 |
|---|---| |---|---|
| timer | RegisterTool + Settings + InjectText(公开) | | timer | RegisterTool + Settings + InjectText(公开) |
| ai_image / files / cmd / kbtree / localuse / mcp / multimodal | RegisterTool + Settings(+ s.Knowledge() 等公开 API) | | ai_image / files / cmd / localuse / mcp | RegisterTool + Settings(+ 公开 RegisterStage) |
> ★ 终审翻案(2026-10-19,逐方法对照公开 SDK 后加回两个):
> **kbtree** 依赖 `KnowledgeAPI.Subtree/CategoryCounts/SearchIn/Categories`
> ——公开 `KnowledgeAPI` 只有 `Search/Add/List`,且公开 SDK 没有
> `KnowledgeTreeView`/`KnowledgeCategoryCount` 类型;
> **multimodal** 依赖 `LLMAPI.Chat` + `LLMMessage`/`LLMContentBlock`
> ——公开 `LLMAPI` 只有 `ListSources/SetSource/CurrentSource`。
> 两者最初被误判为"只用公开能力",因为 `s.Knowledge()`/`s.LLM()` 两边
> **同名不同面**。教训:判外置与否必须逐方法对照公开接口,不能只看方法名。
> ⚠️ 两份并存是禁止的:`loadOne` 先查内置工厂表,命中就不再看外部 `plugin.bin` > ⚠️ 两份并存是禁止的:`loadOne` 先查内置工厂表,命中就不再看外部 `plugin.bin`
> ⇒ 内置版永远赢,外部版**装了也不生效**。外置一个插件时,必须同时把它从 > ⇒ 内置版永远赢,外部版**装了也不生效**。外置一个插件时,必须同时把它从
@ -613,7 +622,7 @@ internal/
│ ├── registry.go — 注册表 + 生命周期 │ ├── registry.go — 注册表 + 生命周期
│ ├── dynamic.go — 加载通道分派(proc / lua / skill);.so 仅剩给明确报错 │ ├── dynamic.go — 加载通道分派(proc / lua / skill);.so 仅剩给明确报错
│ └── manifest.go — plugin.json 元数据 │ └── manifest.go — plugin.json 元数据
├── plugins/ — 内置插件实现(11 个,判定标准见「内置插件的判定标准」) ├── plugins/ — 内置插件实现(13 个,判定标准见「内置插件的判定标准」)
│ ├── all.go — 空白导入(**唯一权威清单**,增删内置插件只改这里) │ ├── all.go — 空白导入(**唯一权威清单**,增删内置插件只改这里)
│ ├── webui/ — HTTP 服务器 + 嵌入式 SPA │ ├── webui/ — HTTP 服务器 + 嵌入式 SPA
│ ├── cli/ — Unix socket CLI │ ├── cli/ — Unix socket CLI
@ -627,7 +636,7 @@ internal/
│ ├── seemedia/ — 直看媒体 │ ├── seemedia/ — 直看媒体
│ ├── seq/ — 序列编排 │ ├── seq/ — 序列编排
│ ├── data/ plugin_data/ — 插件静态资源(非 Go,随内核打包) │ ├── data/ plugin_data/ — 插件静态资源(非 Go,随内核打包)
│ ├── kbtree/ cmd/ localuse/ mcp/ multimodal/ ai_image/ files/ │ ├── cmd/ localuse/ mcp/ ai_image/ files/
│ │ — 源码仍在但**未列入 all.go**:2026-10-19 起外置, │ │ — 源码仍在但**未列入 all.go**:2026-10-19 起外置,
│ │ 以 .hmap 形式安装(SDK 仓 example/ 提供工程); │ │ 以 .hmap 形式安装(SDK 仓 example/ 提供工程);
│ │ 目录待外置工程稳定后删除 │ │ 目录待外置工程稳定后删除

View File

@ -93,7 +93,7 @@ HomeAgent 是一个持续运行的个人智能 Agent 框架。
核心功能已可运行。插件系统和 SDK 已就绪,可独立开发外部插件。 核心功能已可运行。插件系统和 SDK 已就绪,可独立开发外部插件。
- 内置插件(11 个):webui / cli / agentcli / cfgmgr / healthcheck / pluginmgr / clawhubadapter / remotedevice / skillmgr / seemedia / seq - 内置插件(13 个):webui / cli / agentcli / cfgmgr / healthcheck / kbtree / multimodal / pluginmgr / clawhubadapter / remotedevice / skillmgr / seemedia / seq
- 判定标准:**只有必须使用内部 SDK 能力(公开 SDK 没有的接口面)的插件才内置**,其余一律外置为 .hmap 插件(详见 ARCHITECTURE「内置插件的判定标准」) - 判定标准:**只有必须使用内部 SDK 能力(公开 SDK 没有的接口面)的插件才内置**,其余一律外置为 .hmap 插件(详见 ARCHITECTURE「内置插件的判定标准」)
- 外部插件示例([homeagent-sdk](https://github.com/JianFeeeee/homeagentsdk) 仓库 `example/`,含 Go 和 Lua 两种类型):qq / files / a2a / ai_image / bili / browser / calendar / editdoc / memo / music / ocr / rss / sanitizer / weather / luademo - 外部插件示例([homeagent-sdk](https://github.com/JianFeeeee/homeagentsdk) 仓库 `example/`,含 Go 和 Lua 两种类型):qq / files / a2a / ai_image / bili / browser / calendar / editdoc / memo / music / ocr / rss / sanitizer / weather / luademo
- 打包分发:`.hmap` 插件包格式,通过 WebUI 安装 - 打包分发:`.hmap` 插件包格式,通过 WebUI 安装

View File

@ -43,10 +43,20 @@ package plugins
// //
// 反例(已外置,因为它们只要公开能力): // 反例(已外置,因为它们只要公开能力):
// cmd 仅 internal/plugin 的 RegisterFactory → 换成 NewPluginFactory 即可 // cmd 仅 internal/plugin 的 RegisterFactory → 换成 NewPluginFactory 即可
// kbtree 仅 s.Knowledge() + s.Settings()
// localuse 仅 RegisterTool + Settings // localuse 仅 RegisterTool + Settings
// mcp 仅 RegisterTool + Settings // mcp 仅 RegisterTool + Settings + RegisterStage
// multimodal 仅 RegisterTool + Settings //
// ★ 终审翻案(2026-10-19,逐方法对照公开 SDK 后加回两个):
// kbtree KnowledgeAPI.Subtree/CategoryCounts/SearchIn/Categories ——
// 公开 KnowledgeAPI 只有 Search/Add/List,且公开 SDK 没有
// KnowledgeTreeView/KnowledgeCategoryCount 类型 ⇒ 必须内置
// multimodal LLMAPI.Chat + LLMMessage/LLMContentBlock(多模态消息)——
// 公开 LLMAPI 只有 ListSources/SetSource/CurrentSource,
// 没有 Chat,也没有消息类型 ⇒ 必须内置
// (agentcli 的 EventPublish 同理,见上)
//
// 教训:判「只用公开 SDK」不能只 grep 方法名前缀,要**逐方法对照公开接口**。
// s.Knowledge() / s.LLM() 两边同名,但接口面不同;粗看就会误判成可外置。
// //
// ## ★ agentcli 为什么不能外置(2026-10-19 核实) // ## ★ agentcli 为什么不能外置(2026-10-19 核实)
// //
@ -87,6 +97,8 @@ import (
_ "github.com/JianFeeeee/HomeAgent/internal/plugins/clawhubadapter" _ "github.com/JianFeeeee/HomeAgent/internal/plugins/clawhubadapter"
_ "github.com/JianFeeeee/HomeAgent/internal/plugins/cli" _ "github.com/JianFeeeee/HomeAgent/internal/plugins/cli"
_ "github.com/JianFeeeee/HomeAgent/internal/plugins/healthcheck" _ "github.com/JianFeeeee/HomeAgent/internal/plugins/healthcheck"
_ "github.com/JianFeeeee/HomeAgent/internal/plugins/kbtree"
_ "github.com/JianFeeeee/HomeAgent/internal/plugins/multimodal"
_ "github.com/JianFeeeee/HomeAgent/internal/plugins/pluginmgr" _ "github.com/JianFeeeee/HomeAgent/internal/plugins/pluginmgr"
_ "github.com/JianFeeeee/HomeAgent/internal/plugins/remotedevice" _ "github.com/JianFeeeee/HomeAgent/internal/plugins/remotedevice"
_ "github.com/JianFeeeee/HomeAgent/internal/plugins/seemedia" _ "github.com/JianFeeeee/HomeAgent/internal/plugins/seemedia"