为插件作者建一个文档站,重点是**能按描述搜到 API**,以及**明确能力边界**。
## 为什么 API 参考要生成而不是手写
公开 API 面有 115 个符号、11 个接口。手抄必然与代码漂移——这是文档站最常见的
死法(本仓 README 里已经有过几处「文档说一套、代码是另一套」)。
所以 `tools/apidoc` 直接从 `sdk/*.go` 提取签名、文档注释与代码块示例,渲染成
`docs/api/*.md`。发现文档不对时改的是**源码注释**,不是生成物。生成页首行带
「勿手改」标记,防止有人改了下次构建白改。
- `extract.go`:go/ast + go/doc 提取(只用标准库,离线可跑,不引入依赖)
- `gensite/`:渲染 Markdown + 检索索引
- `gensite/usages.go`:从 `example/` 21 个示例插件里反查**真实调用点**,
贴在每个 API 下(源码注释里几乎没有可运行示例,但示例插件都是能编译跑的真代码)
## 能力边界:写这个站时查出的三处文档错误
这是本次最有价值的部分。原以为「公开 SDK 里有的 API 外部插件都能用」,
实测对照桥接模板后发现三处不符,站内已更正:
1. **`PluginMgr()` 被写成「仅内置可用」——错的。** 桥接模板第 692 行显式
`base.SetPluginMgrAPI(procPluginMgr{})`,公开 `PluginMgrAPI` 注释也写「外部插件可调用」。
真正的区别是**方法数**:公开面 3 个(ReloadOne/ListLoadedPlugins/IsPluginDisabled),
内部面 9 个。容易混淆是因为两个包里有同名但不同的接口。
2. **`Events()` 外部插件恒为 nil。** `SetEventSubscriber` 全仓只有定义、无调用点,
故 subscriber 从未被注入。外部插件的事件订阅实际由生成的运行时走
`events.subscribe` RPC 完成——旧文档把它当成可用入口,会让人写出必然失效的代码。
3. **`UnregisterOutputChannel` 是静默无效,不是报错。** 桥接只注入 registrar、
不注入 unregistrar,于是 `regOutputUnreg == nil`,函数命中 else 分支**直接返回 nil**
(sdk/plugin.go:539-549)——不报错、通道也没注销。
每条裁定的依据写进 `tools/apidoc/tiers.json`(文件:行号 或 grep 结论),
站上以告警框呈现,读者可自行核对。判断依据三源:桥接模板的 `base.Set*` 注入点、
`internal/sdk` 完整面、`internal/plugin/proc/protocol.go` 的 RPC 表。
## 检索(用户的核心诉求)
两套互补:
- **MkDocs 内置搜索**:全文,中文走 jieba 分词。
- **自建 API 检索**(`docs/javascripts/api-search.js` + `assets/api-index.json`):
支持四类查询——按名称、**按功能描述**(「注册工具」→ RegisterTool、
「崩溃」→ SetAutoRestart)、按 `限定符.方法`(`memory.recall` → MemoryAPI.Recall)、
按签名片段(`(string) error`)。并标出「仅内置」,避免外部插件作者踩空。
自建的理由:Material 内置搜索按整页文本索引,搜 `InjectText` 会列出所有提到它的
页面,但分不清哪条是它的定义;而且它要等 mkdocs build 才更新。
## 文档结构
- `docs/guide/`:快速开始、Go/Lua 首个插件、能力边界、打包发布、多平台、受限 SDK 与安全
- `docs/api/`:10 个按「你想做什么」划分的章节(工具/阶段/记忆/通道/配置/生命周期/
事件/LLM/常量/桥接)+ 仅内置汇总页
- `docs/versions.md`:SDK 版本语义(跟随内核中版本、patch 恒为 .0)、
1.0.0 是唯一破坏性变更、RPC 协议版本
## 验证
- `mkdocs build --strict` 零告警
- 23 个页面的全部站内链接与锚点可达(自动校验)
- 1440 / 768 / 390px 三视口:无横向溢出、无控制台错误
- 四种检索模式实测有结果且跳转锚点正确
- 构建产物 `site_build/` 已 gitignore
用法:`tools/apidoc/build.sh`(生成+构建)、`tools/apidoc/build.sh serve`(预览)。
17 KiB
其他类型
剩余的类型与方法:PluginSDK 本体的访问器、StageContext 的并发控制,以及多模态辅助类型。没有归入上面任何一个主题,但可能仍会用到。
DocMemoryAPI
DocMemoryAPI provides access to the document vector store.
| 方法 | 说明 |
|---|---|
Insert |
|
InsertWithMedia |
InsertWithMedia 写入文档并关联媒体。attachments 里带 Data 的会落进 |
Query |
|
Remove |
|
Stats |
DocMemoryAPI.Insert
Insert(doc *Doc) error
memory.go:76
DocMemoryAPI.InsertWithMedia
InsertWithMedia(doc *Doc, attachments []MediaAttachment) error
InsertWithMedia 写入文档并关联媒体。attachments 里带 Data 的会落进 内容寻址存储(相同字节只存一份),只带 Digest 的直接引用已有内容。 媒体成为文档直接持有的一等记忆块:文档向量会融合它们的原生向量, 因此图片按自己的向量被召回,不依赖任何生成的描述文本。
memory.go:81
DocMemoryAPI.Query
Query(text string, topK int) []*Doc
memory.go:75
DocMemoryAPI.Remove
Remove(id string)
memory.go:82
DocMemoryAPI.Stats
Stats() map[string]interface{}
memory.go:83
EventSubscriber
EventSubscriber allows plugins to subscribe to kernel events. This is a restricted interface: plugins can subscribe but the kernel controls which events are delivered.
| 方法 | 说明 |
|---|---|
Subscribe |
EventSubscriber.Subscribe
!!! warning "仅内核内置插件可用"
Subscribe(eventType EventType, handler EventHandler) func()
plugin.go:279
IOInjector
IOInjector provides methods for injecting input and interrupts into the agent pipeline. All methods accept (source, channel) where channel is the target output channel for routing the agent's response.
| 方法 | 说明 |
|---|---|
InjectInputMedia |
|
InjectInputMediaOpts |
|
InjectInputMediaSync |
|
InjectInputMediaSyncOpts |
|
InjectInputSync |
InjectInputSync 注入输入事件并同步等待 agent 回复,返回回复文本(无回复时返回空串)。 |
InjectInputSyncOpts |
|
InjectInterruptMedia |
|
InjectInterruptMediaOpts |
|
InjectInterruptText |
|
InjectInterruptTextOpts |
|
InjectText |
|
InjectTextNoMemory |
|
InjectTextOpts |
以下 Opts 变体让调用点在这一次注入上声明记忆与裁剪行为。 |
SetToolBlocks |
SetToolBlocks 插件工具注入多模态内容块(image_url/audio_url),内核在下一条 |
IOInjector.InjectInputMedia
InjectInputMedia(source, channel, text string, blocks []ContentBlock)
plugin.go:230
IOInjector.InjectInputMediaOpts
InjectInputMediaOpts(source, channel, text string, blocks []ContentBlock, opts InjectOptions)
plugin.go:241
IOInjector.InjectInputMediaSync
InjectInputMediaSync(source, channel, text string, blocks []ContentBlock) string
plugin.go:231
IOInjector.InjectInputMediaSyncOpts
InjectInputMediaSyncOpts(source, channel, text string, blocks []ContentBlock, opts InjectOptions) string
plugin.go:242
IOInjector.InjectInputSync
InjectInputSync(source, channel, text string) string
InjectInputSync 注入输入事件并同步等待 agent 回复,返回回复文本(无回复时返回空串)。 用于通道消息的完整闭环:收到入站 → agent 处理 → 回复取回 → 送回通道。
示例插件里的真实用法
| 插件 | 位置 | 代码 |
|---|---|---|
a2a |
example/a2a/plugin.go:345 |
reply := p.sdk.InjectInputSync(p.name, p.name, |
acp |
example/acp/plugin.go:237 |
reply = p.sdk.InjectInputSync(p.name, p.name, |
plugin.go:226
IOInjector.InjectInputSyncOpts
InjectInputSyncOpts(source, channel, text string, opts InjectOptions) string
plugin.go:240
IOInjector.InjectInterruptMedia
InjectInterruptMedia(source, channel, text string, blocks []ContentBlock)
plugin.go:232
IOInjector.InjectInterruptMediaOpts
InjectInterruptMediaOpts(source, channel, text string, blocks []ContentBlock, opts InjectOptions)
plugin.go:243
IOInjector.InjectInterruptText
InjectInterruptText(source, channel, text string)
plugin.go:221
IOInjector.InjectInterruptTextOpts
InjectInterruptTextOpts(source, channel, text string, opts InjectOptions)
示例插件里的真实用法
| 插件 | 位置 | 代码 |
|---|---|---|
browser |
example/browser/plugin.go:1319 |
p.sdk.InjectInterruptTextOpts(p.name, p.name, |
calendar |
example/calendar/plugin.go:527 |
p.sdk.InjectInterruptTextOpts("calendar", "calendar", msg, sdk.InjectOptions{NoMemory: true}) |
memo |
example/memo/plugin.go:299 |
p.sdk.InjectInterruptTextOpts(p.name, p.name, |
qq |
example/qq/plugin.go:1493 |
p.sdk.InjectInterruptTextOpts(p.name, p.name, text, sdk.InjectOptions{ |
plugin.go:239
IOInjector.InjectText
InjectText(source, channel, text string)
plugin.go:222
IOInjector.InjectTextNoMemory
InjectTextNoMemory(source, channel, text string)
示例插件里的真实用法
| 插件 | 位置 | 代码 |
|---|---|---|
browser |
example/browser/plugin.go:1114 |
p.sdk.InjectTextNoMemory(p.name, p.name, fmt.Sprintf("[浏览器 %s 已导航到 %s]", id, rawURL)) |
plugin.go:223
IOInjector.InjectTextOpts
InjectTextOpts(source, channel, text string, opts InjectOptions)
以下 Opts 变体让调用点在这一次注入上声明记忆与裁剪行为。
上面那些不带 opts 的方法等价于传零值 InjectOptions(记入记忆 + 不裁剪), 保留它们是为了不破坏已有插件;新代码应当用 Opts 变体把意图写清楚。
plugin.go:238
IOInjector.SetToolBlocks
SetToolBlocks(blocks []ContentBlock)
SetToolBlocks 插件工具注入多模态内容块(image_url/audio_url),内核在下一条 tool message 的 content 数组里带上这些块,让模型在后续轮次看到图/听到音频。
plugin.go:229
KnowledgeAPI
KnowledgeAPI provides access to the knowledge store.
| 方法 | 说明 |
|---|---|
Add |
|
List |
|
Search |
KnowledgeAPI.Add
Add(name, content string) error
knowledge.go:6
KnowledgeAPI.List
List() ([]string, error)
knowledge.go:7
KnowledgeAPI.Search
Search(query string, topK int) ([]*Knowledge, error)
knowledge.go:5
LLMAPI
LLMAPI provides access to the LLM provider manager.
| 方法 | 说明 |
|---|---|
CurrentSource |
|
ListSources |
|
SetSource |
LLMAPI.CurrentSource
CurrentSource() string
llm.go:7
LLMAPI.ListSources
ListSources() []string
llm.go:5
LLMAPI.SetSource
SetSource(name string) error
llm.go:6
MemoryAPI
MemoryAPI provides access to the graph memory (entity-relation store).
| 方法 | 说明 |
|---|---|
Commit |
|
Introspect |
|
MergeEntities |
|
Purge |
|
Recall |
MemoryAPI.Commit
Commit(triples []Triple) error
memory.go:6
MemoryAPI.Introspect
Introspect() (map[string]interface{}, error)
memory.go:7
MemoryAPI.MergeEntities
MergeEntities(source, target string) (int, error)
memory.go:8
MemoryAPI.Purge
Purge(criteria map[string]string, mode string) (int, error)
memory.go:9
MemoryAPI.Recall
Recall(query []string, depth int) ([]Entity, []Relation, error)
memory.go:5
Plugin
Plugin is the interface every plugin must implement.
| 方法 | 说明 |
|---|---|
Name |
|
Start |
|
Stop |
Plugin.Name
Name() string
plugin.go:14
Plugin.Start
Start(sdk *PluginSDK) error
plugin.go:15
Plugin.Stop
Stop() error
plugin.go:16
PluginMgrAPI
PluginMgrAPI 提供插件管理能力(外部插件可调用)。 由 bridge 注入 dispatch 实现,走 C ABI CORE_PLUGIN_RELOAD_ONE 等。
| 方法 | 说明 |
|---|---|
IsPluginDisabled |
IsPluginDisabled 查询插件是否被禁用。 |
ListLoadedPlugins |
ListLoadedPlugins 列出已加载插件。 |
ReloadOne |
ReloadOne 重载单个插件(停止后重新加载)。 |
PluginMgrAPI.IsPluginDisabled
IsPluginDisabled(name string) bool
IsPluginDisabled 查询插件是否被禁用。
plugin.go:290
PluginMgrAPI.ListLoadedPlugins
ListLoadedPlugins() []string
ListLoadedPlugins 列出已加载插件。
plugin.go:288
PluginMgrAPI.ReloadOne
ReloadOne(name string) error
ReloadOne 重载单个插件(停止后重新加载)。
plugin.go:286
SettingsAPI
| 方法 | 说明 |
|---|---|
DataDir |
DataDir returns the plugin-specific data directory (guaranteed to exist): |
Defs |
Defs returns config definitions matching the prefix. |
Dump |
Dump returns all config values. |
Get |
Get reads the plugin's own config value (config_ table). |
GetCore |
GetCore reads the core config table. |
GetPlugin |
GetPlugin reads another plugin's config table. |
List |
List returns all keys matching the given prefix. |
ListCore |
ListCore lists core config keys matching the prefix. |
ListPlugin |
ListPlugin lists another plugin's config keys matching the prefix. |
Plugins |
Plugins returns a list of all plugin config namespaces. |
RegisterDef |
RegisterDef registers a config definition for UI display. |
Set |
Set writes a config value to the plugin's own config table. |
SetCore |
SetCore writes to the core config table. |
SetPlugin |
SetPlugin writes to another plugin's config table. |
SettingsAPI.DataDir
DataDir() string
DataDir returns the plugin-specific data directory (guaranteed to exist): /plugin_data/<plugin_name>. Plugins should persist any runtime files (generated images, caches, downloads) here.
settings.go:25
SettingsAPI.Defs
Defs(prefix string) []*ConfigDef
Defs returns config definitions matching the prefix.
settings.go:40
SettingsAPI.Dump
Dump() map[string]interface{}
Dump returns all config values.
settings.go:43
SettingsAPI.Get
Get(key string) (interface{}, error)
Get reads the plugin's own config value (config_ table).
settings.go:5
SettingsAPI.GetCore
GetCore(key string) (interface{}, error)
GetCore reads the core config table.
settings.go:14
SettingsAPI.GetPlugin
GetPlugin(plugin, key string) (interface{}, error)
GetPlugin reads another plugin's config table.
settings.go:28
SettingsAPI.List
List(prefix string) ([]string, error)
List returns all keys matching the given prefix.
settings.go:11
SettingsAPI.ListCore
ListCore(prefix string) ([]string, error)
ListCore lists core config keys matching the prefix.
settings.go:20
SettingsAPI.ListPlugin
ListPlugin(plugin, prefix string) ([]string, error)
ListPlugin lists another plugin's config keys matching the prefix.
settings.go:34
SettingsAPI.Plugins
Plugins() []string
Plugins returns a list of all plugin config namespaces.
settings.go:46
SettingsAPI.RegisterDef
RegisterDef(def ConfigDef)
RegisterDef registers a config definition for UI display.
settings.go:37
SettingsAPI.Set
Set(key string, value interface{}) error
Set writes a config value to the plugin's own config table.
settings.go:8
SettingsAPI.SetCore
SetCore(key string, value interface{}) error
SetCore writes to the core config table.
settings.go:17
SettingsAPI.SetPlugin
SetPlugin(plugin, key string, value interface{}) error
SetPlugin writes to another plugin's config table.
settings.go:31
SocialAPI
SocialAPI provides read-only access to the social graph (person profiles and relationships). External plugins can query person traits and social networks but cannot modify them.
| 方法 | 说明 |
|---|---|
GetNetwork |
|
GetPerson |
|
GetRelations |
|
GetTrait |
|
ListPersons |
SocialAPI.GetNetwork
GetNetwork(name string, depth int) ([]*PersonProfile, error)
memory.go:104
SocialAPI.GetPerson
GetPerson(name string) (*PersonProfile, error)
memory.go:101
SocialAPI.GetRelations
GetRelations(name string) ([]SocialRelation, error)
memory.go:103
SocialAPI.GetTrait
GetTrait(name, trait string) (string, bool)
memory.go:102
SocialAPI.ListPersons
ListPersons() ([]string, error)
memory.go:105
TextMemoryAPI
TextMemoryAPI provides access to chronological text event storage.
| 方法 | 说明 |
|---|---|
Append |
TextMemoryAPI.Append
Append(evt TextEvent) error
memory.go:44
AudioURL
type AudioURL struct { URL string `json:"url"` }
plugin.go:862
ImageURL
type ImageURL struct { URL string `json:"url"` Detail string `json:"detail,omitempty"` }
plugin.go:857
MemItem
type MemItem struct { Role string `json:"role"` Content string `json:"content"` Score float64 `json:"score"` }
MemItem represents a memory item in stage context.
plugin.go:179
PluginSDK
type PluginSDK struct { name string regTool ToolRegistrar regStage StageRegistrar regAPI APIRegistrar regOutput OutputChannelRegistrar …
PluginSDK is the main API surface provided to plugins at runtime. It wraps tool registration, settings, memory, knowledge, LLM, and IO injection.
plugin.go:337
SDKVersion
var SDKVersion
SDKVersion 是对外暴露的 SDK 版本号。
plugin.go:10
PluginSDK.UnregisterOutputChannel
!!! warning "仅内核内置插件可用"
外部插件的桥接模板只注入 SetOutputChannelRegistrar(proc_main.go.tmpl:693-705),未注入 SetOutputChannelUnregistrar(grep Unregistrar 在 templates/ 下无命中)。因此外部插件的 regOutputUnreg 为 nil,UnregisterOutputChannel 会命中 if reg != nil 的 else 分支直接返回 nil(sdk/plugin.go:539-549)——即静默无效:不报错、通道也没注销。内置插件由 internal/sdk/plugin.go:330 注入 cfg.RegOutputUnreg,真正生效。外部插件要让通道下线,只能重载插件。
func (s *PluginSDK) UnregisterOutputChannel(name string) error
UnregisterOutputChannel 注销一个输出通道(动态通道随资源生灭时必须调用)。
plugin.go:539