Skip to content

Latest commit

 

History

239 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hermes-plugin-milky

standard-readme compliant

Hermes 的 Milky QQ 平台适配器

让 Hermes 进入 QQ 私聊和群聊,成为一个会判断何时回应的 AI 参与者:

  • 接收消息,并识别提及、引用、图片等上下文;
  • 根据会话状态判断何时回应、何时保持沉默;
  • 发送文本、@、引用、图片、语音、视频和文件;
  • 提供群组、成员、文件和好友/入群请求等 QQ 能力。

Warning

当前仍有一类权限隔离尚未完成:

  • ToolSpec: 26 个 QQ 工具没有独立的调用者和目标授权。模型、其他会话或 cron 可能查询无关群/好友,或执行禁言、踢人、撤回、退群、删好友、接受/拒绝请求等操作。

最低限度的安全配置:

  • MILKY_ALLOWED_CHATS 只填写你能控制、成员可信且用途明确的会话;
  • 不要加入公开群、成员可随意加入的群或不受控私聊;
  • 记住:该配置只限制入站会话,不等于 ToolSpec 或通用出站 sender 的授权;
  • 留空、未配置或原生空列表表示阻止全部普通入站;全部放行需显式填写 group:* 和 dm:*。

示例:

MILKY_ALLOWED_CHATS=group:123456789,dm:987654321

示例中的 ID 仅为占位值。

允许使用 dm:* 放行所有私聊,或使用 group:* 放行所有群聊;通配符只匹配对应的 dm:/group: 命名空间,也可以和具体 chat key 混用。

详细的模块职责、生命周期和行为契约见 ARCHITECTURE.md;主规范、未归档 change 和已归档 change 的测试证据见 openspec/。

目录

核心能力

插件适合希望把 Hermes 放进 QQ 私聊和群聊的场景:

  • 自然参与: 根据提及、引用、关键词和会话状态,决定回应还是保持沉默;
  • 静默标记兜底提示: 普通文本出站会精确过滤 Hermes 在拒绝 [SILENT] 后生成的固定提示;相似或包含该提示的其他文本,以及媒体和文件发送,不受影响。Gateway 仍可能将过滤结果记为 delivered,但不会产生 QQ 消息。
  • 多媒体消息: 接收图片、语音等上下文,并发送文本、@、引用、图片、语音、视频和文件;入站视频只展示 Milky 提供的资源引用和时长,不提供视频内容分析;当前语音交给 Hermes core 的 STT 流程,插件不负责 provider 适配或音频格式转换;
  • QQ 信息能力: 查询群组、成员、文件和好友/入群请求,并提供部分 QQ 操作;
  • 会话安全边界: 支持 chat 白名单、禁言状态同步、消息去重和有界历史缓冲。
  • QQ 会话介绍: 在支持 system prompt section 的 Hermes 宿主中,首次 Milky friend/group session prompt 可看到当前会话的最小资料;介绍来自入站消息快照,不实时查询 Milky。
  • 人工贴纸维护: 通过显式 /milky sticker 命令维护插件持久目录中的图片库;普通消息、入站图片、 关键词、Will 和 Agent 输出不会自动收集贴纸。
  • Agent 贴纸能力: 插件运行时依赖 jieba 且库中存在可用条目时,sticker_search 可严格搜索、显式放宽意图或浏览有界候选, sticker_send 可按同样的查询或已知贴纸 ID 向当前 Milky 会话发送一张贴纸;目标、路径和 URL 仍由工具边界管理。

运行环境:Python 3.13+、Hermes Gateway、Milky v1.3 服务以及插件声明的 httpx、Pillow、jieba。 Hermes 负责 Agent 队列及入站资源的下载、缓存和权限边界;本插件负责 Milky 适配和已声明的 QQ 能力。

安装

从 Hermes 安装

hermes plugins install ByteColtX/hermes-plugin-milky --enable

--enable 会在安装成功后直接启用插件并跳过确认提示。首次安装完成后,可以用下面的命令确认 插件状态:

hermes plugins list

如果 hermes-plugin-milky 显示为 disabled,启用插件:

hermes plugins enable hermes-plugin-milky

插件启用后,按配置完成 Milky 服务地址、access token 和可选白名单配置, 然后重启 Gateway:

hermes gateway restart

已有安装需要拉取新版本时,执行:

hermes plugins update hermes-plugin-milky
hermes gateway restart

更新插件代码或配置后都需要重启 Gateway,运行中的进程不会自动加载新的插件代码。

从源码设置开发环境

git clone https://github.com/ByteColtX/hermes-plugin-milky.git
cd hermes-plugin-milky
uv sync

源码安装适合开发和调试。拉取新代码后重新同步环境,并重启 Gateway:

git pull
uv sync
hermes gateway restart

Important

启动前先完成下方的 Milky 配置,并确保启动 Hermes 的进程会加载 ~/.hermes/.env。 配置只在启动时读取;修改后需要重启 Gateway。

配置

Dashboard 管理

当前工作区包含 dashboard/manifest.json、API 和 dashboard/dist/ 预构建资源。 已在固定版本的真实 Hermes 与 Debian 环境验证配置、视觉导入和浏览器维护流程;版本、证据类型及环境限制见 Dashboard evidence。 已验证的宿主 core 为 b3a1900e72a16da450ff637aaa37cc23f68992a6,Web SDK 为 1.1.0; 尚未确认最低 Hermes 发行版本。将完整 directory plugin 安装到宿主启用的用户插件位置, 重启 Hermes Dashboard 后打开 Milky QQ;项目本地安装不足以挂载插件 Python API。 Gateway 与 Dashboard 分别拥有连接和任务生命周期,缺少 QQ 地址或凭证也可以访问配置页。

普通配置逐个顶层键按 plugins.entries.hermes-plugin-milky.settings → 旧 config → 当前 profile 的 MILKY_* 环境 → 默认值解析。settings 使用原生类型,Will 使用完整对象选源; 非法高优先级值会报错。地址可放在 YAML,access token 仍由宿主凭证机制管理。 例如在现有 plugins 配置中合并:

plugins:
  enabled: [hermes-plugin-milky]
  entries:
    hermes-plugin-milky:
      settings:
        base_url: http://127.0.0.1:3000
        allowed_chats: ["group:123456"]
        session_buffer_size: 20
        group_member_event_notifications: false

页面明确选择 profile;保存普通设置与保持/替换/清除凭证是独立操作。 保存前检查版本、逐字段核验保存结果,但 core 没有跨入口条件事务,不能保证检测全部竞争。 保存成功只表示持久化;Gateway 重启后使用新快照,页面运行态保持 unknown。

图库浏览为只读,预览通过宿主认证按可见 ID 获取。Web 上传与命令 inbox 分开:单图 10 MiB, 每批最多 50 张及 100 MiB,每 profile 暂存配额 500 MiB;无活动引用的上传 7 天后可显式回收。 上传后另行确认导入;失败项可重新明确提交或丢弃批次。编辑、删除、重新分析、清理和索引修复 使用持久任务,每 profile 一个执行批次、最多 10 个排队。任务区分 queued、running、 succeeded、partial、failed、cancelled、interrupted,历史保留 7 天且最多 1000 条。 取消不撤销已提交条目,关闭和恢复不自动重放;过期请求返回 expired。

