侧边栏壁纸
博主头像
龍騰博客 博主等级

行动起来,活在当下

  • 累计撰写 173 篇文章
  • 累计创建 31 个标签
  • 累计收到 7 条评论

目 录CONTENT

文章目录

Hermes Agent + 飞书 + Gemini + Ollama 本地兜底 完整部署教程

管理员
2026-08-15 / 0 评论 / 0 点赞 / 3 阅读 / 0 字

在 SER8 上新建一个独立的 Hermes 实例(容器名 hermesII),对接国内飞书,主模型用 Gemini,配额用完时自动切换到本地 Ollama 兜底。这份教程是把实际部署中踩过的所有坑(反复重启、配置报错、fallback不生效、响应慢等)全部填平后的最终版本,照着做可以少走弯路。


架构总览

飞书(国内) ──WebSocket──> hermesII 容器(独立实例)
                              │
                              ├─ 主模型: Gemini (远程API)
                              └─ Fallback: Ollama mistral-nemo:12b (本地,127.0.0.1:11434)
  • 与你原有的 Lark(国际版)Hermes 实例完全隔离,容器名、目录、配置互不干扰。

  • network_mode: host,方便直接访问宿主机上的 Ollama 端口,不用处理容器间网络。


第一步:飞书开放平台创建自建应用

  1. 打开 https://open.feishu.cn/ ,登录后进入"开发者后台"→"创建企业自建应用"

  2. 应用名随意(比如 Hermes-Gemini),创建后进入应用详情页

  3. 在"凭证与基础信息"里记下 App IDApp Secret

  4. 左侧菜单"应用能力"→ 开启"机器人"能力

  5. 左侧"权限管理"→ 添加以下权限:

    • im:message

    • im:message.p2p_msg

    • im:chat(如需群聊)

  6. 在“事件与回调”中订阅方式选长连接(WebSocket),不用 Webhook——不需要公网 IP,不用做端口转发

  7. 在“事件与回调”中添加事件选“接收消息v2.0”

  8. 去"版本管理与发布"发布新版本。

第二步:拿 Gemini API Key

打开 https://aistudio.google.com/apikey ,登录 Google 账号,创建一个 API Key,记下来。

重要坑点:Google 从 2026年6月19日起开始拒绝旧版"Standard"类型的 Google Cloud API Key 访问 Gemini API,这类旧 key 将在 2026年9月完全失效,报错是 HTTP 401 UNAUTHENTICATED。创建 Key 时确认走的是 https://aistudio.google.com/api-keys 这个页面生成的标准 Gemini API Key,如果已经有旧 key 遇到 401 报错,去这个页面检查类型并重新生成一个。

建议给对应的 Google Cloud 项目开启计费,免费额度(15 RPM / 1500 RPD)在 Agent 场景下(一次对话可能触发多次模型调用)容易很快打满,开计费后自动升到 Tier 1(150-300 RPM,几乎无日限),按量付费,平时用量小基本花不了多少钱。

第三步:SER8 上建独立目录

mkdir -p /vol3/1000/docker/hermesII/data
cd /vol3/1000/docker/hermesII

第四步:写 .env

cat > .env << 'EOF'
# 飞书应用凭证
FEISHU_APP_ID=cli_你的appid
FEISHU_APP_SECRET=你的appsecret

# Gemini
GOOGLE_API_KEY=你的gemini_key

# 不启动Dashboard面板,只跑网关,省资源
HERMES_DASHBOARD=0

# 个人自用场景,放开允许名单(默认拒绝所有陌生人,包括你自己第一次发消息)
GATEWAY_ALLOW_ALL_USERS=true
EOF
chmod 600 .env

如果不放心 GATEWAY_ALLOW_ALL_USERS=true 这么开放,可以不加,改走 config.yaml 里的 pairing 配对流程,第一次跟机器人说话时会给配对提示。

第五步:写 config.yaml

mkdir -p data
cat > data/config.yaml << 'EOF'
_config_version: 12

model:
  default: gemini-3.5-flash
  provider: gemini
  base_url: https://generativelanguage.googleapis.com/v1beta

fallback_providers:
  - provider: custom
    model: mistral-nemo:12b
    base_url: http://127.0.0.1:11434/v1
    api_key: no-key-required
    context_length: 65536

gateway:
  enabled: true
  platforms:
    feishu:
      enabled: true
      extra:
        connection_mode: "websocket"
        dm_policy: "open"
        group_policy: "allowlist"
        allow_bots: "mentions"
EOF

