让 Hermes 说人话:把英文系统提示就地换成随机中文(忙时回执/进度心跳/自主进化复盘/后台任务/超时/权限/接口报错…),三层网覆盖,词库 106 类 672 条。

fmode-ops 2fc44c7be2 feat: plugin-tips-cn 0.4.1 — 把 Hermes 英文系统提示换成随机中文 1 napja
bin 2fc44c7be2 feat: plugin-tips-cn 0.4.1 — 把 Hermes 英文系统提示换成随机中文 1 napja
.gitignore 2fc44c7be2 feat: plugin-tips-cn 0.4.1 — 把 Hermes 英文系统提示换成随机中文 1 napja
CHANGELOG.md 2fc44c7be2 feat: plugin-tips-cn 0.4.1 — 把 Hermes 英文系统提示换成随机中文 1 napja
LICENSE 2fc44c7be2 feat: plugin-tips-cn 0.4.1 — 把 Hermes 英文系统提示换成随机中文 1 napja
README.md 2fc44c7be2 feat: plugin-tips-cn 0.4.1 — 把 Hermes 英文系统提示换成随机中文 1 napja
__init__.py 2fc44c7be2 feat: plugin-tips-cn 0.4.1 — 把 Hermes 英文系统提示换成随机中文 1 napja
package.json 2fc44c7be2 feat: plugin-tips-cn 0.4.1 — 把 Hermes 英文系统提示换成随机中文 1 napja
phrases.json 2fc44c7be2 feat: plugin-tips-cn 0.4.1 — 把 Hermes 英文系统提示换成随机中文 1 napja
plugin.yaml 2fc44c7be2 feat: plugin-tips-cn 0.4.1 — 把 Hermes 英文系统提示换成随机中文 1 napja
validate_phrases.py 2fc44c7be2 feat: plugin-tips-cn 0.4.1 — 把 Hermes 英文系统提示换成随机中文 1 napja

README.md

plugin-tips-cn

让 Hermes 说人话。 把 Hermes 和工具吐出来的英文系统提示,就地换成随机的日常中文—— 同一种情况每次说法都不一样,消息看着不像机器。

适用于 Hermes Agent。词库 106 类 / 672 条中文候选。


它解决什么

Hermes 本体没有 i18n。config.yaml 里的 language / locale / 顶层 system_prompt 都没人读, 运行时硬编码的英文只能从消息出口改。

唯一官方支持的例外是 display.status_phrases + $HERMES_HOME/status_phrases.yaml, 但它只覆盖长任务心跳这类表面;其余全靠本插件

效果示例

原来(英文) 换成(随机一条)
⚡ Interrupting current task (3 min elapsed). I'll respond shortly. 收到!先按下暂停键,马上看你这条。 (跑了 3 分钟)
⏳ Queued for the next turn. I'll respond once the current task finishes. 手头这摊还没收工,你这条我收好了,一腾出手就办。
⏩ Steered into current run. Your message arrives after the next tool call. 收到,你这条我捎在手上了,下一步就按它走。
↪ Redirected current run. I'll adjust using your correction. 好,方向调过来了,按你的纠正走。
⏳ Working — 12 min — running: browser_exec 还在跑,12 分钟了——稳住,就快出结果了。 — 此刻在干:browser_exec
💾 Self-improvement review: Memory updated · Skill 'x' patched 💾 这一轮的修行成果:🧠 这段我刻进记忆里了。 · 🔧 「x」又精进了一点。
⚠️ No reply: the request was cancelled by a new correction on every attempt… ⚠️ 它没回话: 你连着改了几次方向,每次都把这一轮打断了。我不硬撑了——你最后那条纠正已在队列里,下一步就照它走。
⚠️ Hermes is shutting down — your current task will be interrupted… ⚠️ 我要下线一会儿——手上这个任务会中断。等我回来,你随便说句话,我就接着往下做。
Cronjob Response: 每日额度日报 📬 定时任务「每日额度日报」的结果:
Permission denied 这条路没权限——不硬闯,我换条道走。
[Command timed out after 60s] 等了 60 秒还没动静,先掐了。
Traceback (most recent call last): 程序崩了一下,堆栈如下。