回滚前停止新维护请求并关闭 Dashboard,保留图库、上传和任务记录。旧插件只读环境配置, 需手动把要保留的普通 settings 转成原 MILKY_* 格式后再回滚,不删除宿主凭证。 后端更新需要重启 Dashboard,刷新页面不能替换已加载 Python 路由。 开发前端使用 npm ci --prefix dashboard、npm run build --prefix dashboard, npm run check --prefix dashboard 检查源码与预构建产物一致;Python wheel 不包含 directory plugin。

Milky 最小配置

建议将环境变量集中保存到 ~/.hermes/.env:

MILKY_BASE_URL=http://127.0.0.1:3000
MILKY_ACCESS_TOKEN=<从安全凭证存储注入>
MILKY_ALLOWED_CHATS=group:123456789,dm:987654321
MILKY_SESSION_BUFFER_SIZE=20
MILKY_HOME_CHANNEL=group:123456789
# MILKY_MAX_LOCAL_MEDIA_BYTES=33554432
# MILKY_GROUP_MEMBER_EVENT_NOTIFICATIONS=false
# MILKY_WILL_POLICY=<JSON 字符串,见下方 Will policy>

上面的 QQ/群号仅为合成示例。.env 只应保存在本机或安全的部署环境中,不要提交到版本库。

变量 必需 作用
MILKY_BASE_URL 有效地址必需,环境可选 Milky 服务基址,也可来自 YAML settings/config;Action 使用 <base>/api/{action},事件流使用 <base>/event。远程部署请使用 HTTPS。
MILKY_ACCESS_TOKEN 是 Milky access token,只用于 Bearer 认证。
MILKY_ALLOWED_CHATS 否 入站 chat key 白名单,支持具体 group:<群号>/dm:<QQ号> 以及 group:*/dm:*;通配符只匹配对应命名空间,可混用;留空、未配置或原生空列表表示阻止全部普通入站;全部放行需显式填写 group:* 和 dm:*。
MILKY_WILL_POLICY 否 决定消息等待(wait)或触发(trigger)的嵌套 JSON 配置。
MILKY_SESSION_BUFFER_SIZE 否 wait 历史消息上限,默认 20;设为 0 可关闭历史缓冲。
MILKY_HOME_CHANNEL 否 系统消息和 cron 的默认目标;不参与入站白名单。
MILKY_MAX_LOCAL_MEDIA_BYTES 否 出站本地资源原始字节数上限,默认 33554432(32 MiB),合法范围 8388608(8 MiB)至 33554432(32 MiB)。
MILKY_LONG_TEXT_FORWARD_THRESHOLD 否 超长文本合并转发阈值,默认 0(关闭);只接受 0..4096 的十进制整数,只有可见规范化文本长度严格大于正值时才选择一个 forward。
MILKY_GROUP_MEMBER_EVENT_NOTIFICATIONS 否 群成员入退群即时通知,默认 false;只接受大小写不敏感的 true/false,只在启动时读取。开启后追加固定英文 Tip,并在 Hermes 已确认或持久化恢复的 session key 且接受注入时立即触发 Agent turn。

消息 chat key 只接受 group:<十进制群号> 或 dm:<十进制 QQ 号>;白名单另支持完整的 group:* 和 dm:* 规则,temp 会话不会回退到其他目标。

Hermes Agent 推荐配置

下面的配置让群聊共享 session、在 Agent 忙碌时排队,并减少进度消息。请合并到 ~/.hermes/config.yaml,保留已有的其他配置:

启用 MILKY_GROUP_MEMBER_EVENT_NOTIFICATIONS=true 时,还必须保留下面的 plugins.entries.hermes-plugin-milky.allow_gateway_injection: true;这是允许成员事件通过 已有 Gateway session 触发 Agent turn 的插件级授权。

# 群友共享同一个 group:<群号> 会话
group_sessions_per_user: false

# 显式指定时区
timezone: Asia/Shanghai

# 长任务跟进与卡住自恢复
agent:
  gateway_timeout: 360          # 无 Agent 活动 6 分钟后终止当前 turn
  gateway_timeout_warning: 120  # 超时前 2 分钟发出提醒
  gateway_auto_continue_freshness: 3600
  gateway_notify_interval: 180  # 每 3 分钟发一次“仍在处理”
  session_stall_timeout: 120    # 有排队消息且无进展 2 分钟时提醒
  local_stream_stale_timeout: 180  # 本地 provider 无实际流内容 3 分钟后重连
  api_max_retries: 1            # 减少卡住 provider 的重复等待
  # 已确认主模型支持图片输入时,直接以内联图片交给主模型
  image_input_mode: native

# 自定义 provider 的模型不会总能从模型目录自动识别视觉能力。
# 请在已有 model 配置中保留其他字段,并追加 supports_vision: true。
model:
  supports_vision: true

# 群聊消息不要打断当前任务,排队处理
display:
  busy_input_mode: queue
  busy_ack_enabled: false
  tool_progress_command: false
  background_process_notifications: result
  memory_notifications: off
  platforms:
    milky:
      thinking_progress: off       # 关闭“思考中”状态
      tool_progress: off           # 关闭工具进度
      interim_assistant_messages: false
      long_running_notifications: false  # 可选:关闭“仍在处理”心跳
      show_reasoning: false        # 关闭最终回复中的思考摘要
      streaming: false             # 关闭本插件会话的流式输出
      busy_ack_detail: false       # 隐藏忙碌提示中的迭代/工具详情
      busy_steer_ack_enabled: false
      live_status: off             # 关闭支持状态文本时的实时状态

# Slash Command 发送者门禁;命令名不带 /
platforms:
  milky:
    extra:
      # 私聊管理员 QQ 号
      allow_admin_from:
        - "123456789"

      # 私聊普通用户允许的只读命令
      user_allowed_commands:
        - "status"
        - "context"
        - "agents"

      # 群聊管理员 QQ 号
      group_allow_admin_from:
        - "123456789"

      # 群聊普通用户允许的只读命令
      group_user_allowed_commands:
        - "status"
        - "context"
        - "agents"

# 成员入退群即时通知需要允许插件向已有 Gateway session 注入消息
plugins:
  entries:
    hermes-plugin-milky:
      allow_gateway_injection: true

# 关闭后台自动复盘、自动写入记忆/Skill
auxiliary:
  background_review:
    enabled: true

memory:
  memory_enabled: true
  user_profile_enabled: true
  nudge_interval: 20    # 每累计 20 个对话回合,触发一次自动记忆复盘,写入 MEMORY.md / USER.md

# 关闭自动建议创建 Skill
skills:
  creation_nudge_interval: 0

# /goal 的最大自动续行轮数
goals:
  max_turns: 20

