16GB MacBook Air 上的 MiniCPM5-2B:中英双语抽取测试与 Ollama 部署
员工的工作请求经常在一句话里混用中英文。为了看看 MiniCPM5-2B 在本地机器上如何处理这种表达,我们在一台 16GB 的 Apple M5 MacBook Air 上跑了一组 12 条结构化抽取测试。本文展示输入与输出、完全匹配的判定规则、实测延迟,以及用来复现其中一条用例的 Ollama 步骤。
MiniCPM5-2B 是我们面向端侧助手的紧凑文本模型。在这组测试中,12 条中英混合请求里有 11 条完全匹配了所有要求的 JSON 键和值。我们同时给出这一结果、独立基准数据和模型已公布的指令遵循与工具调用分数。
为什么中英混合的员工请求是一项苛刻的测试
一条工作请求可能用中文提到工单号,用英文说明优先级,还要求返回固定的 JSON 结构。MiniCPM5-2B 需要把这些细节对应起来,同时保留 ID、数字和缺失值。我们围绕这些具体的抽取检查设计了 12 条用例:常规更新、信息缺失、否定、日期、金额,以及应用要求的准确动作值。
MiniCPM5-2B 是文本输入、文本输出的模型,参数量为 2,516,756,480,原生上下文长度为 131,072 tokens,详见模型卡。它支持推理与工具调用工作流,以 Apache 2.0 协议发布。长上下文是模型能力;设备上实际能舒适运行的上下文,取决于可用内存和服务栈。
独立基准与我们公布的模型评测
2026 年 10 月 8 日,Artificial Analysis 将 MiniCPM5-2B 列在其 Intelligence Index 的 12 分,并在开放权重 Tiny 类别(包含最高 4B 参数的模型)中位列 49 个模型中的第 2 名。MiniCPM5-2B 约有 2.5B 参数;排在它前面的 K2 Horizon 3.7B 体量更大。这是已公布的 Tiny 类别排名,并不是单独的 3B 以下排名。
Artificial Analysis 评测的是开启推理的 MiniCPM5-2B 版本。下面在笔记本上进行的抽取测试关闭了思考模式,并使用了 Q4_K_M 量化,因此 11/12 的结果是不同设置下的另一项测量。Artificial Analysis 没有公布该模型的输出速度数据。
我们的 MiniCPM5-2B 模型卡显示:指令遵循在 Multi-IF 上为 71.8,工具调用在 BFCL v4 上为 66.6。这些是模型卡中公布的结果。本文的中英混合抽取用例则是在下文所述的笔记本上单独运行的。
根据手头的设备选择部署格式
我们的 Ollama 模型页提供了一条直接用于笔记本的路径,使用 Q4_K_M GGUF 构建。我们的 Ollama 指南也记录了手动配置 GGUF 的方法。其他受支持的服务路径包括 llama.cpp、vLLM、SGLang、Transformers 和 LiteRT-LM;本文的步骤与测量均使用 Ollama。模型支持 131,072 tokens 的原生上下文,而本次本地测试配置为 4,096 tokens。
MiniCPM5-2B GGUF 格式
模型文件大小
实际用途
F16
5.04 GB
内存允许时的参考级权重
Q8_0
2.68 GB
更大的量化选项
Q4_K_M
1.56 GB
适合笔记本试用的紧凑选项
上表列出的是部署指南中的 GGUF 文件大小。在我们 16GB MacBook Air 的测试中,Ollama 报告 Q4_K_M 模型加载后占用 1.8GB,上下文为 4,096 tokens。模型文件大小与加载后的模型大小是两回事;下面的配置会写明实际使用的格式和上下文。
用 Ollama 跑同一套 Q4_K_M 配置
OpenBMB Ollama 模型提供了这里使用的 Q4_K_M 构建。安装 Ollama 后,拉取模型并启动本地服务:
ollama pull openbmb/minicpm5-2b
ollama serve
在另一个终端里,向 Ollama 的本地 API 发送一条中英混合的抽取请求。这个示例使用与我们测试相同的推理设置:关闭思考、temperature 0、JSON 模式、4,096 tokens 上下文、最多输出 128 tokens。程序会在执行任何下游动作之前,将解析出的对象与要求的键和值进行校验。
import json
import urllib.request
expected = {"ticket": "INC-204", "priority": "high", "owner": "Maya"}
payload = {
"model": "openbmb/minicpm5-2b:latest",
"messages": [
{"role": "system", "content":
"Extract only facts stated in the request. Return one JSON object, no prose. "
"Keys: ticket, priority, owner"},
{"role": "user", "content":
"请把 ticket INC-204 设为 high priority, owner is Maya. "
"Return JSON with ticket, priority, owner."}
],
"stream": False, "think": False, "format": "json",
"options": {"temperature": 0, "num_ctx": 4096, "num_predict": 128}
}
request = urllib.request.Request(
"http://127.0.0.1:11434/api/chat",
data=json.dumps(payload, ensure_ascii=False).encode(),
headers={"Content-Type": "application/json"})
with urllib.request.urlopen(request) as response:
reply = json.load(response)
actual = json.loads(reply["message"]["content"])
assert isinstance(actual, dict) and actual == expected
print(actual)
在 16GB MacBook Air 上实测的 MiniCPM5-2B 速度
我们在一台 16GB 统一内存的 Apple M5 MacBook Air 上,用 Ollama 0.33.2 运行 openbmb/minicpm5-2b:latest(Ollama 模型 ID 71a44e98400f,Q4_K_M),系统为 macOS Darwin 25.6,一次只发一个请求,关闭思考,temperature 0,JSON 模式,num_ctx=4096,num_predict=128。在第 2–12 条用例中,请求耗时中位数为 0.321 秒,测量范围从本地 HTTP POST 发出前直到完整 JSON 响应返回。生成速率中位数为 76.5 tokens/秒,按每个请求用 Ollama 报告的生成 token 数除以它报告的生成时长计算,再取中位数。这 11 次热请求包含完全匹配失败的用例 4;只有用例 1 因加载模型(报告的加载时间 1.541 秒)被排除在热启动计时中位数之外。输入长度在 66 到 84 个 prompt tokens 之间,输出为 12 到 37 个生成 tokens。完整的用例日志见下方附录。
这组小型中英双语测试说明了什么
我们测试了 12 条中英混合的合成员工请求。每条请求的系统消息都会列出要求的键;模型在关闭思考、temperature 0、4,096 tokens 上下文的设置下返回 JSON。我们解析响应,只有当它是对象、且键和值完全符合预期、没有多余键时,才计为通过。键的顺序无关紧要;近义词或归一化变体都不算通过。12 条中有 11 条通过。用例 4 未通过完全匹配校验,因为模型返回的动作值是 "cancelled",而要求的是 "cancel"。
测试集中的三条请求
下面每条用例展示原始的中英混合输入、要求的 JSON 对象和模型输出。完整的 12 条用例见附录。
一条常规工单更新(用例 1)。
原始输入: 请把 ticket INC-204 设为 high priority, owner is Maya. Return JSON with ticket, priority, owner.
期望 JSON: {"ticket":"INC-204","priority":"high","owner":"Maya"}
模型输出: {"ticket":"INC-204","priority":"high","owner":"Maya"}
完全匹配结果: 通过
缺失值用 null 表示(用例 9)。
原始输入: 客户要更新 address, but the new address is missing. Return JSON with action, new_address. Use null when missing.
期望 JSON: {"action":"update_address","new_address":null}
模型输出: {"action":"update_address","new_address":null}
完全匹配结果: 通过
一处 schema 不一致(用例 4)。
原始输入: Customer asks to cancel 订单 B-52, but no reason was provided. Return JSON with order, action, reason. Use null for missing reason.
期望 JSON: {"order":"B-52","action":"cancel","reason":null}
模型输出: {"order":"B-52","action":"cancelled","reason":null}
完全匹配结果: 失败
如果应用要求固定的动作枚举,这一差异就很关键。在执行动作之前,请把解析出的对象与函数 schema 做校验,并映射或拒绝超出允许集合的值。上面三个例子分别展示了正常更新、信息缺失的请求,以及完全匹配失败的用例。
如何判断 MiniCPM5-2B 是否适合你的工作流
做工作流试点时,先收集一批有代表性、已获授权的请求,并保留原始的中英文切换。测试前先定义好动作和必填字段,再覆盖常规请求、信息缺失和错误代价高的场景。这里的 12 条用例展示了方法,也提供了一个透明的起始集合。
MiniCPM5-2B 是紧凑型本地文本抽取和结构化输出的有力候选。中英混合请求 11/12 的结果、已公布的基准分数,以及 Ollama 路径,为团队在自己的任务上做匹配试验提供了具体依据。如果需要图像输入或更小的设备预算,可以在 MiniCPM 家族中选择合适的模型。
实际的下一步,是在真正重要的请求和设备上跑同一套 schema 校验。下方完整样本集让这次笔记本上的结果可检查、可复现。
常见问题
MiniCPM5-2B 能在不把员工请求发往云端 API 的情况下运行吗?
可以。上面的 Ollama 配置在本地执行推理,因此员工请求无需发送到云端推理 API。
最低需要什么机器?
本文测试在一台 16GB Apple M5 MacBook Air 上运行 Q4_K_M 构建,上下文 4,096 tokens,一次一个请求。我们的 GGUF 指南列出 Q4_K_M 模型文件为 1.56GB;实际容量取决于运行时、上下文长度和并发负载。
指数分数高就能保证中英双语任务成功吗?
指数和笔记本测试衡量的是不同的事情。Artificial Analysis 报告的是开启推理模型的一般性能。这里的本地运行衡量的是关闭思考、Q4_K_M 量化下的精确 schema 抽取:12 条请求中 11 条通过,失败的那个值已在上文展示。
*来源与测试设置:*基准信息于 2026 年 10 月 8 日对照 Artificial Analysis 和我们的模型卡核对。部署信息来自我们的 Ollama 模型页和 Ollama 指南。本地结果来自 12 条用例脚本;完整用例集见下方附录。
附录:完整的 12 条中英双语抽取用例
下面保留每条原始输入、期望对象、实际对象和完全匹配结论。只有用例 4 不一致。
用例 1 — 通过
原始输入:请把 ticket INC-204 设为 high priority, owner is Maya. Return JSON with ticket, priority, owner.
期望 JSON:{"ticket":"INC-204","priority":"high","owner":"Maya"}
模型输出:{"ticket":"INC-204","priority":"high","owner":"Maya"}
用例 2 — 通过
原始输入:For ticket INC-205, 负责人是 Leo,优先级 low。Return JSON with ticket, priority, owner.
期望 JSON:{"ticket":"INC-205","priority":"low","owner":"Leo"}
模型输出:{"ticket":"INC-205","priority":"low","owner":"Leo"}
用例 3 — 通过
原始输入:请将 order A-17 的 delivery date 改为 2026-10-03, status pending. Return JSON with order, date, status.
期望 JSON:{"order":"A-17","date":"2026-10-03","status":"pending"}
模型输出:{"order":"A-17","date":"2026-10-03","status":"pending"}
用例 4 — 失败
原始输入:Customer asks to cancel 订单 B-52, but no reason was provided. Return JSON with order, action, reason. Use null for missing reason.
期望 JSON:{"order":"B-52","action":"cancel","reason":null}
模型输出:{"order":"B-52","action":"cancelled","reason":null}
用例 5 — 通过
原始输入:请为客户 Ava 写英文回复。The package is delayed by two days. Return JSON with language and delay_days only.
期望 JSON:{"language":"English","delay_days":2}
模型输出:{"language":"English","delay_days":2}
用例 6 — 通过
原始输入:Ticket INC-210 已解决, but customer needs an English summary. Return JSON with ticket, status, output_language.
期望 JSON:{"ticket":"INC-210","status":"resolved","output_language":"English"}
模型输出:{"ticket":"INC-210","status":"resolved","output_language":"English"}
用例 7 — 通过
原始输入:把 invoice INV-88 标为 unpaid; due date 是 2026-10-15. Return JSON with invoice, payment_status, due_date.
期望 JSON:{"invoice":"INV-88","payment_status":"unpaid","due_date":"2026-10-15"}
模型输出:{"invoice":"INV-88","payment_status":"unpaid","due_date":"2026-10-15"}
用例 8 — 通过
原始输入:Route request R-71 to sales, not support; 优先级为 medium。Return JSON with request, team, priority.
期望 JSON:{"request":"R-71","team":"sales","priority":"medium"}
模型输出:{"request":"R-71","team":"sales","priority":"medium"}
用例 9 — 通过
原始输入:客户要更新 address, but the new address is missing. Return JSON with action, new_address. Use null when missing.
期望 JSON:{"action":"update_address","new_address":null}
模型输出:{"action":"update_address","new_address":null}
用例 10 — 通过
原始输入:Please schedule meeting M-12 on 2026-10-08 at 14:30, 时区是 Asia/Shanghai. Return JSON with meeting, date, time, timezone.
期望 JSON:{"meeting":"M-12","date":"2026-10-08","time":"14:30","timezone":"Asia/Shanghai"}
模型输出:{"meeting":"M-12","date":"2026-10-08","time":"14:30","timezone":"Asia/Shanghai"}
用例 11 — 通过
原始输入:订单 C-90 要 refund 25 USD, not 52 USD. Return JSON with order, action, amount, currency.
期望 JSON:{"order":"C-90","action":"refund","amount":25,"currency":"USD"}
模型输出:{"order":"C-90","action":"refund","amount":25,"currency":"USD"}
用例 12 — 通过
原始输入:For task T-6, 输出中文还是英文? User says: reply in English. Return JSON with task and reply_language.
期望 JSON:{"task":"T-6","reply_language":"English"}
模型输出:{"task":"T-6","reply_language":"English"}