W
AI-Wiki
CONCEPT

本地 vLLM 部署约束

定义

本地 vLLM 部署约束 是指在 Hyper-Extract 中把本地 vLLM 作为 LLM 与 embedding 提供方时,需要同时满足的一组工程限制。

这些限制的核心目标不是“本地模型能响应请求”这么简单,而是让 Hyper-Extract 的结构化抽取流程稳定工作,特别是让受约束解码从第一个 token 起就输出 JSON。

在本文档中的语境

Hyper-Extract Provider System 的语境里,Hyper-Extract 支持三类连接方式:OpenAI、阿里百炼和本地 vLLM。三者都通过同一个 create_client() 接口接入,但本地 vLLM 的约束最多,因为服务是用户自己部署,模型行为、输出模板、量化方式和端口配置都要自行保证。

对应的本地连接方式如下:

from hyperextract import create_client

llm, emb = create_client(
    llm="vllm:Qwen3.5-9B@http://localhost:8000/v1",
    embedder="vllm:bge-m3@http://localhost:8001/v1",
    api_key="dummy",
)

这里有两个固定模式:

  • LLM 使用 vllm:<model>@<endpoint>
  • embedding 使用 vllm:<embedder>@<endpoint>
  • 即使是本地服务,也仍可在客户端传入 api_key;示例里使用的是 dummy

关键机制与组成

LLM 与 embedding 通常分开部署

本地 vLLM 部署时,LLM 服务与 embedding 服务通常不是同一个进程,而是分开启动、分开监听端口。

典型做法是:

  • LLM 服务监听 http://localhost:8000/v1
  • embedding 服务监听 http://localhost:8001/v1
  • Hyper-Extract 在 create_client() 时分别传入两个 endpoint。

这种拆分不是可有可无的部署偏好,而是因为 embedding 服务需要显式以嵌入任务启动。

embedding 服务必须使用 --task embed

启动 embedding 服务时,需要显式指定:

vllm serve BAAI/bge-m3 \
 --task embed \
 --dtype float16 \
 --max-model-len 8192 \
 --port 8001

这里最关键的参数是 --task embed。没有它,服务就不是按 embedding 接口语义启动,不能按 Hyper-Extract 预期作为向量化提供方使用。

LLM 服务的典型启动参数

一个被文档直接给出的本地 LLM 启动示例如下:

vllm serve /path/to/qwen3.5-9b-gptq-marlin \
 --served-model-name Qwen/Qwen3.5-9B \
 --trust-remote-code \
 --quantization gptq_marlin \
 --dtype bfloat16 \
 --max-model-len 8192 \
 --gpu-memory-utilization 0.90 \
 --default-chat-template-kwargs '{"enable_thinking": false}' \
 --port 8000 \
 --api-key dummy

从这个示例可以提炼出本地部署时最常见、也最值得关注的参数约束:

  • --served-model-name Qwen/Qwen3.5-9B:对外暴露的模型名,要与客户端中 vllm:<model>@<endpoint><model> 语义保持一致。
  • --port 8000:LLM 服务端口;示例中 embedding 服务单独占用 8001
  • --dtype bfloat16:LLM 服务的数据类型。
  • --max-model-len 8192:上下文长度上限,示例里 LLM 与 embedding 都使用了 8192
  • --gpu-memory-utilization 0.90:GPU 显存利用率阈值。
  • --api-key dummy:即使是本地 OpenAI 兼容服务,也可以配置 API key。
  • --trust-remote-code:示例中启用。
  • --quantization gptq_marlin:量化方案采用 GPTQ-Marlin。
  • --default-chat-template-kwargs '{"enable_thinking": false}':显式关闭 thinking mode。

这些参数并非全部都是 Hyper-Extract 独占要求,但在 Hyper-Extract 的结构化输出场景下,它们共同决定了服务是否可被稳定调用。

为什么强调关闭 thinking mode

推荐使用非 thinking 模型

文档对本地 vLLM 部署给出的明确建议是:优先使用非 thinking 模型

如果部署的是带 thinking 行为的模型,例如 Qwen3.5 的 thinking mode,那么模型可能在正式答案前先输出:

<think>...</think>

这会直接破坏 Hyper-Extract 依赖的结构化输出前提。

根因:受约束解码要求从第一个 token 开始就是 JSON

Hyper-Extract 的结构化抽取依赖受约束解码,其要求不是“最终能生成 JSON”即可,而是从第一个 token 开始就必须进入 JSON 轨道

<think> 标签的问题在于:

  • 它出现在 JSON 之前。
  • 它不是 JSON 的合法起始内容。
  • 因而会与受约束解码的“JSON-from-first-token”要求冲突。

这也是为什么本地部署时不能把 thinking 模式仅仅视为“多输出一点推理过程”的无害特性;在结构化抽取场景里,它会让输出从一开始就偏离约束。

关闭方式

如果使用 Qwen3.5-9B 一类模型,文档建议在 vLLM 启动时显式传入:

--default-chat-template-kwargs '{"enable_thinking": false}'

这不是可选美化参数,而是为了避免 <think> 标签打断结构化输出。

量化方案约束

优先 GPTQ-Marlin,不优先 AWQ

文档给出的量化建议很明确:优先使用 GPTQ-Marlin,而不是 AWQ

推荐原因不是抽象的“效果更好”,而是具体的兼容性问题:

  • AWQ 与 vLLM 0.21.0 存在兼容性问题。
  • GPTQ-Marlin 是文档直接示范并优先推荐的量化方案。

因此,如果本地部署目标是让 Hyper-Extract 稳定完成结构化抽取,那么量化选择不应只看显存占用或通用推理速度,还要考虑与当前 vLLM 版本的实际兼容性。

典型部署形态

除直接运行 vllm serve 外,文档还给出了一个 Docker 启动样例:

docker run --runtime nvidia --gpus all \
 --ipc=host \
 -v ~/.cache/huggingface:/root/.cache/huggingface \
 -p 8000:8000 \
 vllm/vllm-openai:latest \
 --model Qwen/Qwen3.5-9B \
 --trust-remote-code

这个示例说明,本地 vLLM 在 Hyper-Extract 语境中本质上是作为 OpenAI 兼容接口来暴露服务的;真正重要的不是采用裸机还是 Docker,而是最终能提供 Hyper-Extract 可连接的 endpoint,并满足上文提到的输出与兼容性约束。

细节、边界与例外

不是所有“能返回 JSON”的模型都适合

本地部署的判断标准不是模型偶尔能生成 JSON,而是它能否在受约束解码条件下稳定从首 token 输出 JSON。只要模型会先吐出 <think>、解释文字或其他前缀,即使后面补成 JSON,也不满足要求。

本地与云端的 thinking 问题边界不同

文档还给出了一个重要对照:DeepSeek-R1 通过阿里百炼接入时被验证为可用,因为百炼后端会过滤 <think> 标签,所以 AutoGraphwith_structured_output 可以正常工作。

但这个例外不应被误解为“thinking 模型天然兼容结构化输出”。真正成立的是:

  • 通过特定云通道时,服务商后端可能帮你清洗 <think>
  • 通过其他接入渠道时,这种过滤未必存在。
  • 在本地 vLLM 场景里,应默认自行承担 thinking 输出带来的风险。

因此,本地 vLLM 部署约束 与云端兼容性规则不能混为一谈。

api_key 在本地仍可能出现

虽然服务部署在本机,示例仍在服务端和客户端都使用了 api_key="dummy" / --api-key dummy。这说明在 OpenAI 兼容接口语境下,本地服务也可能保留 API key 这一层协议字段;不要因为“是本地”就假设客户端一定不传 key。

相关条目