WSL2 + DeepSeek + Cloudflare Tunnel + 熊.online 完整部署 | v0.13.0 | 2026-05-14
操作系统:Windows 11,WSL 功能未启用
关键发现:wsl.exe 存在但总是返回帮助信息 — 说明 Windows 功能未启用
解决:需要管理员权限运行 dism.exe /online /enable-feature
# 启用 WSL(需管理员权限)
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
# 重启电脑后
wsl --install -d Ubuntu # 安装 Ubuntu
wsl --set-default-version 2 # 设 WSL2 为默认
wsl -l -v 发现状态为 "Stopped"。
| 组件 | 版本 |
|---|---|
| Ubuntu (WSL2) | 26.04 |
| Python | 3.14.4 |
| Node.js | 22.22.2 |
| uv (包管理器) | 0.11.14 |
| Docker | 29.4.3 |
| nginx | 1.28.3 |
官方推荐:一键安装脚本 curl .../install.sh | bash
实际采用:手动 clone + uv 安装,更可控
cd ~ && git clone https://github.com/NousResearch/hermes-agent.git
cd hermes-agent && uv sync && uv pip install -e ".[web]"
bash -c "hermes" 在非交互 shell 中 alias 不生效。必须创建 /usr/local/bin/hermes 脚本文件。
/usr/local/bin/hermes。
整个部署过程中反复遇到 \$'\r': command not found 错误。根源是 PowerShell 写入文件默认用 CRLF,WSL 需要 LF。
# PowerShell 侧统一使用这个模式
(Get-Content "file.sh" -Raw).Replace("`r`n","`n") | Set-Content "file_lf.sh" -NoNewline
wsl.exe cp /mnt/c/.../file_lf.sh /path/in/wsl/
为什么选 DeepSeek:国内直连速度快、价格极低(deepseek-chat ~¥1/百万token)、注册即送免费额度
可用模型:deepseek-chat / deepseek-reasoner / deepseek-v4-flash / deepseek-v4-pro
# ~/.hermes/config.yaml
model:
provider: deepseek
default: deepseek-chat
# ~/.hermes/.env
DEEPSEEK_API_KEY=sk-xxxxx
DEEPSEEK_BASE_URL=https://api.deepseek.com
sk-26050...8673 在终端明文可见。立即删除旧 Key、创建新 Key、更新配置。
| 方案 | 难度 | 适用 | 关键配置 |
|---|---|---|---|
| 个人微信 (Weixin) | ⭐ | 个人使用 | 扫码即连 |
| 企业微信 (WeCom) | ⭐⭐⭐ | 团队 | Bot ID + Secret |
| WeCom 回调 | ⭐⭐⭐⭐ | 高级 | 公网服务器 |
# 终端运行
hermes gateway setup
# 选择 Weixin / WeChat → Y 开始扫码
# 终端会输出二维码链接,复制到浏览器打开
# 微信扫码 → 确认登录
WEIXIN_DM_POLICY=allowlist # 白名单模式(推荐)
WEIXIN_GROUP_POLICY=disabled # 禁用群聊
hermes dashboard --port 9119 --host 0.0.0.0 --no-open --insecure
0.0.0.0(安全考虑),必须加 --insecure。但在 Cloudflare Tunnel + HTTPS 保护下是安全的。
左下角 Switch to Chinese → 选择 简体中文。所有导航、配置页面立即中文化。
# 必须先安装 Web 依赖
uv pip install -e ".[web]" # 安装 fastapi + uvicorn
默认 SOUL.md 是英文的,需要用中文重写。关键指令:
| 优化项 | 效果 |
|---|---|
| DuckDuckGo 搜索 | 免费网页搜索(要求安装 ddgs 包) |
| 网关自启动 | ~/.bashrc 添加 systemctl start hermes-gateway |
| 浏览器工具 | agent-browser 可用,Playwright 暂不兼容 Ubuntu 26.04 |
| 时区设置 | Asia/Shanghai(影响 cron 和会话重置) |
agent.max_turns: 90 # 工具调用上限(探索场景可设 150+)
compression_threshold: 0.5 # 上下文压缩阈值
tool_progress_mode: all # 工具进度显示模式
| 方法 | 结果 | 原因 |
|---|---|---|
| trycloudflare 临时 URL | 成功 | 但 URL 随机变化 |
| token 模式 + 自定义域名 | 530/404 | QUIC 超时、路由未配置 |
| cloudflared tunnel login | 失败 | cert.pem 始终无法写入 |
| HTTP2 协议切换 | 部分改善 | 延迟降低但仍有超时 |
| Nginx 反代 + token 模式 | 成功 | 需要额外配置 |
# API Key 格式(不是 Bearer Token)
curl -H "X-Auth-Email: [email protected]" -H "X-Auth-Key: cfk_xxxxx" \
-X PUT ".../cfd_tunnel/TUNNEL_ID/configurations" \
-d '{"config":{"ingress":[{"hostname":"hermes.熊.online","service":"http://localhost:8888"},...]}}'
Nginx 反代层 (port 8888):
由于 token 模式 Tunnel 只能暴露一个 --url,用 Nginx 做统一入口:
/ → http://127.0.0.1:9119 (Dashboard)
/chat → /var/www/chat (聊天页面)
info子域名 → port 8889 (信息页)
Authorization: Bearer xxxX-Auth-Email + X-Auth-Key# Zone ID
curl "https://api.cloudflare.com/client/v4/zones?name=熊.online" \
-H "X-Auth-Email: ..." -H "X-Auth-Key: ..."
# DNS 记录管理
curl "...zones/$ZONE/dns_records" -X POST -d '{"type":"CNAME","name":"hermes",...}'
# Tunnel ingress 配置
curl "...accounts/$ACCOUNT/cfd_tunnel/$TUNNEL/configurations" -X PUT -d '{...}'
# ~/.hermes/.env
API_SERVER_ENABLED=true
API_SERVER_KEY=***REDACTED***
API_SERVER_HOST=0.0.0.0
API_SERVER_PORT=8642
# 重启网关
hermes gateway restart
curl https://api.熊.online/v1/models -H "Authorization: Bearer ***REDACTED***" 应返回模型列表。
纯前端 HTML 页面,直接调用 Hermes API Server 的 OpenAI 兼容接口。不需要后端服务器。
浏览器 → https://hermes.熊.online/chat (静态 HTML)
→ fetch('https://api.熊.online/v1/chat/completions', {
headers: { 'Authorization': 'Bearer ***REDACTED***' },
body: JSON.stringify({model:'deepseek-chat', messages:[...]})
})
任何支持 OpenAI 兼容 API 的客户端都能接入:
| 客户端 | 配置 |
|---|---|
| ChatBox | API 地址填 https://api.熊.online/v1 |
| LobeChat | 同上,Key 填 ***REDACTED*** |
| NextChat | 可部署到 Cloudflare Pages |
| 方案 | 结果 |
|---|---|
| YOURLS + MySQL | 放弃 — 容器内 MySQL 连接过于复杂 |
| Shlink (SQLite 内置) | 成功 — 一个容器搞定 |
docker run -d --name shlink -p 8080:8080 \
-e DEFAULT_DOMAIN=s.熊.online \
-e INITIAL_API_KEY=abc123def456 \
shlinkio/shlink:latest
# 创建短链
curl -X POST https://s.熊.online/rest/v3/short-urls \
-H "X-Api-Key: abc123def456" \
-H "Content-Type: application/json" \
-d '{"longUrl":"https://github.com/NousResearch/hermes-agent"}'
# 健康检查
curl https://s.熊.online/rest/v3/health
┌─────────────────────────┐
│ Cloudflare CDN │
│ (DNS + Tunnel + HTTPS) │
└───────────┬─────────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
hermes.熊.online api.熊.online s.熊.online
│ │ │
┌───────┴───────┐ ┌───────┴───────┐ ┌───────┴───────┐
│ Nginx :8888 │ │ Hermes :8642 │ │ Shlink :8080 │
│ / → :9119 │ │ (API Server) │ │ (Docker) │
│ /chat → html │ └───────┬───────┘ └───────────────┘
└───────┬───────┘ │
│ ┌───────┴───────┐
┌───────┴───────┐ │ DeepSeek │
│Dashboard :9119│ │ API (外部) │
│ info.熊.online│ └───────────────┘
│ :8889 │
└───────────────┘
| 地址 | 端口 | 服务 | |
|---|---|---|---|
| hermes.熊.online | :8888→:9119 | Dashboard 管理面板 | 无 |
| hermes.熊.online/chat | :8888→静态 | AI 聊天网页 | |
| api.熊.online | :8642 | OpenAI 兼容 API | |
| s.熊.online | :8080 | Shlink 短链 | |
| info.熊.online | :8889 | 部署总览文档 |
| 服务 | 自启? | 启动方式 |
|---|---|---|
| cloudflared | 是 | systemd service |
| hermes-gateway | 是 | systemd user + ~/.bashrc |
| nginx | 是 | systemd service |
| Docker (Shlink) | 是 | Docker restart policy |
| Dashboard | 否 | 需手动启动 |
hermes dashboard --port 9119 --host 0.0.0.0 --no-open --insecure
Windows + WSL 混用环境,所有脚本文件必须转换为 LF 换行。这是整个部署中最大的时间浪费源。
从国内连接 Cloudflare 极度不稳定(QUIC 超时、TCP 超时)。HTTP2 协议稍好。最终靠 API 直接配置 ingress 才解决。
cloudflared tunnel login 需要浏览器回调下载 cert.pem,在 WSL 环境中几乎不可用。token 模式配合 API 直接配置是可行路径。
DeepSeek Key 在微信配置时泄露到终端日志。配置完成后应立即检查并更换泄露的 Key。
token 模式 Tunnel 只能暴露单个 --url。用 Nginx 做统一入口是必须的架构选择。
CNAME 记录必须在 Tunnel ingress 配置之前添加。Tunnel ingress 配置好之后需重启 cloudflared 服务。
部署完成第二天,发现多个子站点存在问题需要修复:
| 站点 | 问题 | 原因 |
|---|---|---|
| exp.熊.online | 弹窗要求用户名密码 | nginx auth_basic 未清理 |
| info.熊.online | 弹窗要求用户名密码 | nginx auth_basic 未清理 |
| api.熊.online | 访问显示裸 JSON | API 端口无首页文档 |
| s.熊.online | 404 + 短链不跳转 | 无网页界面 + 域名错误 |
WWW-Authenticate: Basic realm="Password"。
# 查看 HTTP 响应头确认认证类型
curl -I https://exp.熊.online
# → WWW-Authenticate: Basic realm="Password"
# 查看 nginx 配置
grep -r auth_basic /etc/nginx/sites-enabled/
根因:/etc/nginx/sites-enabled/exp 和 info 中配置了:
auth_basic "Password";
auth_basic_user_file /etc/nginx/.htpasswd;
早期测试时加的认证,部署完成后忘记清理。
sed -i 's/^[[:space:]]*auth_basic/#auth_basic/' /etc/nginx/sites-enabled/exp
sed -i 's/^[[:space:]]*auth_basic_user_file/#auth_basic_user_file/' /etc/nginx/sites-enabled/exp
# info 同理
nginx -t && nginx -s reload
关键发现:cloudflared 使用 token 模式运行(--token 参数),本地 /root/.cloudflared/config.yml 完全被忽略。所有路由必须通过 Cloudflare Zero Trust 仪表板管理。
# 查看运行模式
ps aux | grep cloudflared
# → --token xxx ← token 模式!
路由配置位置:Zero Trust → Networks → 连接器 → 个人 → 已发布应用程序路由
需求:api.熊.online 打开后显示文档 + 在线测试界面,而非裸 JSON。
方案:新建 nginx 站点 :8092,/ 返回暗色主题 API 文档页,/v1/ 代理到 API Server :8642。风格与 exp/info 统一。
# /etc/nginx/sites-enabled/api (port 8092)
server {
listen 8092;
root /var/www/api;
index index.html;
location /v1/ {
proxy_pass http://127.0.0.1:8642;
proxy_set_header Host $host;
proxy_buffering off;
proxy_read_timeout 120s;
}
location = / {
try_files /index.html =404;
}
}
# ~/.hermes/.env
API_SERVER_KEY= # 清空 = 允许无认证
API_SERVER_HOST=127.0.0.1 # 仅监听本地
| 问题 | 根因 | 修复 |
|---|---|---|
| 首页 404 | Shlink 只有 API | 编写短链网页 (:8091) |
| 短码跳转 404 | nginx 只代理 /rest/ | 添加 location / → Shlink |
| 生成短链 401 | 请求无 API Key | 网页嵌入 Shlink Key |
| 域名显示 localhost | DEFAULT_DOMAIN 未设 | 重建容器设 s.熊.online |
server {
listen 8091;
location /rest/ { proxy_pass http://127.0.0.1:8080; } # API
location = / { try_files /index.html =404; } # 网页
location / { proxy_pass http://127.0.0.1:8080; } # 跳转
}
= /(精确)→ /rest/(前缀)→ /(通配)。顺序错了会导致 404。
docker stop shlink && docker rm shlink
docker run -d --name shlink --restart unless-stopped -p 8080:8080 \
-e DEFAULT_DOMAIN=s.熊.online -e IS_HTTPS_ENABLED=true \
shlinkio/shlink:latest
docker exec shlink shlink api-key:generate # 重建后旧 Key 失效
| 域名 | Tunnel → | 后端服务 |
|---|---|---|
| 熊.online | :8891 | 导航页 |
| hermes.熊.online | :8888 | /→:9119 Dashboard, /chat→html, /v1/→:8642 |
| api.熊.online | :8092 | /→API文档, /v1/→:8642 |
| s.熊.online | :8091 | /→网页, /rest/→:8080, /*→:8080跳转 |
| exp.熊.online | :8890 | 本页 |
| info.熊.online | :8889 | 总览 |
初版部署后每个站点都需要逐一浏览器验证。exp/info 的认证弹窗就是因为跳过了这一步。
cloudflared 有 config.yml 和 token 两种模式。token 模式下本地配置被忽略,必须在 Zero Trust 仪表板管理。改 config.yml 没用。
= /(精确)> ^~ /prefix/ > /regex/ > /(通配)。静态页用精确匹配保证不被 API 代理吞掉。
Shlink 的 DEFAULT_DOMAIN 创建即固定。重建 = 丢失数据。生产环境先规划参数。
API 端点返回 JSON 正确,但 api.域名 根部应有文档页面——否则用户打开就是一脸懵的 JSON。