# plugin-tips-cn **让 Hermes 说人话。** 把 Hermes 和工具吐出来的英文系统提示,就地换成**随机的日常中文**—— 同一种情况每次说法都不一样,消息看着不像机器。 > 适用于 [Hermes Agent](https://hermes-agent.nousresearch.com/docs)。词库 **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 ```bash npx plugin-tips-cn # 装到 $HERMES_HOME/plugins/plugin-tips-cn hermes plugins enable plugin-tips-cn # 启用 # 然后重启一次网关 ``` ### 方式二:直接拷目录 ```bash 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 ```bash 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` 才要重启网关。 ```jsonc { "id": "cmd_timeout", // 类别 id "label": "命令超时", // 给人看的说明 "patterns": ["\\[Command timed out after (?P\\d+)s\\]"], // 正则,忽略大小写,可用命名组 "variants": ["等了 {n} 秒还没动静,先掐了。", "..."] // 命中时随机挑一条,想加多少加多少 } ``` 改完跑一遍自检: ```bash 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` | 只关掉「适配器出口」那一层 | ## 验证 插件会连**你自己的工具输出**一起汉化,所以验证脚本打印的「原文」会被换掉、看着像没生效。 把空格换成 `·` 再打印就能绕过(正则匹配不上): ```python print(out.replace(" ", "·")) ``` 端到端自检: ```bash echo "⚡ Interrupting current task. I'll respond to your message shortly." # 读回来是中文就说明网关里的插件在跑 ``` 验证第 3 层是否装上了:看网关日志里有没有 ``` plugin-tips-cn: 已包住 N 个适配器出站方法(覆盖 M 个类) ``` ## 设计原则 - **只做「已知句式 → 中文候选」的就地替换**,其余文字和结构原样保留,所以 JSON / 代码块不会被打坏 - 技术细节(路径、错误码、秒数、工具名)**保留**,不吞信息 - 不替换结构化字段(如 `"exit_code": 124`),只替换自然语言句子 - 替换失败一律吞掉——**绝不因为改文案而拦住发送** ## 许可 MIT