几个必须注意的点(都是踩过的坑,原因见后面"坑点合集"):

  • _config_version: 12 必须加,不加会报"配置过旧无法自动迁移"警告(实测容器首次启动后会自动把它迁移到更新的版本号,属于正常现象,不用管)。

  • fallback 的 provider 必须写 custom,不能写 ollama——Hermes 没有单独的 ollama provider,本地 Ollama 走 OpenAI 兼容接口要用通用的 custom

  • fallback 的 modelmistral-nemo:12b,不要用 qwen2.5:14b——qwen2.5:14b 原生上下文只有 32,768,低于 Hermes 要求的最低 64,000,启动时会直接报错拒绝。

第六步:写 docker-compose.yml

services:
  hermesII:
    image: nousresearch/hermes-agent:latest   # 建议后续锁定具体版本号而非latest
    container_name: hermesII
    restart: unless-stopped
    network_mode: host
    command: ["gateway", "run"]     # 必须加,否则会陷入反复重启死循环
    env_file:
      - .env
    environment:
      - HERMES_DASHBOARD=0
    volumes:
      - ./data:/opt/data
    mem_limit: 1.5g    # 不要设太低,512m会被OOM杀掉导致反复重启

给数据目录放开权限(fnOS 下容器内是 uid 10000):

chown -R 10000:10000 /vol3/1000/docker/hermesII/data

第七步:给现有 Ollama 服务加两行环境变量

编辑你已有的 Ollama docker-compose.yml(和 open-webui 在同一份里),加 OLLAMA_CONTEXT_LENGTHOLLAMA_KEEP_ALIVE:

services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    restart: always
    ports:
      - "11434:11434"
    volumes:
      - /vol3/1000/docker/ollama/models:/root/.ollama
    devices:
      - /dev/dri
    environment:
      - OLLAMA_VULKAN=1
      - OLLAMA_IGPU_ENABLE=1
      - OLLAMA_CONTEXT_LENGTH=65536      # 新增,满足Hermes最低64K要求
      - OLLAMA_KEEP_ALIVE=5m             # 新增,闲置5分钟自动卸载释放共享内存
    group_add:
      - "44"
      - "105"
  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open-webui
    restart: always
    ports:
      - "8081:8080"
    volumes:
      - /vol3/1000/docker/ollama/webui:/app/backend/data
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
    depends_on:
      - ollama
docker compose up -d --force-recreate ollama

只重建 ollama 这一个服务,open-webui 不受影响。

第八步:拉取 mistral-nemo:12b

docker exec -it ollama ollama pull mistral-nemo:12b

约 7GB,视网速需要几分钟。

第九步:启动 Hermes 并验证

cd /vol3/1000/docker/hermesII
docker compose up -d
docker compose logs -f

正常应该看到,没有反复重启、没有 Goodbye 退出:

→ gateway is now running under s6 supervision (auto-restart on crash, ...)
┌─────────────────────────────────────────────────────────┐
│           ⚕ Hermes Gateway Starting...                 │
└─────────────────────────────────────────────────────────┘
[Lark] [...] connected to wss://msg-frontier.feishu.cn/ws/v2?...

再确认 fallback 配置对不对:

docker exec -it hermesII hermes fallback list

应该显示:

