tei-triton-serving

v2026.09.24

High-performance serving of embedding and reranker models in production. Covers HuggingFace Text Embeddings Inference (TEI) on GPU, Docker deployment, ONNX/FP16 quantization, dynamic batching, NVIDIA Triton for co-serving embedding + reranker + LLM, and NVIDIA NIM as a managed alternative. Benchmarks and configs. USE WHEN: user mentions "TEI", "Text Embeddings Inference", "Triton", "Triton Inference Server", "NIM", "NVIDIA NIM", "embedding server", "reranker server", "ONNX embedding", "serve BGE", "serve E5" DO NOT USE FOR: vector DB serving - use `rag-architecture`; LLM serving in general - use provider-specific docs; cost tracking - use `cost-allocation`

GitHub
安装命令
npx skhub add claude-dev-suite/tei-triton-serving
Markdown
SKILL.md

TEI, Triton, and NIM for Embedding/Reranker Serving

Embeddings and rerankers are the hottest-path CPU/GPU work in RAG. Serving them with sentence-transformers in-process or behind a plain Flask/FastAPI app leaves 3–10x throughput on the table. Three production paths:

  1. TEI (Text Embeddings Inference) — HuggingFace's purpose-built server for sentence-transformers/BGE/E5 and cross-encoder rerankers. Rust core, dynamic batching, ONNX + FP16 + CUDA kernels. Easiest win.
  2. NVIDIA Triton — general model server. Use when you need to co-serve embedding + reranker + small LLM (or ASR/CV models) behind one gateway, with ensembles and model pipelines.
  3. NVIDIA NIM — NVIDIA-managed containers (embedding, reranking, LLM) with OpenAI-compatible APIs. Trade vendor lock for zero ops.

TEI Quick Start

# BGE large on a single GPU
docker run -d --gpus all --shm-size 1g -p 8080:80 \
  -v $PWD/data:/data \
  ghcr.io/huggingface/text-embeddings-inference:latest \
  --model-id BAAI/bge-large-en-v1.5 \
  --max-batch-tokens 16384 \
  --max-concurrent-requests 512 \
  --dtype float16

Embedding call:

curl -s http://localhost:8080/embed \
  -H "Content-Type: application/json" \
  -d '{"inputs":["How do I rotate credentials?","What is the SLA?"]}'

OpenAI-compatible:

curl -s http://localhost:8080/v1/embeddings \
  -H "Content-Type: application/json" \
  -d '{"model":"bge-large","input":["hello"]}'

Cross-encoder Reranker on TEI

docker run -d --gpus all -p 8081:80 \
  ghcr.io/huggingface/text-embeddings-inference:latest \
  --model-id BAAI/bge-reranker-large \
  --dtype float16

curl -s http://localhost:8081/rerank \
  -H "Content-Type: application/json" \
  -d '{"query":"rotate credentials","texts":["KMS rotates nightly","..."]}'

ONNX / CPU-friendly Builds

# Pre-quantized ONNX version of BGE-small
docker run -d -p 8080:80 \
  ghcr.io/huggingface/text-embeddings-inference:cpu-latest \
  --model-id BAAI/bge-small-en-v1.5 \
  --pooling mean

CPU mode with INT8 ONNX gives ~200–400 QPS/core on short inputs and is plenty for low-traffic or air-gapped deployments.

Sizing

ModelGPUMax batch tokensTypical p95 (batch 32)Throughput
bge-small-en-v1.5 (384d)L43276825 ms4k docs/s
bge-large-en-v1.5 (1024d)L41638460 ms1.2k docs/s
bge-large-en-v1.5A101638440 ms2k docs/s
e5-mistral-7b (4096d)A1008192180 ms300 docs/s
bge-reranker-largeL41638455 ms per pair batch400 pairs/s

Batch size is set indirectly via --max-batch-tokens. TEI auto-batches concurrent requests up to this token budget.

TEI Client in Python

from openai import OpenAI
client = OpenAI(base_url="http://tei:80/v1", api_key="-")
vecs = client.embeddings.create(model="bge-large", input=[...]).data

Or native JSON:

import httpx
async with httpx.AsyncClient(base_url="http://tei:80") as c:
    r = await c.post("/embed", json={"inputs": batch}, timeout=30.0)
    vectors = r.json()

NVIDIA Triton Inference Server

Use when you need:

  • Co-serving embedding + reranker + generation on one GPU pool.
  • Model ensembles (Python pre/post-processing sandwiched around an engine).
  • Multi-framework: TensorRT + ONNX + PyTorch under one server.

Model Repository Layout

model_repo/
  bge-large/
    config.pbtxt
    1/
      model.onnx
  bge-reranker/
    config.pbtxt
    1/
      model.onnx
  rag_ensemble/
    config.pbtxt          # ties tokenizer -> embedder -> pooler
    1/

Minimal config.pbtxt for an ONNX Embedder

name: "bge-large"
platform: "onnxruntime_onnx"
max_batch_size: 128
input [ { name: "input_ids" data_type: TYPE_INT64 dims: [ -1 ] },
        { name: "attention_mask" data_type: TYPE_INT64 dims: [ -1 ] } ]
