mcpField
O
Ollama / vLLM Bridge

Ollama / vLLM Bridge

本地服务
github.com/setheerwagenAI 与智能体01 周前更新

关于 Ollama / vLLM Bridge

Ollama / vLLM Bridge 是 MCP 服务,允许 MCP 客户端通过 Ollama、vLLM 或配置好的 OpenAI 兼容端点调用本地语言模型。其特点是独立校验 JSON 输出是否符合 Schema,并在不符合时自动重试。

主要功能

  1. 01调用 Ollama、vLLM 或 OpenAI 兼容后端
  2. 02按 Schema 校验结构化 JSON
  3. 03自动重试校验失败的输出
  4. 04列出可用模型
  5. 05生成文本和嵌入
  6. 06将后端连接限制在本地网络

适用场景

  1. 01将文本摘要任务交给本地模型
  2. 02从文本中提取符合 Schema 的数据
  3. 03使用本地后端分类文本
  4. 04通过配置的后端生成嵌入

MCP 相关

该 MCP 服务提供工具,用于调用已配置的本地模型后端、列出模型和生成嵌入。MCP 客户端可借此进行模型调用;模型本身不具备工具或文件访问能力。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"setheerwagen-mcp-ollama-vllm": {
"command": "uvx",
"args": [
"mcp-ollama-vllm"
],
"env": {
"LOCAL_BACKEND": "<LOCAL_BACKEND>",
"LOCAL_HOST": "<LOCAL_HOST>",
"LOCAL_API_KEY": "<LOCAL_API_KEY>",
"LOCAL_TIMEOUT": "<LOCAL_TIMEOUT>",
"LOCAL_EMBED_MODEL": "<LOCAL_EMBED_MODEL>",
"LOCAL_EMBED_PATH": "<LOCAL_EMBED_PATH>",
"LOCAL_SCHEMA_MODE": "<LOCAL_SCHEMA_MODE>"
}
}
}
}
启动命令uvx mcp-ollama-vllm
需要配置的环境变量
LOCAL_BACKENDBackend to use: ollama, vllm or openai
LOCAL_HOSTModel endpoint URL; must be local or on your own network. Required for the openai backend
LOCAL_API_KEY密钥Optional bearer token, openai backend only; sent as a header, never logged
LOCAL_TIMEOUTRead timeout in seconds
LOCAL_EMBED_MODELDefault embedding model
LOCAL_EMBED_PATHOptional, openai backend only: embedding endpoint path
LOCAL_SCHEMA_MODEOptional, openai backend only: auto, response_format or none

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypimcp-ollama-vllm1.0.6

工具

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_BACKENDollama后端:ollama、vllm 或 openai
LOCAL_HOST因后端而异端点;必须位于本机或你自己的网络中
LOCAL_TIMEOUT600读取超时时间,单位为秒
LOCAL_EMBED_MODELnomic-embed-text(Ollama)默认嵌入模型
LOCAL_API_KEY-可选;仅适用于 openai。以 Bearer 标头发送,绝不记录到日志中
LOCAL_EMBED_PATH/v1/embeddings可选;仅适用于 openai。嵌入端点路径
LOCAL_SCHEMA_MODEauto可选;仅适用于 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否00 = 尽可能确定
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_embed3 个向量,768 维,2.05 秒
向量的含义相似句子 0.815,不相似句子 0.574
错误路径(5 种情况)每种均返回易于理解的消息,且没有堆栈跟踪
通过真实 MCP 客户端进行的 stdio 握手四个工具均可见,且必需字段正确
网络防护允许 LAN 和环回地址,拒绝公共主机和公共 IP
使用模拟服务测试 vLLM 路径70/70
Schema 保真度:决定性指标

使用 qwen3:4b 对 30 个不同输入进行测试,每种 Schema 测试 10 个。测量的是输出首次尝试时符合 Schema 的频率,即未使用内置重试的情况:

Scheman首次尝试符合失败耗时(秒,最小值/中位数/最大值)
A 扁平(enum、数值范围、additionalProperties: false)1010/10035 / 56 / 516
B 嵌套对象(2 层)1010/10056 / 93 / 832
C 对象列表(minItems,每个元素都有 enum)1010/10063 / 107 / 827
总计3030/30(100 %)035 / 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_format json_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。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。