Primary:   gemini-3.5-flash  (via gemini)
Fallback chain (1 entry):
  1. mistral-nemo:12b  (via custom)  [http://127.0.0.1:11434/v1]

第十步:飞书里测试对话

搜索你创建的机器人名字,发消息测试。群聊要 @机器人,单聊直接发即可。

第十一步(可选):验证 fallback 真的能切换

临时把 .envGOOGLE_API_KEY 改错(比如末尾加个字符)触发认证失败:

docker compose restart hermesII

飞书里发条消息,看日志:

docker compose logs -f hermesII

正常流程是:Gemini 报 401 → 重试 3 次都失败 → 提示 "switching to fallback provider" → 本地 mistral-nemo:12b 接管并回复。

测完务必把 .env 里的 Key 改回正确值

要让新的 .env 生效,得让容器重新创建,而不是单纯重启:

cd /vol3/1000/docker/hermesII
docker compose up -d --force-recreate

或者更彻底一点(等效,更保险):

docker compose down
docker compose up -d

验证 Key 是不是真的进去了

推荐先直接进容器里确认环境变量的值,而不是靠猜:

docker exec -it hermesII env | grep -E "GOOGLE_API_KEY|GEMINI_API_KEY"

对照一下显示出来的值,是不是跟你 .env 文件里现在写的一致(不是那个末尾多字符的错误版本)。

重新测试

docker compose logs -f hermesII

飞书里发条消息,这次应该能看到直接走 gemini-3.5-flash 成功,不再触发 fallback 那一串 401 报错和切换日志了。


顺带记一下这个坑,以后每次改 .env 或者 docker-compose.yml 里的环境变量,记得用 docker compose up -d(会自动识别配置变化决定要不要重建)而不是 docker compose restart,后者只适合"进程卡住了想让它重新跑一遍"这种不涉及配置改动的场景。我之前几轮教程里如果写的是 restart,建议你之后统一换成 up -d,更保险。


坑点合集(实际部署中遇到的问题及根因)

#

现象

根因

解决

1

容器反复重启,日志显示 "Warning: Input is not a terminal (fd=0)" 然后 "Goodbye!"

镜像默认入口是交互式 CLI,后台模式没有 TTY 输入,程序判定没人操作后自动退出,配合 restart: unless-stopped 变成死循环

docker-compose.yml 里加 command: ["gateway", "run"],强制启动网关服务而非交互界面

2

日志报 "config predates version 12 无法自动迁移" 警告

config.yaml 没声明版本号

加一行 _config_version: 12

3

日志报 "No env user allowlists configured",机器人可能拒绝所有人

默认策略拒绝陌生发送者

.envGATEWAY_ALLOW_ALL_USERS=true,config.yaml 里飞书配置加 dm_policy: "open"

4

docker compose down 提示 "No resource found" 但容器名冲突报错

Compose 项目名默认取当前文件夹名;如果目录改过名但容器是旧目录名下创建的,down 找不到对应关系,容器仍占用着名字

改用 docker rm -f <容器名> 直接按容器名清理,不依赖项目名匹配

5

又开始反复重启,日志出现 "exited UNCLEANLY (no exit path ran — SIGKILL / OOM / VM death)"

mem_limit: 512m 设太低,新增 fallback provider、飞书 SDK、70个技能加载后常驻内存超过这个硬限制,被 cgroup 直接 SIGKILL(跟宿主机内存是否紧张无关,是容器级别的限制)

mem_limit 调到 1.5g

6

Hermes 启动/配置 fallback 时报 "context window of 32,768 tokens... below the minimum 64,000"

qwen2.5:14b 在 Ollama/GGUF 下原生训练上下文只有 32,768,YaRN长上下文外推在 Ollama/llama.cpp 里没有可靠支持,不能靠环境变量硬调

换成原生支持 ≥64K 上下文的模型,如 mistral-nemo:12b(原生128K)

7

日志报 "Fallback to ollama failed: provider not configured"

Hermes 没有名为 ollama 的 provider 类型,官方支持列表里本地/自建端点要用 custom

config.yaml 里 fallback 的 provider 字段改成 custom

8

Gemini 报 HTTP 401 UNAUTHENTICATED,提示 "Google began rejecting legacy 'Standard' Google Cloud keys"

用的是旧版 Standard 类型 API Key,Google 已开始逐步淘汰(2026年9月全面失效)

去 https://aistudio.google.com/api-keys 重新生成标准 Gemini API Key

9

fallback 触发后本地模型响应很慢

多重原因叠加:①Gemini 先重试3次失败才切换,固定耗时几秒;②OLLAMA_KEEP_ALIVE=5m 导致闲置后模型被卸载,触发时要冷启动重新加载;③14B级模型对 780M iGPU 有一定压力

docker exec -it ollama ollama ps 看是否在冷启动;用 docker logs ollama | grep -i offload 确认层是否完整offload到GPU;如持续慢可考虑更小模型


排错速查表

现象

排查命令

容器状态

docker compose logs --tail=80 hermesII

是否被 OOM 杀过

docker inspect hermesII | grep -i oomkilled

数据目录权限

ls -la /vol3/1000/docker/hermesII/data,应为 uid 10000

fallback 配置是否生效

docker exec -it hermesII hermes fallback list

Ollama 模型是否已加载/冷启动

docker exec -it ollama ollama ps

Ollama 是否吃到 GPU

docker logs ollama --tail=50 | grep -i -E "vulkan|gpu|offload"

整体内存 / 各容器占用

free -h / docker stats --no-stream

内存监控

free -h
docker stats --no-stream

hermesII 本身(不触发 fallback 时)占用不大,平时观察整体基线是否稳定即可;fallback 真正触发、本地模型被加载的那一刻会有一次性内存峰值,属于正常现象。

0

评论区