关于 Ollama / vLLM Bridge
Ollama / vLLM Bridge 是 MCP 服务,允许 MCP 客户端通过 Ollama、vLLM 或配置好的 OpenAI 兼容端点调用本地语言模型。其特点是独立校验 JSON 输出是否符合 Schema,并在不符合时自动重试。
主要功能
- 01调用 Ollama、vLLM 或 OpenAI 兼容后端
- 02按 Schema 校验结构化 JSON
- 03自动重试校验失败的输出
- 04列出可用模型
- 05生成文本和嵌入
- 06将后端连接限制在本地网络
适用场景
- 01将文本摘要任务交给本地模型
- 02从文本中提取符合 Schema 的数据
- 03使用本地后端分类文本
- 04通过配置的后端生成嵌入
MCP 相关
该 MCP 服务提供工具,用于调用已配置的本地模型后端、列出模型和生成嵌入。MCP 客户端可借此进行模型调用;模型本身不具备工具或文件访问能力。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
uvx mcp-ollama-vllmLOCAL_BACKENDBackend to use: ollama, vllm or openaiLOCAL_HOSTModel endpoint URL; must be local or on your own network. Required for the openai backendLOCAL_API_KEY密钥Optional bearer token, openai backend only; sent as a header, never loggedLOCAL_TIMEOUTRead timeout in secondsLOCAL_EMBED_MODELDefault embedding modelLOCAL_EMBED_PATHOptional, openai backend only: embedding endpoint pathLOCAL_SCHEMA_MODEOptional, openai backend only: auto, response_format or none本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
4收录的工具列表 · 更新于 1 周前
此工具无需参数。
详细介绍
mcp-ollama-vllm
一个 MCP 服务,让 Claude Code 之类的 MCP 客户端可以直接与本地语言模型交互。后端在启动时固定接入:Ollama、vLLM 或通用的 OpenAI 兼容端点(通过 LOCAL_BACKEND 设置一次,见下文)。第三种 openai 不是用于加载任意工具的插件加载器,而是另一种固定的后端实现,支持 LM Studio、llama.cpp server、LocalAI、TGI、koboldcpp、SGLang 或 Jan 等服务;这些服务都提供与 vLLM 相同的 OpenAI 兼容 /v1 端点,但本身并非 vLLM。
为什么选择它
这个服务能做到、而普通的 Ollama/vLLM 调用做不到的一件事:
经过 Schema 验证的 JSON 输出,由其自带的无依赖验证器检查;如果不符合要求,就会自动重试(最多共尝试 3 次,并将具体的不符合项反馈给模型)。无论配置的是哪一个后端,都以相同方式通过相同的工具调用来实现。 Ollama 的 format 和 vLLM 的 response_format 都能引导模型生成 JSON,但都无法保证结果确实符合你的 Schema;这个服务会独立检查,而不是相信后端的说法。
改进前 / 后(仅供说明,并非真实运行结果):
// Backend's own schema enforcement alone (Ollama "format" / vLLM "response_format"):
// looks like valid JSON, but a required field went missing under load and nobody
// checked it against the schema before handing it onward.
{"amount": 42.10, "currency": "EUR"}
// local_structured: the same case, but validated against the schema and
// automatically retried with the concrete violation fed back to the model.
{"amount": 42.10, "currency": "EUR", "total": 42.10}
// -> attempts: 2, valid_on_first_try: false, outcome already re-validated
特意说明的已知限制:
- 使用 vLLM 时,模型列表提供的元数据少于 Ollama:不包含大小或量化信息,这些字段会显示为
unknown/0,而不是臆造一个值。 - 使用通用的
openai后端时,模型列表通常提供的信息更少:此类服务大多只会通过/v1/models返回id(和owned_by),因此parameter_size/quantization会显示为unknown,大小会显示为0,也没有适配器标记;只有端点确实报告了max_context时才会包含该字段。顶层的metadata_reported字段(见下文的list_models)会如实显示“未报告”这一情况。 think参数(推理轨迹)对 vLLM 或openai不起作用;它只接入了 Ollama。见下文的“think参数”。
**操作系统:**CI 会在 Linux 上运行自测;由模拟环境支持的配置自测 selftest_vllm.py 也会在 Windows 上运行。该服务完全使用 Python 编写,不包含特定于操作系统的调用;此外,真实后端路径已于 2026-08-07 在 Windows 上进行过一次测试。需要运行中的后端服务(Ollama 或 vLLM);配置自测 selftest_vllm.py 使用模拟环境,无需真实服务。macOS 未单独测试。
用途
AI Agent 的大量工作都属于纯语言处理:写作、总结、分类,以及从文本中提取结构化数据。这些工作可以交由本地模型处理,而搜索、读取、写入和编排仍由调用方客户端负责。
这里明确讨论的是一次模型调用,而不是 Agent。本地模型接收文本并返回文本。它没有工具、无法访问文件,也不会分多步执行。
安全属性
这些限制是设计的一部分,而不只是文档中的说明:
- 不访问文件。 服务不提供任何读取或写入文件的工具。它只从环境变量中读取自身的配置。
- 不执行 shell。 没有任何途径可以调用
subprocess、os.system或类似功能。 - 仅限本地计算机或本地网络。 服务进程本身不会发起任何出站调用,唯一例外是调用配置的后端端点;该端点的地址会在启动时和每次调用时再次检查。如果地址指向互联网,服务会拒绝启动(或拒绝该调用)。为此会解析名称,并且名称解析得到的所有地址都必须是本地地址,否则就可能通过 DNS 绕过防护;随后会固定实际连接使用的已解析地址,避免之后重新解析到公共地址而绕过检查。允许环回地址和专用网络(RFC 1918),因此可以连接本地网络上的 vLLM 主机;公共地址则会被拒绝。
- 两个直接依赖项:
mcp和httpx。mcp会进一步传递引入多个其他包(包括anyio、pydantic、starlette、uvicorn、jsonschema、pyjwt)。完整的依赖解析树已固定在uv.lock中;CI 会使用uv lock --check检查锁文件是否仍与pyproject.toml一致,然后安装锁定的精确依赖树以运行测试,因此锁文件不一致会导致 CI 失败。另有一个独立的建议性pip-audit任务,每次向 main 推送或提交拉取请求时,以及每周都会检查锁定的依赖树是否存在已知漏洞;此任务不会阻止拉取请求。
这些属性限定了此服务的行为;它们并不代表驱动该服务的 AI 工具也具有这些属性。你调用的模型在本地运行,但决定调用该模型的 Claude/ChatGPT 会话是另一项托管服务,并且会像任何 MCP 调用一样看到它发起的调用。如果这点很重要,请使用本地模型驱动该服务。
请参阅 SECURITY.md,了解此处涵盖的信任边界(将模型视为预言机而非 Agent、通过模型输出进行提示词注入、固定 DNS 重新绑定地址)以及未涵盖的内容。
安装
请将下方的 /path/to/mcp-ollama-vllm 替换为你解压或克隆此项目的目录。
需要 Python 3.10 或更高版本。设置虚拟环境有两种等效方式;请选择你已有的工具。
使用标准的 venv 模块和 pip:
cd /path/to/mcp-ollama-vllm
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
如果 python3 -m venv 失败(某些发行版缺少 python3-venv 包),可以安装该包,也可以使用 uv:
cd /path/to/mcp-ollama-vllm
uv venv .venv --seed
.venv/bin/pip install -r requirements.txt
(--seed 可确保将 pip 安装到环境中。或者,不使用 --seed 时,可通过 uv pip install --python .venv/bin/python -r requirements.txt 进行安装。)
模块位于 src/mcp_ollama_vllm/ 下(采用 src 布局,因此包名不会与其他 MCP 服务的顶层模块冲突)。上述任一方式都会创建一个可通过 PYTHONPATH=src 运行的项目副本(请参阅下文“Entry for the MCP configuration”),你也可以使用 .venv/bin/pip install . 安装软件包本身;这还会提供 mcp-ollama-vllm 控制台脚本,且无需设置 PYTHONPATH。
运行服务需要可访问的 Ollama 或 vLLM 后端:
- Ollama:在本地安装(参见 https://ollama.com),使用
ollama serve启动,并至少拉取一个模型(例如ollama pull llama3.2:3b);使用ollama list检查。对于local_embed,还需拉取一个嵌入模型,例如ollama pull nomic-embed-text。 - vLLM:在此计算机上运行 OpenAI 兼容的 vLLM 端点,或使用本地网络上的端点。只能通过下文所述的环境变量切换后端;服务本身不会改变。
MCP 配置项
在 ~/.claude.json 中,或在 Claude Code 配置的 mcpServers 部分中:
{
"mcpServers": {
"local-models": {
"command": "/path/to/mcp-ollama-vllm/.venv/bin/python",
"args": ["-m", "mcp_ollama_vllm.server"],
"env": {
"PYTHONPATH": "/path/to/mcp-ollama-vllm/src",
"LOCAL_BACKEND": "ollama",
"LOCAL_HOST": "http://localhost:11434",
"LOCAL_TIMEOUT": "600"
}
}
}
}
将 /path/to/mcp-ollama-vllm 替换为你解压此项目的绝对路径。也可以通过 CLI 配置:
claude mcp add local-models --env PYTHONPATH=/path/to/mcp-ollama-vllm/src -- \
/path/to/mcp-ollama-vllm/.venv/bin/python -m mcp_ollama_vllm.server
默认情况下,claude mcp add 会将服务绑定到当前工作目录;若要在所有目录中全局可见,请追加 --scope user。
另外,运行 .venv/bin/pip install .(参见上文“安装”)后,就不再需要 PYTHONPATH 条目,args 可以直接设为 ["-m", "mcp_ollama_vllm.server"],也可以直接将已安装的控制台脚本用作 command:/path/to/mcp-ollama-vllm/.venv/bin/mcp-ollama-vllm,无需设置 args。
首次调用
重启 MCP 客户端,使其加载新服务,然后不带参数调用 list_models。如果响应中包含你的模型,就证明服务已启动、找到后端并成功与其通信,一步即可完成验证。
环境变量
| 变量 | 默认值 | 含义 |
|---|---|---|
LOCAL_BACKEND | ollama | 后端:ollama、vllm 或 openai |
LOCAL_HOST | 因后端而异 | 端点;必须位于本机或你自己的网络中 |
LOCAL_TIMEOUT | 600 | 读取超时时间,单位为秒 |
LOCAL_EMBED_MODEL | nomic-embed-text(Ollama) | 默认嵌入模型 |
LOCAL_API_KEY | - | 可选;仅适用于 openai。以 Bearer 标头发送,绝不记录到日志中 |
LOCAL_EMBED_PATH | /v1/embeddings | 可选;仅适用于 openai。嵌入端点路径 |
LOCAL_SCHEMA_MODE | auto | 可选;仅适用于 openai。可设为 auto、response_format 或 none(参见下文的 local_structured) |
LOCAL_HOST 的默认值为:Ollama 使用 http://localhost:11434,vLLM 使用 http://localhost:8000。openai 没有默认值:通用后端没有可合理推测的端口,因此必须设置 LOCAL_HOST,否则服务会拒绝启动。
为避免现有配置失效,仍会读取旧名称 OLLAMA_HOST、OLLAMA_TIMEOUT 和 OLLAMA_EMBED_MODEL。如果两种名称均已设置,则优先使用 LOCAL_ 名称。
切换到远程 vLLM 端点
通过本地网络访问 vLLM 设备只需修改两个变量;服务本身无需更改:
{
"mcpServers": {
"local-models": {
"command": "/path/to/mcp-ollama-vllm/.venv/bin/python",
"args": ["-m", "mcp_ollama_vllm.server"],
"env": {
"PYTHONPATH": "/path/to/mcp-ollama-vllm/src",
"LOCAL_BACKEND": "vllm",
"LOCAL_HOST": "http://192.168.x.x:8000",
"LOCAL_TIMEOUT": "600",
"LOCAL_EMBED_MODEL": "BAAI/bge-m3"
}
}
}
}
也可以使用不同名称(例如 local-models 和 local-models-server)并行注册两个后端,这样它们便可在同一会话中使用。
多次注册同一服务
同时使用多个名称注册同一服务完全没问题,每个名称都可以配置自己的 LOCAL_BACKEND/LOCAL_HOST。这样就能在同一会话中访问不同后端,例如:
- 一个条目对应本地 Ollama(
http://localhost:11434); - 另一个条目对应网络中另一台设备上的 vLLM;
- 第三个条目对应那台远程设备上的 Ollama。
这三个条目都是同一服务的实例,只是配置不同。请为它们设置描述性名称,并在使用前确认哪个条目对应目标设备和后端,不要只依赖名称来判断。
openai 后端的工作方式相同,不需要新的路由机制:可以使用两个端口不同的通用服务条目,例如 LM Studio 使用 http://localhost:1234,llama.cpp 服务使用 http://localhost:8080:
{
"mcpServers": {
"local-models-lmstudio": {
"command": "/path/to/mcp-ollama-vllm/.venv/bin/python",
"args": ["-m", "mcp_ollama_vllm.server"],
"env": {
"PYTHONPATH": "/path/to/mcp-ollama-vllm/src",
"LOCAL_BACKEND": "openai",
"LOCAL_HOST": "http://localhost:1234"
}
},
"local-models-llamacpp": {
"command": "/path/to/mcp-ollama-vllm/.venv/bin/python",
"args": ["-m", "mcp_ollama_vllm.server"],
"env": {
"PYTHONPATH": "/path/to/mcp-ollama-vllm/src",
"LOCAL_BACKEND": "openai",
"LOCAL_HOST": "http://localhost:8080"
}
}
}
}
如果用于聊天的端点不同时提供嵌入服务(参见下文的 local_embed),请注册第二个条目并设置匹配的 LOCAL_EMBED_PATH,或将其指向另一个支持嵌入的后端,而不要指望一个端点同时完成这两项工作。
超时时间有意设置得较长:在长上下文或高负载情况下,本地模型可能需要数分钟才能响应。连接建立有独立的短时限,为 10 秒,与超时时间无关。因此,如果 Ollama 服务已关闭,系统会立即提示,而不会挂起十分钟。
其他 MCP 客户端
该服务使用纯 stdio MCP,因此任何支持 command/args/env 样式服务条目的客户端都能以相同方式工作。不同客户端的配置文件和键名各不相同;请查阅相应文档,了解 mcpServers 样式的配置块(或等效配置)位于何处。以下是两个通用示例:
// Checkout-based, PYTHONPATH points at src/:
{
"mcpServers": {
"local-models": {
"command": "/path/to/mcp-ollama-vllm/.venv/bin/python",
"args": ["-m", "mcp_ollama_vllm.server"],
"env": {
"PYTHONPATH": "/path/to/mcp-ollama-vllm/src",
"LOCAL_BACKEND": "ollama"
}
}
}
}
// After `pip install .`, using the installed console script directly:
{
"mcpServers": {
"local-models": {
"command": "/path/to/mcp-ollama-vllm/.venv/bin/mcp-ollama-vllm",
"env": { "LOCAL_BACKEND": "ollama" }
}
}
}
工具
list_models
无参数。按模型返回名称、参数量、量化方式、磁盘占用(GB 和字节)、系列和能力。能力中会显示哪些模型支持 embedding 等信息。
使用 vLLM 时,后端不会报告大小和量化方式;因此,这些字段分别显示为 unknown 和 0,而不是凭空编造值。作为补充,以下三个字段会提供信息:
is_adapter:该条目是否为 LoRA 适配器(vLLM 会在模型列表中将适配器作为独立条目公开)。Ollama 的条目也包含此字段,值始终为False,因为 Ollama 没有适配器这一概念。base_model:该适配器所属的基础模型(来自parent字段)。Ollama 的条目也包含此字段,值始终为空。max_context:模型的上下文长度(max_model_len),由 vLLM 报告。此字段仅适用于 vLLM;Ollama 的条目完全不包含该字段。
顶层还会额外提供 adapters(适配器数量)、backend(当前活动后端的标识符)和 metadata_reported(True/False):表示后端是否实际报告了上述元数据字段,以便调用方区分“该后端未报告”(通常是 openai,值为 False)和“已报告,但恰好为空”。
local_ask
| 参数 | 必填 | 默认值 | 含义 |
|---|---|---|---|
model | 是 | - | 模型名称,例如 llama3.2:3b |
prompt | 是 | - | 指令 |
system | 否 | - | 角色/行为指令 |
temperature | 否 | 0 | 0 = 尽可能确定 |
max_tokens | 否 | - | 生成令牌数上限 |
context | 否 | - | 添加到提示词开头的文本 |
think | 否 | false | 启用模型的推理过程(见下文) |
返回:answer、model、model_digest、backend、duration_seconds、tokens(input/output/total)、tokens_per_second、finish_reason。模型名称和摘要可追踪每次运行,即使之后相同标签指向了不同的权重。
think 参数
思考模型(在 list_models 中标记为具有 thinking 能力,例如 qwen3:4b)会先进行推理,再给出实际答案。推理过程会占用与答案相同的令牌预算。因此,当 max_tokens 设置得较小时,尽管调用在技术上成功,答案仍会是空的;实测每次调用的推理令牌数约为 3000 到 4000。
因此,think 的默认值为 false,并且桥接器在每次 Ollama 调用中都会发送 think: false。需要推理过程的用户可显式设置 think: true。
think 字段会始终发送,即使值为 false 也是如此。针对 Ollama 0.31.2 的实测结果:对于不具备 thinking 能力的模型,think: false 不会产生问题;只有 think: true 会被拒绝,并返回 HTTP 400 "... does not support thinking"。因此,无需通过 /api/tags 预先查询能力,而且在唯一可能出错的情况下,错误消息会清楚说明原因。
使用 vLLM 时,桥接器接受该参数,但不会对其进行处理:目前尚未接入此处的思考控制。特意让两个后端的调用形式保持一致。通用的 openai 后端也会像 vLLM 一样接受并忽略 think,原因相同:此处不包含通用推理控制(reasoning_effort、chat_template_kwargs 等)。
local_structured
| 参数 | 必填 | 含义 |
|---|---|---|
model | 是 | 模型名称 |
prompt | 是 | 指示要从哪些文本中提取什么内容 |
schema | 是 | 所需结果的 JSON schema |
system | 否 | 角色/行为指令 |
think | 否 | 启用模型的推理轨迹,默认为 false(参见 local_ask) |
将 schema 传递给后端进行 schema 强制执行,并额外根据 schema 验证答案本身。使用 Ollama 时会通过
format 字段传递;使用 vLLM 时则通过与 OpenAI 兼容的 response_format
传递,内容为 {"type": "json_schema", "json_schema": {...}}(正如当前 vLLM 文档
所建议的那样)。如果较旧的 vLLM 版本通过 HTTP 400 拒绝该参数,则会回退
一次,改用较旧的 guided_json,因此桥接仍可正常使用。对于通用的 openai 后端,默认也使用带有
json_schema 的 response_format;如果服务器通过 HTTP 400 拒绝该参数,则会回退
(与 vLLM 不同,这里没有 guided_json 可尝试)到不带 schema 的调用,然后由内置验证器检查结果;如果
违反 schema,则会将具体违规内容反馈给模型并重试,与任何其他未经验证的答案相同。进程范围内会记住
采用了哪种方式,因此后续调用会跳过注定失败的 response_format 往返过程。LOCAL_SCHEMA_MODE 可覆盖此检测:
response_format 模式始终要求服务器支持该参数(如果服务器拒绝,则会直接报错,不进行回退),none 则始终不发送该参数。发生违规时,最多重试两次,并将具体违规内容传递给模型。没有
这些反馈,温度为 0 时的重试只会逐字返回相同答案,因此毫无意义。只有在此之后才会报错,错误中会包含违规内容和无效的原始
输出。
返回:outcome(已解析的对象)、attempts、valid_on_first_try、model、
model_digest、backend、duration_seconds、tokens。
验证涵盖 format 实际使用的子集:type(也包括类型列表)、properties、required、
additionalProperties、items(也包括元组形式)、enum、const、minimum/maximum、
minLength/maxLength、minItems/maxItems、anyOf/oneOf/allOf。这不是
完整的规范草案:不会验证 $ref、pattern 和 format。任何未在上述列出的关键字都会被静默忽略,不会强制执行,其中包括
exclusiveMinimum/exclusiveMaximum、multipleOf、uniqueItems、
patternProperties、if/then/else 和 contains。
local_embed
| 参数 | 必填 | 默认值 | 含义 |
|---|---|---|---|
texts | 是 | - | 要生成嵌入向量的文本列表 |
model | 否 | nomic-embed-text(Ollama) | 嵌入模型 |
返回:vectors、count、dimensions、model、backend、duration_seconds。
自测
两项独立测试。selftest.py 使用真实的 Ollama 运行;selftest_vllm.py 是快速的离线模拟门禁,无需任何后端。除此之外,vLLM 路径还曾通过真实的 vLLM 进行一次确认(参见下文“在真实 vLLM 上测量的内容”)。
.venv/bin/python selftest.py --rounds 10 --model llama3.2:3b # against real Ollama
.venv/bin/python selftest_vllm.py # against a mock
两项测试都无需 MCP 客户端。selftest.py 通过真实的 FastMCP 调度(mcp.call_tool)调用工具,也就是已连接客户端所走的相同路径,并且不会自行拉取任何模型。
Ollama 测试首先使用一组已知违规项检查架构验证器本身(确切数量会随着验证器覆盖的关键字增多而增加)。没有这一步,高架构命中率毫无意义:这也可能意味着验证器根本不会标记任何内容。随后,通过替换 HTTP 访问,以确定性方式检查重试和中止逻辑,因为无法在真实模型上按需生成架构违规。
selftest_vllm.py 会启动自己的微型 HTTP 服务器,模拟三个 OpenAI 兼容端点,然后关闭服务器。它会检查两方面:是否正确读取了答案,以及发送的请求是否构造正确(是否确实发送了带有 json_schema 的 response_format,而不是 Ollama 的 format)。即使现在 vLLM 路径也已通过真实的 vLLM 确认(参见下文“在真实 vLLM 上测量的内容”),它仍适合作为快速、离线且确定性的门禁:模拟服务不需要后端,且每次运行结果都相同。你自己的 vLLM 构建版本仍可能表现不同,因此使用它进行检查依然值得。
测得的限制
以下数据来自一台笔记本电脑上的测试运行,其显存较为有限(约 4 GB VRAM),Ollama 使用 -np 1,即单个处理槽。请将这些数据视为大致量级,而非保证;在其他硬件上,运行时间会有所不同。
完全通过的项目
| 项目 | 结果 |
|---|---|
| 使用已知错误架构测试架构验证器 | 每项均正确检测 |
| 重试和中止逻辑(无模型) | 8/8 |
list_models(Ollama) | 8 个模型,字段完整 |
local_ask 的答案、令牌数和耗时 | 正确;报告的耗时与外部测量值相差不超过 0.04 秒 |
local_ask 的 context 和 max_tokens | 均生效(max_tokens: 16 恰好生成了 16 个输出令牌) |
local_embed | 3 个向量,768 维,2.05 秒 |
| 向量的含义 | 相似句子 0.815,不相似句子 0.574 |
| 错误路径(5 种情况) | 每种均返回易于理解的消息,且没有堆栈跟踪 |
| 通过真实 MCP 客户端进行的 stdio 握手 | 四个工具均可见,且必需字段正确 |
| 网络防护 | 允许 LAN 和环回地址,拒绝公共主机和公共 IP |
| 使用模拟服务测试 vLLM 路径 | 70/70 |
Schema 保真度:决定性指标
使用 qwen3:4b 对 30 个不同输入进行测试,每种 Schema 测试 10 个。测量的是输出首次尝试时符合 Schema 的频率,即未使用内置重试的情况:
| Schema | n | 首次尝试符合 | 失败 | 耗时(秒,最小值/中位数/最大值) |
|---|---|---|---|---|
A 扁平(enum、数值范围、additionalProperties: false) | 10 | 10/10 | 0 | 35 / 56 / 516 |
| B 嵌套对象(2 层) | 10 | 10/10 | 0 | 56 / 93 / 832 |
C 对象列表(minItems,每个元素都有 enum) | 10 | 10/10 | 0 | 63 / 107 / 827 |
| 总计 | 30 | 30/30(100 %) | 0 | 35 / 93 / 832 |
每个交付的对象都通过了针对其 Schema 的独立重新验证,内容在事实层面也正确(示例 B:从文本中正确提取了发件人、净金额、货币和提醒标记)。
如何理解这 100 %。 后端的语法绑定已在生成时强制规定 JSON 格式;因此,这一比率主要衡量 Schema 是否足够严格,而非模型是否足够聪明。对于这三个 Schema、这个模型以及简短的德语文本,结果成立。对于充斥着自由字符串、没有 enum 和范围限制的 Schema,效果会如预期般减弱。本次测试中一次也没有用到重试循环;在实际的 vLLM 运行中也没有触发重试(local_structured 同样在首次尝试时返回了有效结果,attempts: 1);其正确性则通过替换 HTTP 访问进行了确定性验证。
关于运行时,以及影响测量结果的因素
耗时从 35 秒到 832 秒不等,这并非服务器导致的。在测量的前半段,同一台机器上运行着另一个无关测试(Python 进程的 CPU 占用一度达到 1011 %,平均负载为 21 到 28)。这里运行的 Ollama 使用 -np 1,即整台机器只有一个处理槽。测得的影响如下:
- 有其他负载时,一次调用耗时 309 到 832 秒;没有其他负载时,耗时 35 到 107 秒。
- 一次调用触发了默认的 600 秒超时并失败。
- 有其他负载时,切换到
llama3.2:3b完全不可能:只要另一个模型占用处理槽,对尚未加载的模型的请求就会无限期等待。两次尝试都在 400 秒后中止,没有得到响应。 - 显而易见的解决方法
num_gpu: 0(完全使用 CPU 计算,以避开 4-GB-VRAM 资源争用)并未奏效,因为瓶颈在 CPU,而不是 GPU。
这些都不是此服务的问题,而是在单台机器上采用这种方案的实际限制,因此这里对此进行了说明。
使用真实 vLLM 进行了哪些测量
vLLM 路径通过模拟服务进行了验证:端点正确、请求格式正确(response_format 搭配 json_schema)、回退到 guided_json、LoRA 检测、统一元数据,以及易于理解的错误信息。除此之外,还使用一个真实的 vLLM 运行了这些工具:一个提供 Qwen2.5-1.5B-Instruct(max_model_len 8192)的 OpenAI 兼容端点,通过真实的 FastMCP 调度(mcp.call_tool)调用,也就是已连接客户端会使用的同一路径。
list_models->GET /v1/models:后端报告为vllm,识别出模型,max_context为 8192,adapters为 0。local_ask->POST /v1/chat/completions:返回回答文本、令牌数(输入/输出/总计)和tokens_per_second,finish_reason为stop。local_structured->POST /v1/chat/completions,并携带response_formatjson_schema:返回符合架构的对象,首次尝试即有效(valid_on_first_try: true,attempts: 1)。这正是架构强制执行路径,如今已通过真实 vLLM 验证,而不只是模拟服务。local_embed->POST /v1/embeddings:后端返回 404,服务端将其妥善处理为清晰的提示(“backend reports: Not Found ...; with vLLM the model is fixed when the service starts”)。这是正确行为,并非故障:测试用的 vLLM 提供的是聊天模型,而非嵌入模型,因此没有可调用的嵌入端点,服务端也明确说明了这一点。
因此,真正尚待验证的是 vLLM 的嵌入路径:该路径已在真实 Ollama 上测量过,但尚未在提供嵌入模型的 vLLM 部署上测量(测试用的 vLLM 仅支持聊天)。使用 vLLM 嵌入功能时,必须运行支持嵌入的 vLLM;对于仅支持聊天的 vLLM,服务端会有意返回清晰的错误信息。除此之外,你自己的构建版本在采用 response_format 还是旧版 guided_json 路径,以及使用更大模型和 LoRA 适配器时的架构保真度方面,仍可能有所不同。
对运行操作的启示
- 一次运行一个模型。 使用
-np 1时,本地 Ollama 实例是两个并行 Agent 无法共享的资源。开始较长的运行前,查看一下curl localhost:11434/api/ps会很有帮助。 - 600 s 超时很紧,并不宽裕。 在有外部负载时,曾超过此限值。进行测量时,请调高
LOCAL_TIMEOUT。 - 思考型模型仍然开销更大,但不再有风险。
qwen3:4b为“Paris”这个回答生成了 186 个输出令牌,因为它会先推理再回答。对于分类和提取,不带思考步骤的模型(llama3.2:3b、granite4:micro)仍然更快、更省资源。对于 qwen3 变体,think参数(默认值为false)控制推理过程:桥接程序会自行关闭该参数,因此较紧的max_tokens会产生回答,而非空字符串。 - 让长时间运行的任务支持恢复。 否则,在有外部负载时每次中止都会导致此前收集的所有值丢失。
注意事项
- Ollama 的
format不能保证结果有效。 语法绑定会强制输出 JSON 格式,但无法可靠地确保required、enum或数值边界得到遵守。这正是该服务还会自行验证的原因。使用format却不自行验证的用户,会得到不完整且不会提示的对象。 - 小模型需要严谨的架构。 设置的
enum、required和边界越多,结果就越实用。架构若全是自由字符串,就容易让模型编造内容。 - 显存争用是最耗时的问题。 如果两个请求使用的模型无法同时装入显存,Ollama 就会在每次切换时重新加载模型。此时一次调用会耗时数分钟,而服务端并没有任何问题。尽可能只使用一个模型。
许可证
MIT,参见 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。