Commit Graph

5 Commits

Author SHA1 Message Date
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
b6a6168e65 chore(module)!: module 路径随托管地迁移 gitcode → github
SDK 仓已迁到 GitHub 作为权威源,go module 路径同步:
`gitcode.com/JianFeeeee/homeagent-sdk` → `github.com/JianFeeeee/homeagentsdk`

## 范围

74 个文件:71 个 Go 源码 + go.mod/go.sum + hmapdev 模板 + CI 与打包脚本。

主仓(HomeAgent)侧对应改动是 6d38a164。本仓这些文件当时**不在工作树上**
(见下),所以那次的批量替换扫不到它们——这里是补偿。

## 为何这批文件当时不在工作树上(事故与恢复)

10-08 07:58 主仓做 checkout/cherry-pick 序列时,把本仓工作树的 20 个文件
误清理了(`example/*/plugin.go` + `plg.json`,10 个示例)。

根因是**结构误判**:本仓是「一目录两仓库」结构下主仓 `third_party/` 里的
**独立 git 仓库**,而主仓 .gitignore 忽略了 `example/`,于是它在主仓
`git status` 里不可见 —— 看着像「不受跟踪的构建产物」,实际是本仓受跟踪的源码。

已全部恢复(`git checkout -- <D 文件>`,只恢复删除、不动迁移改动),
20 个文件与 HEAD 逐字节一致。

**破坏的连锁后果**:恢复出来的 `plugin.go` 仍写着旧 module 路径,
因为迁移时它们已不在工作树上。所以这次补偿是必须的,不是可选优化。

## 保留 gitcode 引用

以下位置的 `gitcode` **有意保留**,不要"顺手清掉":
- README.md / docs 里「二进制实际分发地址目前是 gitcode」的说明
- mkdocs.yml 的 gitcode 镜像链接(国内直连更快)
- `tools/hmapdev/templates.go` 里「SDK 已从 gitcode 迁到 GitHub」的注释

gitcode 仍是**只读镜像**(源码同步;release 附件暂时仍在那边)。

## 验证

- 本仓根 module:`go build ./...` + `go test ./...` 全通过
- 全部 20 个 example:`go vet` 通过(补偿前 10 个因旧 module 路径失败)
- example/qq:`go test` 通过,且能从源码 build 出可用 plugin 产物
2026-10-09 07:25:25 +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