サーバー

Paprika を動かすサーバー側(Hub・Worker)のセットアップ・運用ガイド。 操作する側(SDK)の使い方は はじめに へ。

3 つの構成パターン

サーバーをどう配置するか。用途に合わせて選びます(後で組み替えも可能、例: A → B にスケールアップ)。

図にすると:

A.  [host]   redis ── hub ── worker (Chrome × N lanes)
              http://localhost:8000


B.  [hub-host]   redis ── hub  ── WebSocket ── [worker-host #1]  worker
                  http://paprika.lan:8000                        worker
                                                  ── WebSocket ── [worker-host #2]  worker
                                                  …
                                                  ── WebSocket ── [worker-host #N]  worker


C.  [dev-host]   python -m server --mode hub   (Worker は CLI / 別 Docker / なし)
                  http://localhost:8000
A. 1 台B. 分散C. Hub のみ
主用途お試し・小〜中規模本番・並列を増やす開発・検証
必要なものDockerDocker(各ホスト)Python 3.10+
起動コマンドdocker compose up -d --build同上 + 各 worker で -f docker-compose-worker.yml up -dpython -m server --mode hub
Redis同梱(自動)同梱(自動)任意(無ければインメモリ)

インストール・起動

A. 1 台で全部(Docker Compose)

git clone https://github.com/paps-jp/paprika.git
cd paprika
cp .env.example .env
docker compose up -d --build

起動後:

前提: Docker + Docker Compose v2、Linux ホスト推奨。1 レーンあたり ~500MB–1GB RAM(既定 2 レーン)。 LLM 機能(codegen / vision-agent / page.agent)を使うときだけ OpenAI 互換の LLM エンドポイントが必要、Fetch だけなら不要。

B. ハブ + リモート worker(分散)

① ハブホスト:A と同じ手順で立ち上げる。ただし他マシンからアクセスするので .env で公開アドレスを設定:

# .env (ハブホスト)
NOVNC_PUBLIC_HOST=paprika.lan            # 管理画面の noVNC リンクが指すホスト名/IP
PUBLIC_BASE_URL=http://paprika.lan:8000  # worker がアセットを POST する先
LANE_POOL=2                              # ローカル worker のレーン数(不要なら 0)

② worker ホスト(追加したい台数だけ):

git clone https://github.com/paps-jp/paprika.git /opt/paprika
cd /opt/paprika && cp .env.example .env
# .env を編集:
#   HUB_URL=ws://paprika.lan:8000
#   NOVNC_PUBLIC_HOST=<このホストの LAN IP/DNS>
docker compose -f docker-compose-worker.yml up -d

③ 接続確認:ハブの GET /workers(管理画面の「ワーカー」タブでも可)に alive=true で現れれば OK。

スケールアップ 手順 ② を別のホストで繰り返すだけでワーカーが増えます。クローン時の worker_id 衝突や noVNC ホスト名は Hub が自動補正するので、ホスト名の被りは気にしなくて大丈夫(→ フリート運用)。

C. Hub のみ(CLI/開発)

Docker を使わず Python から直接起動できます。SDK の開発や軽い検証向けです。

git clone https://github.com/paps-jp/paprika.git
cd paprika
python -m venv .venv && source .venv/bin/activate   # 任意
pip install -r requirements.txt

# ハブのみ起動(最小・インメモリ)
python -m server --mode hub --host 0.0.0.0 --port 8000

# Redis 永続化付き
python -m server --mode hub --port 8000 \
  --redis-url redis://localhost:6379 \
  --data-dir ./data/jobs

Worker も CLI で立てる場合:

# 別ターミナルで(要: Chrome + Xvfb + noVNC = Linux)
python -m server --mode worker \
  --hub-url ws://localhost:8000 \
  --lane-pool 1 \
  --novnc-public-host localhost --novnc-base-port 6080
Worker は Docker 推奨 Worker は実ブラウザを動かすため、--lane-pool 使用時は Chrome + Xvfb + noVNC を自前で用意する必要があります。本格運用では Docker 版が圧倒的に楽です(依存込みで一発)。

主な CLI オプション:

オプション対象説明
--mode hub / worker / all動作モード(all = hub 既定)
--host / --porthubバインドアドレス / ポート(既定 0.0.0.0 / 8000)
--redis-urlhubRedis DSN。無指定はインメモリ
--data-dirhubジョブ作業ディレクトリ(既定 ./data/jobs)
--public-base-urlhubworker に教える公開 URL
--reloadhubコード変更で自動リロード(開発用)
--hub-urlworker接続先ハブの WS URL
--lane-pool Nworkerレーン(専用 Chrome+noVNC)を N 本起動
--novnc-public-host / --novnc-base-portworkernoVNC の公開ホスト / 先頭ポート
--labels K=V,…workerルーティング用ラベル
--worker-secretbothworker↔hub 認証の共有シークレット(両側一致)
LAN 内信頼前提 Hub は codegen 用に Docker ソケットを使い、認証もありません。インターネットに公開せず LAN 内で運用するか、公開時はリバースプロキシで basic-auth / IP 制限を。

環境変数

Docker は .env ファイルから読み、CLI は引数で渡せます。よく使うものだけ抜粋(全項目は Hub 起動時のログでも確認できます)。

Hub 側

変数デフォルト説明
PUBLIC_BASE_URLhttp://hub:8000worker に教える hub URL
WORKER_SECRET(無)worker WS handshake 認証
CODEGEN_LLM_URL / CODEGEN_MODEL_NAMEcodegen 用 LLM endpoint / model
PAPRIKA_RUNNER_IMAGE / ..._MAX_CONCURRENT...:latest / 3runner イメージ / 同時数

Worker 側

変数デフォルト説明
HUB_URLws://paprika.lan:8000hub の WS URL
WORKER_SECRET(無)hub と一致が必要
LANE_POOL(file)Lane (ブラウザ) 数
MAX_CONCURRENT2同時ジョブ数
NOVNC_PUBLIC_HOST / NOVNC_BASE_PORTlocalhost / 6080noVNC(hub 自動補正あり)
AGENT_URL / AGENT_LLM_URL / COGAGENT_URLLLM / vision エンドポイント
PAPRIKA_WORKER_AUTO_FETCH_SOURCE1tarball 自己更新

paprika-client / Runner

変数デフォルト説明
PAPRIKA_HUBhttp://localhost:8000SDK の接続先
PAPRIKA_JOB_ID(無)runner が session に紐づける parent job(自動セット)
PAPRIKA_CLIENT_ACTION_LOG1[paprika] page.X(...) ログ

更新・フリート運用

Hub を更新する

ssh root@paprika.lan
cd /opt/paprika && git pull && docker compose restart hub

server/worker/*.py を変えた場合も、hub 再起動で新ソース tarball が配信され、worker は次の handshake で自己更新します。pip 依存を増やしたときだけ docker compose up -d --build hub

worker を追加する(B構成のスケールアップ)

新しい Linux ホストで B 構成の手順 ② を繰り返すだけ。Hub に自動接続されレーンが増えます。

自己更新・クローン検知・noVNC 補正

保存場所

収集物は Hub の Docker volume(paprika_paprika-data)/CLI なら --data-dir 配下に集約されます。

パス内容
{data}/jobs/{id}/assets/画像 / 動画 / etc.
{data}/jobs/{id}/log.txtジョブログ
{data}/jobs/{id}/attempts/{n}/codegen の各 attempt 出力
{data}/jobs/{id}/state/{key}.jsonpage.set_state() の永続データ
{data}/jobs/hosts/ · presets/ · skills/ホスト別 cookie / preset / skill snippets

アーキテクチャ(詳細)

中央集権型です。Operator / SDK → Hub → Worker(Lane = Chrome)。収集物は Hub に集約され、worker は通り道です。

コンポーネント役割規模
HubAPI サーバ、ジョブ管理、ワーカー登録、管理 UI1 プロセス
WorkerXvfb + Chrome の Lane を保持、CDP 経由でページ操作多数の LAN ホスト
Lane独立 Chrome + noVNC viewer1 worker あたり 1–2
Runnercodegen / rerun の使い捨て sandboxjob ごとに spawn → 削除
Agent serviceLLM / VLM プロキシ(任意。LLM 機能を使う場合)1 プロセス
Redisjob state の永続化(任意。無ければインメモリ)Hub 同居

データの流れ: POST /jobs → Hub がキュー → WS で worker に割当 → worker が実行し POST /jobs/{id}/assets で Hub にアップロード → GET /jobs/{id}/result で取得。進捗は WS /jobs/{id}/events でライブ配信。

HTTP API

全エンドポイントはフラット(/api/ prefix なし)、認証なし(LAN 内信頼前提)。インタラクティブな一覧は Swagger UI /docs。SDK 経由が基本ですが、未ラップのレジストリ系は await cli._json("GET", "/hosts") でも叩けます。

グループ主なエンドポイント
JobsPOST /jobs · GET /jobs[/{id}] · /result · /assets.json · /cancel · DELETE · WS /jobs/{id}/events
SessionsPOST /sessions · action 群(/navigate /click /evaluate /set_input_files …)· /cookies · /network
WorkersGET /workers · WS /workers/{id}/link · POST /workers/{id}/status
Registries/hosts · /profiles · /presets · /engines · /skills · /conventions · /settings

トラブルシュート

worker が hub に繋がらない

  1. docker logs paprika-worker-1 でログ確認
  2. HUB_URL が正しいか / curl http://paprika.lan:8000/health で到達性
  3. WORKER_SECRET 不一致だと bad worker secret で切断

セッションが 404 になる

無操作が続くと reaper が回収します(既定 idle: SDK セッション 300 秒 / fetch keep_session 60 秒)。長時間動かすなら投入時に延長:

cli.session("...", idle_ttl_s=3600)
# あるいは: await page.keepalive(idle_ttl_s=600)

ジョブが queued のまま

GET /workersalive=true な worker と空きレーンがあるか確認。全レーン埋まりで POST /jobs すると 503("fleet at capacity")が返るので、クライアントは指数バックオフを。

codegen-loop が同じスクリプトで失敗し続ける

Live パネル → コードタブで各 attempt の script.py を確認。共通の失敗パターンは conventions / skills に登録すると次回以降の prompt に注入されます。