# 推荐闲置 2 小时后重置,避免群聊上下文无限变旧
session_reset:
  mode: idle
  idle_minutes: 120
  notify: false

管理员可以执行所有已注册命令;普通用户默认可以执行 /help 和 /whoami,以及对应 user_allowed_commands 中列出的命令。私聊和群聊的管理员列表分别配置;某个作用域未配置 对应的 *_allow_admin_from 时,该作用域的 Slash Command 门禁不会启用。

示例不向普通用户开放 /milky。Hermes core 按顶层命令授权,放行 milky 也会放行其子命令; 插件不额外设置子命令权限门禁。

建议只开放明确的只读命令。config、tools、model、sessions、cron、goal、 memory、suggestions 和 skills 等命令包含配置、会话、工具或任务状态变更,不建议加入普通用户白名单。

修改后需要重启 Gateway;配置只在启动时读取。

设置 MILKY_LONG_TEXT_FORWARD_THRESHOLD 为正数后,超出阈值的有序文本/native image/record/video 批次会通过一次 send_group_message 或 send_private_message 发送,顶层 message 只含一个 forward,节点身份优先使用已确认的 Bot uin/昵称。身份读取失败、昵称为空或含控制字符时固定使用 user_id=10001、 sender_name=QQ用户;成功出参使用单一 data.message_seq。配置为 0 或文本长度不超过阈值时, 继续使用普通分块和 [SPLIT] 三条预检。文档/文件不是该自动 forward 的目标,继续走独立 file upload;Hermes 若只提供分离的 MEDIA: 调用,插件不会猜测其与文本属于同一批次。

需要回滚时删除该变量或改回 0,然后重启 Gateway。本文不宣称 Milky 服务端对 forward 总大小或节点数量的未验证上限。

group_sessions_per_user: false 会让群友共享同一个 Hermes session;这适合群聊,但也意味着 群内消息会共同影响上下文。busy_input_mode: queue 让 Hermes 负责 queue、follow-up、 pending 和 interrupt/steer,插件不复制 Agent 执行队列。

Tip

平台显示设置必须放在 display.platforms.milky 下,不要放到全局 display 下。

关闭自动压缩进度提示

如果不希望在 QQ 中看到自动压缩过程中的 413/compression 提示,可通过 CLI 关闭:

hermes config set compression.progress_notices false
hermes gateway restart

该配置只隐藏常规压缩进度提示;压缩最终失败时的错误提示仍会保留。

image_input_mode: native 与 model.supports_vision: true 只应配置在已确认支持 OpenAI-compatible image_url 输入的主模型上。如果接口不支持原生图片输入,请移除这两项并 使用文本视觉路径。修改后重启 Gateway;日志应出现 Image routing: native。

记忆后端

建议使用 Hermes 的 holographic 记忆后端:

hermes config set memory.provider holographic

它适合本地部署:数据保存在本地,不依赖付费云服务,并支持围绕实体召回长期上下文。修改后 重启 Gateway 使配置生效。

查看本地记忆库中的事实记录:

sqlite3 -header -column ~/.hermes/memory_store.db "SELECT fact_id, content, category, tags, trust_score, updated_at FROM facts ORDER BY fact_id;"

Will policy

Will 决定一条消息是先等待,还是交给 Hermes:

  • wait:放入当前 chat 的有界缓冲,暂不启动 Agent;
  • trigger:先取出该 chat 的等待历史,再把当前消息交给 Hermes。

MILKY_WILL_POLICY.engine 只选择一套引擎。routing 和 willingness 共用消息特征、都输出 wait/trigger,但不会叠加运行。

三类关键词(routing.keywords、willingness.forceKeywords 和 willingness.interestKeywords)只匹配当前消息顶层的 text、markdown 内容,按连续文本直接 做子串匹配。结构化 @ 的显示名称、回退 QQ 号和“全体成员”展示文字不参与关键词匹配, 也不会带来兴趣关键词倍率;直接 @Bot 的独立触发与提及增益仍按对应配置生效。 用户在普通文本里手动输入 @提醒小助手 时,“提醒”仍可命中。相邻 text、markdown 可以 组成关键词,但中间有 @、图片等非文本片段时不会跨过它拼接,例如“提”+@某人+“醒” 不会命中“提醒”。未命中兴趣关键词时仍使用 defaultMultiplier 计算原有基础和属性增益。

引擎 决策方式 适合场景
routing(默认) 当前消息命中规则就触发,结果确定 希望行为可预测、方便排查
willingness 按 chat 维护分数,再按概率抽样 希望机器人偶尔参与、减少刷屏

routing:确定性规则

routing 只看当前消息:不维护分数、不使用随机数。direct 等规则字段的值只能是 wait 或 trigger;keywords 不填写动作,命中时固定为 trigger。

字段 命中条件 默认行为
direct friend 私聊 trigger
mention 直接 @Bot(只认 mention.user_id == self_id) trigger
mentionAll @全体成员 wait
quote 回复 Bot 的消息(只认 reply.data.sender_id == self_id) wait
poke 协议明确指向 Bot 的 poke wait
allMessage 每条普通 friend/group 消息 wait
keywords 正文包含任意一个非空关键词 空数组(不命中)

一条消息可以同时命中多条规则;结果按 OR 合并,任一规则为 trigger 就触发,不会被其他 wait 抵消。图片没有独立 routing 规则,单独出现时仍由 allMessage 决定; friend_nudge 和 group_nudge 在普通消息流程中保持 observe-only。

示例:只有私聊、@Bot 或包含“提醒”的消息进入 Hermes,其余消息等待:

{
  "engine": "routing",
  "routing": {
    "allMessage": "wait",
    "direct": "trigger",
    "mention": "trigger",
    "keywords": ["提醒"]
  }
}

willingness:分数 + 概率

willingness 不把每个信号直接设成 wait/trigger,而是为每个 chat 单独维护一个分数。 普通消息大致经过以下步骤:

  1. 先让分数按静默时间衰减;
  2. 根据文本、提及、reply、图片、私聊等特征增加分数;
  3. 命中 willingness.interestKeywords 时提高本次增益倍率;
  4. 命中 willingness.forceKeywords 时直接得到 trigger,否则在分数超过 probabilityThreshold 后换算成概率并抽样,得到 wait 或 trigger。

因此,同一条消息可能因为当前分数或随机抽样不同而得到不同结果。willingness.interestKeywords 只控制加分倍率,不是确定性触发器;willingness.forceKeywords 才是包含即触发的 确定性规则。

