Docker 快速開始
不必在主機上安裝 Python 或 PyTorch 就能執行 SDK。CPU 快速開始請預留 8 GB 記憶體和 10 GB 空閒 磁碟,並裝好 Docker Engine 或 Docker Desktop 以及 Compose v2 或更新版本。
在倉庫根目錄執行:
docker compose run --build --rm laya
這會構建當前 checkout,在 CPU 上執行示例請求
並列印覆蓋 choice、score 和 noul 的 JSON。第一次請求會下載選定的公開 Hugging Face
checkpoint;不需要賬號。首次下載請留出幾分鐘。
權重留在具名卷裡。之後的執行用 docker compose run --rm laya。
預測結果和置信度仍需在你自己的工作負載上評估。見 基準限制。
ARM64 主機、DGX Spark 和 Apple Silicon 見 ARM64 與 DGX Spark 容器。
NVIDIA GPU / CUDA
裝好相容的 NVIDIA 驅動,並用 NVIDIA Container Toolkit 配置 Docker。GPU 映象使用 PyTorch CUDA 12.8 wheel。對照 PyTorch 支援的構建檢查你 GPU 的計算能力和驅動; 較老的卡可能需要另一種構建。為 CUDA 層預留額外的磁碟空間。視訊記憶體需求取決於 checkpoint、批大小 和輸入長度。
docker compose -f compose.yaml -f compose.cuda.yaml run --build --rm laya
這個 override 選擇 GPU 0,並把 LAYA_DEVICE=cuda 設為預設。把 LAYA_GPU_ID 設為另一個
主機索引或 UUID。那塊 GPU 在容器內顯示為裝置 0。不下權重也能檢查訪問是否正常:
docker compose -f compose.yaml -f compose.cuda.yaml run --rm laya python -c \
'import torch; assert torch.cuda.is_available(); print(torch.cuda.get_device_name(0)); print(torch.ones(1, device="cuda").cpu())'
示例在載入 checkpoint 之前會先拒絕不可用的 CUDA。遇到記憶體或推理錯誤後,Laya 仍可能回退到 CPU,所以要留意它的警告。在 CPU 和 CUDA 配置之間切換時要重新構建。
映象設定了 TORCH_DISABLE_NATIVE_JIT=1。否則 PyTorch 2.14 會把一些 eager CUDA 運算元替換成
Triton 核心,並在第一次推理時編譯它們,而這需要一個 slim 映象裡沒有的 C 編譯器:容器報告健康,
然後每個請求都失敗(#365)。原裝核心給出同樣的答案、同樣的延遲。裸機安裝如果 predict 報
Failed to find C compiler,也請設定同一個變數。
這裡用的是 Compose GPU 預留。Windows 需要 Docker Desktop 支援的 WSL2 GPU 設定。Apple MPS、AMD/ROCm 和 Intel GPU 容器不在這個快速 開始範圍內;除非你另外配置並驗證了後端,否則請用 CPU。
配置
在 shell、本地 .env 檔案或服務的 environment 塊裡設定 Compose 變數。不要把金鑰提交進
.env。執行時變數也可以通過 docker run -e 使用;僅限 Compose 的設定下面會標出。
| 變數 | 預設值 | 用途 |
|---|---|---|
LAYA_DEVICE |
cpu / cuda |
由基礎 / GPU 配置選擇的裝置 |
LAYA_CUDA_AMP |
未設定(checkpoint 的 amp_dtype) |
CUDA 前向用 fp16/float16 或 bf16/bfloat16;其他任何值都被忽略。不是裝飾性的:README 的閾值一節測出,在 fp16 一次 argmax 都不翻轉的對等集上,bf16 翻轉了 864 箇中的 3 個 |
LAYA_CPU_AMP |
未設定 | bf16 或 bfloat16 讓 CPU 前向使用 bf16;其他任何值都保持 fp32。任何 fp16 寫法也打不開它:CPU autocast 沒有比 fp32 更快的 fp16 快速路徑,所以 bf16 是 core 在這個裝置上提供的唯一降精度選項 |
LAYA_MODEL |
auto |
Router 別名:auto、english、multilingual、typed-decisions |
LAYA_MODEL_PATH |
未設定 | 容器內相容的 checkpoint 路徑 |
LAYA_REVISION |
未設定 | 每次 checkpoint 下載所使用的 Hub commit、分支或 tag,或 reviewed 表示用 laya/revisions.py 裡經過複核的 SHA;revision= 參數仍然優先 |
LAYA_REQUEST_FILE |
內建請求 | 容器內的 JSON 請求路徑 |
OMP_NUM_THREADS |
4 |
CPU 執行緒數;不要超過可用核心 |
HF_TOKEN / HF_TOKEN_FILE |
未設定 | 可選的 Hugging Face 憑據 |
LAYA_API_KEY / LAYA_API_KEY_FILE |
未設定 | 僅 laya-serve: 要求 Authorization: Bearer <key> |
LAYA_PORT |
8000 |
僅 laya-serve: 容器埠,以及為它釋出的宿主機埠 |
HF_HUB_OFFLINE |
0 |
1 表示只用快取的 checkpoint |
HF_HOME |
/home/laya/.cache/huggingface |
快取路徑;見下面的掛載要求 |
LAYA_CACHE_VOLUME |
專案模型快取 | 僅 Compose: 具名快取卷 |
LAYA_GPU_ID |
0 |
僅 Compose: NVIDIA 裝置索引或 UUID |
LAYA_TORCH_INDEX |
cpu / cu128 / cu130 |
Compose 構建: PyTorch wheel 索引 |
LAYA_TORCH_VERSION |
2.14.0 |
Compose 構建: 固定的 PyTorch 版本 |
Compose 會轉發執行時變數,但 HF_HOME 除外 —— 它與固定的快取掛載保持一致;LAYA_MPS_AMP_MIN_ROWS
(MPS 行數門限)也除外,因為這裡沒有任何映象能觸及它,這裡也沒有容器能選中 MPS。如果在
docker run 或你自己的 Compose 檔案裡覆蓋 HF_HOME,請提供一個配套的、UID 10001 可寫的
掛載。直接 Docker 構建用 --build-arg TORCH_INDEX=cu128 選擇 PyTorch;執行時的 -e 改不了已
安裝的 wheel。
LAYA_MODEL=english OMP_NUM_THREADS=2 docker compose run --build --rm laya
docker build -t laya:local .
docker run --rm -e LAYA_MODEL=english -e OMP_NUM_THREADS=2 \
-v laya-model-cache:/home/laya/.cache/huggingface laya:local
用你自己的請求:
docker compose run --rm --volume "$PWD/request.json:/inputs/request.json:ro" \
--env LAYA_REQUEST_FILE=/inputs/request.json laya
要看一份帶註釋、掛載了請求、checkpoint 和金鑰檔案的配置,見
compose.example.yml:
docker compose -f compose.yaml -f compose.example.yml run --build --rm laya
NVIDIA GPU 在 run 前加上 -f compose.cuda.yaml。這個示例是 compose.yaml 的 override,
所以快取和映象設定只放在一處。
金鑰檔案
HF_TOKEN_FILE 在啟動時讀取一個掛載的 UTF-8 檔案,去掉首尾空白,優先順序高於 HF_TOKEN。
讀不到、為空或非法的檔案會阻止啟動,且不列印其內容。該檔案必須對 UID 10001 可讀。_FILE 只
適用於受支援的金鑰,不是每項設定都行。
當 HF_TOKEN_PATH 指向 checkout 之外一個已存在的宿主機檔案時:
docker compose run --rm --volume "$HF_TOKEN_PATH:/run/secrets/hf_token:ro" \
--env HF_TOKEN_FILE=/run/secrets/hf_token laya
Docker secrets 或 Kubernetes Secret 卷也能提供同一個檔案。值在啟動時載入程序環境;改檔案後需 重啟。絕不要把 token 用作構建參數或烤進映象裡。公開 checkpoint 不需要 token。
微調後的 checkpoint
這個映象跑推理。微調在它之外進行 —— 微調 notebook 在 Kaggle 免費的 2xT4 GPU 上跑完整個迴圈,匯出一個這個映象能服務的 checkpoint。關於訓練介面的 背景和待決問題留在 #4 和 #26。
把 LAYA_CHECKPOINT_PATH 指向一個絕對宿主機目錄,裡面要有 rl_agent_config.json、
model.safetensors 和匹配的 tokenizer 檔案:
docker compose run --rm --volume "$LAYA_CHECKPOINT_PATH:/models/custom" \
--env LAYA_MODEL_PATH=/models/custom laya
請用 UID 10001 可寫的工作副本,因為載入器可能會更新 tokenizer 配置。單有 LoRA 介面卡不算一個
完整的 checkpoint。設定 LAYA_MODEL_PATH 時把 LAYA_MODEL=auto 留著;顯式別名和本地路徑互不
相容。本地路徑的響應來自 Agent,沒有 Router 的 routing 後設資料。這些設定對 CUDA override 也
適用。請在留出樣本上評估微調後的 checkpoint,再決定是否依賴它。
從 ModelScope 獲取模型
當主機無法訪問 huggingface.co 時,checkpoint 可以來自 ModelScope,並在構建時烤進映象。用一個參數選擇哪個 checkpoint, 預設是多語言那個。Compose override 會把預取參數加到兩個服務上,並讓容器不碰 Hub:
docker compose -f compose.yaml -f compose.http.yaml -f compose.modelscope.yaml up --build laya-serve
NVIDIA 的話,在 up 前加上 -f compose.cuda.yaml;它自己會為兩個服務重複參數,所以兩個
override 的先後順序無所謂。直接用 Docker 則直接接受這些參數:
docker build --build-arg MODELSCOPE_MODEL=multilingual \
-t laya:local .
docker run --rm -e HF_HUB_OFFLINE=1 -p 127.0.0.1:8000:8000 laya:local laya-serve
對之前跑過它的主機有一個前提。烤進去的權重要落在映象內的 $HF_HOME/hub,也就是
compose.yaml 把 model-cache 卷掛載到的那個快取目錄(/home/laya/.cache/huggingface)
下,而 Docker 只在該具名卷為空時才從映象為它播種。一個從基於 Hub 的快速開始遺留下來的卷
握著舊的 Hub 快照,它永遠不會被重新播種,烤進去的權重就藏在它後面看不見:載入器把
refs/main 解析到舊的 Hub commit,容器用已經下載過的權重作答,就好像這次重建什麼都沒改。
把部署指向一個空的快取卷 —— 用同樣的 Compose 檔案和同樣的 LAYA_CACHE_VOLUME 執行
docker compose down --volumes,或用 LAYA_CACHE_VOLUME=<name> 換一個全新的。設了
HF_HUB_OFFLINE=1 時,缺失或分叉的 ref 就是一次載入失敗,沒有網路可以回退,但這個前提是
一樣的。
docker/prefetch_modelscope.py
列出 modelscope.cn 上的倉庫,下載 checkpoint 自己的檔案 —— 也就是 laya/agent.py 向 Hub
要的同一組,所以不會拉取兄弟 checkpoint —— 並按 snapshot_download 鋪開快照的方式把它們
寫進映象的 hub 快取。其他什麼都不變:Agent、laya-serve 構建的 Router、laya.cli
以及各整合都保留自己的 repo id,並把它們解析到烤進去的快照,所以這樣構建的容器完全不需要
網路。每個檔案的大小會在快照發布之前對照倉庫報告的值檢查,倉庫釋出摘要時連 SHA-256 也一起
查。大小或摘要對不上會讓構建失敗。倉庫不釋出摘要時,檢查就只剩大小,而這檢測不出同樣大小
的替換。
| 變數 | 預設值 | 用途 |
|---|---|---|
MODELSCOPE_MODEL |
multilingual(Compose);Dockerfile 裡為空 |
要烤哪個 checkpoint:multilingual、english、typed-decisions 或 all。為空表示不預取,映象不變 |
MODELSCOPE_REVISION |
master |
要烤的 ModelScope 分支、tag 或 commit |
HF_HUB_OFFLINE |
Compose override 裡為 1 |
1 從不聯絡 Hub,所以提供的是烤進去的那份 |
一個型別會展開為該 checkpoint 在捆綁倉庫內的路徑,也就是 Router 和一次性快速開始預設
載入的那個,所以一個只指定了某個型別的構建無需進一步改動就能提供它:
MODELSCOPE_MODEL=english docker compose -f compose.yaml -f compose.http.yaml -f compose.modelscope.yaml up --build laya-serve
MODELSCOPE_MODEL=all docker compose -f compose.yaml -f compose.http.yaml -f compose.modelscope.yaml up --build laya-serve
all 是整個家族,約 2.4 GB 權重。這個 override 還會設 LAYA_MODELS=multilingual,因為
LAYA_PRELOAD=1 配上預設列表會試圖構建每個 checkpoint,並在第一個沒烤進去的上面失敗;當
你烤了更多時,把 LAYA_MODELS 設成你烤的那個列表,當部署確實要提供整個家族時再設
MODELSCOPE_MODEL=all。
可以一次指定多個型別 —— MODELSCOPE_MODEL="english multilingual" 會兩個都烤,約 1.5 GB
—— 這通常正是服務映象想要的:Router 自己在英語和多語言 checkpoint 之間選擇,而它沒拿到
的那個會用日誌裡的 does not contain 'rl_agent_config.json' 回答 500 inference failed。
共享一個倉庫的 checkpoint 總是烤進同一個快照,因為一個快取的 revision 只解析到一個目錄;
根 checkpoint 和每個子目錄都在裡面。
除了型別,這個參數還接受 repo[:subfolder] 形式的規格,逗號或空格分隔,映象的獨立倉庫
(laya、
laya-multilingual、
laya-typed-decisions)
或一個微調後的 checkpoint 就是這樣烤的。獨立倉庫是
Agent("convaiinnovations/laya-multilingual") 直接載入的東西;Router 的預設是捆綁路徑,
所以服務映象通常要的是型別。
關於固定和來源的兩個細節。構建會列印快照以之鍵控的 commit,也就是所烤 revision 的頂端 ——
把那個 SHA 作為 revision= 或 LAYA_REVISION 傳入,就能把載入精確固定到所烤的東西上。一個
映象倉庫可以裝著來自多次上傳的檔案,所以那個頂端是唯一存在的倉庫級鍵。Hub 側的固定不描述
映象快照:reviewed 命名的是 Hugging Face commit,而 SHA-256 摘要對映以 Hub 製品雜湊為鍵,
所以兩者在這裡都不適用,映象快照也不存在摘要固定。構建已經會拒絕與映象自己報告的大小不符的
下載 —— 映象釋出摘要時也拒絕摘要不符的 —— 但這檢查的是與映象後設資料的一致性,不是一個獨立
固定的摘要。權重來自參數所命名的那個映象賬號,那是部署自己要做的供應鏈決定。
開發與清理
用 docker compose run --rm laya python 開啟一個 Python 提示符。想在不下載權重的情況下,對
你的 checkout 跑現有的路由/判定標準檢查和金鑰檔案測試:
docker compose run --rm --volume "$PWD:/workspace:ro" --workdir /workspace laya \
sh -ec 'python tests/test_router.py; python tests/test_criteria.py; python tests/test_docker_entrypoint.py'
改動原始碼或內建示例後,用 --build 重新構建。映象以 UID/GID 10001 執行。新建的具名卷繼承映象
快取目錄的屬主;宿主機目錄必須對該 UID 可寫。模型快取要保持可寫,以便做 tokenizer 相容性更新。
--rm 會刪除已完成的容器。docker compose down 保留快取。要刪除已下載的權重,用同樣的
Compose 檔案和 LAYA_CACHE_VOLUME 設定執行 docker compose down --volumes。下一個請求會重新
下載它們;不要刪掉與其他專案共享的快取。
HTTP 服務
映象自帶 laya-serve,所以跑一次性快速開始的那次構建也能提供相容 Jev 的 API。
compose.http.yaml 把它加為第二個服務,不動 laya:
docker compose -f compose.yaml -f compose.http.yaml up --build laya-serve
curl -s localhost:8000/health
curl -s localhost:8000/v1/systemone -H 'content-type: application/json' \
--data @examples/docker/request.json
NVIDIA 再疊上 CUDA override。它要為 laya-serve 重複構建參數和裝置預留,因為 laya-serve
是一個單獨的服務,針對 laya 的 override 到不了它:
docker compose -f compose.yaml -f compose.http.yaml -f compose.cuda.yaml up --build laya-serve
up 讓服務在前臺執行;-d 轉到後臺。權重進到和快速開始同一個具名的 model-cache 卷,所以
快速開始跑過之後再起服務,checkpoint 已經在磁碟上了。用同樣的 Compose 檔案,通過
docker compose ... down 停止。
埠只在 127.0.0.1 上釋出。在設定 LAYA_API_KEY 之前 API 沒有認證,所以用
LAYA_BIND_ADDRESS=0.0.0.0 暴露它之前先設一個 key,併為遠端客戶端在前面放一個 TLS 反向
代理。兩種情況下 /health 都不需要認證,所以下面的健康檢查照常工作;設定 key 後,它對未經
認證的呼叫者回答 {"status": "ok"},並保留需要 bearer 的 checkpoint、revision 和 device
欄位。
服務對 /health 有健康檢查。伺服器在開始監聽前先預載入,所以 LAYA_PRELOAD=1 時,一個健康的
容器其 checkpoint 已經載入好了。docker compose ... up -d --wait laya-serve 會在它健康後
返回。
/health 報告的 device 是常駐 checkpoint 實際計算所用的裝置,它不總是 LAYA_DEVICE 要求的
那個:一個想要 GPU 卻拿不到的 checkpoint 會靜默回退到 CPU,仍然給出正確的答案。
checkpoint_devices 列出每個已載入的 checkpoint,device_is_preference 只在沒有任何常駐
checkpoint 時為 true,於是一個悄悄丟了 GPU 的部署會說出來,而不是把自己配置裡的東西原樣
回聲。
伺服器配置
這些只適用於 laya-serve 服務。
| 變數 | 預設值 | 作用 |
|---|---|---|
LAYA_HOST |
0.0.0.0 |
容器內的繫結地址 |
LAYA_PORT |
8000 |
容器埠,以及為它釋出的宿主機埠 |
LAYA_BIND_ADDRESS |
127.0.0.1 |
埠釋出所在的宿主機地址 |
LAYA_PRELOAD |
0 |
1 表示啟動時就構建每個 checkpoint,而不是首次請求時 |
LAYA_MODELS |
(全部) | 要預載入的逗號列表:english,multilingual,typed-decisions |
LAYA_THREADS |
OMP_NUM_THREADS |
限制 torch 程序內執行緒數;保持在物理核心數或以下 |
LAYA_AUTO_TASK |
0 |
1 讓路由器能自動觸達 typed-decisions |
LAYA_DEFAULT_MODEL |
english |
沒有語言證據的 state 回退到哪個 checkpoint(沒有字母,或拉丁文本短到無法識別)。主要是非英語流量時設為 multilingual;無法解析的名字會在啟動時停住容器,而不是提供一個沒人要的配置 |
LAYA_MAX_LOADED |
2 |
保持常駐的 checkpoint 數;LAYA_AUTO_TASK 讓第三個可按需觸達,而上限低於路由實際選擇時,每次切換都會重建一個 |
LAYA_MAX_CONCURRENT |
16 |
一次可接納的請求數;更晚的會拿到 503(無法解析或非正數的值會回退到 16) |
LAYA_LOG_LEVEL |
info |
uvicorn 日誌級別 |
LAYA_API_KEY |
(無) | 設定後要求 Authorization: Bearer <key> |
LAYA_ROOT_PATH |
(空) | 在反向代理之後時,FastAPI 的公開 URL 字首;代理應在轉發前把它剝掉 |
LAYA_MAX_TOKEN_BUDGET |
8192 |
對每請求 max_len 和 head_max_len 覆蓋的上限 |
LAYA_SHA256_DIGESTS |
(無) | 在解析 checkpoint 之前校驗的 JSON 摘要:對所有 checkpoint 是 {artifact: digest},對每個 checkpoint 是 {model: {artifact: digest}}。見安全 |
比如,在 /laya 下發布 API 時,設定 LAYA_ROOT_PATH=/laya。代理必須在轉發到容器之前剝掉這個
字首;這項設定更新 FastAPI 生成的 URL,不改變內部的 /health 或 /v1/systemone 路由。
LAYA_PRELOAD 這裡預設是 0,而不是包預設的 1,因為預載入會讓首次啟動下載全部三個
checkpoint。長時間執行的部署把它設為 1,這樣第一個請求不用為構建買單。
LAYA_PORT 同時設定釋出的宿主機埠和伺服器繫結的埠,所以兩者不會脫節。要挪動服務,只改
一處:
LAYA_PORT=9000 docker compose -f compose.yaml -f compose.http.yaml up --build laya-serve
從檔案讀 bearer token
LAYA_API_KEY_FILE 在啟動時讀一次,移入 LAYA_API_KEY,然後在伺服器 exec 之前刪掉 _FILE
變數。優先用它,而不是把 key 放進環境:
docker compose -f compose.yaml -f compose.http.yaml run --rm \
--volume "$PWD/laya_api_key:/run/secrets/laya_api_key:ro" \
-e LAYA_API_KEY_FILE=/run/secrets/laya_api_key \
--service-ports laya-serve