
什么是 openjev-sglang?
openjev-sglang 是一个使用 SGLang 上的 Qwen3.6-35B-A3B 实现 TypeSafe/Jev HTTP API 的服务器。它会将分类问题转换为高效的首个 token 概率读取,而不是生成冗长的响应。
默认部署会在每个 NVIDIA B200 GPU 上运行一个 SGLang 容器。SGLang 0.5.19 提供 Rust 前端、基数缓存以及可中断的预填充 CUDA 图。另有一个独立的 Python 进程使用 FastAPI、uvloop、基于 Rust 的 Hugging Face 分词器,以及与本地主机上 SGLang 的异步连接池来处理公共 API。
不会向 TypeSafe 发送任何请求。公共端点提供评估 API,而 SGLang 的生成和管理路由则继续仅在本地主机上保持私有。
主要功能
- 三种评估类型: Noul 用于判断“是/否”概率,Choice 用于分类选择,Score 用于有序等级评分。
- 单 token 推理: 每个问题都根据答案标签的对数概率进行评估,不生成思维链。
- 共享前缀缓存: 在并发运行问题分支前,先预热公共提示词前缀。
- 最多 64 个问题: Choice 和 Score 问题默认支持 2 到 64 个答案。
- 结构化状态: 状态和指令可以是字符串、JSON 对象或数组。
- 运维端点: 内置健康检查、存活检查、模型目录、限制信息、OpenAPI 架构、Swagger UI 以及交互式 Scalar 参考文档。
- Modal 部署: 提供的应用支持自动扩缩容、持久化模型缓存、启动恢复以及缩容至零。
- 可配置的保护机制: 支持请求限制、并发控制、超时以及可选的 Bearer 身份验证。
前置条件
安装 uv;如果计划使用提供的云端部署,还需获取 Modal 账户。首次构建会导入较大的 SGLang 镜像,首次启动 GPU 时会下载模型权重并编译或捕获内核。
CUDA 依赖保留在 SGLang 容器中。在工作站上运行 uv sync 只会安装 API、部署工具和测试。
安装项目
克隆仓库,进入其目录并同步 Python 环境:
uv sync
如果当前机器尚未完成 Modal 身份验证,请执行设置:
uv run modal setup
运行临时 Modal 部署
如需进行端到端测试,可启动临时服务器、运行包含的推理检查,并在完成后关闭服务器:
uv run modal run modal_app.py
此命令会将冒烟测试报告保存为 smoke-result.json。报告包含启动等待时间、推理延迟以及可用的缓存使用信息。
部署稳定端点
将应用部署为持久化的 Modal 服务:
uv run modal deploy modal_app.py
部署会输出类似 https://YOUR-SERVER.us-west.modal.direct 的 URL。将其存储在环境变量中,以便访问:
export OPENJEV_URL="https://YOUR-SERVER.us-west.modal.direct"
默认的 Modal 端点有意保持未进行身份验证。它使用美国西部路由区域,并可在配置的美国区域中调度计算资源。没有明确的容器上限,实例在空闲五分钟后会缩容至零。
已缩容至零的服务器在启动期间会返回 HTTP 503。包含的冒烟测试命令会自动重试这些启动响应。
针对已部署的 URL 运行冒烟测试:
uv run openjev smoke "$OPENJEV_URL"
冒烟测试会测试全部三种答案类型,检查包含 64 个答案的问题,执行基本语义检查,并验证 65 个答案会被拒绝。
提交首次评估
主要路由是 POST /v1/systemone。下面的请求使用 Noul、Choice 和 Score 问题对客户消息进行分类:
curl "$OPENJEV_URL/v1/systemone" \
-H 'Content-Type: application/json' \
-d '{
"model": "jev-latest",
"state": [
{"role": "system", "content": "You are a support assistant."},
{"role": "user", "content": "I was charged twice. Please refund the duplicate."}
],
"questions": {
"refund": {
"type": "noul",
"instructions": "Does the user request a refund?"
},
"department": {
"type": "choice",
"instructions": "Which department should handle this?",
"criteria": {
"billing": "Payments and refunds",
"technical": "Software bugs"
}
},
"urgency": {
"type": "score",
"instructions": "How urgent is the request?",
"criteria": ["Routine", "Urgent", "Emergency"]
}
}
}'
也可以将 JSON 保存在文件中,然后无需进行 shell 引号转义即可提交:
curl "$OPENJEV_URL/v1/systemone" \
-H 'Content-Type: application/json' \
--data-binary @examples/request.json
了解问题类型
- Noul: 返回
yes的归一化概率。 - Choice: 返回概率最高的选项以及完整的概率分布。响应中会保留原始选项名称。
- Score: 返回从零开始的期望等级,该值根据每个等级的索引及其概率计算,并同时返回图例。
选项会以字母标签及其描述的形式呈现给模型。响应键通常对模型隐藏。如果选项描述为 null,则使用其键来提供含义。
提供状态和指令
state 和 instructions 均接受字符串、JSON 对象或数组。聊天消息列表,或包含恰好为 {"messages": [...]} 的对象,会使用模型原生的聊天模板进行渲染。
原始消息的角色和对象会保留,每个分类问题都会作为另一条用户消息添加。其他结构化值会完整序列化到用户消息中。包含 messages 及其他字段的对象也会保留,以免元数据被静默丢弃。
聊天状态仅支持文本内容。不支持图像、音频和视频内容。
探索服务端点
POST /v1/systemone执行 Noul、Choice 和 Score 评估。GET /v1/models返回包含 TypeSafe 和 OpenAI 风格字段的模型目录。GET /v1/limits报告接入限制。GET /health报告就绪状态、SGLang 健康状况和启动耗时。GET /health/live检查 API 进程是否存活。GET /打开 Scalar API 参考页面,其中包含可编辑的请求示例。GET /docs打开内置的 Swagger UI。GET /openapi.json返回生成的 OpenAPI 架构。
请求中的模型名称 jev-latest 是兼容性别名。默认的公开模型 ID 为 Qwen/Qwen3.6-35B-A3B,而 NVIDIA 的仓库在内部用作权重来源。
推理工作方式
- API 会校验架构、答案数量、请求体大小、上下文长度和总令牌预算。
- API 在禁用思考的情况下使用原生聊天模板渲染一次,然后将共享前缀与分别进行令牌化的问题后缀分离。
- API 将公共前缀以
max_new_tokens=1发送到 SGLang。采样出的令牌会被丢弃,使该前缀可供机会式 radix 缓存复用。 - API 会为每个问题并发提交一个分支。每个分支包含前缀、问题后缀、助手标头和
Answer:标记。 - API 仅请求有效答案标签的对数概率,并使用稳定的 softmax 对其重新归一化。
对于 N 个问题,服务器会发起 N + 1 次单令牌调用:一次缓存预热调用,以及每个问题各一次调用。生成的令牌会被忽略,因此不存在自回归续写或生成的思维链。未启用推测解码。
标签从 A 到 Z,随后使用经过验证的单令牌组合,例如 AA 和 AB。启动期间会使用活动令牌器检查全部 64 个标签。这样可以避免使用 10 或 64 之类的多令牌数字标签。
了解用量和缓存指标
响应标头 x-openjev-prefix-tokens 报告请求的公共前缀大小。当 SGLang 提供缓存计数时,x-openjev-cached-tokens 包含各分支缓存命中的总和。
SGLang 0.5.19 的 Rust 前端不会公开这些缓存计数。在该配置下,标头会缺失,冒烟测试报告会记录 null,而不是零。调度器日志仍可显示实际的缓存命中情况。Server-Timing 标头会分别记录提示词准备、共享预填充和分支推理的耗时。
usage.input_tokens 是预热调用和分支调用所报告的完整提示词数量之和,其中包括缓存令牌。usage.output_tokens 为 N + 1。这些数值描述的是后端活动,而不是唯一计算的令牌数或 TypeSafe 计费估算。
配置限制和身份验证
默认接入限制包括:64 个问题;每个 Choice 或 Score 问题 2 至 64 个答案;2 MiB JSON 请求体;每个分支(包括输出)32,768 个令牌;提交的输入令牌总数 262,144;16 个并发评估;以及 64 个并发后端调用。
无效请求会在推理前返回 HTTP 422。超过大小限制的请求体返回 413,过载返回带有 Retry-After 的 529,后端超时返回 504。失败或取消的评估会取消同级分支,并尝试在 SGLang 中中止这些分支。
可通过 OPENJEV_* 环境变量进行配置。常用设置包括:
OPENJEV_MODEL:默认为nvidia/Qwen3.6-35B-A3B-NVFP4。OPENJEV_SERVED_MODEL_NAME:覆盖公开模型 ID,并将其传递给 SGLang。OPENJEV_REVISION:选择固定的检查点版本。OPENJEV_FRONTEND:默认为rust;python是显式备用选项。OPENJEV_MAX_INPUT_TOKENS:默认为32768。OPENJEV_MAX_TOTAL_INPUT_TOKENS:默认为262144。OPENJEV_MAX_CONCURRENT_REQUESTS:默认为16。OPENJEV_MAX_CONCURRENT_BRANCHES:默认为64。OPENJEV_REQUEST_TIMEOUT:默认为120秒。OPENJEV_TEMPERATURE:默认为1.0,会影响标签归一化。OPENJEV_API_KEY:为公共 API 启用可选的 Bearer 身份验证。OPENJEV_BACKEND_API_KEY:为 SGLang 配置单独的可选 Bearer 密钥。
Modal 启动器会自动从本地环境转发 OPENJEV_PROFILE、OPENJEV_FRONTEND 和 OPENJEV_SERVED_MODEL_NAME。将其他远程设置添加到 image.env(...),或使用 Modal Secret 存储凭据。
使用现有 SGLang 后端在本地运行
要使用已运行的后端,请将 API 指向其本地 URL:
uv run openjev serve --connect http://127.0.0.1:30000
后端必须使用匹配的模型和分词器版本,并支持所选 token 的对数概率、基数缓存以及足够的上下文长度。
如果 B200 主机或容器在独立的 Python 环境中安装了 SGLang 0.5.19,请显式提供其解释器:
uv run openjev serve --sglang-python /path/to/sglang/bin/python
开发与验证
使用随附的命令运行离线测试、分词器集成检查、代码检查和模式生成:
uv run pytest
uv run pytest -m integration
uv run ruff check .
uv run openjev schema
常规测试会离线覆盖单元行为和 API 行为。集成测试使用真实分词器,并会下载一个较小的 Hugging Face 文件,但不需要 GPU。模式生成同样不需要 GPU 或模型下载。
高级部署提示
保持 GPU 容器处于热状态
Modal 在服务空闲五分钟后会将其缩减至零。若持续较低的启动延迟比避免 GPU 空闲使用更重要,请在 modal_app.py 中设置 min_containers=1。
预留首次启动初始化时间
首次启动 GPU 时会下载权重并编译或捕获内核。模型权重、SGLang 调优数据和 Triton 编译数据会持久化在 openjev-huggingface Modal Volume 中。后续启动会复用这些文件,但启动时仍会执行 CUDA 图捕获。
将缓存复用视为机会性的优化
基数复用不是按请求固定的 KV 会话。混合 Qwen 模型中的递归状态、缓存页边界、缓存压力和并发流量都可能降低命中率。后端使用 --mamba-radix-cache-strategy extra_buffer。
谨慎解读置信度
OpenJev 将置信度定义为 1 - H(probabilities) / log(number_of_options),并将结果限制在零到一之间。均匀概率会产生零置信度,而集中于单一选项的概率分布会产生一。
这些概率以所提供的选项为条件,并可能受到提示词措辞和标签顺序的影响。它们不是经过校准的正确性估计。
为后端故障做好准备
如果 SGLang 意外退出,API 也会退出。随后,Modal 启动器会终止容器,以便 Modal 替换它,而不是让公共 HTTP 进程继续连接到已失效的推理后端。正常关闭时,两个监视器都会被正常禁用。
结语
openjev-sglang 为在 Qwen3.6 上运行兼容 Jev 的分类任务提供了一种专注的方式,并能高效读取单 token 概率。其 Modal 配置支持可扩展的 B200 部署,而本地命令则支持测试和连接现有 SGLang 服务。请从冒烟测试开始,验证限制和身份验证设置,并随着流量增长监控耗时和缓存标头。