directForce、mentionForce、quoteForce 可让对应信号跳过随机抽样,直接 trigger。 其中 mentionForce 只匹配直接提及当前 Bot(mention.user_id == self_id),quoteForce 只匹配 至少一个明确引用当前 Bot 的 reply(reply.data.sender_id == self_id);他人提及、mention_all、 here、他人引用和无法确认目标的引用都会继续走其他 force 条件或概率抽样。 forceKeywords 与这些 force 字段等价地跳过随机抽样,但不额外增加 score;两类关键词同时 命中时,interestKeywords 仍控制增益倍率,forceKeywords 决定最终触发。 mentionGain 只在直接提及当前 Bot 时加分;quoteGain 只在至少一个 reply 明确引用当前 Bot 时加分,has_reply 只保留 reply 存在性事实,不会单独产生 quoteGain;pokeGain 只在协议 确认 Bot 为接收者的 self-poke 时加分。非 Bot 或无法确认目标的 mention、reply、poke 均不产生 对应 gain。routing 的 quote 规则同样只认引用 Bot;friend_nudge 和 group_nudge 仍是 observe-only,不会直接创建 Agent turn。通过 Gate 且得到 trigger 后立即扣除一次 replyCost 参与成本,不等待 Hermes 接受、资源解析或最终发送;后续失败不回滚。等待、Gate 拒绝、命令、temp 和系统事件不会扣费。

示例:默认按概率参与,命中“提醒”时提高增益,命中“紧急”时直接触发:

{
  "engine": "willingness",
  "willingness": {
    "interestKeywords": ["提醒"],
    "forceKeywords": ["紧急"],
    "keywordMultiplier": 1.2,
    "directForce": false,
    "mentionForce": false,
    "quoteForce": false
  }
}

迁移时将旧的 willingness.keywords 改为 willingness.interestKeywords;旧字段不会被静默 兼容。需要包含即触发时再配置 willingness.forceKeywords,省略或配置为空数组表示关闭。 两者都只匹配规范化正文的直接子串,不支持正则、分词或隐式大小写转换。

配置提示

  • 不确定时使用默认的 routing;它最容易预测和调试;
  • 想切换到概率决策时,将 engine 改为 willingness,并配置 willingness 对象;
  • 完整配置可以同时保留两套参数,但运行时只使用 engine 选中的一套;
  • priority 当前只保留在配置 schema 中,不参与 routing 优先级或 willingness 权重计算;
  • 旧的扁平字段、routing.group、routing.image 和 routing.mentionHere 不会被静默转换, 启动时会直接拒绝。

完整默认配置示例(包含两套引擎参数,默认折叠):

展开完整默认配置
{
  "engine": "routing",
  "routing": {
    "direct": "trigger",
    "mention": "trigger",
    "mentionAll": "wait",
    "quote": "wait",
    "poke": "wait",
    "allMessage": "wait",
    "keywords": []
  },
  "willingness": {
    "maxScore": 100,
    "initialScore": 0,
    "decayHalfLifeSeconds": 600,
    "probabilityThreshold": 55,
    "probabilityAmplifier": 0.04,
    "replyCost": 35,
    "textGain": 12,
    "mentionGain": 100,
    "quoteGain": 15,
    "directGain": 40,
    "imageGain": 8,
    "pokeGain": 80,
    "interestKeywords": [],
    "forceKeywords": [],
    "keywordMultiplier": 1.2,
    "defaultMultiplier": 1,
    "hotWindowSeconds": 15,
    "warmWindowSeconds": 60,
    "hotDecayWeight": 0.3,
    "warmDecayWeight": 0.7,
    "mentionForce": false,
    "quoteForce": false,
    "directForce": false
  },
  "priority": 1000
}

未列出的字段使用插件默认值。配置写入 MILKY_WILL_POLICY 时必须是 JSON 字符串。

Home channel 与 cron

MILKY_HOME_CHANNEL 只影响 Hermes 系统消息和 cron 的默认出站目标,不参与入站 allowlist。 显式的 milky:group:<id> 或 milky:dm:<id> 目标优先。

未配置 home channel 时不会回退到 origin、默认频道、群聊或私聊,也不会猜测目标。standalone cron 每次创建并关闭临时 Milky client,目前只支持无附件文本;媒体和文件输入返回 unsupported。

日志

运行时日志使用 hermes_plugins.milky.* 标准 logger,并传播到 Hermes root。关键消息使用 event=milky.lifecycle、milky.action、milky.sse、milky.inbound、milky.resource、 milky.outbound、milky.mute 或 milky.tool 标签;插件不创建独立日志文件、handler、异步队列 或脱敏后端。

常用查看命令:

hermes logs -f
hermes logs --level DEBUG -f
hermes logs gateway -f

Action、Tool 和出站日志保留结果分类、已知状态码和 duration_ms,不记录 token、Authorization、 完整 URL、请求/响应 body、消息正文、媒体引用、路径、文件内容、Tool 原始参数或结果。Tool 日志 使用 delivered 表示已取得远端响应;协议拒绝和 HTTP 错误也使用该分类并附已知状态码,只有 invalid_input、unsupported 和 transport_unknown 是插件本地 Tool 结果分类。日志不可用或 handler 失败不改变连接、重连、Gate/Will、扣费、发送和未知结果语义。

协议 raw 保真与日志/输出最小化是两条独立边界:parser、入站规范化和 canonical 会保留远端已收到的 字段和值,包括名称看起来像 token、authorization、password 或 cookie 的业务扩展;这些 raw 字段不会因此进入正文、关键词、会话介绍、隐式工具调用或授权判断。Hermes logger 只接收插件选择的 固定低敏元数据;Tool 结果、模型上下文和 session 持久化属于不同的宿主出口,插件不假设宿主会在每个 出口自动清洗秘密,也不在插件日志中复制 raw。当前仓库的 fake host/fixture 证据不能替代真实 Hermes logger、Tool 结果后处理、模型上下文和 session 落盘验证。

常用运维

Gateway 与 Hermes 状态

# 查看 Gateway 状态
hermes gateway status

# 重启 Gateway,使启动配置生效
hermes gateway restart

# 执行 Hermes 整体状态和深度健康检查
hermes status --deep
hermes doctor

日志与配置

# 查看最近的 Gateway 日志和错误
hermes logs gateway -n 100
hermes logs errors --since 30m

# 临时提高日志级别并实时跟踪
hermes logs gateway --level DEBUG --since 15m -f

# 列出日志文件,以及检查配置文件位置和有效性
hermes logs list
hermes config path
hermes config env-path
hermes config check
hermes config get security.redact_secrets

插件与 Milky smoke

# 查看已安装插件及启用状态
hermes plugins list

# 在源码 checkout 中执行只读 Milky smoke
uv run scripts/milky_smoke.py

# 生成用于提交 issue 的脱敏诊断摘要
hermes dump

milky_smoke.py 默认只执行登录、群列表、Bot 成员禁言同步和有界 SSE 连接。发送消息或上传文件 必须显式使用 --allow-write,并且目标还必须位于 MILKY_ALLOWED_CHATS 中;没有明确授权时不要 使用该选项。hermes dump 用于生成脱敏诊断摘要,不要把包含敏感配置的命令输出直接粘贴到公开 issue。

排查日志中的 chat key 脱敏

Hermes core 默认会对日志中的敏感字段进行脱敏。如果需要临时确认日志中的完整 chat_key,可用 Hermes CLI 关闭全局脱敏:

hermes config set security.redact_secrets false

