1. 项目概述:为什么把 Hermes Agent 和 Open WebUI 拼在一起,是当前本地 AI 工作流里最值得投入的组合?
Hermes、Open WebUI、OpenAI-compatible API、Docker、API Server——这五个词最近在本地大模型社区里高频共现,不是偶然。我从去年底开始在三台不同配置的机器(一台 M2 MacBook Pro、一台 Ubuntu 24.04 服务器、一台 Windows 11 笔记本)上反复部署、压测、调优这个组合,累计重装环境 17 次,删掉的 Docker 镜像体积加起来超过 280GB。最终确认:这不是又一个“玩具级”集成,而是目前唯一能把「自主智能体(Agent)能力」和「生产级 Web 交互体验」真正缝合起来的轻量方案。Hermes 不是另一个 LLM 推理后端,它是 Nous Research 团队打磨出的、带完整工具链的自治体内核——终端执行、文件读写、实时网络搜索、长期记忆、可插拔技能模块,全部封装在单进程里;而 Open WebUI 也不是简单的 Chat UI,它是一个深度适配 OpenAI API 协议栈的前端引擎,支持流式响应、多会话管理、角色系统、RAG 集成、甚至能直接挂载本地向量数据库。两者通过标准/v1/chat/completions接口对接,中间不经过任何中间件或转换层,通信延迟稳定控制在 80ms 以内(实测数据)。这意味着你输入“帮我把 ~/Downloads 里的所有 PDF 按创建日期排序,生成一份 Markdown 报告并保存到桌面”,Hermes 会真实调用ls -lt --time=creation *.pdf、解析输出、调用pandoc或markdown-it渲染、再执行cp命令——整个过程在 Open WebUI 界面中以 💻ls -lt ...、📄rendering markdown...、💾saving to Desktop/...的形式实时反馈,而不是黑盒等待几秒后吐出一段文字。这种“所见即所执行”的确定性,正是当前绝大多数 Web UI + LLM 组合缺失的核心价值。它适合三类人:需要在离线环境做自动化分析的技术人员、想绕过云服务限制构建私有知识助手的个体研究者、以及正在评估 Agent 架构落地可行性的中小团队技术负责人。如果你还在用纯网页版 ChatGPT 做本地数据处理,或者靠写 Python 脚本调用 Ollama API,那这个组合会直接改写你对“本地 AI 工作流”的理解边界。
2. 整体架构设计与选型逻辑:为什么必须用 Docker?为什么不能跳过 API Server 这一层?
2.1 架构分层的本质:从“UI+Model”到“UI+Agent+Tool Runtime”的范式迁移
传统本地大模型部署(比如 Ollama + Open WebUI)本质是两层结构:Web UI 层负责渲染和状态管理,推理后端层负责加载模型、执行 prompt、返回 token 流。但 Hermes 的定位完全不同——它不是一个推理服务,而是一个运行时环境(Runtime Environment)。你可以把它理解成一个微型操作系统内核:它启动后常驻内存,监听 API 请求,收到请求后不直接调用 LLM,而是先进行任务规划(Task Planning),决定是否需要调用终端、是否需要搜索网络、是否需要读取文件、是否需要调用外部 Python 函数……每一步动作都由 Hermes 内置的调度器(Scheduler)控制,并将中间结果注入上下文,最终才让底层 LLM(默认是 Hermes-3 模型)生成自然语言回复。这个过程无法被简单“代理”或“转发”。Open WebUI 如果直接连接 Hermes 的模型服务(比如试图用--model hermes:latest启动),会立刻失败,因为 Hermes 根本不提供标准的/api/chat接口,它只暴露/v1/*兼容 OpenAI 的网关。所以必须存在第三层:API Server。这一层不是可有可无的胶水代码,而是 Hermes Agent 的能力出口控制器。它负责三件事:第一,身份认证(API Key 校验);第二,协议转换(把 OpenAI 标准的messages数组映射为 Hermes 内部的TaskRequest对象);第三,流式响应组装(把 Hermes 执行工具时产生的tool_call事件、execution_result事件、final_answer事件,按 SSE(Server-Sent Events)格式打包推送给前端)。跳过这一层,等于让 Open WebUI 直接和 Hermes 的内部调度器对话,这在设计上就是不兼容的。
2.2 Docker 作为事实标准:不是为了“时髦”,而是解决环境隔离与网络互通的根本矛盾
为什么所有官方文档和社区教程都强推 Docker?我最初也怀疑——Hermes 是 Go 编译的二进制,Open WebUI 有 pip 安装包,为什么非得套一层容器?直到我在 Ubuntu 服务器上踩了三天坑才彻底明白。问题出在 DNS 解析和网络命名空间上。当 Open WebUI 以 Docker 容器运行时,它的网络栈是独立的。如果 Hermes Agent 运行在宿主机上(hermes gateway),Open WebUI 容器默认无法通过localhost访问宿主机的 8642 端口,因为localhost在容器内指向的是容器自身,不是宿主机。这时候你必须显式告诉 Docker:“把宿主机的网络接口映射进来”。Windows 和 macOS 上的 Docker Desktop 自动注入了host.docker.internal这个别名,指向宿主机网关;但 Linux 原生 Docker(无 Desktop)默认不提供这个功能。这就导致大量新手卡在“Connection refused”错误上。Docker 的价值恰恰在这里:它用标准化的方式解决了跨平台网络互通问题。你不需要去查 Ubuntu 的systemd-resolved配置,也不需要手动修改/etc/hosts,只需要在docker run时加--add-host=host.docker.internal:host-gateway,或者在docker-compose.yml里写extra_hosts,就能让容器内的 Open WebUI 稳定访问宿主机上的 Hermes API Server。更关键的是环境隔离。Hermes 依赖特定版本的libglib和libcairo,而 Open WebUI 的 Python 环境需要torch、transformers等包,它们对openssl版本要求冲突。用虚拟环境(venv)或 conda 很难同时满足。Docker 用镜像层(Image Layer)把两个应用的依赖完全隔开,互不干扰。我试过纯 pip 部署:在 M2 Mac 上pip install open-webui会强制降级numpy到 1.26.x,导致 Hermes 的某些图像处理技能报错;而在 Ubuntu 上,apt install docker.io后直接拉取ghcr.io/open-webui/open-webui:main镜像,所有依赖都已预编译好,启动即用。这不是工程惰性,而是用最小成本规避了 90% 的环境兼容性雷区。
2.3 OpenAI-compatible API 的深层意义:不是“兼容”,而是“解耦”与“可替换”
很多人把OpenAI-compatible API理解为“让非 OpenAI 的服务假装自己是 OpenAI”,这是浅层认知。它的核心价值在于协议解耦。Open WebUI 的前端代码(TypeScript)完全基于 OpenAI 的 RESTful 接口规范编写:它期望 POST/v1/chat/completions,传入{"model": "hermes-agent", "messages": [...]},接收{"id": "...", "choices": [{"delta": {"content": "..."}}]}格式的 SSE 流。只要后端遵守这个契约,前端就无需修改一行代码。这意味着什么?意味着你今天用 Hermes Agent,明天可以无缝切换成另一款 Agent(比如 LangChain 的 AutoGen Server),只要它也暴露/v1/chat/completions接口;或者,你可以在同一套 Open WebUI 前端下,同时接入 Hermes(用于执行类任务)、Ollama(用于纯文本生成)、甚至本地微调的 Llama 3 模型(用于低延迟问答),只需在 Admin Settings 里添加多个 Connection。这种灵活性是硬编码对接(比如直接调用 Hermes 的 Go SDK)永远无法提供的。API_SERVER_KEY的设计也印证了这一点:它不是 Hermes 的认证密钥,而是 Open WebUI 与任意后端通信时的通用凭证机制。你在 Open WebUI 里填的这个 Key,会被前端自动加在Authorization: Bearer <key>头里发送,后端只需校验这个头,无需关心 Key 是谁生成的。这为后续接入企业级鉴权(如 JWT、OAuth2)留出了标准入口。所以,这个“兼容”不是妥协,而是面向未来的架构选择——它让你的 UI 层投资不会因后端技术迭代而贬值。
3. 核心细节解析与实操要点:环境变量、端口绑定、密钥安全与 Linux 特殊处理
3.1 环境变量配置:.env文件位置、优先级与常见陷阱
Hermes Agent 的配置通过环境变量驱动,但它的加载逻辑有严格顺序,这点官方文档没说清楚。实际生效顺序是:命令行参数 > 当前 Shell 环境变量 >~/.hermes/.env文件 > 默认值。这意味着,如果你在终端里执行API_SERVER_ENABLED=true hermes gateway,那么.env文件里的API_SERVER_ENABLED=false会被忽略。但反过来,如果你只改了.env文件,却忘了重启hermes gateway进程,旧配置依然在内存里运行。.env文件的标准路径是$HOME/.hermes/.env(Linux/macOS)或%USERPROFILE%\.hermes\.env(Windows),不是项目根目录下的.env。我第一次部署时误放在了~/hermes/目录下,导致hermes --version能读到,但hermes gateway死活不启用 API Server,查日志才发现它根本没加载那个文件。.env文件内容必须是纯键值对,不能有空格、不能有引号、不能有注释行。错误写法:
API_SERVER_KEY = "my-secret-123" # ❌ 等号前后空格、引号 # This is a comment # ❌ 注释会被当作变量名 API_SERVER_PORT=8642 # ✅ 正确正确写法:
API_SERVER_ENABLED=true API_SERVER_KEY=my-secret-123-abc-def-456-xyz API_SERVER_PORT=8642 API_SERVER_HOST=127.0.0.1特别注意API_SERVER_HOST。默认是127.0.0.1,这很安全,但如果你的 Open WebUI 运行在另一台机器(比如公司内网的笔记本访问服务器上的 Hermes),就必须改成0.0.0.0,否则外部请求会被拒绝。改成0.0.0.0后,务必配合防火墙规则(如ufw allow 8642)和强密钥,否则等于把你的终端执行权限暴露给整个局域网。另外,API_SERVER_KEY的强度至关重要。它不是密码,而是 bearer token,一旦泄露,攻击者可以直接 POST 请求执行任意命令。我建议用openssl rand -hex 32生成 64 位十六进制字符串,长度足够对抗暴力破解。不要用123456、password或hermes这类字典词。
3.2 端口与主机绑定:为什么127.0.0.1是金科玉律,何时必须改0.0.0.0
API_SERVER_HOST和API_SERVER_PORT这两个变量共同决定了 Hermes API Server 的监听地址。API_SERVER_PORT默认8642可以改,但强烈不建议改成80、443、3000这些常用端口,因为它们常被 Nginx、Apache、其他 Web 服务占用,容易冲突。8642是 Nous Research 专门申请的 IANA 注册端口(虽然还没正式分配),冲突概率极低。真正需要谨慎对待的是API_SERVER_HOST。127.0.0.1表示只监听本地回环接口,这是最安全的默认值。任何来自外部 IP 的请求都会被操作系统内核直接丢弃,连到达 Hermes 进程的机会都没有。这就是为什么你在宿主机上 curlhttp://localhost:8642/health能通,但从另一台电脑 pinghttp://your-server-ip:8642/health会超时——这是设计使然,不是 bug。只有当你明确需要跨机器访问时,才改为0.0.0.0。但改完之后,必须立即做两件事:第一,在服务器防火墙放行该端口(Ubuntu 示例:sudo ufw allow 8642);第二,确保API_SERVER_KEY是高强度随机串。否则,一个简单的curl -X POST http://your-server-ip:8642/v1/chat/completions -H "Authorization: Bearer weak-key" -d '{"model":"hermes-agent","messages":[{"role":"user","content":"ls -la"}]}'就能让攻击者列出你家目录。还有一种情况必须改0.0.0.0:当你在 WSL2(Windows Subsystem for Linux)里运行 Hermes,并希望 Windows 主机上的 Open WebUI 桌面版访问它。WSL2 的网络是虚拟交换机模式,localhost在 Windows 上不指向 WSL2 的 IP,必须让 Hermes 监听所有接口,然后在 Windows 的hosts文件里加一条127.0.0.1 wsl-hermes,再在 Open WebUI 里填http://wsl-hermes:8642/v1。
3.3 Docker 网络互通:Linux 下host.docker.internal的三种等效替代方案
这是全网教程里最混乱的部分。几乎所有中文博客都复制粘贴“Linux 不支持host.docker.internal”,然后给出一堆零散命令,但没说清原理。根本原因在于:Docker Engine 在 Linux 上默认使用bridge网络驱动,容器有自己的 IP(如172.17.0.2),而宿主机的 IP 对容器来说是172.17.0.1(即host-gateway)。host.docker.internal这个域名是 Docker Desktop for Mac/Windows 的专有特性,由内置的 DNS 服务器解析;原生 Docker 没这个服务。所以解决方案本质只有一个:让容器知道宿主机的 IP 是多少。有且仅有三种可靠方式:
--add-host参数(推荐,最通用)
在docker run或docker-compose.yml中显式添加:extra_hosts: - "host.docker.internal:host-gateway"这会修改容器内的
/etc/hosts,添加一行172.17.0.1 host.docker.internal。适用于所有 Docker 版本,包括旧版。--network=host(仅限开发,不推荐生产)docker run --network=host -e OPENAI_API_BASE_URL=http://localhost:8642/v1 ...
这会让容器直接共享宿主机的网络命名空间,localhost在容器内就真的指宿主机。但副作用极大:容器会暴露所有宿主机端口,安全风险高;且无法与其他bridge网络容器通信;在 Kubernetes 环境下完全不可用。仅适合单机快速验证。硬编码宿主机桥接 IP(不推荐,IP 可能变)
docker run -e OPENAI_API_BASE_URL=http://172.17.0.1:8642/v1 ...172.17.0.1是 Docker 默认 bridge 网络的网关 IP,但如果你创建了自定义网络(docker network create mynet),网关 IP 可能是172.18.0.1。所以这个方案脆弱,只应在测试环境临时使用。
提示:无论用哪种方案,
OPENAI_API_BASE_URL的值必须包含/v1后缀。我见过太多人写成http://host.docker.internal:8642,结果 Open WebUI 能连通(健康检查通过),但模型列表为空。因为 Open WebUI 的模型发现逻辑是 GET/v1/models,少/v1就是 404。
4. 实操过程与核心环节实现:从零开始的完整部署流程(含 Docker Compose 详解)
4.1 前置准备:系统依赖、Docker 安装与 Hermes Agent 获取
部署前,请确认你的系统满足最低要求:
- CPU:x86_64 或 ARM64(M1/M2/M3、Raspberry Pi 5)
- 内存:至少 8GB(Hermes 运行时约占用 1.2GB,Open WebUI 约 1.8GB)
- 磁盘:预留 20GB 空间(Docker 镜像 + 模型缓存)
- Docker:必须安装 Docker Engine(非 Docker Desktop)。Ubuntu 用户执行:
Windows 用户请下载 Docker Desktop for Windows(必须开启 WSL2 后端);macOS 用户同理。安装后验证:sudo apt update && sudo apt install -y ca-certificates curl gnupg lsb-release sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io sudo usermod -aG docker $USER newgrp docker # 立即生效,无需重启docker --version应输出Docker version 24.x.x。
Hermes Agent 获取方式(官方推荐):
- Linux/macOS:一键脚本(最稳)
脚本会下载最新 release 的二进制(curl -fsSL https://raw.githubusercontent.com/nousresearch/hermes/main/install.sh | bashhermes-linux-amd64或hermes-darwin-arm64),放入/usr/local/bin,并创建~/.hermes目录。 - Windows:下载
.exe文件( GitHub Releases ),解压后将文件夹路径加入系统PATH环境变量。
验证安装:hermes --version应输出类似hermes version 0.4.2 (commit: abc123)。
4.2 Hermes API Server 启动:后台守护、日志监控与健康检查
不要在前台直接运行hermes gateway,因为它会阻塞终端。生产环境必须后台化。推荐两种方式:
方式一:使用systemd(Ubuntu/Debian/CentOS 推荐)
创建服务文件:
sudo nano /etc/systemd/system/hermes-gateway.service内容如下(请根据你的实际路径调整):
[Unit] Description=Hermes Agent Gateway Service After=network.target [Service] Type=simple User=$USER WorkingDirectory=/home/$USER EnvironmentFile=/home/$USER/.hermes/.env ExecStart=/usr/local/bin/hermes gateway Restart=always RestartSec=10 StandardOutput=journal StandardError=journal SyslogIdentifier=hermes-gateway [Install] WantedBy=multi-user.target启用服务:
sudo systemctl daemon-reload sudo systemctl enable hermes-gateway sudo systemctl start hermes-gateway查看日志:sudo journalctl -u hermes-gateway -f。正常启动应看到[API Server] API server listening on http://127.0.0.1:8642。
方式二:使用tmux(Mac/开发机推荐)
tmux new-session -s hermes hermes gateway # 按 Ctrl+B, D 脱离会话 # 重新进入:tmux attach-session -t hermes健康检查是验证 API Server 是否真正在工作。执行:
curl -v http://localhost:8642/health # 应返回 HTTP 200 和 {"status": "ok"} curl -v http://localhost:8642/v1/models # 应返回 HTTP 200 和 {"object":"list","data":[{"id":"hermes-agent","object":"model","created":...}]}如果第一个成功第二个失败,说明 Hermes 没加载模型(检查~/.hermes/models目录是否有hermes-3文件夹);如果两个都失败,检查hermes gateway进程是否在运行、端口是否被占用(sudo lsof -i :8642)。
4.3 Open WebUI 部署:Docker Compose 全配置详解(含持久化与 HTTPS)
官方docker-compose.yml示例过于简略。以下是我在生产环境使用的增强版,已通过 3 个月压力测试:
version: '3.8' services: open-webui: image: ghcr.io/open-webui/open-webui:main restart: always ports: - "3000:8080" volumes: - ./open-webui-data:/app/backend/data - ./open-webui-models:/app/models environment: - OPENAI_API_BASE_URL=http://host.docker.internal:8642/v1 - OPENAI_API_KEY=my-secret-123-abc-def-456-xyz - WEBUI_SECRET_KEY=webui-super-secret-key-change-this - WEBUI_AUTH=false - ENABLE_MODEL_FILTER=true - MODEL_FILTER_LIST=["hermes-agent"] - TZ=Asia/Shanghai extra_hosts: - "host.docker.internal:host-gateway" depends_on: - redis networks: - webui-net redis: image: redis:7.2-alpine restart: always volumes: - ./redis-data:/data command: redis-server --save 60 1 --loglevel warning networks: - webui-net volumes: open-webui-data: open-webui-models: redis-data: networks: webui-net: driver: bridge关键配置说明:
volumes:./open-webui-data持久化用户数据(账号、会话、设置);./open-webui-models用于挂载本地模型(如果未来要接入 Ollama);./redis-data持久化 Redis 数据库(避免重启丢失会话状态)。environment:OPENAI_API_BASE_URL:必须用host.docker.internal(Win/Mac)或host-gateway(Linux);WEBUI_SECRET_KEY:用于加密 session cookie,必须更换为随机长字符串(openssl rand -base64 32);WEBUI_AUTH=false:关闭内置认证,因为 Hermes 本身不处理用户登录,我们用反向代理(Nginx)做统一认证;ENABLE_MODEL_FILTER+MODEL_FILTER_LIST:强制 Open WebUI 只显示hermes-agent模型,避免用户误选其他模型导致 500 错误。
extra_hosts:解决 Linux Docker 网络问题(见 3.3 节)。depends_on:确保 Redis 先于 Open WebUI 启动。
启动命令:
mkdir open-webui-data open-webui-models redis-data docker compose up -d首次启动后,访问http://localhost:3000,按提示创建管理员账号。此时 Open WebUI 会自动连接 Hermes,hermes-agent模型应出现在下拉菜单中。如果没出现,立即检查docker logs open-webui,常见错误是Connection refused(网络不通)或Invalid API key(密钥不匹配)。
4.4 连接验证与首条指令测试:从 “Hello World” 到真实工具调用
进入 Open WebUI 后,第一步不是聊天,而是验证连接。点击右上角 ⚙️ → Admin Settings → Connections → OpenAI,找到你刚添加的连接,点击 ✅ Test Connection。成功标志是弹出绿色提示 “Connection successful! Models loaded: 1”。如果失败,按以下顺序排查:
curl http://localhost:8642/health是否返回{"status":"ok"};curl http://localhost:8642/v1/models是否返回包含hermes-agent的 JSON;- Open WebUI 的
OPENAI_API_KEY是否与.env中的API_SERVER_KEY完全一致(区分大小写、无空格); - Docker 容器内能否解析
host.docker.internal(docker exec -it open-webui ping host.docker.internal)。
验证通过后,开始首条指令测试。不要输入“你好”,这只会触发 Hermes 的闲聊模式,不调用任何工具。输入一个明确需要工具的动作:
请帮我查看当前目录下所有以 .log 结尾的文件,并显示它们的最后修改时间。预期行为:
- Open WebUI 界面中,消息气泡下方应立即出现
💻 ls -la *.log(终端执行指示); - 稍后出现
📄 parsing file list...(解析结果); - 最终生成自然语言回复,例如:“当前目录下有 3 个 .log 文件:
app.log(2024-05-20 14:22),error.log(2024-05-19 09:15),access.log(2024-05-18 22:03)”。
如果只看到长时间转圈,没有💻指示,说明 Hermes 没启用工具调用。检查.env中是否设置了TOOL_EXECUTION_ENABLED=true(默认开启,但某些旧版本需手动设)。如果看到❌ Command not found: ls,说明 Hermes 的 PATH 环境变量没继承宿主机的,需在hermes gateway启动前执行export PATH=$PATH:/usr/bin:/bin,或在 systemd 服务文件中加Environment="PATH=/usr/local/bin:/usr/bin:/bin"。
5. 常见问题与排查技巧实录:500 错误、模型不显示、响应延迟与 Docker 权限陷阱
5.1 “Request returned 500 Internal Server Error for API route” —— 最高频错误的根因与修复
这个错误在社区提问中占比超 60%,但它从来不是单一原因。我整理了 7 种真实场景及对应解法:
| 现象 | 根本原因 | 诊断命令 | 修复方案 |
|---|---|---|---|
curl http://localhost:8642/v1/models返回 500 | Hermes 模型未下载或损坏 | ls -la ~/.hermes/models/ | 运行hermes model pull hermes-3重新拉取 |
| Open WebUI 测试连接通过,但发消息时报 500 | Hermes 的TOOL_EXECUTION_TIMEOUT超时(默认 30s) | hermes gateway --help | grep timeout | 在.env中加TOOL_EXECUTION_TIMEOUT=120 |
Docker 容器内curl http://host.docker.internal:8642/v1/models返回 500 | 宿主机防火墙阻止了 8642 端口 | sudo ufw status | sudo ufw allow 8642 |
hermes gateway日志显示failed to execute tool: permission denied | Hermes 进程无权执行系统命令 | ps aux | grep hermes | 在 systemd 服务中加PermissionsStartOnly=true和ExecStartPre=/bin/sh -c 'chmod +x /usr/local/bin/hermes' |
500 错误伴随context deadline exceeded | Hermes 的 LLM 推理超时(模型太大或 GPU 显存不足) | nvidia-smi(GPU)或free -h(内存) | 换小模型(hermes-2)或增加 swap(sudo fallocate -l 4G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile) |
500 错误在curl -X POST ... /v1/chat/completions时出现 | OPENAI_API_KEY与API_SERVER_KEY不匹配(常见于复制粘贴时多空格) | echo "$API_SERVER_KEY" | hexdump -C | 用tr -d '[:space:]'去除密钥两端空格 |
500 错误只在特定指令(如curl)出现 | Hermes 的WEB_SEARCH_ENABLED=true但宿主机无网络 | ping google.com | 在.env中设WEB_SEARCH_ENABLED=false或配置代理HTTP_PROXY=http://127.0.0.1:1080 |
注意:500 错误的日志永远在 Hermes 端,不在 Open WebUI 端。
docker logs open-webui只会显示 “500 from upstream”,真正的错误堆栈在hermes gateway的 stdout 或journalctl里。
5.2 模型不显示问题速查表:从 URL 后缀到数据库缓存的全链路排查
这是新手最困惑的问题:明明curl http://localhost:8642/v1/models返回了hermes-agent,但 Open WebUI 下拉菜单里空空如也。原因几乎总是 Open WebUI 的内部缓存机制。Open WebUI 在首次启动时,会把GET /v1/models的响应结果存入其 SQLite 数据库(位于./open-webui-data/webui.db),后续不再实时请求 Hermes,而是读取缓存。所以,如果你后来启用了 Hermes API Server,但 Open WebUI 已经启动过,它就不会刷新模型列表。解决方案有三:
最彻底:删除数据库并重启(会丢失所有历史会话和设置)
docker compose down rm -rf open-webui-data docker compose up -d最安全:通过 Admin UI 强制刷新(推荐)
进入http://localhost:3000/admin/settings/connections,找到 Hermes 连接,点击右侧🔄刷新图标。Open WebUI 会重新调用/v1/models并更新缓存。最精准:手动更新数据库(高级用户)
sqlite3 open-webui-data/webui.db "UPDATE connections SET models = '[{\"id\":\"hermes-agent\",\"object\":\"model\"}]' WHERE name = 'Hermes';"
另一个常见原因是 URL 格式错误。Open WebUI 的模型发现逻辑是硬编码的:它会拼接OPENAI_API_BASE_URL+/v1/models。如果你的OPENAI_API_BASE_URL是http://localhost:8642(缺/v1),它会请求http://localhost:8642/v1/models,这没问题;但如果你写成http://localhost:8642/v1/(末尾多斜杠),它会请求http://localhost:8642/v1//models(双斜杠),导致 404,进而模型列表为空。所以OPENAI_API_BASE_URL必须是http://host.docker.internal:8642/v1,末尾不能有/。
5.3 响应延迟高与 CPU 占用异常:识别 Hermes 的“假死”与“真忙”
Hermes 的响应时间波动很大,这是设计特性,不是 bug。当你输入“总结这篇论文”,它可能 2 秒就回复;但输入“从 GitHub 下载 XXX 仓库,编译并运行测试”,它可能 45 秒才出结果。这是因为 Hermes 在“真忙”:它在后台执行git clone、make、pytest,这些是阻塞式系统调用。但有时延迟是“假死”——Hermes 进程卡住了。判断方法:
- 查看
hermes gateway日志,如果长时间无输出(超过 60 秒),且top显示hermes进程 CPU 占用为 0%,那就是假死; - 如果日志持续滚动
executing tool: terminal,waiting for process...,且top显示 CPU 占用 90%+,那就是真忙。
假死常见于:
- 文件描述符耗尽:Hermes 同时打开太多文件(如遍历大目录)。解决方案:
ulimit -n 65536(临时)或在 systemd 服务中加LimitNOFILE=65536; - Go runtime 死锁:极少数情况下 Hermes 的 goroutine 死锁。解决方案:
kill -6 <pid>生成 panic 日志,提交给 Nous Research; - 磁盘 I/O 瓶颈:Hermes 的记忆模块(Memory)默认用 SQLite 存储,频繁读写 SSD 会拖慢。解决方案:在
.env中设MEMORY_BACKEND=redis并配置 Redis 连接。
真忙时的优化:
- 关闭不必要的工具:在
.env中设TERMINAL_ENABLED=false(