
引子
以前在苹果生态里搞 AI,有点像去五星级酒店点外卖:
你想吃火锅,前台微笑着说:「先生,我们这里只提供酒店特供套餐。」
Foundation Models 很香,隐私也漂亮,但菜单是苹果定的。第三方大模型?抱歉,请去云端排队,或者自己用 Metal 手搓推理引擎------后者相当于自带锅、自带火、自带厨师,还得保证不被 jetsam 一脚踹出房间。
WWDC26 之后,情况变了。
CoreAI 登场。苹果不再只给你「自家菜」,而是递过来一套厨房:你可以把 PyTorch 模型端上来,切好、炖好、装进 .aimodel,然后在 iPhone / Mac 上本地端着碗吃。
今天这篇,就从「这到底是什么」讲到「第三方 Qwen 怎么真在本机流式吐字」------有背景、有逻辑、有能跑通的代码味道。读完你未必马上能上架,但至少不会再把 CoreAI 当成「又一个神秘 Framework」。
无需等待,让我们马上开始 CoreAI 大冒险吧!;-)
一、WWDC26 的背景:苹果为什么要推 CoreAI?
先把时间线捋直,不然后面代码会像无根之木。
过去几年,苹果的端侧 AI 路线大致是:
- Core ML:偏传统机器学习 / 视觉音频,擅长「看图说话」,不太擅长「边想边聊」。
- Apple Intelligence + Foundation Models:系统级大模型能力,隐私优先,但模型选择权在苹果手里。
- 开发者的真实痛点:我想跑 Qwen、Llama、自家微调模型------既要 GPU/ANE 性能,又要 Swift 友好 API,还想少写一点 KV Cache 手活。
WWDC26 给出的答案叫 CoreAI:
- 面向 Apple silicon 的新一代本地推理框架;
- 配套工具链:coreai-torch(导出)→ coreai-build(编译)→ CoreAI runtime(运行);
- 官方参考仓 apple/coreai-models:既有导出配方,也有 Swift 侧 LLM 引擎、采样、KV Cache 等运行时工具;
- 系统门槛很硬核:macOS / iOS 27.0+,Xcode 27.0+------不是「旧系统装个 SDK 就能用」那种温柔。
一句话概括:
CoreAI 不是「又一个聊天 SDK」,而是「把任意合适结构的模型,变成苹果芯片上的一等公民」的整条产线。
Foundation Models 像「苹果官方外卖」;CoreAI 像「你自带食材,厨房和灶台归苹果管」。

二、先建立心智模型:CoreAI 在干什么?
别一上来就背 API。先记住这条流水线:
text
AUTHOR(可选) 按平台重写模型结构(静态 shape、ANE/GPU 友好)
↓
COMPRESS(可选) 量化 / 调色盘压缩,换体积与精度
↓
EXPORT PyTorch → AIProgram(coreai-torch / TorchConverter)
↓
COMPILE(可选) AOT 编译到目标平台(coreai-build)
↓
RUN 设备上加载 .aimodel,Swift / Python 推理
对应用开发者来说,日常最常碰到的是最后一步 RUN 。
而对「第三方大模型适配」来说,你会拿到的是别人已经导好的 LanguageBundle------目录里通常长这样:
text
some_model_bundle/
xxx.aimodel/ ← 真正的模型资产(含 main.mlirb 等)
metadata.json
tokenizer/
.aimodel 不是「把权重 zip 一下改个后缀」。它是经过导出、优化、面向 Apple 运行时的模型资产;LLM 场景下,外面还会包一层 LanguageBundle,把 tokenizer、上下文长度、function map 这些聊天相关的元数据绑在一起。
浅层理解:
CoreAI = 本地跑模型的「操作系统级厨房」。
深层理解:
你写的不是「HTTP 请求 OpenAI」,而是「加载 bundle → 创建推理引擎 → token 级 generate 流」。