三层网(少一层就会漏)

手段 覆盖
1 transform_tool_result 工具结果(超时、权限、路径、接口报错…)
2 transform_llm_output 助手最终回复
3 包住适配器的出站方法 网关自己发到聊天的通知

第 3 层是关键。 忙时回执、进度心跳、自主进化复盘、后台任务收工、cron 投递…… 源码里是直接 adapter.send(...) 发出去的,不经过任何插件钩子。只做 1+2 的话, 聊天里看到的还是英文——这就是「装了插件却没生效」的真因。

第 3 层具体包了每个平台适配器(含子类)的 22 个发文本方法:send / edit_message / send_stream_frame / send_private_notice / send_slash_confirm / send_clarify / send_exec_approval / send_image(caption) / send_document / send_voice / send_video / emit_warning / send_final_ledgered …,以及「返回值即文本」的 _format_exec_approval。 位置参数和关键字参数都兜住,一个方法里有多个文本参数(title + message)也一并处理。

安装

方式一:npm

npx plugin-tips-cn                       # 装到 $HERMES_HOME/plugins/plugin-tips-cn
hermes plugins enable plugin-tips-cn     # 启用
# 然后重启一次网关

方式二:直接拷目录

cp -r plugin-tips-cn "$HERMES_HOME/plugins/"
hermes plugins validate "$HERMES_HOME/plugins/plugin-tips-cn"
hermes plugins enable plugin-tips-cn

方式三:git clone

git clone https://git.fmode.cn/fmode/plugin-tips-cn.git "$HERMES_HOME/plugins/plugin-tips-cn"
hermes plugins enable plugin-tips-cn

必须重启一次网关:插件在进程启动时 discovery 一次,长驻进程不会自动重扫。 容器里由 s6 托管的网关,杀掉进程会自动拉起。

改词库(不用重启)

只改 phrases.json——按 mtime 热加载,改完立刻生效。只有改 .py / plugin.yaml 才要重启网关。

{
  "id": "cmd_timeout",                       // 类别 id
  "label": "命令超时",                        // 给人看的说明
  "patterns": ["\\[Command timed out after (?P<n>\\d+)s\\]"],   // 正则,忽略大小写,可用命名组
  "variants": ["等了 {n} 秒还没动静,先掐了。", "..."]           // 命中时随机挑一条,想加多少加多少
}

改完跑一遍自检:

python3 validate_phrases.py

它会拦下四类静默 bug:JSON 不合法、正则编译失败、候选用 {n} 而正则组名叫 code (这种错不会报异常,只会在运行时把字面量 {n} 发给用户)、类别缺 patterns/variants。

开关

环境变量 作用
PLUGIN_TIPS_CN_DISABLE=1 整个插件静默(默认开)
PLUGIN_TIPS_CN_LOG=1 每次替换打一条日志,方便调试
PLUGIN_TIPS_CN_NO_SEND=1 只关掉「适配器出口」那一层

验证

插件会连你自己的工具输出一起汉化,所以验证脚本打印的「原文」会被换掉、看着像没生效。 把空格换成 · 再打印就能绕过(正则匹配不上):

print(out.replace(" ", "·"))

端到端自检:

echo "⚡ Interrupting current task. I'll respond to your message shortly."
# 读回来是中文就说明网关里的插件在跑

验证第 3 层是否装上了:看网关日志里有没有

plugin-tips-cn: 已包住 N 个适配器出站方法(覆盖 M 个类)

设计原则

  • 只做「已知句式 → 中文候选」的就地替换,其余文字和结构原样保留,所以 JSON / 代码块不会被打坏
  • 技术细节(路径、错误码、秒数、工具名)保留,不吞信息
  • 不替换结构化字段(如 "exit_code": 124),只替换自然语言句子
  • 替换失败一律吞掉——绝不因为改文案而拦住发送

许可

MIT