output [ { name: "sentence_embedding" data_type: TYPE_FP16 dims: [ 1024 ] } ]
dynamic_batching {
  max_queue_delay_microseconds: 2000
  preferred_batch_size: [ 8, 16, 32 ]
}
instance_group [ { count: 2 kind: KIND_GPU } ]
optimization { execution_accelerators { gpu_execution_accelerator : [ {
  name : "tensorrt"
  parameters { key: "precision_mode" value: "FP16" }
  parameters { key: "max_workspace_size_bytes" value: "4294967296" }
} ] } }

Launch

docker run --gpus all --rm -p 8000:8000 -p 8001:8001 -p 8002:8002 \
  -v $PWD/model_repo:/models \
  nvcr.io/nvidia/tritonserver:24.08-py3 \
  tritonserver --model-repository=/models --log-verbose=1

Ports: 8000 HTTP, 8001 gRPC, 8002 metrics (Prometheus).

Python Client

import tritonclient.http as httpclient
import numpy as np

c = httpclient.InferenceServerClient("triton:8000")
ids = np.array([[101, 7592, 102]], dtype=np.int64)
mask = np.array([[1, 1, 1]], dtype=np.int64)
inputs = [
    httpclient.InferInput("input_ids", ids.shape, "INT64"),
    httpclient.InferInput("attention_mask", mask.shape, "INT64"),
]
inputs[0].set_data_from_numpy(ids); inputs[1].set_data_from_numpy(mask)
out = c.infer("bge-large", inputs).as_numpy("sentence_embedding")

Ensembles (tokenize → embed → pool)

Compose a Python backend tokenizer model with the ONNX embedder so clients send raw text. See ensemble_scheduling in Triton docs; treat the ensemble name as the public endpoint.

NVIDIA NIM

NIM containers expose OpenAI-compatible endpoints for embedding (nvidia/nv-embedqa-e5-v5, nvidia/llama-3.2-nv-embedqa-1b-v2) and reranking (nvidia/llama-3.2-nv-rerankqa-1b-v2). Deploy:

docker run -it --rm --gpus all \
  -e NGC_API_KEY=$NGC_API_KEY \
  -p 8000:8000 \
  nvcr.io/nim/nvidia/llama-3.2-nv-embedqa-1b-v2:latest

Call:

from openai import OpenAI
nim = OpenAI(base_url="http://nim:8000/v1", api_key="-")
nim.embeddings.create(model="nvidia/llama-3.2-nv-embedqa-1b-v2", input=[...])

Trade-offs: NIM gives fastest path to GPU-optimized models with no tuning, but you pay NVIDIA list pricing (or run on approved GPUs) and accept their update cadence.

Production Patterns

Sidecar vs Centralized

  • Sidecar (one TEI per app pod): simple, high locality, wasted GPU.
  • Centralized (pool of TEI/Triton pods behind a service): shared GPU pool, better utilization, adds RTT.

Centralized wins above ~5 app replicas.

Health, Readiness, Autoscaling

  • TEI: GET /health returns 200 when loaded; readyz waits for model load.
  • Triton: GET /v2/health/ready per-model; surface as K8s readiness probe.
  • Autoscale on text_embeddings_inference_queue_size / nv_inference_queue_duration_us from Prometheus, not raw CPU.

Warmup

Send 1–2 batched requests at startup so CUDA kernels compile before real traffic. Skip and you eat 2–5 s on the first query.

Token Budget Alignment

Match --max-batch-tokens to your chunk size distribution. If 95% of chunks are 512 tokens, max-batch-tokens = 16384 gives batches of 32 which is usually the sweet spot.

Benchmarks (indicative, tune for your workload)

StackModelGPUDocs/sp95 (ms)
sentence-transformers in-processbge-largeL4600120
TEI FP16bge-largeL4180050
TEI FP16bge-largeA10240040
Triton + TensorRT FP16bge-largeL4220035
Triton + TensorRT INT8bge-largeL4300030
NIM (nv-embedqa-e5-v5)L40S350025

Anti-Patterns

Anti-PatternFix
Running sentence-transformers in the web workerMove to TEI/Triton/NIM
FP32 on GPUUse FP16 or INT8 (TensorRT)
Tiny batch sizes (1–4)Tune max-batch-tokens to allow dynamic batching
One model per container on the same GPUTriton with instance_group shares VRAM
No warmupSend warmup request at pod start
Autoscaling on CPU when GPU is the bottleneckScale on queue depth / queue duration
Cross-encoder rerank called one pair at a timeBatch 16–64 pairs per request

Production Checklist

  • Embedding and reranker served on TEI/Triton/NIM (not in-process)
  • FP16 or INT8 precision verified against golden eval set
  • Dynamic batching configured with max_queue_delay_microseconds 1–3 ms
  • Warmup request on pod start
  • Prometheus metrics scraped: queue depth, p95 latency, batch size hist
  • K8s HPA scaling on queue metric, not CPU
  • Health + readiness probes wired
  • Separate model versions; shadow new versions before cutover
  • Per-model rate limits at the gateway to prevent GPU starvation
发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

2026年9月24日

分类

未分类

许可证

MIT

源路径

skills/rag-ops/tei-triton-serving

默认分支

main

最新提交

9496306

Tree SHA

fe4e2f1