三、最简 Runtime:先看「一张图」怎么跑
在聊 LLM 之前,先看官方风格的最小推理------这能帮你分清两层 API:
- 底层 :
AIModel+InferenceFunction+NDArray(通用张量推理) - 上层 :
LanguageBundle+EngineFactory+InferenceEngine(给 LLM 准备好的引擎)
底层大概长这样:
swift
import CoreAI
let model = try await AIModel(contentsOf: modelURL)
guard let fn = try model.loadFunction(named: "main") else { return }
var input = NDArray(shape: [1, 3, 224, 224], scalarType: .float32)
var view = input.mutableView(as: Float32.self)
// 填入图像数据...
var outputs = try await fn.run(inputs: ["image": input])
let result = outputs.remove("logits")?.ndArray
导出侧对应的是 Python:
python
from coreai_torch import TorchConverter, get_decomp_table
import torch
ep = torch.export.export(model, args=(torch.randn(1, 3, 224, 224),))
ep = ep.run_decompositions(get_decomp_table())
program = (
TorchConverter()
.add_exported_program(ep, input_names=["image"], output_names=["logits"])
.to_coreai()
)
program.optimize()
program.save_asset("model.aimodel")
看到没?苹果这次把「训练框架 → 设备资产 → App 调用」串成了闭环。
LLM 只是在这条环上,多叠了一层语言引擎。

四、进入正题:第三方 LLM 怎么接进 CoreAI?
下面以社区已转换好的 Qwen3.5-2B CoreAI LanguageBundle 为例(Hugging Face 上已有现成资产)。思路适用于同类 pipelined LLM。
1)加载:不是 open(file),是「建一座小发电厂」
swift
import CoreAIShared
import CoreAILanguageModels
import Tokenizers
// 某些 pipelined decode 图是 static [1,1],需要在创建引擎前设置
if getenv("COREAI_CHUNK_THRESHOLD") == nil {
setenv("COREAI_CHUNK_THRESHOLD", "1", 1)
}
let bundle = try LanguageBundle(at: bundleDirectory)
let config = ModelConfig(
name: bundle.name,
tokenizer: bundle.tokenizer,
vocabSize: bundle.vocabSize,
maxContextLength: bundle.maxContextLength,
serializedModel: [bundle.modelAssetPath],
function: bundle.language.functionMap?.name(for: "main") ?? "main"
)
let engine = try await EngineFactory.createEngine(
config: try JSONEncoder().encode(config),
modelURL: try bundle.requireModelURL(for: ModelBundle.ComponentKey.main),
options: EngineOptions(variant: "coreai-pipelined")
)
let tokenizer = try await bundle.loadTokenizer()
这里有几个「看起来像咒语、其实很关键」的点:
| 点 | 为什么重要 |
|---|---|
LanguageBundle |
一次读齐模型路径、词表、上下文长度、tokenizer |
EngineFactory.createEngine |
真正把 .aimodel 特化成可推理引擎 |
variant: "coreai-pipelined" |
告诉运行时:这是流水线式 decode 图,不是普通前馈 |
COREAI_CHUNK_THRESHOLD=1 |
适配 static [1,1] decode;设错了可能加载成功、生成翻车 |
第一次加载往往会触发 specialization(特化):系统按当前设备把计算图「烤」成更合适的执行形态。冷启动可能几十秒,还可能吃掉数 GB 磁盘缓存------所以产品上千万别让用户盯着转圈圈傻等,得有「首次准备模型」的心智。
2)Prompt:别直接 encode(用户原话)
很多模型吃的是 chat template,不是裸字符串。正确姿势是先组 messages,再套模板:
swift
let messages: [[String: String]] = [
[
"role": "system",
"content": "你是一个直接回答问题的助手。优先使用中文,回答简洁清楚。"
],
[
"role": "user",
"content": prompt
]
]
let inputIds = try tokenizer
.applyChatTemplate(messages: messages)
.map { Int32($0) }
guard inputIds.count < maxContextLength - 1 else {
throw MyError.promptTooLong
}
对 Qwen 这类带 /think、/no_think 的模型,还可以在应用层做三种模式:
- 高效 :系统提示 +
/no_think,尽量直接给答案 - 推理 :追加
/think,允许展开思考 - 跟随输入:尊重用户自己写的控制指令
这不是 CoreAI 的硬性要求,却是「第三方模型适配」里最容易被忽略的产品细节------引擎只会吐 token,不会替你懂产品语义。
3)生成:AsyncSequence 式地「一个字一个字挤牙膏」
swift
try await engine.reset()
let stream = try engine.generate(
with: inputIds,
samplingConfiguration: SamplingConfiguration(
temperature: 0.7,
topK: 20,
topP: 0.8,
minP: nil
).normalized(),
inferenceOptions: InferenceOptions(maxTokens: maxTokens)
)
var answer = ""
for try await step in stream {
try Task.checkCancellation()
let tokenId = Int(step.tokenId)
// 解码、过滤 stop sequence、<think> 块等
let chunk = decodeAndFilter(tokenId)
if !chunk.isEmpty {
answer += chunk
// 推到 SwiftUI
}
}
这就是本地 LLM 的「灵魂回路」:
text
prompt → chat template → token ids
→ engine.generate 流
→ 逐 token 解码
→ 停词 / 过滤 / UI 追加
采样参数(Temperature / Top-K / Top-P / Min-P)和云 API 概念一致;差别在于:一切发生在进程内,隐私不错,内存账单很真实。