修改后必须重启 Gateway;该配置只在进程启动时读取。排查完成后立即恢复脱敏:

hermes config set security.redact_secrets true

该开关影响 Hermes 全局的日志、工具输出和聊天响应,不只影响 Milky。关闭期间可能泄露 API key、 token 或密码,仅应在受控环境中短时使用。

功能与使用

消息与媒体

普通入站只处理 message_receive:

  • friend 和 group 消息进入普通 Agent 流程;
  • temp 会话直接忽略;
  • Milky SSE GET /event 中的 message_recall、request、notice、lifecycle 和未知事件默认只观察,少数系统事件可作为上下文;
  • face segment 的正文占位符对非 emoji 表情 pack 优先使用随插件发布的本地 catalog 名称;未命中、冲突或目录不可用时回退原 face_id,缺失 ID 时使用 NOT SUPPORTED;
  • 同一 chat 按顺序处理,wait 消息进入有界历史,trigger 时再交给 Hermes。

入站 video segment 在正文中显示为 [video:resource_id=<resource_id>,duration=<duration>];字段缺失时单独使用 NOT SUPPORTED,不展示 Milky temp_url。普通 wait 和 trigger 流程都只保留视频引用,不自动查询链接或交给媒体 materializer。 Agent 可显式调用 get_resource_temp_url 获取临时链接;该工具只返回远端响应,不下载、解码、抽帧、转录或分析视频。 获得临时链接不表示 Hermes 或当前模型能够理解视频内容。

支持 system prompt section 的 Hermes 宿主会额外注册 hermes-plugin-milky.qq-session-context。合法 friend 介绍只包含 user_id、nickname、 sex;合法 group 介绍只包含 group_id、group_name、member_count、description、 announcement。快照在资源解析、MessageEvent mapper 成功后、Hermes handle_message() 前登记, 以 dm:<id>/group:<id> 隔离并使用有界进程内缓存;不同 Hermes user session 可以共享同一个 group 介绍。介绍不会写入当前消息正文、历史 channel_context、platform hint 或出站正文。

昵称、群名、描述和公告按不可信 metadata 处理:控制字符和换行会被中和并限制长度,协议 raw、未知 扩展、凭证字段、媒体 URL、文件路径和敏感正文不会渲染到会话介绍。Gate deny、wait、temp、系统事件、重复消息或 资源/mapper 失败不登记介绍;缓存淘汰和资料缺失安全返回空 section。Hermes 已持久化 prompt 恢复 时保留原介绍字节,显式 prompt rebuild 才使用当前本地快照;插件不提供实时刷新。

message_recall 的上下文行为如下:

  • 只有字段完整且 message_scene 为 friend 或 group 时才登记;friend 写入 dm:<peer_id>,group 写入 group:<peer_id>,非法场景或 ID 只记录安全诊断;
  • 合法事件进入对应 chat 的有界 system context FIFO,在下一次同 chat trigger 的 channel_context 中按 ingress 顺序出现一次,格式为 <event message_recall> ...;
  • 无 operator_id 或 operator_id == sender_id 时显示 uid <sender_id> recalled message msg_seq <message_seq>;群聊仅在 operator_id != sender_id 时显示 Admin uid <operator_id> recalled uid <sender_id>'s message msg_seq <message_seq>,好友有不同操作人时不添加管理员角色;
  • 撤回事件不创建普通 Agent turn、不发送回复、不调用主动撤回工具,也不调用 get_message 或下载资源;插件只展示撤回元数据,不承诺恢复被撤回消息正文;
  • 该路径仍是 observe-only,不经过普通消息的 Gate/Will,也不扣 reply cost。fixture 和 fake host 测试不代表真实 Milky 服务端能力已被集成验证。

group_nudge 和 friend_nudge 也只进入对应 chat 的 system context,固定英文 body 分别为 uid <sender_id> poked uid <receiver_id> 和 uid <user_id> poked once。成员事件使用以下基础 英文 body,并由 renderer 统一添加 <event group_member_increase> 或 <event group_member_decrease> 前缀:

  • uid <user_id> joined the group. Details: {"group_id": ..., "user_id": ..., "operator_id": ..., "invitor_id": ...}
  • uid <user_id> left the group. Details: {"group_id": ..., "user_id": ..., "operator_id": ...}

缺失或为 null 的 operator_id/invitor_id 会从 Details 省略;display text、URL、timestamp、raw 扩展和撤回正文不会进入 body。默认 MILKY_GROUP_MEMBER_EVENT_NOTIFICATIONS=false 时,成员事件不 带 Tip、不即时触发 Agent,保留在 system context 等下一次普通消息。设置为大小写不敏感的 true 后,成员 body 末尾追加固定英文 Tip,并只通过 Hermes 已有的 inject_message 交接;没有已确认或持久化恢复的 session key、没有注入授权、宿主不可用或注入被拒绝时,带 Tip 的上下文保留,不猜测 session key, 也不直接调用 Milky Action。配置值在启动后不热切换,修改后需要重启 Gateway。

Agent 发送本地媒体时,在回复中写入:

MEDIA:<local_path>

例如 MEDIA:~/path/to/clip.mp4。显式调用 Hermes send_message 时,把同一指令放在 message 参数中。图片、语音和视频使用 Milky native segment,文档使用独立 file upload。

如果无需回复,只返回 [SILENT],不附加其他内容;该标记由 Hermes core 抑制消息投递,Milky plugin 不单独解析它。

需要模拟自然聊天节奏时,可把区分大小写且未转义的 [SPLIT] 单独放在一行,或直接放在普通 正文行中。独立行标记及其分隔边界会被删除,行中标记只删除自身;空段不发送,文本按原顺序 最多发送三条。超过三段时尾部合并到第三段,每个文本单元仍遵守既有长度边界,若实际文本消息 会超过三条,则在网络访问前整体拒绝。需要显示字面量 [SPLIT] 时使用 [[SPLIT]];语法完整的 CQ-compatible 或 unknown type CQ 候选中的标记不触发分段;候选的 CQ 前缀大小写不敏感,但 malformed 或未闭合 CQ-like 内容中的标记按普通文本规则处理。普通长文本没有有效 [SPLIT] 时 继续使用原有长度分块。

回复同时包含文本分段和 MEDIA: 附件时,Hermes 先投递全部文本,再按提取顺序投递图片、语音、 视频和文档;当前不支持文本段与附件交错,[SPLIT] 不改变 MEDIA: 的独立交接。

Caution

MEDIA: 会读取本地文件并上传;默认只限制常规、非空且不超过 33554432 字节(32 MiB) 的文件,可用 MILKY_MAX_LOCAL_MEDIA_BYTES 在 8388608 至 33554432 字节之间调整, 没有固定的安全目录隔离。Base64 编码会带来约 4/3 的请求体放大;内网连接不代表 Milky、代理或下游平台没有更低的服务端限制。

CQ image 仅用于本地 file:// URI 的 sticker,例如:

