模型管理
HeteroFlow 是本站的异构算力与大模型调度产品。模型管理。模型管理 模型管理是 HeteroFlow 中"从一份模型文件到一个可调用 API 服务"的最短路径。散落在各 GPU 节点磁盘上的大模型文件(HuggingFace 权重目录、GGUF 单文件、乃至节点上已在运行的推理容器)由 Agent 自动扫描上报,平台汇聚成发现列表;管理…
模型管理
模型管理是 HeteroFlow 中"从一份模型文件到一个可调用 API 服务"的最短路径。散落在各 GPU 节点磁盘上的大模型文件(HuggingFace 权重目录、GGUF 单文件、乃至节点上已在运行的推理容器)由 Agent 自动扫描上报,平台汇聚成发现列表;管理员点一次"添加",平台在同一个请求里完成模型注册、引擎选择、服务编排、端点创建、API Key 发放与路由注册——请求返回时,服务已经可以被 OpenAI SDK 直接调用。
本模块管理三类彼此衔接的资产:模型注册表(ModelInfo,模型元数据台账)、模型路由(ModelRoute,模型名到推理服务的流量映射,网关选路依据)与模型级路由策略(model=auto 智能路由下每模型名的参与开关、用途标签与权重)。手动注册只登记元数据;"扫描发现 + 一键收录"才会自动创建推理服务。引擎探测与 GPU 算力选引擎见《推理服务》篇;路由决策在网关的执行细节(负载均衡、灰度、会话粘性)见《推理网关》篇。
功能总览
| 能力 | 一句话说明 | 状态 |
|---|---|---|
| 模型注册 CRUD | 手动注册/更新/删除/按名查询,多租户按租户隔离,节点引用做可见性校验 | 已实现 |
| 模型自动发现 | Agent ModelScanner 定时扫描宿主机模型目录,随心跳上报 DiscoveredModel,前端一键收录 | 已实现 |
| 引擎可用性探测 | Agent 每 20 次心跳重探 Docker 镜像/pip 包并无条件回灌路由器,inference_engines 是选引擎的第一权威依据 | 真机验证 |
| GPU 感知一键建服务 | 按节点引擎可用性 + GPU 算力从模板链 vllm→sglang→llamacpp 选引擎,P100 等旧卡自动落 llama.cpp | 真机验证 |
| 模型路由 ModelRoute | 创建服务自动注册(priority=0/weight=100),支持手动管理与优先级/权重决策 | 已实现 |
| 模型级路由策略 | model=auto 下按模型名配置参与开关、用途(chat/completion/embedding)与质量/速度/成本权重 | 已实现 |
| 模型与推理服务联动 | 注册即可调用;同名模型多节点部署时复用服务组追加副本;模板链供故障恢复跨引擎重建 | 真机验证 |
| API Key 发放 | 一键建服务自动生成 sk- 前缀 Key,明文仅展示一次,库中只存 sha256 哈希 | 已实现 |
一、模型注册 CRUD
模型注册表是平台的模型元数据台账:每行记录一个"模型名 + 节点 + 路径"三元组。同一模型名可以有多行(部署在多个节点上),按名查询会返回全部同名条目。
ModelInfo 字段
| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 模型唯一标识(UUID) |
name | string | 模型名称(可重复,同名多节点部署) |
version | string | 版本号(可选) |
node_id | string | 所在节点 ID |
model_path | string | 模型文件在节点上的绝对路径 |
node_name / node_ip | string | 节点名称与 IP(展示用,可选) |
source | string | auto(自动扫描收录)/ 其他(手动添加) |
model_type | string | 模型架构类型,如 qwen2、llama、chatglm(可选) |
size_bytes | int | 模型文件大小(字节,可选) |
tenant_id | string | 归属租户(创建时取自调用方上下文) |
created_at / updated_at | string | 创建/更新时间 |
各操作的行为边界
| 操作 | 行为 | 联动语义 |
|---|---|---|
手动创建 POST /api/v1/models | 仅登记元数据,不自动建推理服务 | 与一键收录(见第三节)的关键区别 |
按名查询 GET /api/v1/models/by-name?name= | 返回所有同名模型(跨节点) | 多节点部署感知的基础 |
详情 GET /api/v1/models/{modelID} | 路径参数可以是 UUID(精确)或模型名(按名,命中多个返回列表) | — |
更新 PUT /api/v1/models/{modelID} | 仅更新提供的字段,空字段保留原值 | node_id 变化时重新做节点可见性校验 |
删除 DELETE /api/v1/models/{modelID} | 删除前做多租户归属校验(不能删别的租户的模型) | 不级联:已建的推理服务、端点与路由不受影响,需分别删除 |
节点可见性校验:POST /api/v1/models、PUT(仅 node_id 真的变化时)与一键收录引用的节点必须对调用方可见——共享节点对所有人可见,私有节点只对属主租户可见,节点不存在同样 404(不泄露其他租户有哪些节点)。校验发生在模型落库之前,杜绝"模型行挂在别的租户私有节点上"的脏数据。多租户隔离:非平台管理员只能看到/操作本租户的模型,按名查询也只返回本租户的同名模型,跨租户按 ID 读取一律 404。
二、模型自动发现(Agent ModelScanner)
模型自动发现回答一个问题:每个节点磁盘上到底有哪些模型。Agent 内置模型扫描器(agent/pkg/modelscanner),不需要人工登记。
扫描目录配置
| 配置来源 | 优先级 | 说明 |
|---|---|---|
Agent 配置文件 model_scan_dirs | 最高 | 多目录列表 |
环境变量 HETEROFLOW_MODEL_SCAN_DIRS | 中 | 多目录用逗号分隔,如 /data/models,/shared/models |
| 默认值 | 兜底 | 仅模型缓存目录 /data/heteroflow/model-cache |
两个要点容易踩坑:
model_scan_dirs是替换语义,不是追加:一旦配置,默认缓存目录与环境变量都不再生效。仍想扫缓存目录必须把它显式写进列表。/data/models无条件追加:无论上面怎么配置,这个目录始终会被扫描,无法关闭。
每个扫描目录下若存在 hub、models/hub、models 子目录会自动追加,并支持 HuggingFace hub 缓存布局(models--<org>--<name>/snapshots/<hash>/)。
识别的模型形态
| 形态 | format | 识别条件 |
|---|---|---|
| HuggingFace 权重目录 | huggingface | 目录下有 config.json 或 tokenizer_config.json,且同目录存在权重文件(.safetensors / .bin / .pt) |
| GGUF 文件 | gguf | 目录或扫描根下的 .gguf 文件;分片文件(model-00001-of-00003.gguf)自动合并为一组 |
| 运行中的推理容器 | api | Agent 额外发现节点上已在运行的 vLLM/SGLang/llama.cpp 容器及其经 /v1/models 加载的模型,model_type 形如 served-by:<engine> |
模型架构类型从 config.json 的 _model_type 或 model_type 字段推断(如 qwen2、llama、chatglm)。
扫描节奏与去重
| 类型 | 触发 | 范围 |
|---|---|---|
| 全量扫描 | Agent 启动时 | 所有扫描目录 |
| 增量扫描 | 每 12 次心跳(默认心跳 5 秒,约 1 分钟) | 对比目录 mtime,仅重扫有变化的目录 |
扫描状态持久化在 {cacheDir}/.scan-state.json(记录每个目录的 mtime),Agent 重启后不必重复全量扫描。扫描结果随心跳以 discovered_models 上报,Server 侧汇聚为发现缓存(DiscoveredModelsCache)。
发现列表的可见性
GET /api/v1/models/discovered 叠加三层过滤:
1. 节点可见性:非平台运营者只返回共享节点与本租户私有节点的扫描结果;查不到节点记录的条目一律不返回(fail-closed);平台运营者看全部。
2. 在线过滤:仅返回 online/busy 节点的结果,离线节点的模型不可见。
3. 收录标记:已在注册表中的条目标记 added: true,前端据此区分"已添加/未添加"。
三、一键部署推理全流程(autoCreateInferenceService)
一键收录是本模块的核心价值:POST /api/v1/models/discovered/add 一个请求,把选中的发现模型变成可调用的推理服务。
前置:引擎可用性探测
选引擎的依据是节点心跳上报的 inference_engines 列表,它的可信度来自 Agent 侧的持续探测:
- Agent 每 20 次心跳(约 100 秒)重新探测一次本地 Docker 镜像与 pip 包,并无条件回灌执行器的引擎路由器——避免"控制面认为引擎可用、Agent 侧路由器还冻结在启动时快照"的静默降级。
- 探测是 GPU 感知的:vLLM 的 Docker 镜像只在存在 CC ≥ 7.5 的 NVIDIA GPU 时才被认作可用,旧卡上 pip 安装的 vLLM 标记为
vllm-legacy。 - 镜像判定是精确匹配:本地必须存在与模板完全同名同 tag 的引用才算可用,只有内网私仓前缀的同名副本判为不可用——任务派发用的是裸引用,Agent 不会自动复用前缀不同的本地副本,内网仓库分发需
docker tag成裸引用一次。
引擎/镜像选择(GPU 感知模板链)
控制面按"节点心跳上报的引擎可用性 + GPU 算力"从默认模板链中顺序选择:
模板链(按序尝试):
1. vllm vllm/vllm-openai:latest 门槛:NVIDIA CC ≥ 7.0
2. sglang lmsysorg/sglang:latest —
3. llamacpp heteroflow/inference-llamacpp:gpu —(平台自建 P100 兼容镜像)
选定条件 = 引擎在节点可用列表中 且 满足算力门槛;
全部不匹配 → 在创建任何资源之前明确失败(不进恢复循环)。- P100(CC 6.0)等旧卡会被 vLLM 的算力门槛拦下,自动落到 llama.cpp 兜底——与"P100 与 vLLM latest 不兼容"的真机结论一致。非 NVIDIA 卡或未上报算力时不按 CC 拦截,引擎可用列表是第一权威。
- 链首是 vLLM(2026-09-12 调整,此前 SGLang 在前):SGLang 官方镜像在 L20 + 驱动 580 环境缺完整 CUDA 工具链,flashinfer JIT 必然失败;vLLM 模板已在多节点真机反复验证。
- 链里另有一条
trtllm条目仅作文档化(客户自带已编译引擎,只能显式指定),所有自动选择路径都跳过它。
一次请求创建的四类资源
一键收录(POST /api/v1/models/discovered/add)
├─ ① 模型入库(source=auto)
├─ ② TaskGroup:名称=模型名,task_type=inference,
│ 模板链写入 inference_templates(供恢复循环跨引擎 fallback 重建)
├─ ③ Endpoint:<模型名>-api(端口 8000,主机端口自动避让冲突)
│ + API Key:sk-{UUID}(库中仅存 sha256 哈希)
├─ ④ rank-0 Task:镜像/引擎取自所选模板,model_path 落库,
│ 钉扎到模型所在节点(requested_node_id),提交调度器分配 GPU
└─ ⑤ ModelRoute:priority=0(最高),weight=100
→ 此后按模型名经 /v1/chat/completions 即可调用调度器分配 GPU、Agent 拉起容器后,服务即可用 OpenAI 兼容方式调用(Base URL http://<server>:3333/v1)。
失败、并发与配额
| 场景 | 行为 |
|---|---|
| 节点选不出可用引擎 | 模型注册成功,服务不创建,inference.error 说明原因(选不出引擎在创建任何资源前明确失败,不留半成品) |
| 租户非 active / GPU 或端点配额不足 | 模型注册仍成功(HTTP 200),inference.error 非空,error_code 为 quota_exceeded / tenant_inactive;配额检查与建任务/建组共用同一套检查与豁免口径 |
| 端点重名(此前建过服务且旧组已 failed/stopped) | error_code = endpoint_name_conflict(409 语义),需先删同名端点或换模型名;模型注册本身成功,整体 HTTP 仍为 200 |
| 并发双击/重试 | 同一(租户, 模型名)的创建全程串行化(进程内互斥 + 跨实例数据库咨询锁),拿不到锁按失败处理,不会建出重复任务 |
| 建服务中途出错 | 已创建的资源回滚删除(任务、任务组、端点),不留孤儿资源 |
多节点扩展
同名模型已在本租户有活跃 TaskGroup 时再次一键收录:复用该组,为新节点追加一个 Task(递增 rank),更新组的副本计数,并幂等补建缺失的 ModelRoute。复用判据是严格同租户归属——不同租户的同名模型各建各的组。
四、模型路由 ModelRoute
ModelRoute 是"对外模型名 → 推理服务"的流量映射,网关收到 /v1/chat/completions 请求后按模型名查路由决定转发目标。
自动注册
创建推理服务(含一键收录)时自动注册一条:model_name 优先取请求体的 model_name(对外调用标识),未传时回退取服务名称;engine 从镜像名推断;priority=0(最高)、weight=100、status=active,租户继承 TaskGroup。两条特殊规则:
- 训练组不注册路由:
task_type=training的组被显式挡掉,否则训练组会带着 active 路由污染对话接口的选路。 - 复用/唤醒既有组时幂等补建:按(租户, 模型名)判定是否已有指向该模型名的路由,没有就补一条。同一个服务组因此可能同时挂着多条不同名字、不同租户的路由——这是跨租户复用的正常结果。
手动管理
| 接口 | 说明 |
|---|---|
GET /api/v1/model-routes | 列出路由 |
POST /api/v1/model-routes | 创建(非平台运营者:priority 不得为负、模型名与 default 租户已有路由同名返回 409、只能指向自己创建的任务组) |
PUT /api/v1/model-routes/{id} | 更新权重/优先级(不改模型名时不判同名) |
DELETE /api/v1/model-routes/{id} | 删除 |
路由决策
同一模型名的多条路由按 priority 升序分组(0 最高),同组内按 weight 加权分流,高优先级组全部不健康时整体降级到下一组:
模型 "llama-3" 的 3 条路由:
Route A: priority=0, weight=80, engine=vllm (生产)
Route B: priority=0, weight=20, engine=vllm (金丝雀)
Route C: priority=1, weight=100, engine=llamacpp (降级备选)
正常流量:80% → A,20% → B
A、B 都不健康:100% → C这是灰度发布(生产/金丝雀按权重切流)与故障降级(备选组兜底)的基础机制;组内副本间的负载均衡算法、会话粘性与健康摘除由网关执行,见《推理网关》篇。
五、模型级路由策略(model=auto 智能路由)
模型级路由策略作用于智能路由:调用方把请求体里的 model 写成 auto,由平台在调用方租户内自动挑一个模型承接。每个模型名一份策略行,按(tenant_id + model_name)存储。
三层配置与三种策略
智能路由默认关闭(opt-in),配置分三层:密钥级覆盖 > 全局默认 > 关闭(全局开关与默认策略在推理控制面板设置,密钥级覆盖在"我的 API Key"页配置)。开启后按三种策略打分选模型:
| 策略 | 打分依据 |
|---|---|
quality | 模型的质量静态权重(0-100) |
speed | 速度静态权重,叠加近 30 分钟平均延迟惩罚 |
cost | 成本静态权重(目前只是占位,平台未内置真实价格数据) |
策略字段
| 字段 | 取值 | 作用 |
|---|---|---|
routing_enabled | bool | 关闭 = 该模型永不被 auto 选中(含降级链候选);手动指定该模型名调用不受影响 |
purpose | chat / completion / embedding / 未标注 | auto 按请求接口挑同用途的模型 |
quality_weight / speed_weight / cost_weight | 整数 0-100,缺省 50 | 三种策略的静态排序分 |
无策略行 = 默认全量参与(权重全 50、用途未标注)。
用途过滤矩阵
| 请求接口 | 接受的用途 | 说明 |
|---|---|---|
/v1/chat/completions | 仅 chat | — |
/v1/completions | completion 或 chat | 补全模型优先,对话模型可兜底 |
/v1/embeddings | 仅 embedding | — |
已标注且不匹配的模型一律排除;已标注且匹配的优先;未标注的模型只在没有任何已标注且匹配的模型时回退使用(响应头 X-Routing-Purpose-Fallback: unlabeled 可见)。全部不匹配返回 503 no_auto_candidates_for_purpose。
存量模型全部是"用途未标注":系统不按模型名或架构名猜用途,需要管理员逐个标注后 auto 才按用途区分;不标注时选路行为与引入用途之前完全一致。手动指定模型名调用不受用途影响。
执行语义
auto 请求单次最多尝试 3 个候选模型,主选异常按序降级;实际承接模型通过响应头 X-Routed-Model 透出。候选模型按调用方租户收窄,同名路由内按 priority 升序稳定排序后分组——与手动指定路径的裁决口径一致。错误码:no_auto_candidates(503,无候选)、no_auto_candidates_for_purpose(503)、auto_routing_all_failed(502,候选全失败)。
六、模型与推理服务联动
- 注册即可调用:一键收录的模型在服务创建完成那一刻起就可按模型名经 OpenAI 兼容接口调用(自动注册的 ModelRoute 已生效)。
- 多节点部署感知:模型列表按名聚合展示各节点副本;再往新节点一键收录时自动并入同一服务组。
- 故障恢复联动:模板链写入 TaskGroup 的
inference_templates,服务故障进入恢复循环时可跨引擎 fallback 重建(如 vLLM 副本反复失败后降级用 llama.cpp 模板重建),不至于在单一引擎上死循环。 - 反向引用:训练详情页的"一键部署推理"按钮消费
GET /api/v1/nodes/{id}/inference-template查询节点真正能跑的模板(同一套引擎选择逻辑的只读暴露,训练产出的 checkpoint 落盘后可直接部署成推理服务)。
七、API Key 生成与展示
| 机制 | 说明 |
|---|---|
| 生成时机 | 一键收录自动创建 Endpoint 时生成,格式 sk-{UUID} |
| 存储形式 | 库中仅存 sha256 哈希,明文无法找回 |
| 展示次数 | 明文只在 POST /api/v1/models/discovered/add 的响应中出现一次,前端弹窗展示后不再可见 |
| 复用语义 | 响应 reused=false 且 api_key 非空 = 新建服务;reused=true 且 api_key 为空 = 复用已有服务,Key 仍是首次创建时返回的那个 |
| 后续查看 | 服务运行期的 Key 状态经推理服务页面的"API Key 查看"入口管理(见《推理服务》篇) |
页面功能介绍
模型管理面板嵌入推理服务页面(仅平台管理员可见——后端写接口要求 system:write 权限,普通成员不渲染面板,避免展示点了就 403 的按钮)。
面板头部按钮
| 按钮 | 行为 | 调用 API |
|---|---|---|
| 扫描模型(绿色) | 按钮变"扫描中...",展开扫描结果区,拉取全部可见节点的发现模型 | GET /api/v1/models/discovered |
| + 添加模型 / 取消(蓝色) | 展开/收起手动添加内联表单 | —(提交时 POST /api/v1/models) |
| 收起(扫描结果头部) | 隐藏扫描结果区(纯前端状态) | — |
手动添加表单
| 字段 | 必填 | 说明 |
|---|---|---|
| 模型名称 | 是 | 占位"例如: glm-5.1" |
| 版本 | 否 | 占位"v1.0" |
| 节点 | 是 | 下拉仅列 online/busy 节点,格式"节点名 (IP)" |
| 模型路径 | 是 | 节点上的绝对路径,占位"/shared/models/glm-5.1" |
必填为空时提交按钮半透明不可点击;成功后清空表单、收起并刷新模型列表。手动添加不会自动建服务。
扫描结果表
每行展示发现模型(名称/类型/大小/节点),状态列三色判定:
| 状态 | 判定条件 | 操作列 |
|---|---|---|
| 已添加(绿) | 已在注册表(added: true) | "-" |
| 未添加(黄) | 未注册,且节点引擎与模型格式兼容 | 绿色"添加"按钮 |
| 不可用(红) | 未注册,但节点引擎不支持该格式 | "-"(悬停提示原因) |
兼容性判定:GGUF 需要 llama-cpp/llamacpp 引擎;HuggingFace 需要 vllm/vllm-legacy/sglang 引擎;其他 format 一律视为兼容。点"添加"触发一键收录(POST /api/v1/models/discovered/add),成功后该行转"已添加"并刷新模型列表。
模型列表(8 列)
模型名称、来源(蓝"自动扫描"/灰"手动添加"标签)、类型、大小(GB/MB/KB 自适应,无数据显示"-")、节点(名 + IP)、路径(等宽字体超长截断)、智能路由、操作(红色"删除"按钮,二次确认后 DELETE /api/v1/models/{id})。
智能路由列依次渲染:用途徽标(绿"对话 (chat)"/"文本补全 (completion)"/"向量化 (embedding)",未标注为灰虚线框"用途未标注")→ 策略状态徽标(红"不参与"= 关闭了参与开关;蓝"自定义"= 改过权重;灰"默认参与"= 无策略行)→ "策略"按钮(打开编辑弹窗)。
策略编辑弹窗:参与智能路由开关(关闭 = 永不被 auto 选中,手动指定不受影响)、用途下拉(未标注/chat/completion/embedding)、质量/速度/成本三个权重输入(0-100 整数,前端校验)、"恢复默认"(删除策略行,回到默认——已标注的用途也会被清掉)。保存 PUT /api/v1/models/routing-policies/{name},恢复默认 DELETE 同一地址。
策略加载失败态:拉取策略失败(不阻塞模型列表)时,路由列显示橙色"策略未知"徽标与"重试"按钮,并禁用编辑——不按"全部默认"显示,避免把被设为"不参与"的模型误显示成"默认参与",也避免弹窗按默认值初始化后一保存覆盖真实策略。
按对外模型名设置用途/策略:表格行来自模型注册表,而策略按推理服务对外模型名(请求体 model 字段)生效——创建服务时自定义过对外名的模型在表格里没有对应行。面板底部为此提供独立输入区:输入对外名(带输入建议)→"编辑策略"打开同一弹窗;已设策略但注册表无同名行的对外名单独列出可再编辑。名字恰为保留名 auto 时前端直接提示不发请求;含 / 的对外名(HF 风格 org/name)自动改走 ?name= 查询形式(路径段里的 %2F 会被解码成路径分隔符)。策略按租户存储:平台管理员在"查看租户"选中某租户时,面板读写的都是该租户的策略。
后端逻辑概念表
| 概念 | 含义 |
|---|---|
| ModelInfo | 模型注册表行:模型名 + 节点 + 路径的元数据三元组,带租户归属 |
| DiscoveredModel / ModelScanner | Agent 扫描发现的一个模型(名称/路径/大小/format/model_type),随心跳上报;扫描器启动全量 + 每 12 次心跳增量(mtime 对比),状态持久化 .scan-state.json |
| DiscoveredModelsCache | Server 侧汇聚各节点上报的发现结果,GET /models/discovered 的数据源 |
| 引擎可用性探测 | Agent 每 20 次心跳重探 Docker 镜像/pip 包并无条件回灌路由器,inference_engines 随心跳上报 |
| 精确匹配裸 tag | 镜像判定规则:本地必须存在与模板完全同名同 tag 的引用,私仓前缀副本不判可用 |
| 模板链(DefaultInferenceTemplates) | vllm→sglang→llamacpp 的默认引擎优先序,按节点可用性 + 算力顺序选定 |
| MinGPUCC / ExplicitOnly | 模板最低算力门槛(vLLM 为 7.0,P100 被拦落 llama.cpp)/ 仅可显式指定的条目(trtllm),自动路径跳过 |
| autoCreateInferenceService | 一键收录的后端执行体:选引擎 → 建组/端点/任务/路由的完整编排,失败回滚 |
| inference_templates | 模板链写入 TaskGroup 的副本,恢复循环据此跨引擎 fallback 重建 |
| ModelRoute | 对外模型名到推理服务的流量映射,priority 分组 + weight 加权 + 健康降级 |
| resolveRoutingModelName / ensureModelRoute | 路由模型名裁决(优先 model_name 回退服务名)/ 复用服务组时按(租户, 模型名)幂等补建路由 |
| 模型级路由策略 / purpose | model=auto 下按模型名的参与开关 + 用途 + 三权重,按(租户, 模型名)存储;purpose 为用途标签(chat/completion/embedding/未标注) |
| (租户, 模型) 咨询锁 | 一键创建的跨实例串行化(advisory lock),防并发建出重复服务 |
| sha256 Key 落库 | API Key 明文只在创建响应出现一次,库中只存哈希 |
API 参考
模型接口均需 JWT 认证:读操作要求 nodes:read,写操作(创建/更新/删除/一键收录/策略写入)要求 system:write(即平台管理员)。路由策略支持平台运营者以 ?tenant_id= 代其他租户配置。
发现列表
GET /api/v1/models/discovered{
"code": 0,
"data": [
{
"name": "qwen2-7b",
"path": "/data/heteroflow/model-cache/qwen2-7b",
"node_id": "node-1",
"node_name": "gpu-node-1",
"node_ip": "192.168.1.10",
"size_bytes": 15000000000,
"format": "huggingface",
"model_type": "qwen2",
"available_engines": ["vllm", "llama-cpp"],
"added": false
}
]
}仅返回可见且在线节点的结果;available_engines 是前端兼容性判定与一键建服务选引擎的依据。
一键收录(含自动建服务)
POST /api/v1/models/discovered/add
Content-Type: application/json
{ "name": "qwen2-7b", "node_id": "node-1", "path": "/data/heteroflow/model-cache/qwen2-7b" }{
"code": 0,
"data": {
"model": { "id": "…", "name": "qwen2-7b", "source": "auto", "…": "…" },
"inference": {
"task_group_id": "tg-xxxx",
"api_key": "sk-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"reused": false
}
}
}inference.error 非空表示服务未建成(模型注册本身成功),附 error_code(quota_exceeded / tenant_inactive / endpoint_name_conflict)与 http_status。返回的 api_key 是明文 Key 的唯一一次出现,请立即保存。
模型级路由策略
GET /api/v1/models/routing-policies # 列出策略行(可带 ?tenant_id=)
PUT /api/v1/models/routing-policies/{name} # 写入/覆盖某模型策略
DELETE /api/v1/models/routing-policies/{name} # 删除(回到默认,用途回到未标注)
GET/PUT/DELETE /api/v1/models/routing-policies?name=… # 名字走 query(含 "/" 的对外名必须用这种形式)PUT 请求体(部分更新,未知字段 400):
{ "routing_enabled": true, "quality_weight": 80, "speed_weight": 40, "cost_weight": 50, "purpose": "chat" }权重须为 0-100 整数(越界 400);purpose 取 chat / completion / embedding,null 清除为未标注,不传沿用当前值。带 / 的对外模型名(如 Qwen/Qwen2.5-7B)在路径形式下返回 400,须用 ?name=(优先于路径段)。
模型 CRUD 与手动路由
| 接口 | 方法 | 说明 |
|---|---|---|
/api/v1/models | GET / POST | 列表(可按 node_id、source 过滤)/ 手动注册 |
/api/v1/models/by-name?name= | GET | 按名查询,返回全部同名(跨节点) |
/api/v1/models/{modelID} | GET / PUT / DELETE | 详情(UUID 或名称)/ 更新 / 删除 |
/api/v1/model-routes | GET / POST | 路由列表 / 手动创建(约束见第四节) |
/api/v1/model-routes/{id} | PUT / DELETE | 更新权重/优先级 / 删除 |
/api/v1/nodes/{id}/inference-template | GET | 节点当前可用的推理模板选项(引擎选择逻辑只读暴露) |
已知边界
1. 成本权重是占位:cost 策略与 cost_weight 目前没有真实价格数据支撑,只按静态权重排序;计价能力另行立项。
2. 智能路由默认关闭:model=auto 是 opt-in,需在推理控制面板开全局开关;存量模型用途全部"未标注",不标注则 auto 不按用途区分(选路与引入用途前一致)。
3. SGLang 在部分环境不可用:L20 + 驱动 580 环境官方镜像缺 nvcc、flashinfer JIT 必然失败;模板链已把链首选回 vLLM,SGLang 保留在链中供显式指定或仅装 SGLang 的节点。
4. 内网私仓镜像不被识别为可用:引擎探测精确匹配裸 tag,只有私仓前缀副本时需手动 docker tag 成裸引用(Agent 打 WARN 日志给出提示命令)。
5. 手动注册不建服务:POST /api/v1/models 只登记元数据,自动建服务仅"扫描发现 + 一键收录"路径触发。
6. 端点重名时模型注册与建服务结果分离:endpoint_name_conflict 下模型行已落库、服务未建,整体响应仍为 200——调用方必须检查 inference.error,不能只看 HTTP 状态码。
7. API Key 明文不可找回:库中仅存 sha256 哈希,遗失只能删除端点重建服务。
8. 删除模型不级联:删除注册行不影响已建推理服务、端点与路由;删除服务也不清理模型行,两边各自管理。
9. 扫描目录配置是替换语义:配置 model_scan_dirs 后默认缓存目录失效(/data/models 除外,始终扫描)。
10. Transformers 已退役:2026-10-02 起各入口 400、存量任务显式失败,从未提供过真推理服务。
<!-- 状态标注说明:本文能力状态口径——"真机验证"指在真实 GPU 节点端到端运行并确认(引擎探测与一键建服务在 NVIDIA T4/P100/A10/A800/L20 等节点反复验证,P100 落 llama.cpp 兜底为真机结论);"已实现"指代码合入且有单元/集成测试覆盖但未做真机端到端。功能口径与平台《模型管理》《推理服务架构与功能详解》文档及 capability-tree A12/A10.3-A10.5 一致;路由决策在网关的执行细节(负载均衡、灰度、会话粘性、限流)见《推理网关》篇,服务生命周期(休眠唤醒、自动伸缩、滚动更新)见《推理服务》篇。 -->