给 IT 团队的 Potato 服务器要求
请把本页转发给你的 IT 或科研计算团队。Potato 以单个进程运行,需要可写磁盘和 HTTPS,并把每条标注保存在这台服务器上。
Potato 是用于标注研究的自托管网络应用。一个 Potato 进程服务一项研究,把所有标注保存在研究文件夹内的文件和 SQLite 数据库里,并需要在该进程前面放置一个反向代理来提供 HTTPS。默认安装不会向外部服务发送任何数据,数据只通过研究配置所开启的功能离开服务器。
本页写给大学、医院或公司里负责运行服务器的人。想让别人托管研究的研究人员,可以把本页原样发出,同时附上项目文件夹和下一节列出的答案。没有 IT 团队时研究人员能走的路线,见把你的 Potato 研究放到网上。
研究团队需要提供的信息
研究团队会发来项目文件夹,研究设计器以 Download project (.zip) 的形式导出它。文件夹里有 config.yaml(研究的配置)以及配置中提到的数据文件。除文件夹之外,研究团队还要回答这些问题:
- 有多少位标注者参加,标注者是用机构账号登录,还是用研究人员列出的用户名登录?
- 研究何时开始,何时结束?
- 条目或答案中是否含有个人信息或敏感信息,研究是依据哪份伦理批准进行的?
- 配置是否使用了会把条目发送给模型提供商的 AI 功能?
config.yaml中的ai_support、chat_support和solo_mode等块标志着这类功能。
软件
Potato 是基于 Flask 构建的 Python 网络应用。最简单的安装方式是已发布的 Docker 镜像 ghcr.io/davidjurgens/potato,为 linux/amd64 和 linux/arm64 构建。镜像约 900 MB,在 gunicorn 下运行 Python 3.11,以无特权用户 potato(uid 1000)运行,监听 7860 端口。项目文件夹挂载到 /app:
docker run -d --name potato-study --restart unless-stopped \
-p 127.0.0.1:8000:7860 \
-v /srv/potato/my-study:/app \
-e POTATO_SECRET_KEY="$(openssl rand -hex 32)" \
ghcr.io/davidjurgens/potato:2.14.0这条命令只在主机回环接口的 8000 端口发布研究,反向代理因此成为唯一的入口。latest 标签跟随 Potato 的开发分支,研究应当固定一个版本标签。POTATO_SECRET_KEY 只生成一次,并保存这个值,因为每次启动都换新密钥会让每位标注者在每次重启时退出登录。当 /app 不能被 uid 1000 写入时,容器会拒绝启动。请用 --user "$(id -u):$(id -g)" 运行容器,或者把文件夹交给 uid 1000。镜像的标签和环境变量见在 Docker 中运行 Potato。
不用 Docker 时,在 Python 3.9 或更新版本上用 pip install potato-annotation 从 PyPI 安装 Potato。备份和单点登录需要加上 hosting 可选依赖,SAML 登录需要 saml 可选依赖,它依赖系统的 libxmlsec1 来构建。potato start config.yaml 运行的是开发服务器,每次响应之后都会关闭连接。对于通过互联网工作的标注者,请按生产部署所示在 gunicorn 下运行 Potato,或者在 potato start 前面放一个反向代理。
进程和工作进程数
Potato 把条目池、分配队列和每位标注者的状态都放在一个进程的内存里。第二个 gunicorn 工作进程或第二个副本,会拿到这三者各自独立的一份,把第一个进程已经分配出去的条目再发一遍,并覆盖第一个进程的保存。因此,GUNICORN_WORKERS 大于 1 时镜像会拒绝启动。要获得更高的并发,请把 GUNICORN_THREADS 从默认的 8 调高。Potato 自己的 Docker 文档认为,八个线程足以应付典型研究中同时在线的几十位标注者。要运行多项研究,就运行多个容器,每个容器使用不同的文件夹和端口。
服务器规模
内存随条目数增长。Potato 的测量显示,每个条目在内存中占 0.9 到 1.4 KB,在 Potato 的参考数据集上,服务器使用了约 365 MB。Potato 自己的云命令 potato deploy 在大多数主机上选择 1 或 2 个 vCPU、2 GB 内存的机器,这是一个合理的起始规格。
磁盘必须持久且可写,并且要有空间存放数据文件、条目所含的媒体文件以及标注。如果容器的磁盘在重启时被清空,而下文介绍的内置备份又没有开启,每一条标注都会丢失。
网络访问与 HTTPS
通过 nginx、Apache 或 Caddy 这样的反向代理来暴露 Potato。代理持有 TLS 证书,并把请求转发到容器的端口。只需要向互联网开放 443 端口,以及用于重定向和证书续期的 80 端口。反向代理介绍了如何在 https://host.example.edu/my-study/ 这样的 URL 路径下提供 Potato,方法是带 X-Forwarded-Prefix 请求头的 POTATO_PROXY_FIX=1,或者 POTATO_URL_PREFIX。实时智能体查看器使用服务器发送事件(server-sent events)进行流式传输。如果研究使用这些查看器,请在 /api/ 位置上关闭代理缓冲。
GET /health 不需要认证,服务器就绪后返回 {"status": "ok"},服务器仍在加载数据时返回 503。请把负载均衡器或监控检查指向这个路由。
Potato 默认不需要任何出站网络访问。标注者的浏览器所加载的脚本、样式表和字体都来自服务器本身,气隙与离线部署列出了 Potato 如何让默认安装保持离线。只有配置开启的功能才需要出站访问:
- AI 功能会调用配置所指定的模型端点,除非该端点是 Ollama 这样的本地模型服务器。
- 备份会写入配置所指定的 Hugging Face 数据集或 S3 存储桶。
- 单点登录会联系身份提供方。
- 远程数据源会读取配置所指定的存储桶或数据库。
- 通过 URL 链接到媒体的条目,会让标注者的浏览器从该 URL 获取媒体。
Potato 写入的文件
Potato 把所有内容都写入研究的文件夹:
| 项目文件夹中的路径 | 内容 |
|---|---|
<output_annotation_dir>/<username>/user_state.json | 每位标注者的答案,每次保存都整份重写 |
user_config.json,位于输出文件夹旁边 | 账号列表,其中的密码是加盐的 PBKDF2-SHA256 哈希 |
project.sqlite、datasets.sqlite | 备忘、编码本和审阅状态等项目数据,使用 SQLite 的 WAL 模式 |
admin_api_key.txt | 未设置密钥时生成的管理员密钥,只有服务器的用户可读 |
potato.log | 服务器日志 |
因此一个目录就包含整项研究。output_annotation_dir 是 config.yaml 中的一项设置,研究设计器把它的值设为 output。
备份
对项目文件夹做夜间备份,就能涵盖标注文件和账号列表。两个 SQLite 文件请用 SQLite 自己的备份命令复制,不要当作普通文件复制,因为 WAL 模式下的数据库文件副本可能已损坏,或者缺少最近的写入:
sqlite3 /srv/potato/my-study/project.sqlite ".backup /backups/my-study/project.sqlite"Potato 内置的备份每五分钟把标注输出和数据库快照复制到兼容 S3 的存储桶或私有的 Hugging Face 数据集。启动时,如果磁盘为空,备份会从存储桶恢复,并且绝不会覆盖磁盘上已有的标注。下面的块把副本发送到大学的对象存储。把这个块加入研究的 config.yaml,并在容器的环境中设置 POTATO_S3_ACCESS_KEY_ID 和 POTATO_S3_SECRET_ACCESS_KEY:
backup:
schedule_minutes: 5
restore_on_boot: true
sinks:
- type: s3
bucket: research-backups
prefix: potato/my-study
endpoint_url: https://s3.example.edu已发布的镜像包含备份客户端。用 pip 安装则需要 hosting 可选依赖。
登录方式
Potato 支持多种让标注者登录的方式,在 config.yaml 中设置:
| 方式 | 适合 | 服务器需要什么 |
|---|---|---|
| 带密码的具名账号(默认) | 已知的标注者名单 | user_config.users 列出用户名,并设置 allow_all_users: false。Potato 会拒绝不在名单上的人注册 |
仅用户名(require_password: false) | 低风险任务和课堂练习 | 无需其他条件。用户名名单仍然适用 |
| OAuth 和 OpenID Connect | Google、GitHub 或 Hugging Face 账号,或者 Microsoft Entra ID、Okta 等任何 OpenID Connect 提供方 | 一个客户端 ID 和密钥,以及在提供方处注册的回调 URL https://<host>/auth/callback/<provider> |
| SAML 2.0 | Shibboleth、ADFS,以及 Okta 和 Microsoft Entra 中的 SAML 应用 | saml 可选依赖,或使用 --build-arg POTATO_EXTRAS=saml 构建的镜像。Potato 在 /saml/metadata 提供服务提供方元数据,并在 /saml/acs 接收断言 |
| 来自众包平台的链接 | Prolific、SONA、CloudResearch Connect 以及类似平台 | 指向服务器的平台研究 URL |
密码使用 PBKDF2-SHA256 做哈希,迭代 100,000 次,每个用户有独立的盐。忘记的密码通过 Potato 显示在屏幕上的一次性链接重置,Potato 不发送任何电子邮件。在终止 TLS 的代理之后,请把 SAML 的 sp_base_url 设为公开地址,否则身份提供方的响应会通不过 Potato 的目标检查。每种方式的配置见 SSO 与 OAuth、密码管理和众包集成。
管理员访问
/admin 的管理面板显示进度、一致性和每位标注者的答案,并可下载结果。面板用管理员密钥打开。请通过 POTATO_ADMIN_API_KEY 设置密钥,或者让 Potato 在首次使用时在项目文件夹中生成 admin_api_key.txt,再通过你所在机构信任的渠道把密钥交给研究人员。在任何服务器上,config.yaml 都要保持 debug: false,因为调试模式会让看起来像本地发来的请求不用密钥就能打开管理页面,而同一台机器上的代理可能让每个请求都看起来像本地请求。
数据保护
Potato 是自托管的,因此标注、账号列表和条目都留在你运行的服务器上。Potato 不含任何分析或使用情况上报,默认安装不会建立任何出站连接。数据只通过网络访问与 HTTPS下列出的功能离开服务器,其中每一项都必须由研究的配置开启并指定目的地。
请对照研究的伦理批准检查三点:
- 标注文件以用户名标识每位标注者,所以用电子邮件地址作用户名,会把电子邮件地址放进数据里。
- 研究中的调查页面可能收集个人信息,答案与其他内容一起放在每位标注者各自的文件里。
- 当配置把条目发送给托管的模型提供商时,条目会在该提供商的条款下离开服务器。
研究结束时,研究团队从管理面板下载结果。请与团队商定服务器上的副本保留多久,并在那个日期之后删除项目文件夹和所有备份存储桶。
延伸阅读
- 在 Docker 中运行 Potato,介绍镜像的标签、环境变量和文件所有权
- 生产部署,介绍 gunicorn、nginx、Apache 和 Docker Compose
- 反向代理,介绍如何在 URL 路径下提供 Potato
- 气隙与离线部署,适用于没有出站访问的网络
- 把你的 Potato 研究放到网上,同一过程中研究人员这一侧