五、再深一层:适配第三方模型时,你会撞上的「人情世故」
代码能跑,不等于产品能用。真实适配里,常见坑如下------每条都来自「本机真跑过」的经验,不是 PPT。
1)模型别塞进 .app
2B 级量化包常常 ~3GB。打进 App 包,审核会皱眉,用户下载会骂娘。正确姿势是:
- 首次启动后下载 / 本地导入
- 先落到 staging,校验齐全再原子替换到正式目录
- 至少确认
.aimodel、main.mlirb、metadata.json、tokenizer/齐全,且大文件不是 Git LFS 指针冒充
2)Think 块会「偷吃」你的可见输出
模型可能先吐 <think>...。如果你在「高效模式」还直接把原文糊到 UI,用户会以为 App 坏了。
应用层需要:
- 过滤完整
<think>...</think> - 未闭合时先别展示后面内容
- 必要时用更强硬的 system prompt 重试一次
CoreAI 负责「算」,你负责「做人话」。
3)Stop sequence 与「假结束」
命中 EOS 不一定等于「答完了」。高效模式下偶发短句提前结束、没有句末标点------这时可以识别为 incomplete EOS,再补一句「只从截断处继续」。
长回答撞到 token 上限,则做续写:把已有答案塞回 prompt,要求「不要重复,从中断处自然接着写」。
4)内存与 entitlement 是物理定律
- Mac 上建议给系统和别的进程留足余量(官方指导常见说法是留出数 GB headroom)
- iPhone 上大模型可能需要更高内存限额相关 entitlement,否则冷 specialization 直接
bad_alloc - 用进程 footprint 观察「加载前后涨了多少」,比凭感觉喊「好卡」有用得多
5)平台策略不同
苹果自己的指导也很直白:
- iOS:偏能效,静态 shape、更激进压缩,模型尽量别太大
- macOS:可更吃规模,动态 shape / 控制流更宽松
同一套「第三方模型适配」,在 iPhone 和 Mac 上可能要选不同压缩配方------这不是口味问题,是物理与系统策略问题。
六、把整条链路压成一张图
text
┌──────────────────────────────┐
│ 已转换的 LanguageBundle │
│ (.aimodel + tokenizer + md) │
└───────────────┬──────────────┘
│ LanguageBundle(at:)
▼
┌──────────────────────────────┐
│ ModelConfig + EngineFactory │
│ variant: coreai-pipelined │
└───────────────┬──────────────┘
│ loadTokenizer()
▼
┌──────────────────────────────┐
│ chat template → input ids │
└───────────────┬──────────────┘
│ engine.generate(...)
▼
┌──────────────────────────────┐
│ Async token stream │
│ → 过滤 / 停词 / UI 流式刷新 │
└──────────────────────────────┘
如果你只会画 HTTP 时序图,现在可以在旁边再贴一张这个------云端是请求-响应;CoreAI 是加载-特化-流式解码。
七、一句中式总结
CoreAI 的出现,意味着苹果终于承认一件事:
「端侧智能」不能只靠皇家厨师,也得允许民间大厨进厨房------但刀具、灶台、排烟系统,还得按苹果标准来。
对开发者而言:
- 浅 :本地加载
.aimodel,Swift 里流式生成 - 中:LanguageBundle + EngineFactory + chat template + 采样
- 深:导出、量化、静态 shape、specialization、内存与平台策略
Foundation Models 解决「大多数 App 的官方智能」;
CoreAI 解决「我就要跑自己的 / 社区的模型,而且要跑在苹果芯上」。
两条腿,才算站稳。
尾声
今天我们顺着「第三方大模型怎么在 CoreAI 里被叫起来」走完了调用侧:bundle、引擎、模板、流式、坑点。
你手里已经有一张能照着做的地图------从「听说 WWDC26 出了 CoreAI」,走到了「真的能在本机看 Qwen 一个 token 一个 token 往外蹦」。
故事到这里就告一段落了,感谢宝子们的观赏。
我是大熊猫侯佩,再会啦!8-)