Chisle
The coding-agent plugin that bills 52% of a bare model across 20 live tasks. Terse persona + tool-output compressor + context diet.
用 Go 或 Lua 为 HologramAgent 编写插件 —— API 参考与开发指南
Translation pending
Developer toolsGo · PythonGitHub @JianFeeeeeOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
github sdk mirror for HologramAgent
Translation pending
Loading the full README.
Read the full README ↗HologramAgent 插件开发 SDK,用于构建与 HologramAgent 平台交互的智能插件。
📖 完整文档:https://sdk.hologramagent.jianfgit.xyz/
快速开始 / API 参考 / 指南 / 示例插件都在那里,本 README 只是速览, 接口细节以文档站为准。直接进入:快速开始 · API 参考 · 能力边界
面向 agent 的纯文本入口:
llms.txt(含全部 API 的llms-full.txt)。
当前:SDK 1.3.0(需内核 1.3.0+)。
版本号跟随内核的中版本,patch 位恒为 .0:
| 内核版本 | 对应 SDK |
|---|---|
| 1.0.0 / 1.0.1 / … / 1.0.4 | 1.0.0 |
| 1.1.0 / 1.1.1 / … / 1.1.N | 1.1.0 |
| 1.2.0 / 1.2.1 / … / 1.2.N | 1.2.0 |
| 1.3.0 起 | 1.3.0 |
内核的 patch 位专用于 bugfix 与漏洞修复,不碰公开接口,所以 SDK 版本号不跟着动—— 否则你要么被迫跟版、要么怀疑自己版本过时,而接口其实一个字都没变。
因此 SDK 仓在一个中版本里只发一次(vX.Y.0),核心的 v1.3.1/v1.3.2/… 不伴随 SDK 发版。
(2026-09-13 曾误发过 v1.3.1,已撤回 —— patch 位带非零数字的 SDK tag 都是错误的。)
1.0.x 插件升到 1.1.x:不需要改代码,也不需要重编。 1.1.0 的新增全部是
「插件调用、内核实现」方向,不调就不受影响(已用 SDK 0.9.2 编的旧 plugin.bin
实测验证:在新内核上直接建链通过,因为握手校验的是主版本号、不是 SDK 全等版本)。
想用新字段时重编即可。
1.1.x 插件升到 1.2.x:接口纯追加;若内核共享区布局改了则需重编。 公开接口没有签名变更
(新增 InjectOptions 与六个 *Opts 变体、ChannelDef.ContextPolicy),不调新能力就不受影响。
1.2.0 同时把 fd3 换成了统一共享区(SuperBlock + StageContext + EvtRing),
这类布局变化由握手时的结构判据拦下(统一区魔数、field_count 描述符个数),
而不是靠某个版本号——它们都是二进制里的派生事实,不会「忘同步」。
InjectOptions.Priority)插件可以声明自己这次注入的中断级别,内核按四级阶梯调度:
| 级别 | 常量 | 谁用 |
|---|---|---|
| L1–L3 | PriorityL1 / PriorityL2 / PriorityL3 | 插件按紧急程度自选(L1 最低) |
| L4 | PriorityL4 | 保留给内核与内核级插件(内核自身事件、内核级通道) |
UnregisterOutputChannel)RegisterOutputChannel 注册的通道此前只增不减。对随资源生灭的通道(典型:远程设备
一台设备一个输出通道),设备掉线后通道还在,模型会继续对一个死通道发消息并以为发成功了。
1.3.0 起成对提供:
| API | 用途 |
|---|---|
UnregisterOutputChannel(name) | 注销输出通道(含能力表与工具) |
OutputChannelUnregistrar / SetOutputChannelUnregistrar | 插件侧拿到注销句柄(内核注入) |
⚠️ 通道名要由插件派生得又合法又唯一(外部 id 不能直接当通道名)——
设备 id 这类外部输入可能带 / 等字符,而通道名会拼进 LLM 函数名 output_send__<name>,
违规会让整条 LLM 请求被上游拒绝(2026-09-13 生产事故:device/<id> 导致全量对话 403)。
派生规则与约束见下方「输出通道」一节。
InjectOptions.Payload)插件可以在一次注入上携带自己的业务字段,内核不解释、只透传:
type InjectOptions struct {
// ... 上面的 NoMemory / ContextPolicy / CleanerName / Priority ...
// Payload 是本次注入要额外并入事件 payload 的字段(不透明)。
// 合并时**内核保留键不可覆盖**(见下)。
Payload map[string]interface{}
}
InputEvent.Payload 本就是通道送入内核的请求体,业务字段本该在那里。但
IOInjector 的注入方法全是 (source, channel, text, opts) —— 插件无法直接
给 payload 加字段。Payload 就是这个扩展口。
典型用途:通道插件需要知道"这条消息是发给谁的"。
// 注入时声明身份(键名加插件前缀,避免与内核字段撞车)
sdk.InjectInterruptTextOpts("qq", "qq", text, sdk.InjectOptions{
NoMemory: true,
Payload: map[string]interface{}{"qq_uid": userID},
})
注入点 opts.Payload
→ 与真 payload 合并(保留键跳过)
→ emitResponse / output_send 把**非保留键**原样写进 stageCtx.Extra
插件在 stage 里直接读:
uid, ok := ctx.Extra["qq_uid"]
no_memory / context_policy / recall_policy / scene_policy /
cleaner_name / priority / type / content / interrupt / stop /
command / scene / source / channel / media_blocks / media_type
由内核语义决定,插件写了会被跳过并记日志(不是拒绝整次注入)。
原因:允许覆盖等于允许插件伪造「这条不记记忆」或「这条要立即打断」—— 那是权限提升,不是便利。
「记不记入记忆」与「要不要据此裁剪上下文」这两件事,原先只有 ToolDef 能声明;
1.2.0 起注入侧也能声明,并且二者共用同一套语义与取值。
type InjectOptions struct {
NoMemory bool // true = 不参与记忆计算(向量化/关键词提取/蒸馏),原文仍留在上下文
ContextPolicy string // ""/none = 不裁剪(默认);prune = 据此裁剪上下文
CleanerName string // 计算层过滤函数名:先经 Cleaner 得到实际有效内容,再计算/裁剪
}
const (
ContextPolicyNone = "none"
ContextPolicyPrune = "prune"
)
// 六个变体,与旧的三参数方法一一对应,只多一个 opts
InjectTextOpts(source, channel, text string, opts InjectOptions)
InjectInterruptTextOpts(source, channel, text string, opts InjectOptions)
InjectInputSyncOpts(source, channel, text string, opts InjectOptions) string
InjectInputMediaOpts(source, channel, text string, blocks []ContentBlock, opts InjectOptions)
InjectInputMediaSyncOpts(source, channel, text string, blocks []ContentBlock, opts InjectOptions) string
InjectInterruptMediaOpts(source, channel, text string, blocks []ContentBlock, opts InjectOptions)
要点:
InjectOptions{} 与旧的三参数方法逐键等价(记入记忆 + 不裁剪)。旧方法保留为
零值糖(InjectText / InjectInterruptText / InjectTextNoMemory …),存量插件不改一行、
不需重编即可继续调用。prune)必须显式声明:它会归档丢弃低相关事件,是有副作用的行为,故默认关闭。
内核只放行 "" / none / prune(ValidContextPolicy),未声明的取值会被拒。Cleaner(由 CleanerName 指定)拿到实际有效内容,
避开「按原文裁剪、按清洗后计算」这种不一致。ChannelDef 也有同名 context_policy(并且 1.2.0 给它补上了 JSON tag——通道定义要跨进程
传给内核,而 Cleaner 是函数必须忽略;无 tag 时新增字段会被静默丢掉)。七个阶段,按一轮任务的真实时序:
on_input → pre_action → [ stepLLM 调模型 ] → post_action
↓
工具循环: before_toolcall → [ 执行工具 ] → after_toolcall
↓ ↓
(还有工具则回到 stepLLM) (否则)
↓
before_output → [ 发送 ] → after_output
| 阶段 | 时机 | 典型用途 |
|---|---|---|
on_input | 输入事件刚进内核 | 权限上下文绑定、改写用户输入 |
pre_action | 即将调 LLM | 标记"接下来要等模型" |
post_action | LLM 已返回、工具未执行 | 审阅/改写模型的 tool_calls |
before_toolcall | 即将执行工具 | 权限门、参数校验、拦截 |
after_toolcall | 工具已返回 | 清洗结果、度量 |
before_output | 即将发送/输出 | 改写文本、发"正在输入"指示 |
after_output | 已发送/一轮结束 | 收尾、状态归位 |
插件侧把 handler 存在 map[string]StageHandler(阶段名为键)⇒
同一阶段注册两次,后者静默覆盖前者。
s.RegisterStage(sdk.StageBeforeToolcall, p.permissionGate) // ← 被下一条覆盖
s.RegisterStage(sdk.StageBeforeToolcall, p.updateStatus) // ← 实际生效的
后果不只是功能不生效 —— 如果被覆盖的是权限门,等于权限被静默关掉。
而内核日志会显示 2 handlers [qq qq],看起来完全正常。
正确做法:把多个功能合并进同一个 handler。 每次阶段往返都有跨进程开销, 合并写法本身也是性能要求。hmapdev 在构建期对重复注册告警:
This is the beginning of the README. Read the rest at the original. Read the full README ↗