mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
docs(i18n): add zh-CN translations for features cloud-deploy, tool-me… (#2507)
Co-authored-by: agent:skill-master <skill-master@openclaw>
This commit is contained in:
co-authored by
agent:skill-master <skill-master@openclaw>
parent
838309e998
commit
95aeab500e
@@ -0,0 +1,517 @@
|
||||
---
|
||||
description: 在云端托管 headless openhuman-core——DigitalOcean App Platform、Fly.io 或任何 VPS 上的 Docker Compose。
|
||||
icon: cloud
|
||||
lang: zh-CN
|
||||
---
|
||||
|
||||
# 云端部署
|
||||
|
||||
OpenHuman 是一个桌面应用,但它的 **Rust 核心**(`openhuman-core`)是一个可以托管在云端的 headless JSON-RPC 服务器。单独部署核心的用途包括:
|
||||
|
||||
- 多设备访问,让多个桌面客户端指向同一个托管核心
|
||||
- 没有本地 Rust 工具链的内部测试人员
|
||||
- 应该比笔记本 session 更长寿的长运行 cron job / webhook
|
||||
|
||||
本指南涵盖四条部署路径,由易到难:
|
||||
|
||||
1. [DigitalOcean App Platform:一键部署](#1-digitalocean-app-platform-一键部署)
|
||||
2. [DigitalOcean App Platform:通过 doctl 手动部署](#2-digitalocean-app-platform-通过-doctl-手动部署)
|
||||
3. [任何 VPS 通过 Docker Compose](#3-任何-vps-通过-docker-compose)
|
||||
4. [Fly.io](#4-flyio)
|
||||
|
||||
每条路径部署的内容相同:一个运行 `openhuman-core serve` 在端口 `7788` 上的单一容器。公共主机应位于提供商的 TLS 之后,例如 `https://core.example.com/rpc`。仅限私有的主机——localhost、RFC1918 网络或 Tailscale 等 tailnet——可以使用纯 HTTP,例如 `http://100.x.x.x:7788/rpc`,当核心无法从公共互联网访问时。桌面应用已经知道如何与远程核心通信;在 `app/.env.local` 中设置 `OPENHUMAN_CORE_RPC_URL` 和 `OPENHUMAN_CORE_TOKEN=...`,然后启动即可。
|
||||
|
||||
---
|
||||
|
||||
## Bearer token 的单一事实来源
|
||||
|
||||
每次 `/rpc` 调用都携带 `Authorization: Bearer <token>`。核心在启动时通过两种方式加载该 token([`src/core/auth.rs`](../../src/core/auth.rs)):
|
||||
|
||||
1. **`OPENHUMAN_CORE_TOKEN` 环境变量**——由调用方预置(Tauri 壳层、Docker、App Platform、systemd unit 等)。核心原样使用此值,**绝不**写入文件。
|
||||
2. **`{workspace}/core.token` 文件**——仅在 `OPENHUMAN_CORE_TOKEN` 未设置时由核心在首次启动时生成。独立运行的 `openhuman core run` 使用此方式,以便 CLI 客户端可以 `cat` 该文件。
|
||||
|
||||
**任何远程 / Docker 化部署的经验法则:始终设置 `OPENHUMAN_CORE_TOKEN`。** 不要在容器中依赖 `core.token`——临时文件系统会在重新部署时丢失它,任何试图从容器外部读取该文件的客户端都会得到过期或空值。这两条路径在启动时故意互斥;混合使用是"重新部署后 dashboard 报 401"的最常见原因。
|
||||
|
||||
要检查*运行中*的核心在使用什么,在主机上运行 [`scripts/print-core-token.sh`](../../scripts/print-core-token.sh)(或在容器内使用 `docker compose exec`):
|
||||
|
||||
```bash
|
||||
scripts/print-core-token.sh --where # 打印 'env' 或 'file:/path'
|
||||
scripts/print-core-token.sh --redact # 前 8 个十六进制字符 + '…'(适合日志)
|
||||
scripts/print-core-token.sh # 完整值(直接管道到客户端)
|
||||
```
|
||||
|
||||
桌面应用的首次运行选择器也在 Core RPC URL + token 字段旁暴露了一个**测试连接**按钮,它向该 URL 和输入的 token 触发 `core.ping`,并在持久化配置之前内联报告 `Connected ✓` / `Auth failed` / `Unreachable`。
|
||||
|
||||
---
|
||||
|
||||
## 开始前的准备工作
|
||||
|
||||
| 设置 | 必需 | 说明 |
|
||||
| ---- | ---- | ---- |
|
||||
| `OPENHUMAN_CORE_TOKEN` | 是 | 客户端发送给 `/rpc` 的 Bearer token。用 `openssl rand -hex 32` 生成。**任何持有此 token 的人都可以驱动核心。** |
|
||||
| `BACKEND_URL` | 是 | 核心通信的 Tinyhumans 后端(生产环境为 `https://api.tinyhumans.ai`)。 |
|
||||
| `OPENHUMAN_APP_ENV` | 否 | `production` 或 `staging`。默认 `production`。 |
|
||||
| `OPENHUMAN_CORE_HOST` | 否 | 容器中默认 `0.0.0.0`。 |
|
||||
| `OPENHUMAN_CORE_PORT` | 否 | 默认 `7788`。 |
|
||||
| `RUST_LOG` | 否 | `info` 足够;`debug` 用于排查。 |
|
||||
|
||||
运行中的容器暴露的端点:
|
||||
|
||||
- `GET /health`,公共存活探针。每条部署路径的健康检查都使用它。
|
||||
- `POST /rpc`,受 bearer 保护的 JSON-RPC 入口。
|
||||
- `GET /events`、`GET /ws/dictation`,公共流式通道。
|
||||
|
||||
`OPENHUMAN_WORKSPACE` 目录(容器内为 `/home/openhuman/.openhuman`)保存核心的配置、sqlite 数据库和技能状态。**在每个生产部署中将其挂载到持久卷**,否则重启时会丢失数据。
|
||||
|
||||
---
|
||||
|
||||
## 1. DigitalOcean App Platform:一键部署
|
||||
|
||||
点击下面的按钮,从本仓库的 [`.do/app.yaml`](../../.do/app.yaml) 创建一个新的 App Platform 应用:
|
||||
|
||||
[](https://cloud.digitalocean.com/apps/new?repo=https://github.com/tinyhumansai/openhuman/tree/main)
|
||||
|
||||
然后,在 App Platform UI 中,**在首次部署完成之前**:
|
||||
|
||||
1. 打开 **Settings → App-Level Environment Variables** 标签页。
|
||||
2. 将占位符 `OPENHUMAN_CORE_TOKEN` 值替换为强 secret(`openssl rand -hex 32`)。标记为 encrypted。
|
||||
3. 如果部署的是 staging,将 `OPENHUMAN_APP_ENV` 改为 `staging`,`BACKEND_URL` 改为 `https://staging-api.tinyhumans.ai`。
|
||||
4. 点击 **Save**。App Platform 用新 secret 重新部署。
|
||||
|
||||
App Platform 处理 TLS、崩溃重启、日志流式传输和 `git push` 时的滚动重新部署(在 `.do/app.yaml` 中设置 `deploy_on_push: true` 以选择加入)。
|
||||
|
||||
> **持久化说明:** App Platform Basic 不提供块存储。核心的工作区位于容器的临时文件系统中,重新部署时会丢失。如需持久存储,请附加托管数据库或升级到支持卷的套餐。参见 [Compose 路径](#3-任何-vps-通过-docker-compose)获取开箱即用持久卷的自助替代方案。
|
||||
|
||||
---
|
||||
|
||||
## 2. DigitalOcean App Platform:通过 doctl 手动部署
|
||||
|
||||
如果你不想点击 UI:
|
||||
|
||||
```bash
|
||||
# 一次性:安装 doctl 并认证。
|
||||
doctl auth init
|
||||
|
||||
# 编辑 .do/app.yaml - 将 OPENHUMAN_CORE_TOKEN 设置为真实值(或通过 --spec 配合 envsubst 在创建时传入)。然后:
|
||||
doctl apps create --spec .do/app.yaml
|
||||
|
||||
# 观察构建:
|
||||
doctl apps list
|
||||
doctl apps logs <app-id> --type build --follow
|
||||
```
|
||||
|
||||
编辑 spec 后更新现有应用:
|
||||
|
||||
```bash
|
||||
doctl apps update <app-id> --spec .do/app.yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 任何 VPS 通过 Docker Compose
|
||||
|
||||
适用于任何安装了 Docker Engine ≥ 24 和 Compose 插件的主机。DigitalOcean Droplet、Hetzner、Linode、EC2、家用服务器。
|
||||
|
||||
每个生产版本都会向 GHCR 发布多标签镜像:
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/tinyhumansai/openhuman-core:latest # 追踪最新生产切版
|
||||
docker pull ghcr.io/tinyhumansai/openhuman-core:v1.2.4 # 按 GitHub Release tag 固定
|
||||
docker pull ghcr.io/tinyhumansai/openhuman-core:1.2.4 # 按 SemVer 固定
|
||||
```
|
||||
|
||||
镜像是 `linux/amd64`。arm64 主机拉取独立 tarball,该 tarball 附在同一 GitHub Release 中(`openhuman-core-<version>-aarch64-unknown-linux-gnu.tar.gz`),或在 arm64 构建器上从源码构建镜像。
|
||||
|
||||
使用已发布镜像快速运行:
|
||||
|
||||
```bash
|
||||
docker run -d --name openhuman-core -p 7788:7788 \
|
||||
-e OPENHUMAN_CORE_TOKEN="$(openssl rand -hex 32)" \
|
||||
-e BACKEND_URL=https://api.tinyhumans.ai \
|
||||
-e OPENHUMAN_APP_ENV=production \
|
||||
-v openhuman-workspace:/home/openhuman/.openhuman \
|
||||
ghcr.io/tinyhumansai/openhuman-core:latest
|
||||
```
|
||||
|
||||
或使用仓库内的 Compose 文件(仍然从 `Dockerfile` 本地构建镜像;在 `docker-compose.yml` 中将 `image:` 字段切换为 `ghcr.io/tinyhumansai/openhuman-core:latest` 以改用已发布镜像):
|
||||
|
||||
```bash
|
||||
# 在服务器上:
|
||||
git clone https://github.com/tinyhumansai/openhuman.git
|
||||
cd openhuman
|
||||
|
||||
# 配置 secrets:
|
||||
cp .env.example .env
|
||||
# 编辑 .env - 至少填写:
|
||||
# BACKEND_URL=https://api.tinyhumans.ai
|
||||
# OPENHUMAN_CORE_TOKEN=<openssl rand -hex 32>
|
||||
# OPENHUMAN_APP_ENV=production
|
||||
|
||||
# 构建并启动:
|
||||
docker compose up -d
|
||||
|
||||
# 验证:
|
||||
docker compose ps
|
||||
curl -fsS http://localhost:7788/health
|
||||
```
|
||||
|
||||
### 无 Docker 的 Headless 安装
|
||||
|
||||
如果主机无法运行 Docker,抓取附在最新 [GitHub Release](https://github.com/tinyhumansai/openhuman/releases/latest) 中的独立 CLI tarball:
|
||||
|
||||
```bash
|
||||
# 选择匹配你主机架构的 tarball。
|
||||
ARCH="$(uname -m)"
|
||||
case "$ARCH" in
|
||||
x86_64) TARGET=x86_64-unknown-linux-gnu ;;
|
||||
aarch64) TARGET=aarch64-unknown-linux-gnu ;;
|
||||
*) echo "Unsupported arch: $ARCH"; exit 1 ;;
|
||||
esac
|
||||
VERSION=1.2.4 # 设置为你想要的版本
|
||||
curl -fsSL "https://github.com/tinyhumansai/openhuman/releases/download/v${VERSION}/openhuman-core-${VERSION}-${TARGET}.tar.gz" \
|
||||
| tar -xz -C /usr/local/bin
|
||||
openhuman-core --version
|
||||
```
|
||||
|
||||
然后在你选择的 service manager 下运行 `openhuman-core serve`(systemd、supervisord 等),使用上述相同的环境变量。
|
||||
|
||||
### Headless 自更新契约
|
||||
|
||||
Headless 部署应将 `openhuman.update_apply` 视为安全原语:它下载 release asset,将其原子地写入当前二进制文件旁边,然后返回。不会自动退出。
|
||||
|
||||
`openhuman.update_run` 遵循 `config.update.restart_strategy`:
|
||||
|
||||
- `self_replace`(默认):stage 二进制文件,发布一个进程内重启请求,让运行中的核心自行 respawn。
|
||||
- `supervisor`:stage 二进制文件并返回 `restart_requested=false`。你的外部 service manager 必须重启进程。
|
||||
|
||||
对于长运行的 Linux 服务,设置:
|
||||
|
||||
```toml
|
||||
[update]
|
||||
restart_strategy = "supervisor"
|
||||
rpc_mutations_enabled = false
|
||||
```
|
||||
|
||||
或等效的环境变量:
|
||||
|
||||
```bash
|
||||
OPENHUMAN_AUTO_UPDATE_RESTART_STRATEGY=supervisor
|
||||
OPENHUMAN_AUTO_UPDATE_RPC_MUTATIONS_ENABLED=false
|
||||
```
|
||||
|
||||
推荐的 `systemd` 配置:
|
||||
|
||||
```ini
|
||||
Restart=always
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
```
|
||||
|
||||
运维流程:
|
||||
|
||||
1. 调用 `openhuman.update_check` 发现 release。
|
||||
2. 在你的 `update.toml` 中配置 `restart_strategy = "supervisor"`(或设置 `OPENHUMAN_AUTO_UPDATE_RESTART_STRATEGY=supervisor`),以便核心 stage 新二进制文件而不尝试自行 re-exec,然后调用 `openhuman.update_apply` 或 `openhuman.update_run`。`restart_strategy` 是配置设置,不是 RPC 参数。
|
||||
3. 显式重启 unit:`systemctl restart openhuman`。
|
||||
|
||||
如果下载或 staging 失败,运行中的二进制文件会保持原位,不会请求重启。如果 staged 二进制文件在重启后被证明有问题,通过你的包管理器、镜像标签或 release artifact 恢复之前的二进制文件,然后再次重启 supervisor。
|
||||
|
||||
Compose 文件([`docker-compose.yml`](../../docker-compose.yml))将核心映射到 `:7788`,挂载命名卷 `openhuman-workspace` 用于持久化,并设置 `restart: unless-stopped` 以便主机重启后核心自动恢复。
|
||||
|
||||
### 更新
|
||||
|
||||
```bash
|
||||
git pull
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
对于暴露 RPC 的生产部署,建议禁用可变的 update RPC(`OPENHUMAN_AUTO_UPDATE_RPC_MUTATIONS_ENABLED=false`),并通过你现有的镜像标签或包管理流程执行 rollout。
|
||||
|
||||
### 日志
|
||||
|
||||
```bash
|
||||
docker compose logs -f openhuman-core
|
||||
```
|
||||
|
||||
### 轮换 bearer token
|
||||
|
||||
`OPENHUMAN_CORE_TOKEN` 是公共互联网与完整 RPC 访问之间的唯一屏障。按时间表轮换它,并在任何疑似泄露后轮换:
|
||||
|
||||
```bash
|
||||
# 1. 生成新 token 并更新服务器端 .env。
|
||||
openssl rand -hex 32 > /tmp/new-token
|
||||
sed -i.bak "s|^OPENHUMAN_CORE_TOKEN=.*|OPENHUMAN_CORE_TOKEN=$(cat /tmp/new-token)|" .env
|
||||
rm /tmp/new-token .env.bak
|
||||
|
||||
# 2. 重启容器,让新值到达核心进程。
|
||||
docker compose up -d --force-recreate openhuman-core
|
||||
|
||||
# 3. 确认运行中的容器正在使用新 token(脱敏)。
|
||||
docker compose exec openhuman-core /bin/sh -c \
|
||||
'echo -n "$OPENHUMAN_CORE_TOKEN" | head -c 8; echo "…"'
|
||||
|
||||
# 4. 更新每个桌面客户端(切换模式 → 在选择器中重新粘贴,或编辑 app/.env.local 中的 OPENHUMAN_CORE_TOKEN 然后重新启动)。仍然持有旧 token 的客户端会在下一次 /rpc 调用时收到 HTTP 401——这是预期行为,不是回归。
|
||||
```
|
||||
|
||||
对于 App Platform,在 **Settings → App-Level Environment Variables** 中执行相同操作:编辑 `OPENHUMAN_CORE_TOKEN` secret,让 App Platform 重新部署。没有单独的 token 文件需要删除;环境变量是唯一的状态。
|
||||
|
||||
### 置于 TLS 之后
|
||||
|
||||
使用 Caddy、nginx 或 Traefik 作为 `:7788` 的反向代理。最小 `Caddyfile`:
|
||||
|
||||
```caddy
|
||||
core.example.com {
|
||||
reverse_proxy localhost:7788
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 将桌面应用指向托管核心
|
||||
|
||||
在桌面应用的环境文件(`app/.env.local`)中:
|
||||
|
||||
```bash
|
||||
# 使用托管核心而不是生成本地 sidecar。
|
||||
OPENHUMAN_CORE_RUN_MODE=external
|
||||
OPENHUMAN_CORE_RPC_URL=https://core.example.com/rpc
|
||||
OPENHUMAN_CORE_TOKEN=<你在服务器上设置的相同 token>
|
||||
```
|
||||
|
||||
对于没有公共 IP 的私有 tailnet-only VM,改用 tailnet URL:
|
||||
|
||||
```bash
|
||||
OPENHUMAN_CORE_RUN_MODE=external
|
||||
OPENHUMAN_CORE_RPC_URL=http://100.x.x.x:7788/rpc
|
||||
OPENHUMAN_CORE_TOKEN=<你在服务器上设置的相同 token>
|
||||
```
|
||||
|
||||
重启桌面应用。`App.tsx` 中的 provider 链会将所有 RPC 调用路由到远程核心;其他一切不变。公共 `http://` 主机被应用选择器拒绝;对任何可从公共网络访问的核心使用 HTTPS。
|
||||
|
||||
---
|
||||
|
||||
## 命名卷所有权与 Docker entrypoint
|
||||
|
||||
Docker 默认创建 `root:root` 拥有的命名卷。因为核心以非 root `openhuman` 用户(UID 10001)运行,banner 之后的首次写入——`init_rpc_token → write_token_file` 到 `$OPENHUMAN_WORKSPACE`——如果没有先修复所有权,会抛出 `Permission denied (os error 13)`。
|
||||
|
||||
镜像在 `/usr/local/bin/docker-entrypoint-core.sh` 附带一个专用 entrypoint,它:
|
||||
|
||||
1. 以 `root` 启动。
|
||||
2. 对 `$OPENHUMAN_WORKSPACE` 和 `$HOME/.openhuman`(`OPENHUMAN_CORE_TOKEN` 未设置时 `core.token` 被写入的目录)运行 `mkdir -p` + `chown openhuman:openhuman`。
|
||||
3. 调用 `exec gosu openhuman openhuman-core "$@"` 来降权并移交控制权给二进制文件。
|
||||
|
||||
这是**幂等的**:在新创建的卷上,chown 会修复 root 拥有的目录;在已经修复过的卷上,chown 是 no-op。从早于该修复的镜像升级时,不需要手动执行 `docker volume rm`。
|
||||
|
||||
该 entrypoint 名为 `docker-entrypoint-core.sh`,仅接入根 `Dockerfile`。E2E 镜像(`e2e/docker-entrypoint.sh`)不受影响。
|
||||
|
||||
---
|
||||
|
||||
## 4. Fly.io
|
||||
|
||||
[Fly.io](https://fly.io) 非常适合 `openhuman-core`:它自动处理 TLS,在所有套餐上支持持久卷,并且可以自动停止空闲机器以削减成本。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 安装并认证 [flyctl](https://fly.io/docs/flyctl/install/)(`fly auth login`)
|
||||
- Fly.io 账户
|
||||
|
||||
### 步骤 1 —— 启动应用
|
||||
|
||||
```bash
|
||||
fly launch --no-deploy --config .fly/fly.toml
|
||||
```
|
||||
|
||||
Fly.io 自动检测 `Dockerfile`。选择靠近你用户的区域,并在提示时跳过首次部署。这会生成一个配置文件。
|
||||
|
||||
### 步骤 2 —— 配置 `.fly/fly.toml`
|
||||
|
||||
仓库在 [`.fly/fly.toml`](../../.fly/fly.toml) 附带一个模板。用 `fly launch` 期间选择的值填充 `<your-app-name>` 和 `<your-region>`:
|
||||
|
||||
```toml
|
||||
app = '<your-app-name>'
|
||||
primary_region = '<your-region>'
|
||||
|
||||
[build]
|
||||
dockerfile = "Dockerfile"
|
||||
|
||||
[env]
|
||||
OPENHUMAN_CORE_HOST = "0.0.0.0"
|
||||
OPENHUMAN_CORE_PORT = "7788"
|
||||
OPENHUMAN_WORKSPACE = "/home/openhuman/.openhuman"
|
||||
RUST_LOG = "info"
|
||||
|
||||
[[mounts]]
|
||||
source = "openhuman_workspace"
|
||||
destination = "/home/openhuman/.openhuman"
|
||||
|
||||
[http_service]
|
||||
internal_port = 7788
|
||||
force_https = true
|
||||
auto_stop_machines = 'stop'
|
||||
auto_start_machines = true
|
||||
# min_machines_running = 0 在空闲时完全停止机器(最便宜),但
|
||||
# 空闲后的第一个请求需要支付冷启动惩罚(容器启动 +
|
||||
# Rust 二进制文件初始化——数秒)。设为 1 可保持一台机器热备。
|
||||
min_machines_running = 0
|
||||
processes = ['app']
|
||||
|
||||
[[http_service.checks]]
|
||||
interval = "30s"
|
||||
timeout = "5s"
|
||||
grace_period = "10s"
|
||||
method = "GET"
|
||||
path = "/health"
|
||||
|
||||
[[vm]]
|
||||
memory = '1gb'
|
||||
cpus = 1
|
||||
```
|
||||
|
||||
### 步骤 3 —— 创建持久卷
|
||||
|
||||
```bash
|
||||
fly volumes create openhuman_workspace --size 5 --region <your-region> --config .fly/fly.toml
|
||||
```
|
||||
|
||||
**将工作区挂载到持久卷**,否则每次重新部署都会丢失数据。
|
||||
|
||||
### 步骤 4 —— 设置 secrets
|
||||
|
||||
```bash
|
||||
# 必需
|
||||
fly secrets set OPENHUMAN_CORE_TOKEN="$(openssl rand -hex 32)"
|
||||
fly secrets set BACKEND_URL="https://api.tinyhumans.ai"
|
||||
fly secrets set OPENHUMAN_APP_ENV="production"
|
||||
|
||||
# 建议——任何可公开访问的部署:
|
||||
fly secrets set OPENHUMAN_AUTO_UPDATE_RPC_MUTATIONS_ENABLED="false"
|
||||
fly secrets set OPENHUMAN_AUTO_UPDATE_RESTART_STRATEGY="supervisor"
|
||||
|
||||
# 可选——错误报告和 analytics:
|
||||
fly secrets set OPENHUMAN_CORE_SENTRY_DSN="https://<key>@o<org>.ingest.sentry.io/<project>"
|
||||
fly secrets set OPENHUMAN_ANALYTICS_ENABLED="true"
|
||||
```
|
||||
|
||||
保存 `OPENHUMAN_CORE_TOKEN` 的值——你稍后需要它来连接桌面应用。**任何持有此 token 的人都可以驱动核心**;像密码一样对待它,并在任何疑似泄露后通过 `fly secrets set OPENHUMAN_CORE_TOKEN="$(openssl rand -hex 32)"` 轮换。
|
||||
|
||||
### 步骤 5 —— 部署
|
||||
|
||||
```bash
|
||||
fly deploy --config .fly/fly.toml
|
||||
```
|
||||
|
||||
验证核心健康:
|
||||
|
||||
```bash
|
||||
curl -fsS https://<your-app-name>.fly.dev/health
|
||||
```
|
||||
|
||||
### 步骤 6 —— 将桌面应用指向托管核心
|
||||
|
||||
在 `app/.env.local` 中:
|
||||
|
||||
```bash
|
||||
OPENHUMAN_CORE_RUN_MODE=external
|
||||
OPENHUMAN_CORE_RPC_URL=https://<your-app-name>.fly.dev/rpc
|
||||
OPENHUMAN_CORE_TOKEN=<你在步骤 4 中设置的 token>
|
||||
```
|
||||
|
||||
或使用桌面应用中的**首次运行选择器**(Core RPC URL + token 字段,带**测试连接**按钮)来配置,无需编辑文件。
|
||||
|
||||
### 持续部署
|
||||
|
||||
要在每次推送到 `main` 时自动重新部署,在 `.github/workflows/fly-deploy.yml` 添加 workflow 文件:
|
||||
|
||||
```yaml
|
||||
name: Fly Deploy
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
- 'Dockerfile'
|
||||
- '.fly/fly.toml'
|
||||
- 'scripts/docker-entrypoint-core.sh'
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy openhuman-core
|
||||
runs-on: ubuntu-latest
|
||||
concurrency: deploy-group
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# 将 Fly action 固定到带标签的 release(或完整 commit SHA),而不是 @master——
|
||||
# 追踪移动分支意味着信任未来推送到那里的每个 commit,包括被入侵的维护者账户所做的任何 commit。
|
||||
- uses: superfly/flyctl-actions/setup-flyctl@1.5
|
||||
- run: flyctl deploy --remote-only --config .fly/fly.toml
|
||||
env:
|
||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
|
||||
```
|
||||
|
||||
用 `fly tokens create deploy` 生成部署 token,并将其作为名为 `FLY_API_TOKEN` 的仓库 secret 添加。
|
||||
|
||||
### 更新
|
||||
|
||||
```bash
|
||||
fly deploy --config .fly/fly.toml
|
||||
```
|
||||
|
||||
对于固定版本的部署,在 `.fly/fly.toml` 中更新镜像标签并重新部署:
|
||||
|
||||
```toml
|
||||
[build]
|
||||
image = "ghcr.io/tinyhumansai/openhuman-core:v1.2.4"
|
||||
```
|
||||
|
||||
### 日志
|
||||
|
||||
```bash
|
||||
fly logs --config .fly/fly.toml
|
||||
```
|
||||
|
||||
### 已知陷阱 —— 卷上的 UID 不匹配
|
||||
|
||||
如果你在从 `Dockerfile` 构建(创建 UID 10001 的 `openhuman` 用户)和拉取预构建的 GHCR 镜像(使用 UID 1000)之间切换,已写入持久卷的文件会被旧 UID 拥有,并在启动时产生 `Permission denied (os error 13)`。
|
||||
|
||||
通过 SSH 进入并重新拥有工作区来修复:
|
||||
|
||||
```bash
|
||||
fly ssh console --config .fly/fly.toml
|
||||
chown -R openhuman:openhuman /home/openhuman/.openhuman/
|
||||
exit
|
||||
fly machine restart --config .fly/fly.toml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 冒烟测试
|
||||
|
||||
仓库附带 [`.github/workflows/deploy-smoke.yml`](../../.github/workflows/deploy-smoke.yml),它在每次触及部署工件的 PR 上运行。它构建 Docker 镜像、启动它并轮询 `/health`,以便云部署路径中的回归在到达 `main` 之前就在 CI 中失败。
|
||||
|
||||
该 workflow 包含两个 job:
|
||||
|
||||
- **`docker-image`**——设置 `OPENHUMAN_CORE_TOKEN` 且不挂载卷。保护 DigitalOcean App Platform 路径(`.do/app.yaml`),其中 token 始终预置且不挂载持久卷。
|
||||
- **`docker-volume-permissions`**——省略 `OPENHUMAN_CORE_TOKEN` 并在 `/home/openhuman/.openhuman` 挂载一个新的匿名卷。复现 issue #2065 的确切失败模式,并断言 `/health` 返回 200 且日志中不存在 `Permission denied (os error 13)`。
|
||||
|
||||
要在本地运行相同的检查:
|
||||
|
||||
```bash
|
||||
docker build -t openhuman-core:smoke .
|
||||
|
||||
# Token-set 路径(App Platform):
|
||||
docker run -d --name oh-smoke -p 7788:7788 \
|
||||
-e OPENHUMAN_CORE_TOKEN=smoke-test-token \
|
||||
openhuman-core:smoke
|
||||
curl -fsS http://localhost:7788/health
|
||||
docker rm -f oh-smoke
|
||||
|
||||
# Fresh-volume / no-token 路径(Docker Compose、VPS):
|
||||
docker volume create oh-vol-test
|
||||
docker run -d --name oh-vol-smoke -p 7789:7788 \
|
||||
-v oh-vol-test:/home/openhuman/.openhuman \
|
||||
openhuman-core:smoke
|
||||
curl -fsS http://localhost:7789/health
|
||||
docker rm -f oh-vol-smoke
|
||||
docker volume rm oh-vol-test
|
||||
```
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
description: 持久化的、工具作用域规则,用于安全关键型指引和学习成果。
|
||||
icon: shield-check
|
||||
lang: zh-CN
|
||||
---
|
||||
|
||||
# 工具级记忆
|
||||
|
||||
工具级记忆层捕获关于智能体应如何使用特定工具的**可执行指引**——它与[记忆工具](memory-tools.zh-CN.md)的通用召回不同,也与 `tool_effectiveness` 统计命名空间相区别。它是把"永远不要给 Sarah 发邮件"转化为智能体在每一轮后续中都必须遵守的硬约束的表面。
|
||||
|
||||
它实现了 [issue #1400](https://github.com/tinyhumansai/openhuman/issues/1400)——一个用于持久化学习成果和高优先级规则的一流存储与检索系统。
|
||||
|
||||
## 存储内容
|
||||
|
||||
每个工具都有自己的命名空间 **`tool-{tool_name}`**,与 `global`、`skill-{id}` 以及仅用于统计的 `tool_effectiveness` 命名空间相区分。在其内部,每条记录都是一个 `ToolMemoryRule`:
|
||||
|
||||
| 字段 | 用途 |
|
||||
| ---- | ---- |
|
||||
| `id` | 每条规则的稳定 UUID。Upsert 会复用相同 id。 |
|
||||
| `tool_name` | 规则适用的工具(例如 `send_email`、`shell`)。 |
|
||||
| `rule` | 智能体必须遵循的自然语言指引。 |
|
||||
| `priority` | `critical`、`high` 或 `normal`。驱动检索 + 压缩策略。 |
|
||||
| `source` | `user_explicit`、`post_turn` 或 `programmatic`——来源。 |
|
||||
| `tags` | 自由标签(`safety`、`permission`……)。 |
|
||||
| `created_at` / `updated_at` | RFC3339 时间戳。 |
|
||||
|
||||
统计(`tool_effectiveness/tool/{name}`)和规则(`tool-{name}/rule/{id}`)按设计位于*不同*的命名空间——一个追踪"发生了什么",另一个追踪"对此该做什么"。
|
||||
|
||||
## 优先级层级
|
||||
|
||||
| 优先级 | 存储位置 | 抗压缩? |
|
||||
| ------ | -------- | -------- |
|
||||
| `critical` | 通过 `ToolMemoryRulesSection` 钉入**系统提示**。 | **是**——系统提示按 session 冻结,不会被 mid-session 压缩器重写。 |
|
||||
| `high` | 同一块系统提示中,排在 critical 之后。 | **是**——机制相同。 |
|
||||
| `normal` | 存储在命名空间中;通过 `memory_recall` 按需检索。 | 否——与任何其他命名空间记忆一样可被压缩。 |
|
||||
|
||||
抗压缩属性是结构性的:critical 和 high 规则 riding 在*系统提示*中,而推理后端的 prefix cache 会在整个 session 期间保持其冻结。没有任何方式能让 token 压缩静默丢弃一条 `critical` 规则。
|
||||
|
||||
## 捕获流水线
|
||||
|
||||
每轮之后有两条自动捕获路径触发(通过 `ToolMemoryCaptureHook`):
|
||||
|
||||
1. **用户指令**——用户消息中的 `never <verb> <noun>`、`don't <verb> ...`、`do not <verb> ...` 或 `stop <verb>ing ...` 等句子会被提升为匹配工具的 **Critical** 规则。通用名词别名将 `"email"` 映射到名为 `send_email` 的工具,`"shell"` 映射到 `bash`/`exec` 等;当没有别名匹配时,规则会落在该轮次中第一个运行的工具上,使其保持在相关调用现场附近。
|
||||
2. **重复工具失败**——在一轮中失败两次或以上的工具会获得一条 **Normal** 优先级的观察记录,失败类别被内联摘要,以便智能体下次考虑该工具时有上下文。
|
||||
|
||||
当学习子系统开启时,该 hook 默认启用。用 `OPENHUMAN_LEARNING_TOOL_MEMORY_CAPTURE_ENABLED=0` 选择性禁用。
|
||||
|
||||
## 工具选择时的检索
|
||||
|
||||
在 session 开始时,harness 通过 `ToolMemoryStore::rules_for_prompt` 预取每条 Critical 和 High 规则,将它们渲染到 `## Tool-scoped rules` 块中,并把该块钉入系统提示。因为提示在 session 生命周期内被冻结,这些规则在每一轮的工具选择时——以及任何实际工具执行之前——都是可见的。
|
||||
|
||||
低优先级指引不占用提示预算;智能体通过针对 `tool-{name}` 命名空间调用 `memory_recall` 按需获取它们。
|
||||
|
||||
## RPC 表面
|
||||
|
||||
`memory` 命名空间下暴露六个方法:
|
||||
|
||||
| 方法 | 用途 |
|
||||
| ---- | ---- |
|
||||
| `memory.tool_rule_put` | Upsert 一条规则。对安全关键型条目使用 `priority='critical'`。 |
|
||||
| `memory.tool_rule_get` | 通过 `(tool_name, id)` 获取一条规则。 |
|
||||
| `memory.tool_rule_list` | 列出某工具的所有规则,按优先级 + 新鲜度排序。 |
|
||||
| `memory.tool_rule_delete` | 删除一条规则。 |
|
||||
| `memory.tool_rules_for_prompt` | 返回渲染后的 Markdown 块 + 结构化快照——session builder 所钉入的内容。 |
|
||||
| `memory.tool_rules_json` | 原始 JSON 列表(供信封消费者使用)。 |
|
||||
|
||||
JSON payload 使用 snake_case(`priority: "critical"`、`source: "user_explicit"`)。每个方法都经过与其他记忆 RPC 相同的 `active_memory_client` 管道。
|
||||
|
||||
## 端到端安全场景
|
||||
|
||||
"永远不要给 Sarah 发邮件"路径已被回归测试覆盖:
|
||||
|
||||
1. 用户在调用了 `send_email` 的轮次中说 *"Never email Sarah at sarah@example.com."*
|
||||
2. `ToolMemoryCaptureHook` 提取该指令,将 `email` 别名映射到 `send_email` 工具,并在 `tool-send_email/rule/{uuid}` 下写入一条 Critical 规则。
|
||||
3. 在下一个 session 中,`prefetch_tool_memory_rules_blocking` 拉取每条 Critical 和 High 规则,session builder 将 `ToolMemoryRulesSection` 追加到系统提示。
|
||||
4. 智能体在选择工具之前就看到 `### \`send_email\`` 后跟 `- **[critical]** Never email Sarah at sarah@example.com.`,并且该规则在任何 mid-session token 压缩中都能存活。
|
||||
|
||||
覆盖率与集成测试位于 `src/openhuman/memory/tool_memory/`。
|
||||
|
||||
## 另请参阅
|
||||
|
||||
- [记忆工具](memory-tools.zh-CN.md)——通用 `recall`、`store`、`forget`。
|
||||
- [智能 Token 压缩](../token-compression.zh-CN.md)——系统提示被保护免受的内容。
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
description: >-
|
||||
诊断登录失败、OAuth 回调无法完成以及远程核心 RPC 认证问题。
|
||||
icon: key
|
||||
lang: zh-CN
|
||||
---
|
||||
|
||||
# 登录故障排查
|
||||
|
||||
当社交登录卡住、返回欢迎界面,或核心日志中出现未授权的 `/auth` 请求时,使用此 checklist。
|
||||
|
||||
## 检查后端可达性
|
||||
|
||||
从桌面应用所在的同一网络,验证公共 OpenHuman 端点:
|
||||
|
||||
```bash
|
||||
curl -I https://tinyhumans.ai/
|
||||
curl -I https://api.tinyhumans.ai/health
|
||||
```
|
||||
|
||||
如果网站能加载但 API 端点失败,桌面应用可能无法将 OAuth 回调兑换为 session。在 issue 报告中记录 HTTP 状态码、区域和 DNS 结果。
|
||||
|
||||
## 检查所选核心
|
||||
|
||||
如果你使用**高级**远程核心模式,在开始 OAuth 之前确认 RPC URL 和 bearer token:
|
||||
|
||||
```bash
|
||||
curl -sS https://your-core.example/rpc \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer CORE_TOKEN" \
|
||||
-d '{"jsonrpc":"2.0","id":1,"method":"core.ping","params":{}}'
|
||||
```
|
||||
|
||||
`401` 响应表示桌面 token 与远程核心 token 不匹配。在重试 Google 或 GitHub 登录之前先修复这个问题。
|
||||
|
||||
## 检查深度链接回调
|
||||
|
||||
成功的桌面 OAuth 以 `openhuman://auth?...` 回调结束。如果浏览器显示了该 URL 但应用仍停留在欢迎界面:
|
||||
|
||||
1. 确保只运行了一个 OpenHuman 桌面实例。
|
||||
2. 重启应用,保持相同的远程核心设置,并重试登录。
|
||||
3. 如果使用远程核心,检查核心是否收到 `openhuman.auth_store_session`。
|
||||
|
||||
对于远程核心,临时手动注入可以确认核心本身是正常的:
|
||||
|
||||
```bash
|
||||
curl -sS https://your-core.example/rpc \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer CORE_TOKEN" \
|
||||
-d '{"jsonrpc":"2.0","id":1,"method":"openhuman.auth_store_session","params":{"token":"JWT_FROM_CALLBACK"} }'
|
||||
```
|
||||
|
||||
不要把真实 JWT 粘贴到公共 GitHub issue 中。对 token 进行脱敏处理,只附加状态码、主机名、应用版本、操作系统和相关日志行。
|
||||
|
||||
## Bug 报告中应包含的内容
|
||||
|
||||
* 应用版本和操作系统。
|
||||
* 核心模式是本地还是远程。
|
||||
* RPC URL 主机、脱敏后的 token 状态和 `core.ping` 结果。
|
||||
* 使用的 OAuth 提供商。
|
||||
* 浏览器中是否出现了 `openhuman://auth` URL。
|
||||
* 如果存在,第一条未授权日志行。
|
||||
Reference in New Issue
Block a user