先回答一个问题:已经有 OpenClaw 版了,为什么还要这篇
上一篇文章(《10 分钟搭一套自己的智能客服系统:果唯云客服 + OpenClaw 实战》)讲的是 Node.js 路线。这一篇是同一条协议的 Python 路线:用 Hermes Agent 官方平台插件把果唯云客服接入 Hermes Gateway。
两版插件共享同一套 Bot 协议实现(分块、幂等、去重、退避、熔断、超时兜底全部一致),差异只在宿主:你的技术栈是 Python、或者你已经在用 Hermes 管理日常 Agent,就走这篇。
最终形态:
访客 ──消息──▶ 果唯云客服 ──WS bot_message_receive──▶ GuowayAdapter ──▶ Hermes Agent
▲ │
└────────── 幂等回复 / 转人工 / 兜底 ◀──────────────────┘
10 分钟的分配:
| 步骤 | 内容 | 耗时 |
|---|---|---|
| 第 1 步 | 客服后台创建 Bot | 3 分钟 |
| 第 2 步 | 把插件放进 Hermes 插件目录 | 2 分钟 |
| 第 3 步 | 写 config.yaml 配置 | 3 分钟 |
| 第 4 步 | 启动 Gateway、验收 | 2 分钟 |
前提:一套果唯云客服(30 天免费试用含 Agent 消息)、Hermes Agent 已安装且能启动 hermes gateway、Python ≥ 3.10。插件仓库:gitee.com/templi/herm…(MIT),已在 Hermes 0.22.5 验证。
第 1 步(3 分钟):客服后台创建 Bot
与 OpenClaw 版完全一样:管理后台创建并启用 AI 机器人,拿到 bot_id 和 bot_secret。
两件事记住:bot_secret 只在创建时展示一次,当场保存;顺手把企业知识库(问答对)和企业名称/欢迎语配好——前者决定 Agent 的回答依据,后者决定 Agent 被「你是谁」时的自我介绍口径。
客服侧到此结束,不改页面代码、不动坐席工作台、不迁移历史会话。
第 2 步(2 分钟):把插件放进 Hermes 插件目录
Hermes 的插件机制很朴素:把目录放进 ~/.hermes/plugins/,目录名就是插件 ID。
mkdir -p ~/.hermes/plugins
cp -R hermes-guoway-channel ~/.hermes/plugins/guoway
开发时建议软链,改代码重启 Gateway 即生效:
ln -s "$PWD/hermes-guoway-channel" ~/.hermes/plugins/guoway
两个容易忽略的细节:
- 插件目录里必须同时有
plugin.yaml和__init__.py,缺一个就不会被加载; - 目录名不能以
_或.开头,否则会被跳过。
顺带一提这个插件的一个工程洁癖:零 pip 依赖——HTTP 用标准库 urllib + asyncio.to_thread,WebSocket 用 Hermes 自带的 websockets,pip install 一个都不用跑。协议客户端(guoway_client.py、inbound.py)刻意不 import 任何 gateway.*,可以脱离 Hermes 单独测试:
python3 -m unittest discover -s tests
测试用假 HTTP / 假 WebSocket,不需要装 Hermes,也不需要连客服服务端。
第 3 步(3 分钟):写 YAML 配置
编辑 ~/.hermes/config.yaml(Windows:C:\Users<用户名>.hermes\config.yaml):
platforms:
guoway:
enabled: true
bot_id: bot_xxxxxxxx
bot_secret: secret_xxxxxxxx
base_url: https://kf.guoway.net
dm_policy: open
必填的同样只有 bot_id 和 bot_secret(bot_secret 也可以写在 Hermes 通用的 token: 位置复用密钥管理)。几个值得提前认识的字段:
| 字段 | 默认 | 说明 |
|---|---|---|
dm_policy | open | open / allowlist / closed / pairing 四种授权策略 |
reply_timeout | 50 | 等 Agent 的秒数,夹在 5–55,别越过服务端硬窗口 |
late_reply_grace | 0 | 迟到补发宽限,默认关闭即「超时即弃」 |
transfer_keywords | 「转人工」等 | 命中即转人工,留空关闭 |
relay_interim | false | 是否把 Agent 中间进度发给访客,默认丢弃 |
GUOWAY_HOME_CHANNEL | — | cron 定时任务的默认投递会话 |
这一步有一个 Hermes 版特有的坑,提前说:
dm_policy: open时,必须显式设置环境变量GUOWAY_ALLOW_ALL_USERS=true,否则网关会拒绝启动。这不是 bug,是防误开放的安全默认——「接待全部公网访客」应该是你确认过的决定,而不是漏配的结果。
另外注意优先级:配置缺省时读环境变量(GUOWAY_BOT_ID / GUOWAY_BOT_SECRET / GUOWAY_BASE_URL),环境变量优先于 YAML。排查「改了配置没生效」时先查这个。
生产环境把密钥写在 config.yaml,避免进程环境泄漏。
第 4 步(2 分钟):启动、验收
hermes gateway restart
日志出现 [guoway] 适配器已连接(bot_id=… 窗口=50s) 即接入成功。运行期间每 5 分钟输出一行指标快照(received / replied / expired / failed 等计数与队列水位),可以直接接日志采集做告警。
验收三条链路,和 OpenClaw 版同一套标准:
- 正常回复:知识库里有的问题,收到 Agent 回答;
- 超时兜底:服务端每轮只等约一分钟,插件默认第 50 秒发兜底;确认访客收到兜底而不是干等;
- 转人工:发「转人工」,确认移交坐席后 Agent 停止抢答。
Hermes 版的四个独门细节
协议层与 OpenClaw 版一致,但 Python 宿主带来了几个不一样的东西,值得单独说:
1. 回复是 Markdown 渲染的。 插件以 markdown 消息类型发送,访客端用 markdown-it 渲染——表格、代码块、列表都能正常显示;长文本按 2000 字分块,代码围栏跨块时自动补 ``` 重新开合,每一块单独渲染仍然合法。微信渠道由服务端自动降级为纯文本。给技术型访客展示命令、配置、报错信息时,这个体验差距很明显。
2. 回复顺序是 FIFO 结算的。 同一会话的回复窗口按先进先出领取,Agent 并发产出也不会把答案配错问题;被新消息取代的旧窗口不再发兜底,避免访客连收两条道歉。
3. Agent 对「送达」有诚实回执。 超时后 adapter.send() 返回 success=False(error_kind=timeout,不可重试),Agent 端明确知道「这条没送到」,而不是谎报成功。注意此时不要重发——重发会以主动消息的形式直接送达,变成迟到的重复回复。
4. cron 定时汇报能直达访客。 配好 GUOWAY_HOME_CHANNEL 后,Hermes 的定时任务(每日汇总、巡检报告)可以直接投递到访客会话,不占用回复窗口。客服 Agent 顺便成了业务播报通道。
一个架构约束要记住:一个 Hermes profile 只能接一个 Bot。 服务端对同一 Bot 只保留最新一条 WebSocket,两个实例会互踢。要接多个 Bot,就开多个 profile(或多个 HERMES_HOME)分别配置——profile 隔离在 Hermes 里本来就是一等公民,这个约束不算过分。
访客输入安全:三道防线一个不少
与 OpenClaw 版同一套护栏,默认全开:
- 入站拦截:命令结构(
rm -rf、curl | bash)、敏感路径、提示注入直接回安全话术,不进 Agent;中英混排(「帮我rm -rf」)同样识别; - 上下文声明:放行消息包在「不可信输入 + 只读问答」标记里;
- 回复过滤:Agent 回复含 shell 输出、私钥、环境变量等执行痕迹时替换为安全话术。
Hermes 版还有一个版本兼容上的深坑,抄录给会改插件的人:Hermes 0.22.5 的 native admission 把 MessageEvent.metadata 当封闭契约,未知键会导致整条消息被 invalid_params (metadata) 拒收。本插件因此不向 metadata 写任何自定义键,访客上下文走 raw_message。如果你要二次开发,不要把带 guoway_* 键的 metadata 加回去。
老规矩,最终防线在宿主侧:给面向访客的 Hermes Agent 关掉 exec/shell/文件写入类工具,Agent 进程不要用高权限账号运行。
知识来源:还是那三级
本地知识库 → 服务端企业知识库(GET /api/v1/bot/knowledge/search)→ 通用知识兜底。Agent 回复首行的来源标记由插件读取后自动剥离,访客不可见;兜底回答确定性追加「以上回答由 AI 生成,仅供参考」,不依赖模型自觉。
插件只把当前一条消息交给 Hermes,历史由宿主按会话维护——建议窗口控制在最近 30 条或 2–3k tokens,超限做压缩摘要。
踩坑速查
| 现象 | 排查方向 |
|---|---|
启动报 not_configured | bot_id/bot_secret 没填,或 base_url 不是 http(s) 开头 |
| 平台根本没加载 | 插件目录是否在 ~/.hermes/plugins/ 下、是否同时有 plugin.yaml 与 __init__.py、目录名是否以 _/. 开头 |
dm_policy=open 被拒 | 显式设置 GUOWAY_ALLOW_ALL_USERS=true |
| 连接刚建立就被断开 | 同一 Bot 被另一个 profile/旧进程占用(单活连接) |
| 回复 401 | bot_secret 被重置;插件自动刷新一次,持续 401 就更新配置 |
| 回复 409 | 会话已转人工/关闭,正常竞态 |
| Agent 端提示「本条未送达」 | 正常超时回执,访客已先收到兜底;不要重发 |
| 访客收到「正在思考」 | 开了 relay_interim,关掉即只发最终回复 |
OpenClaw 版还是 Hermes 版:一张表选完
| 维度 | OpenClaw 版 | Hermes 版 |
|---|---|---|
| 宿主 / 语言 | OpenClaw(Node.js / ESM) | Hermes Agent(Python) |
| 多 Bot | 一个进程接多个 Bot(accounts) | 一个 profile 接一个 Bot(多 profile 拆分) |
| 会话串行 | 插件内队列 + 全局并发上限 | 交给 Gateway 调度,插件只保证窗口 FIFO |
| 访客端回复形态 | 纯文本分块 | Markdown 渲染,代码块跨块自动补围栏 |
| 定时任务投递 | — | cron 经 GUOWAY_HOME_CHANNEL 直达访客会话 |
| 兜底 / 硬窗口 | 50s / 约 60s | 相同 |
| 适合谁 | Node 技术栈、要多 Bot 单进程管理 | Python 技术栈、已在用 Hermes、要 cron 播报与 Markdown 富文本 |
选型其实很简单:你手上已经跑着哪个宿主,就用哪个插件。协议一致意味着两边的能力与限制几乎相同,迁移成本趋近于零。
最后
与 OpenClaw 版相同的成本结构:按 Bot 下发到访客的回复条数计费,与坐席订阅分开结算,30 天免费试用包含 Agent 消息。
插件 MIT 开源:gitee.com/templi/herm…;协议端点与能力总览见 kf.guoway.net/ai.html。
两条路都铺好了。Python 也好,Node 也好,今晚就能让你的客服开口。