[CQ:image,file=file:///path/to/sticker.ext,type=sticker]

CQ 前缀大小写不敏感,但 type、字段和真实 ID 仍按既有规则校验;未知或转换失败的 CQ 控制码保留完整原文。普通图片请使用 MEDIA:<local_path>。sticker 会在发送前转换为 base64://。

本地路径、Path 和 file://localhost 只在 plugin 边界读取一次并受上述本地字节上限约束; 格式合法的 http(s):// 和显式 base64:// 会原样传递,plugin 不下载、读取或解码,也不应用 本地文件大小检查。

Slash command

纯文本 /... 消息会在 canonical、去重和 Gate 之后分流,不进入 Will 历史或普通 Agent 正文。 合法命令交给 Hermes 既有命令分发。无参数 /milky 继续查询实现信息, /milky help 显示顶层帮助;/milky sticker 与 /milky sticker help 等价, /milky allowlist 与 /milky allowlist help 等价。三层帮助均为静态文本, 不要求活动连接,也不会读取配置、创建图库或执行视觉分析;可达性仍由会话 Gate 和 core 授权决定。

Milky · 命令帮助

Usage:
  /milky [command] [args...]

不带参数时查看实现信息。

Commands:
  status      查看运行状态
  sticker     维护贴纸库
  allowlist   管理会话白名单
  help        显示帮助

子命令帮助: /milky <command> help(sticker、allowlist)

/milky status 只读取当前可信实例的本地状态及同一 profile 的最新白名单配置, 不会探测网络、查询 QQ 登录状态、打开图库或应用配置。例如:

Milky · 运行状态

插件: 运行中
事件流: 已连接
本次运行: 2 小时 18 分钟

运行白名单: 6 条规则
白名单配置与当前运行一致。

首次 SSE 建连时显示“事件流: 连接中”,首次失败后的退避、重试和内部重连显示 “事件流: 重连中”。本地观察到连接建立后才显示“已连接”,这一状态不保证端到端收发健康。 “本次运行”从实例完成初始化、进入运行阶段开始,按单调时间累计;SSE 内部重连不重置, 停止或失败后冻结,新运行代次重新计时。少于一分钟显示“不足 1 分钟”,其余舍去秒, 按天、小时、分钟显示非零部分;确认尚未开始时显示“尚未开始”,缺少证据时显示“未知”。

运行白名单统计字面规则,一个通配符计一条。Web 保存与运行集合不同,即使条数相等,也显示:

白名单配置与当前运行不同。
请重启 Gateway 以应用当前配置。

配置读取失败、非法或规则版本变化时显示“白名单配置一致性未知。”,保留其他可确认字段。 缺少事件流观察能力时显示“事件流: 未知”。无唯一可信实例、profile 归属失效或读取期间运行 代次变化时,整条回执为“运行状态暂不可用”及安全说明,不猜选其他 profile。 空运行集合显示 0 条,并说明“当前不接收任何会话的普通消息。”,不等同于插件停止。

贴纸只处理显式人工维护;remove 是 del 的等价别名,例如 /milky sticker remove demo_id。 实际贴纸帮助如下:

Milky · 贴纸维护

Usage:
  /milky sticker [command] [args...]

Commands:
  add [--dry-run]  导入图片
  list [--limit <n>]  查看贴纸
  edit <sticker_id> [--emotion=<enum>] [--tags=<tag1>,<tag2>,...] [--description=<text>] [--clear=<field>[,<field>...]]  编辑字段
  reanalyze <sticker_id>  重新分析
  del <sticker_id>  移除贴纸(别名 remove)
  cleanup [--dry-run]  清理库文件
  reindex  重建技术索引
  help  显示帮助

Options:
  --dry-run  仅适用于 add/cleanup;只预览,未作更改。
  --limit <n>  默认 20,十进制整数 1 至 100;也接受 --limit=<n>。
  --emotion=<enum>  joy、sadness、anger、surprise、fear、disgust、love、approval、confusion、neutral、mixed、unknown
  --tags=<tag1>,<tag2>,...  2 至 5 个不重复标签,每个不超过 16 字符且含中文。
  --description=<text>  非空、不超过 20 字符且含中文,不跨空白 token;不支持 shell 引号语法。
  --clear=<field>[,<field>...]  恢复 emotion、tags、description 对应视觉基线。
  edit 至少指定一个 set/clear;同一字段不能同时设置和清除,不接受重复选项或清除字段。

Examples:
  /milky sticker add --dry-run
  /milky sticker edit demo_id --clear=tags

不带子命令时显示帮助。

非法参数在业务操作前返回就近 Usage 和 Help,不回显输入。例如删除缺少 ID:

指令格式不正确。

Usage:
  /milky sticker del <sticker_id>

Help:
  /milky sticker help

查询使用“Milky · 贴纸维护”标题及“本次显示 N 条”,数量表示当前有界返回数。 空库示例为“尚未添加贴纸。”和“本次显示 0 条”,附贴纸帮助入口;非空列表保留 ID、 情绪中文含义及枚举、标签、描述、字段来源、格式、大小、创建时间、使用次数和最近使用 UTC 时间。 尚未使用时明确显示“尚未使用”。

预览使用“贴纸导入预览”或“贴纸清理预览”标题,计划结果显示“待添加”“待隔离”或 “待清理”,末尾明确“未作更改。”;导入预览逐候选保留情绪、标签、描述和贴纸判定, 不虚构 ID 或显示原文件名。正式批次分别报告添加、重复、隔离、拒绝、失败和延后计数, 有失败或延后时明确未完成项;空 inbox 显示“没有待处理的图片。”。 清理和索引重建分别保留各自计数单位,不声称缺失文件已恢复。

下列列表与导入预览使用合成条目,文本由实际展示入口生成:

Milky · 贴纸维护

本次显示 1 条

ID: demo_id
情绪: 喜悦 (joy)
标签: 开心、反应
描述: 小图表达情绪
来源: 人工 (manual)
字段来源: 情绪=视觉 (vision)、标签=人工 (manual)、描述=视觉 (vision)
格式: PNG
大小: 68 字节
创建时间: 2026-01-01T00:00:00+00:00 UTC
使用次数: 0
最近使用: 尚未使用
贴纸导入预览

待添加: 1
待隔离: 0
重复图片: 0
拒绝图片: 0
视觉分析失败: 0
存储失败: 0
延后处理: 0

候选 1: 待添加
贴纸判定: 是
情绪: 喜悦 (joy)
标签: 开心、反应
描述: 小图表达情绪

未作更改。

slash 回执已从贴纸 JSON、实现信息英文错误前缀和旧标题改为中文纯文本;allowlist 帮助也改为 紧凑布局。这是展示兼容性变化,程序消费者继续使用原有结构化维护结果、Web JSON 和 Agent Tool 字段与分类,不应解析中文回执。长帮助、列表和批次继续由既有发送流程分块或合并转发,发送失败 不会重复执行维护操作。

首次有效维护命令才会在 Hermes plugin-data 下创建 stickers/inbox/、stickers/library/、 stickers/junk/ 和独立 stickers.db。add 递归扫描 inbox,只接受 PNG、JPEG、GIF、WebP,单文件 上限为 10 MiB,按流式 SHA-256 去重;视觉辅助只在显式 add、add --dry-run 或 reanalyze 中调用。每次 add 最多处理 50 张唯一候选,同时最多 10 个视觉调用;其余候选留在 inbox 并报告 batch_deferred。合法 is_sticker=true 原文件原子移动到 library,false 原文件移动到 junk, 视觉失败或结构非法留在 inbox。dry-run 只校验、去重、分析和预览,不移动文件或写入数据库。

库条目保存 detected_* 视觉基线、当前生效 emotion/tags/description、字段级 vision/manual 来源、created_at/updated_at/detected_at、use_count 和 UTC last_used_at。edit 只更新指定 字段;--clear 恢复对应视觉基线。reanalyze 只刷新视觉基线,人工字段保持不变;返回 false 时 报告 not_sticker 并保留原条目。list 输出受限摘要,不输出路径、URL、原文件名或图片 bytes。 cleanup 不扫描或删除 junk;reindex 只重建 library 的 sticker_files 技术索引,不创建贴纸条目。

贴纸 command handler 只收到 raw_args,不推断 Milky friend/group 或操作者身份,也不增加 MILKY_STICKER_OPERATOR_IDS 等插件授权配置。贴纸维护不创建旁路 Milky client、Agent Tool、主 Agent transcript、普通消息 handoff 或脱离命令生命周期的后台视觉任务。

sticker_search 和 sticker_send 是独立的语义 Tool,不是任意 Milky Action。插件通过 plugin.yaml 的 python_dependencies 和项目运行时依赖声明提供 Pillow>=12.3.0、jieba>=0.42.1;两个工具只在库中有可用条目时进入 Agent definitions,空库时隐藏,不按需导入或检查可选 tokenizer,也不影响维护命令和其他 Tool。sticker_search 只接受 mode、intent、emotion、tags 和 1..10 的 limit(默认 5)。有查询字段时默认 strict,空对象或只有 limit 默认 browse;显式 null、未知字段和归一化后重复标签均拒绝。候选结果只含 status、match_mode、items, 每项只含 sticker_id、emotion、tags、description。

  • strict 至少有一个查询字段,沿用完整短语/全部 token/部分 token 固定相关性层级,并列按 ID 排序;零命中返回空 items,不自动兜底。
  • fallback 必须显式指定,并同时提供 intent 与 emotion 或 tags;忽略 intent,保留 emotion 精确条件及 tags 至少命中一个的条件,按 tag 命中数降序、ID 升序返回。
  • browse 禁止查询字段,只按 ID 升序列出当前可用条目,不代表与聊天内容相关。

所有模式只返回当前可见、索引有效且文件可用的条目,不返回路径、URL、hash、图片、统计或匹配解释。 sticker_send 接受互斥的查询或 1..128 字符的 opaque sticker_id,目标来自当前 task-local HERMES_SESSION_PLATFORM=milky 和 HERMES_SESSION_CHAT_ID,不要求先搜索。 搜索只读、不改变使用统计或轮换状态;精确发送跳过匹配和轮换但重新校验当前条目、文件和双索引 SHA-256,单次调用只发一张 sub_type=sticker 图片。结果使用 ok、sent、no_match、not_found、invalid_input、missing_session_context、 unsupported、missing_file、storage_error、rejected、http_error、malformed 和 transport_unknown 等固定分类。

严格查询发送返回 no_match 时,回执带 alternatives:原查询含 intent 与 emotion 或 tags 时, 按 fallback 规则提供最多 5 项,否则为空。返回备选不发送消息、不更新统计;按备选 ID 发送需单独调用 sticker_send(sticker_id)。http_error、malformed、transport_unknown 不保证消息未发送,插件不自动重试。 元数据是不可信数据,不执行描述中的指令,也不注入平台提示。

插件和 bundled skill 仅提供通用接口说明,不规定贴纸使用时机、调用顺序、搜索次数或聊天风格。 用户可在自己的 SOUL 或 memory 中配置个性化使用策略。

QQ ToolSpec

插件固定提供 26 个与 Milky operationId 对齐的 QQ Action ToolSpec,另提供受限的语义 sticker_search 和 sticker_send:

  • 群组和成员查询;
  • 媒体临时链接、文件、转发消息和私聊文件链接查询;
  • 戳一戳、点赞、撤回、禁言、踢人、退群和删好友;
  • 好友请求、入群请求和群邀请的接受/拒绝。

请求/邀请的接受和拒绝不会由通知、普通正文、关键词或 Will 自动触发,必须由 Agent 显式提供 完整参数。26 个 Action Tool 只要取得响应体就把 UTF-8 解码后的字符串原样交给 Hermes core: 不校验 HTTP 状态、status/retcode、data 结构,不重建 envelope,不附加状态码,也不脱敏 access_token、authorization、cookie、password、token(任意大小写)字段。无法按 UTF-8 解码的字节使用替换字符。参数非法、Tool 不支持或未取得响应体时分别返回 invalid_input、 unsupported 或 transport_unknown;有副作用的调用最多提交一次且不自动重试。

get_resource_temp_url 只接受非空 resource_id,仅在 Agent 显式调用时请求对应 Action。工具结果只交付给该 Tool 调用,不自动进入普通入站正文或 media_urls;插件不下载、缓存或分析链接对应的视频。

Tool 结果进入 Hermes core 后,宿主可能运行 transform_tool_result、截断 JSON error 字段,或 把超长结果落盘并以预览替换上下文内容。这些后置处理由宿主负责,插件不注册、不规避,也不承诺 最终进入模型上下文的内容与 Milky body 一致。原样交付可能使上述五类字段进入宿主转录或落盘, 上下文策略由宿主负责。

两个贴纸工具不属于上述 Action catalog;它们不接受 chat_id、session_id、路径或 URL,每次调用独立校验参数和当前会话。 sticker_search 的结果只含有界元数据;sticker_send 的 target、库读取、统计 claim 和单次发送由独立的贴纸 service 管理。

入站文件只显示为安全占位符,例如 [file:file_id=<file_id>,file_name=<file_name>,file_hash=<file_hash>];它不会被当作本地路径 或出站文件。

连接与生命周期

连接时依次完成登录信息、群列表和每个群的 Bot 成员状态同步,之后才启动事件流并开放普通 消息入口。断开时会取消 event、pipeline、TTL 任务,解除 sender/command 绑定,并关闭 HTTP/SSE 资源。

非 Tool 出站成功在插件侧使用远端 data.message_seq 的稳定字符串作为 message_seq,交给 Hermes 时映射为宿主要求的 message_id;协议拒绝、传输未知、 malformed 和 unsupported 会保持明确失败分类。缺少消息序号时不会伪造稳定去重 ID。

QQ 热白名单管理

纯文本命令通过 Hermes core 授权后执行;顶层 milky 许可覆盖 allowlist 的全部子命令。 插件不另查管理员或 QQ 角色。普通用户命令白名单示例不包含 milky;未配置管理员时是否关闭 门禁也服从 core。未放行会话可以发送下面的直接管理命令,其他命令仍需通过会话 Gate。 结构化 mention、图片、未知子命令和其他命令展开的别名不享有路由例外;已放行会话的 core 别名正常执行。

帮助使用紧凑的 Usage 与 Commands,不另列 Targets 或 Examples:

Milky · 会话白名单

Usage:
  /milky allowlist [command] [args...]

Commands:
  list          查看名单
  add [target]  添加规则
  del [target]  移除规则(别名 remove)
  help          显示帮助

  e.g. target: group:123456、dm:654321、group:*、dm:*

add/del 省略目标时使用当前会话。
不带子命令时显示帮助。
  • /milky allowlist 或 /milky allowlist help:显示静态帮助;Commands 末尾的一行 e.g. 给出 add/del 共用目标,不读取或修改配置。
  • /milky allowlist list:完整查看配置及运行规则、来源和差异,不支持分页;长消息由既有发送流程合并转发或拆分。
  • /milky allowlist add:添加当前群或当前私聊。
  • /milky allowlist add dm:123456:添加显式目标;同样支持 group:123456、group:、dm:。
  • /milky allowlist del:删除当前会话的字面条目;remove 与 del 完全等价。
  • /milky allowlist del group:*:只删除该通配符,保留其他具体条目。

目标管理范围是当前 profile 的 group/dm 规则,裸数字和 temp 被拒绝。具体条目与通配符独立, 添加具体条目不会因已有通配符返回 unchanged;删除具体条目不会修改通配符。回执只报告条目结果。 管理命令不查询来源群或目标群状态,不以禁言状态阻止名单读写,也不附加禁言提示。 新授权群首次收到获准入站的消息时按需准备成员状态;状态未知、准备失败或禁言仍拒绝普通消息。 回执沿既有发送流程处理,是否送达不改变已提交的规则。

QQ 修改只保存 plugins.entries.hermes-plugin-milky.settings.allowed_chats,读回确认后在线发布。 settings 空列表遮蔽环境值;纯环境部署首次实际修改提升为 settings。回执使用中文结果: “更改已保存并生效”表示已持久化并在线应用;“更改已保存,尚未生效”提示重启 Gateway; “规则已存在/规则不存在”表示未作更改,不隐式 reload。无法确认的结果不会报告成功或自动重试。 缺少可信调用来源、明确 profile 或唯一活动实例时提示管理暂不可用,绝不使用环境中的旧会话。 list 保留 settings、legacy、environment、default 来源名称;一致时只显示一份完整名单, 不一致时分别显示“当前配置”和“当前运行”,提示重启 Gateway。Web 仍只保存配置,不新增重启按钮。

实例内修改串行,保存前版本检查及读回核验不等于与 Web/人工编辑的跨入口条件事务。 未知结果不自动重试或回滚;回执发送失败不重放修改。其他配置保持启动快照。 完整断开重连(含原注册工厂新建实例)重新读取当前 profile 白名单;纯 SSE 重连保留在线策略。 实际撤销会清理插件尚未交接的等待正文、系统上下文和 Will 状态,删除再添加不复活旧批次; 已交给 Hermes 的任务、出站工具、cron 和 home channel 不被撤销。

BREAKING 迁移与回滚: 升级前,需要全放行的部署将旧空配置改为两个通配符。 旧版本把空列表解释为全部放行,回滚前先停止接收并设置经审阅的非空受限名单; 若需全部关闭,在宿主停用平台。只回滚代码并保留空列表会扩大权限。 真实 QQ 验收范围与当前验证结果见 openspec/changes/archive/2026-09-26-add-hot-chat-allowlist/evidence.md。

API 与开发

插件不是以 Python package entry point 发布;Hermes 从根目录加载 plugin.yaml,再调用唯一 公开入口 __init__.py::register(ctx)。

对象 作用
__init__.py::register(ctx) 解析启动配置,注册 platform、/milky、ToolSpec、standalone sender、QQ 指引 section 和 QQ 会话介绍 section。
__init__.py::register_tools(ctx) 委托 outbound.tools 注册固定 ToolSpec;注册阶段不联网。
MilkyAdapter 管理连接、停止、入站交接和出站委托。
MilkyOutboundSender 校验 group:/dm: 目标,格式化消息并调用 Milky Action/upload。
SlashCommandService 管理活动 Milky client,处理 /milky 和显式贴纸维护命令。
stickers/ 懒加载独立 stickers.db,校验 inbox 图片,执行维护命令以及受限 sticker_search/sticker_send 搜索、精确发送和 claim;jieba 由插件运行时依赖提供。

支持 register_system_prompt_section 的 Hermes 宿主会在 after_memory 登记 hermes-plugin-milky.qq-platform-guidance,并在连接完成后使用已确认的 QQ UID 和昵称渲染 媒体、CQ-compatible、无回复和 bundled skill 指引。旧宿主仍可完成平台注册,但只获得首句提示。

同一宿主还会登记 hermes-plugin-milky.qq-session-context;其 callback 只读取当前 HERMES_SESSION_CHAT_ID 对应的本地安全快照,不发起网络或文件 I/O。旧宿主、没有当前 chat context、资料缺失或快照已淘汰时不注入会话介绍。

详细的稳定模块边界见 ARCHITECTURE.md;可观察行为和测试要求见 openspec/。新建的未归档 change 会放在 openspec/changes/。

贡献

欢迎通过 GitHub Issues 提问、报告 问题或提交 pull request。贡献前请阅读 CONTRIBUTING.md 和 ARCHITECTURE.md。

贡献要求:

  • 行为变化先补充脱敏契约或 fixture,再实现并增加回归测试;
  • 使用 uv 管理 Python 环境和依赖,不使用 pip、pipx 或直接调用 python/python3;
  • 遵循 Google Python Style Guide,并保持各模块依赖边界;
  • 运行 uv run pytest -q、uv run ruff check .、uv run ruff format --check .、uv build 和 git diff --check;
  • 不提交 token、Authorization header、真实 QQ/群 ID、真实媒体 URL/路径、文件内容或敏感正文;
  • 使用中文 Conventional Commits。

PR 应说明变更范围、实际执行的命令、测试结果和未解决风险。若行为契约发生变化,请同步 更新对应的 OpenSpec change;安全问题不要公开粘贴到 issue。

维护者、致谢与许可证

维护者:ByteColtX。问题、功能建议和安全联系入口见 CONTRIBUTING.md。

感谢 Hermes Gateway 的 platform adapter contract、Milky v1.3 协议生态,以及提供协议 fixture、测试和文档改进的贡献者。

本项目使用 MIT License,版权所有 © 2026 ByteColtX。完整条款见 LICENSE。

About

Hermes 的 Milky 协议 QQ 平台适配器:将 QQ 私聊与群聊接入 Hermes Gateway,支持智能响应、文本与多媒体消息收发,以及群组、成员、文件和好友/入群请求等 QQ 能力。

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages