mirror of
https://gitcode.com/JianFeeeee/homeagent-sdk.git
synced 2026-10-31 06:17:25 +00:00
现场(2026-10-19 内核日志,13 次调用全灭):
用户反复说"gethistory 拉不到私聊消息",纠正多次仍不改。
查日志:qq_get_history 失败 13 次,错误全部是
napcat get_group_msg_history: retcode=200: 消息undefined不存在
而 get_friend_msg_history 一次都没被调用过。
## 根因
handleGetHistory 用 `convInt64(args["group_id"])` 的 **err==nil** 判"调用方给了群号",
而 convInt64 对 0 也成功返回。于是:
模型 qq_get_message 取私聊正文
→ 返回体无条件带 "group_id": 0(QQ 协议里私聊群号就是 0)
→ 模型把字段原样抄进 qq_get_history
→ 代码认为"要查群 0" → get_group_msg_history(group_id=0)
→ NapCat 回「消息undefined不存在」,看起来像历史拉不到
实机验证:直接 POST NapCat
get_group_msg_history {"group_id":0} → retcode=200 消息undefined不存在
get_friend_msg_history {"user_id":…} → retcode=0 正常
## 改动
1. 新增 argPeerID:把 0 判为"未提供"(群号/QQ号都是正整数,0 从不是合法会话)。
权限门用的 argInt64 早有 parsed!=0 检查 —— 本仓此前是「权限门严、取数松」。
2. handleGetHistory 改用它,并区分三种情况:只给 user_id 走私聊、只给 group_id
走群聊、两个都给直接报错(旧实现静默选群分支,把私聊打成群查询)。
3. 同一零值缺陷一并修:handleSendFile(group_id=0 会发往不存在的群)、
handleMarkRead(markChatRead(0) 静默清空不存在的会话)、
handleResolveNickname(group_id=0 打群成员端点)。
4. **msgToGetMsgResult 私聊不再输出 group_id** —— 这是根因的上游。只改下游判据
只能止血,模型仍会一次次把 0 抄回来。
5. schema 写死参数语义:「私聊必须省略 group_id、只传 user_id;不要传 0」。
模型需要知道自己该传什么,否则会反复试错。
6. skill 补「拉私聊历史」整节 + 常见报错对照表;顺带修正一句错述
(原文称"本插件声明 auto_start: false",实际三方都未声明,按契约默认自启)。
7. 新增 history_endpoint_test.go:5 条判据钉住端点选择与返回体契约,
用 httptest 打桩 NapCat,断言真实命中的 action。
## 版本
1.4.3 → 1.4.4。已构建 hmap 并经 plugin_install 装到本机生产实例,
增量重载后 qq 子进程换新、工具全部重注册、skill 移交 skillmgr。
86 lines
3.8 KiB
Markdown
86 lines
3.8 KiB
Markdown
---
|
||
name: qq-tools
|
||
description: QQ 消息收发(NapCat 协议桥接)。本插件**常驻自启**:它在监听外部消息,不能按需加载。
|
||
---
|
||
|
||
# QQ 消息
|
||
|
||
## 本插件为什么常驻
|
||
|
||
QQ 插件在 **HTTP 端口上监听 NapCat 推送**。用户发来的 QQ 消息**随时可能到达**,
|
||
到达后它立刻注入给 agent。
|
||
|
||
如果把本插件改成按需加载:
|
||
|
||
- 内核启动时它不会被拉起
|
||
- **没有人会去拉起它** —— 因为没有人知道有消息在等着
|
||
- 结果:QQ 消息**静默丢失**,用户以为 agent 不理他
|
||
|
||
所以 `auto_start` 必须保持默认(true)。这不是性能取舍,是**功能正确性**。
|
||
|
||
## 工具
|
||
|
||
| 工具 | 用途 |
|
||
|---|---|
|
||
| `qq_get_message` | 按 message_id 取单条正文(消息刚到时先用它) |
|
||
| `qq_get_history` | 按会话拉最近历史(**群聊传 group_id,私聊传 user_id**) |
|
||
| `qq_list_chats` | 会话列表,按最新消息排序,带未读数与摘要 |
|
||
| `qq_mark_read` | 清零某会话未读数 |
|
||
| `output_send__qq` | 主动发消息(群/私聊由 meta 决定) |
|
||
|
||
## ★★ 拉私聊历史:只传 user_id,**不要**传 group_id
|
||
|
||
这是本插件最容易踩的坑,已经真实咬过人(2026-10-19 连续 13 次调用全失败):
|
||
|
||
```
|
||
qq_get_history(user_id=2198972886) ✅ 拉私聊
|
||
qq_get_history(group_id=123456) ✅ 拉群聊
|
||
qq_get_history(group_id=0, user_id=2198972886) ❌ 报「消息undefined不存在」
|
||
```
|
||
|
||
**为什么容易错**:`qq_get_message` 取回一条**私聊**消息时,QQ 协议里群号
|
||
就是 `0`。如果返回体带了这个字段,把它原样抄进 `qq_get_history` 就会去查
|
||
"群 0" —— 而群 0 不存在,NapCat 只会回一句含糊的
|
||
`retcode=200 消息undefined不存在`,看起来像"历史拉不到",其实是参数用错。
|
||
|
||
现在的行为(已修):
|
||
|
||
- 私聊消息的返回体**不再包含 `group_id`** —— 没这个字段就别去抄它
|
||
- 后端把 `group_id=0` 判为"未提供",走私聊端点
|
||
- 两个都传会直接报错并说明"只能二选一",而不是静默选错分支
|
||
|
||
**判断该传哪个**:看消息来源。私聊注入会带 `user_id=...`;群聊注入带
|
||
`group_id=...`。拿不准就先 `qq_list_chats`,它会标明每个会话是群还是私聊。
|
||
|
||
## 常见报错怎么读
|
||
|
||
| 现象 | 含义 | 怎么办 |
|
||
|---|---|---|
|
||
| `消息undefined不存在` | 通常是把私聊当群聊查了(group_id=0 或群号错) | 私聊改传 `user_id`,别传 `group_id` |
|
||
| `message_id 可能已过期` | NapCat 侧短号有时效(约 7 天) | 改用 `qq_get_history` 按会话拉 |
|
||
| `只能访问当前 QQ 会话` | 权限门:非 owner 只能读本轮会话 | 不是 bug;需要跨会话请让 owner 操作 |
|
||
|
||
## 注意
|
||
|
||
- 依赖 **NapCat** 服务在运行且已登录。
|
||
- 消息可能过期(NapCat 侧有时效),发送失败时会明确报错。
|
||
- **不要在群里 @全体成员**、不要连续刷屏。
|
||
|
||
## ★ 用 plugin_stop / plugin_start,不要用 plugin_disable / plugin_enable
|
||
|
||
四个工具看着像同义词,语义差别很大:
|
||
|
||
| 工具 | 作用 | 跨重启 |
|
||
|---|---|---|
|
||
| `plugin_stop` | **运行态**关闭,现在停掉 | **不影响**,重启照常加载 |
|
||
| `plugin_start` | **运行态**拉起,现在开回来 | 不影响 |
|
||
| `plugin_disable` | **持久**禁用 | **会记下来**,开机也不自启 |
|
||
| `plugin_enable` | 解除持久禁用 | 会记下来 |
|
||
|
||
**释放上下文用 `plugin_stop`。** 用 `plugin_disable` 会顺手把"开机自启"
|
||
永久关掉 —— 用户下次开机发现插件没了,得再手动开回来。
|
||
|
||
(本插件**没有**声明 `auto_start: false`,即按契约默认**自启** —— 这是对的,
|
||
它必须常驻。所以这里更要小心:`plugin_disable` 会写进持久禁用清单,
|
||
让重启后的自启也失效,只能再用 `plugin_enable` 解。)
|