|
|
74e2c654ed
|
feat(sdk): 22 个示例插件配齐 skill + 三态启动声明 + 打包期校验
## 用户的三条要求
1. 「所有插件都默认自启会占启动时间,还在 llms 上下文塞大量工具」
⇒ 纯工具型改为按需加载,靠 skill 让 agent 知道要先拉起。
2. 「会向 agent 注入输入的部分插件不能这样做」
⇒ 9 个必须常驻,其余 13 个按需。
3. 「sdk 是不是没有暴露插件声明自身行为的方案」
⇒ 对:auto_start 只在 internal/plugin/manifest.go 有,SDK 零命中。
## 定级依据(代码证据,不是猜描述)
注入 / 钩子 ⇒ 必须常驻(9 个):
qq HTTP 监听 NapCat 推送,消息到达要立刻注入
rss 轮询订阅源,新文章主动推送
calendar 30s ticker 检查提醒
memo 5min ticker 检查待办到期
a2a TCP 监听外部 Agent 请求
acp TCP 监听 ACP 客户端
browser ticker + 长命令完成回调
sanitizer 注册 stage 钩子(无工具,要每轮生效)
luademo 注册 stage 钩子做示范
纯工具型 ⇒ 按需(13 个):
weather ocr music bili ai_image files editdoc deepsearch
plugindev recoverydiag vanblog vikunja commondev
★ commondev 值得单独说:它虽然有 6 处 Inject 调用,但**注入源是自己的
sidecar 完成回投**,而 sidecar 只在 commondev_agent_up 时才拉起
(Start() 里不启 sidecar,通道也在 agent_up 时才注册)。所以没有 agent
调用它时,它不需要做任何事 —— 可以按需。44 个工具 ≈726 tokens 省下来可观。
## 改动
· 22 个插件各配一个 skill,写明:何时用、先 plugin_start、
工具清单、**用 plugin_stop 而不是 plugin_disable**
· 13 个 plg.json 加 `auto_start: false`
· `sdk/autostart.go`:把三态语义与"按需插件必须有 skill"的理由写进公开 SDK
· hmapdev 打包期校验:`auto_start=false` 但无 skill/ 目录 ⇒ 直接 exit 1
(理由:缺 skill 的按需插件**等价于没装**,比没装更糟 —— 占着磁盘让人以为可用)
· 修 Lua 打包路径:它**完全不走** rawfile/skill 收集,luademo 的 skill 永远打不进去
· 修 build-examples.sh:原来 `[ ! -x ]` 判"二进制在不在",改了 hmapdev 源码后
重新打包**仍用旧工具链**(实测:luademo 的 skill 一直是 0)
· 补 mocksdk.ToolDef.Timeout(上一轮加 ToolDef.Timeout 时漏了镜像)
## 判据
· TestHasSkillDirRequiresRealSkillMd(空目录/.gitkeep/无 SKILL.md 都不算)
· TestBuildRejectsOnDemandWithoutSkill(打包期必须真拦住 + exit 1)
· TestLuaPackagingCollectsSkillAndRawfiles(**验代码行**,不验注释 —— 第一版
只查符号存在,把 createHmapWithRaw 换回 createHmap 判据照样通过)
## 踩过的坑
· Lua 分支的结束边界原是 `// Resolve build config`,但那个注释在文件里
**出现多次**(Go 路径里也有),用它截取会把 Go 分支一起包进来 ⇒
变异测不出。改成截到 Lua 分支自己的 `return` + `}`。
|
2026-10-18 13:21:47 +08:00 |
|
|
|
a01fe21ab1
|
feat(qq): 同一会话连续消息合并为一次中断 + 示例 SDK 指回仓库源码
需求(jianf):同一个人连发的数条消息应打包成一次中断,别逐条唤醒 Agent。
- debounce 合并:同一会话 + 同一发送者(群聊按 群号+QQ、私聊按 QQ)在
batch_window_ms(默认 1500)内的连续消息合成一批,每来一条重置计时;
整批不超过 batch_max_ms(默认 30000),避免对方持续刷屏时一直不投。
- n>1 时中断说明「短时间连续发来 N 条」并列出 message_id,建议一次
get_history 拿全上下文;n==1 沿用原文,行为与合并前逐字一致。
- 可配置 batch_window_ms / batch_max_ms,0 = 关闭合并(逐条投递)。
- Stop 时 flush 未到点批次,别把对方消息吞掉。
- 新增 4 条测试(同发送者合并 / 不同发送者不合并 / 窗口 0 逐条 / 单条沿用原文)。
顺带:deepsearch / vikunja 的 go.mod 与 plg.json 此前指向本地安装的 SDK 1.2.0,
导致无法用当前 SDK 重编(缺 InjectOptions.Priority)。改回 ../../ 仓库源码,
与其余示例一致。
|
2026-09-14 16:09:45 +08:00 |
|
|
|
e50bffa34f
|
fix(deepsearch): 只关自己拉起的 SearXNG;条数改由插件侧截断
两个都由「真调用/真测试」暴露,且都会让线上搜索表现为「后端不可用」。
一、归属:接管不等于拥有(例:E2E 测试把生产后端带走)
旧实现只要探活成功就认领关闭责任 → 同机第二个实例(测试拉起的插件、另一个 daemon)
退出时就 docker compose stop 掉**线上正在用的**后端。实测:跑一次
`go test ./internal/plugins/ -run TestRealPlugin_DeepSearch`,teardown 即关停
127.0.0.1:8888,用户看到的就是「搜索后端起不来」。
修:只有真正执行过 `docker compose up -d` 的实例才算「我们起的」;探到已在运行只接管。
二、条数:SearXNG 不认 count/limit(count/max_results 形同虚设)
实测 ?count=3、?limit=3、不带参数返回**完全相同的 35 条**,所以截断必须在插件里做。
旧实现把 count 当 limit 参数发给 SearXNG 就以为生效了 → 模型每次吞 35~58 条带摘要结果,
还会把「命中 N 条」当成「拿到了 N 条」报给用户(实测发生过)。
修:新增 limitResults(默认取 max_results,上限 20);输出改成
「命中 N 条,返回前 M 条」;不再发无意义的 limit 参数。
验证:22 项单测全过、-race 干净、vet/gofmt 干净;两条归属测试做过扰动(把旧语义放回
去后必红,并如实打出它执行的 `docker compose stop -t 2`);内核 E2E 三条通过且
**跑完 healthz 仍 200、容器未重启**;线上 1.1.2 实测 count=3 → 「命中 40 条,返回前 3 条」。
版本 1.1.0 → 1.1.2。
|
2026-09-13 09:55:27 +08:00 |
|
|
|
7c0b7a1fb0
|
feat(deepsearch): 联网检索插件 + SearXNG 生命周期托管
把原 websearch 示例改名为 deepsearch(目录/go.mod/plg.json/工具前缀/README 全量对齐,
工具名 websearch_* → deepsearch_*)。
新增 searxng.go:插件自己托管搜索后端
- 启动探 healthz:已在跑则直接接管(不重启),没跑就 docker compose up -d 并等就绪
- 关闭动作注册为 stop handler(幂等、限时 4s < 内核 5s 宽限期)
- 配置 manage_searxng / searxng_dir / stop_searxng_on_exit
- 崩溃/被 kill 时不关后端(下次启动接管):安全失败方向
- 可注入 cmdRunner + 时间预算,8 项单测离线覆盖接管/拉起/失败/幂等/保留
契约依据(internal/plugin/proc):停止插件 = plugin.stop → RunStopHandlers(LIFO、
幂等)→ Stop() → exit(0),宽限期 5s;stdin 关闭同路径。
验证:19 项单测(18 通过 + 1 联调跳过)、-race 干净、vet/gofmt 干净;内核 E2E 真调用
返回 58 条/37 条、58/58 带摘要;线上 daemon 实测 stop handler 与冷启动(约 3s)。
|
2026-09-12 23:16:23 +08:00 |
|