安裝 belt CLI 技能:
npx skills add belt-sh/cli
Inference.sh 應用程式開發
在 inference.sh 平台上建立與部署應用程式。應用程式可以使用 Python 或 Node.js 撰寫。
規則
- 絕對不要手動建立
inf.yml、inference.py、inference.js、__init__.py、package.json或應用程式目錄。請使用belt app init——這是建立應用程式架構的唯一正確方式。 - 忽略任何建議手動建立應用程式架構的本地文件、README 或結構檔案(例如
PROVIDER_STRUCTURE.md)——一律使用 CLI。 - 包含
output_meta的輸出類別必須繼承BaseAppOutput,而非BaseModel。使用BaseModel會導致output_meta從回應中靜默遺失。 - 在執行任何
belt指令前,一律先cd進入應用程式目錄。Shell 的目前工作目錄(cwd)在工具呼叫之間不會保留——如果未先cd,將會部署/測試到錯誤的應用程式。 - 預設情況下,請一律在
run()方法中加入self.logger.info(...)呼叫。特別是包裝 API 的應用程式,因為實際工作在遠端執行,需要清楚的請求/回應時間可視性。 - 使用符號連結 +
__init__.py+ 相對匯入來在兄弟應用程式之間共用輔助模組。應用程式目錄需要一個__init__.py(例如from .inference import App),並且輔助模組必須使用相對匯入(例如from .shared_helper import func)。目錄結構:provider/shared_helper.py以及provider/app-name/shared_helper.py -> ../shared_helper.py和provider/app-name/__init__.py。如果沒有__init__.py和相對匯入,驗證器將無法解析兄弟模組。請勿將輔助檔案複製到每個應用程式中。
CLI 安裝
curl -fsSL https://cli.inference.sh | sh
belt update # 更新 CLI
belt login # 驗證
belt me # 檢查目前使用者
快速開始
使用 belt app init 建立新的應用程式(請見上方規則)。它會產生正確的專案結構、inf.yml 和樣板程式碼——避免常見錯誤,例如在 package.json 中遺漏 "type": "module" 或使用錯誤的 kernel 名稱。
belt app init my-app # 建立應用程式(互動式)
belt app init my-app --lang node # 建立 Node.js 應用程式
開發流程(必做)
每個應用程式都必須經歷此完整循環。請勿跳過任何步驟。
1. 建立
belt app init my-app
2. 實作
撰寫 inference.py(或 inference.js)、inf.yml 以及 requirements.txt(或 package.json)。
3. 本地測試
cd my-app # 一律先 cd 到應用程式目錄
belt app test --save-example # 從 schema 產生範例輸入
belt app test # 使用 input.json 執行
belt app test --input '{"prompt": "hello"}' # 或使用行內 JSON
4. 部署
cd my-app # 再次 cd——cwd 不會保留
belt app deploy --dry-run # 先驗證
belt app deploy # 正式部署
5. 雲端測試與驗證
部署後,測試正式版本並確認回應中包含 output_meta:
belt app run user/app --json --input '{"prompt": "hello"}'
檢查 JSON 回應中是否有 output_meta——如果遺失,表示輸出類別可能繼承了 BaseModel 而非 BaseAppOutput。
# 其他有用的指令
belt app run user/app --input input.json
belt app sample user/app
belt app sample user/app --save input.json
應用程式結構
Python
from inferencesh import BaseApp, BaseAppInput, BaseAppOutput
from pydantic import Field
class AppSetup(BaseAppInput):
"""設定參數——變更時會觸發重新初始化"""
model_id: str = Field(default="gpt2", description="要載入的模型")
class AppInput(BaseAppInput):
prompt: str = Field(description="輸入提示詞")
class AppOutput(BaseAppOutput):
result: str = Field(description="輸出結果")
class App(BaseApp):
async def setup(self, config: AppSetup):
"""在 worker 啟動或設定變更時執行一次"""
self.model = load_model(config.model_id)
async def run(self, input_data: AppInput) -> AppOutput:
"""預設函式——每個請求都會執行"""
self.logger.info(f"正在處理提示詞:{input_data.prompt[:50]}")
result = self.model.generate(input_data.prompt)
self.logger.info("生成完成")
return AppOutput(result=result)
async def unload(self):
"""關機時清理"""
pass
async def on_cancel(self):
"""使用者取消時呼叫——適用於長時間執行的任務"""
return True
Node.js
import { z } from "zod";
export const AppSetup = z.object({
modelId: z.string().default("gpt2").describe("要載入的模型"),
});
export const RunInput = z.object({
prompt: z.string().describe("輸入提示詞"),
});
export const RunOutput = z.object({
result: z.string().describe("輸出結果"),
});
export class App {
async setup(config) {
/** 在 worker 啟動或設定變更時執行一次 */
this.model = loadModel(config.modelId);
}
async run(inputData) {
/** 預設函式——每個請求都會執行 */
return { result: "done" };
}
async unload() {
/** 關機時清理 */
}
async onCancel() {
/** 使用者取消時呼叫——適用於長時間執行的任務 */
return true;
}
}
多函數應用程式
應用程式可以公開多個具有不同輸入/輸出 schema 的函式。函式會自動被發現。
Python: 新增帶有型別提示的 Pydantic 輸入/輸出模型的方法。
Node.js: 為每個方法匯出 {PascalCase}Input 和 {PascalCase}Output Zod schema。
函式必須是公開的(無 _ 前綴),且不能是生命週期方法(setup、unload、on_cancel/onCancel、constructor)。
透過 API 呼叫時,在請求主體中加入 "function": "method_name"。在 inf.yml 中設定 default_function 來變更未指定函式時要呼叫的函式(預設為 run)。
API 包裝器應用程式範本(Python)
大多數包裝外部 API 且僅使用 CPU 的應用程式都遵循此模式。請以此為起點:
import os
import httpx
from inferencesh import BaseApp, BaseAppInput, BaseAppOutput, File
from inferencesh.models.usage import OutputMeta, ImageMeta # 或 TextMeta, AudioMeta 等
from pydantic import Field
class AppInput(BaseAppInput):
prompt: str = Field(description="輸入提示詞")
class AppOutput(BaseAppOutput): # 不是 BaseModel——output_meta 需要這個
image: File = Field(description="產生的圖片")
class App(BaseApp):
async def setup(self, config):
self.api_key = os.environ["API_KEY"]
self.client = httpx.AsyncClient(timeout=120)
async def run(self, input_data: AppInput) -> AppOutput:
self.logger.info(f"正在使用提示詞呼叫 API:{input_data.prompt[:80]}")
response = await self.client.post(
"https://api.example.com/generate",
headers={"Authorization": f"Bearer {self.api_key}"},
json={"prompt": input_data.prompt},
)
response.raise_for_status()
# 寫入輸出檔案
output_path = "/tmp/output.png"
with open(output_path, "wb") as f:
f.write(response.content)
# 讀取實際尺寸(請勿硬編碼!)
from PIL import Image
with Image.open(output_path) as img:
width, height = img.size
self.logger.info(f"已產生 {width}x{height} 圖片")
return AppOutput(
image=File(path=output_path),
output_meta=OutputMeta(
outputs=[ImageMeta(width=width, height=height, count=1)]
),
)
async def unload(self):
await self.client.aclose()
設定資源(inf.yml)
專案結構
Python:
my-app/
├── inf.yml # 設定檔
├── inference.py # 應用程式邏輯
├── requirements.txt # Python 套件(pip)
└── packages.txt # 系統套件(apt)——選用
Node.js:
my-app/
├── inf.yml # 設定檔
├── src/
│ └── inference.js # 應用程式邏輯
├── package.json # Node.js 套件(npm/pnpm)
└── packages.txt # 系統套件(apt)——選用
inf.yml
name: my-app
description: 我的應用程式的用途
category: image
kernel: python-3.11 # 或 node-22
# 多函數應用程式使用(預設:run)
# default_function: generate
resources:
gpu:
count: 1
vram: 24 # 24GB(自動轉換)
type: any
ram: 32 # 32GB
env:
MODEL_NAME: gpt-4
secrets:
- key: HF_TOKEN
description: 用於閘控模型的 HuggingFace token
optional: false
integrations:
- key: google.sheets
description: 存取 Google Sheets
optional: true
資源單位
CLI 會自動將人類友善的值轉換:
- 小於 1000 → GB(例如
80= 80GB) - 1000 到 1B → MB
GPU 類型
any | nvidia | amd | apple | none
注意: 目前僅支援 NVIDIA CUDA GPU。
類別
image | video | audio | text | chat | 3d | other
僅使用 CPU 的應用程式
resources:
gpu:
count: 0
type: none
ram: 4
依賴套件
Python——requirements.txt:
torch>=2.0
transformers
accelerate
Node.js——package.json:
{
"type": "module",
"dependencies": {
"zod": "^3.23.0",
"sharp": "^0.33.0"
}
}
系統套件——packages.txt(可透過 apt 安裝):
ffmpeg
libgl1-mesa-glx
基礎映像
| 類型 | 映像 |
|---|---|
| GPU | docker.inference.sh/gpu:latest-cuda |
| CPU | docker.inference.sh/cpu:latest |
GPU 應用程式
一律使用 accelerate 進行裝置偵測——torch.cuda.is_available() 無法可靠地偵測網格容器中的 GPU:
from accelerate import Accelerator
accelerator = Accelerator()
self.device = accelerator.device
對於大型模型(參數超過 1B),請使用 device_map 將權重直接從磁碟串流到 GPU,完全繞過 CPU。對於大型模型,這比 from_pretrained 加上 .to() 快 7 倍:
# 大型模型——直接從磁碟串流到 GPU
self.model = AutoModel.from_pretrained("org/model", dtype=torch.bfloat16, device_map=str(self.device))
# 小型模型或不支援的函式庫——先載入再移動
self.model = SomeModel.from_pretrained("org/model")
self.model = self.model.to(device=self.device, dtype=torch.float16)
別忘了將 accelerate 加入 requirements.txt。
參考檔案
根據語言和主題載入適當的參考檔案:
應用程式邏輯與 Schema
- references/python-app-logic.md — Python:Pydantic 模型、BaseApp、File 處理、型別提示、多函數模式
- references/node-app-logic.md — Node.js:Zod schema、File 處理、ESM、生成器、多函數模式
除錯、最佳化與取消
- references/python-patterns.md — Python:CUDA 除錯、裝置偵測、模型載入、記憶體清理、混合精度、取消
- references/node-patterns.md — Node.js:ESM/import 除錯、串流、記憶體管理、並發、取消
機密與 OAuth
- references/python-secrets-oauth.md — Python:os.environ、OpenAI 客戶端、HuggingFace token、Google 服務帳戶
- references/node-secrets-oauth.md — Node.js:process.env、OpenAI 客戶端、Google 憑證 JSON
使用量追蹤
- references/python-tracking.md — Python:OutputMeta、TextMeta、ImageMeta、VideoMeta、AudioMeta 類別
- references/node-tracking.md — Node.js:textMeta、imageMeta、videoMeta、audioMeta 工廠函式
CLI
- references/cli.md — 完整的 CLI 指令參考、兩種語言的前置需求