十款模型横向对比结束后,我们得到了一个明确的工程选择:Qwen3.5-2B Q4_K_M 是本轮唯一同时通过资源门槛和结构化 Agent 路由门槛的候选。
这回答了“选哪个模型”,但还没有回答“应该怎样使用它的上下文窗口”。
在第一轮 iPhone 测试里,Qwen3.5-2B 从 4K context 切到 8K,Metal 内存只增加了 52 MiB,短 Prompt 的 TTFT 也几乎没有变化。只看这组数据,很容易得出“8K 几乎没有成本”的结论。
实际并非如此。为 8K context 预留容量,和真正处理接近 8K 的活跃 Prompt,是两种不同的负载。确定模型以后,我们又在同一台 iPhone 上做了一轮针对性测试,把这两个成本拆开测量。
为什么选型后还要再测一轮
之前的十模型横评同时考察了两类要求:
左右滑动查看全部列
| 门槛 | Qwen3.5-2B 结果 |
|---|---|
| 结构化 Parse | 100.0% |
| Skill 选择 | 95.8% |
| Action 选择 | 95.8% |
| Mode 判断 | 96.9% |
| 8K Metal 分配 | 1819.8 MiB |
| 8K 真机加载 | 3/3 成功 |
这组结果让 Qwen3.5-2B 成为手机本地 Agent 路由的默认候选。但当时 4K/8K 真机矩阵使用的是 85-token 短 Prompt,测到的是加载、context 分配、短请求延迟、内存和基础生成,并不是把窗口填满后的计算成本。
所以第二轮只回答一个更具体的生产问题:
当 Runtime 给选定模型输入接近装满的 4K 或 8K Prompt 时,prefill 需要多久,生成速度是否变化,内存和热状态又会怎样?
测试配置
模型文件和推理栈与主横评保持一致:
左右滑动查看全部列
| 项目 | 配置 |
|---|---|
| 设备 | iPhone 15 Pro Max,A17 Pro,8 GB |
| 系统 | iOS 26.6.1 |
| 模型 | Qwen3.5-2B GGUF Q4_K_M |
| 模型大小 | 1221.5 MiB |
| Runtime | SwiftLlama + llama.cpp b8668 |
| 后端 | Metal 全层卸载,Flash Attention |
| 解码 | temperature 0,top-p 1,top-k 1,seed 42 |
| 重复次数 | 每档 3 次独立进程启动 |
| 冷却 | 每次启动之间间隔 30 秒 |
模型加载完成后,App 使用模型自己的 tokenizer 校准完整渲染后的 Prompt,其中包含 system 和 chat 模板:
- 4K context:精确 3,584 个 Prompt tokens;
- 8K context:精确 7,680 个 Prompt tokens;
- 两档均预留 512 tokens 输出空间;
- 每次实际生成 63 tokens。
4K 与 8K 的执行顺序交错,避免固定顺序带来过于直接的偏差。prefill 和生成时间来自 llama.cpp 原生性能计数;TTFT 另从 Swift 推理调用边界计时到第一个非空流式片段。
左右滑动查看完整图表
图表暂时无法生成,下方仍可查看源代码。
查看图表源代码
flowchart LR
A[加载选定模型] --> B[使用模型 tokenizer 校准完整 Prompt]
B --> C1[4K 下输入 3584 tokens]
B --> C2[8K 下输入 7680 tokens]
C1 --> D[Prefill 生成 63 tokens 记录内存与热状态]
C2 --> D
D --> E[比较容量成本与实际使用成本]
4K 与 8K 的真实计算成本
下表为三次独立启动的 P50。我们同时保留 min/P50/max,不用一个中位数掩盖样本量较小的事实。
左右滑动查看全部列
| Context | Prompt | Prefill min / P50 / max | Prefill 速度 | TTFT P50 | 63-token 总耗时 | Decode | 进程内存 | Metal 内存 | Thermal |
|---|---|---|---|---|---|---|---|---|---|
| 4K | 3,584 | 8.885 / 13.375 / 13.432s | 268.0 tok/s | 13.385s | 16.570s | 19.9 tok/s | 291.3 MiB | 1769.4 MiB | Fair 1,Serious 2 |
| 8K | 7,680 | 20.980 / 30.583 / 31.103s | 251.1 tok/s | 30.602s | 33.868s | 19.7 tok/s | 322.4 MiB | 1821.4 MiB | Serious 3 |
比单个数字更重要的是下面三个判断。
开放容量的成本很低
短 Prompt 分配测试中,8K 只比 4K 多使用 52 MiB Metal 内存。这次生成完成后差值仍然是 52 MiB:1821.4 对 1769.4 MiB。模型文件不会变大,8 GB iPhone 也仍有可用的内存空间。
这说明 8K 可以开放,但不意味着每轮都应该把它填满。
活跃上下文会直接转化成计算和等待
7,680-token 的 prefill P50 为 30.583 秒,是 3,584-token 的 13.375 秒的 2.29 倍。Prefill 吞吐也从 268.0 降到 251.1 tokens/s。
Decode 基本不变,分别是 19.9 和 19.7 tokens/s。也就是说,长等待主要发生在答案开始之前;一旦进入生成阶段,输出速度几乎相同。
所以,“短 Prompt TTFT 为 0.274 秒”和“接近填满 8K 时 TTFT 为 30.602 秒”并不矛盾。它们是在相同 context 上限下,对两种完全不同的活跃 Prompt 长度进行测量。
真正更严的边界是发热
两档近满窗口都产生了明显热压力。3,584-token 三次中有两次以 Serious 结束,7,680-token 三次则全部以 Serious 结束。每轮之间留出的 30 秒冷却也没有改变这个结果。
如果只看内存,8K 会显得非常轻松。加入延迟和热状态后就能看出,context 必须由 Runtime 主动管理,不能被当成无限追加文本的容器。
最终采用的部署策略
我们把“设备支持的硬上限”和“日常工作的软预算”分开:
- 物理内存不少于 8 GB 的设备开放 8K context 上限。
- 普通活跃 Prompt 尽量保持在 2K-4K。
- 推理前用模型 tokenizer 统计完整渲染后的 token 数。
- 超过软预算时,优先压缩较早的会话历史,不先删除当前指令和相关证据。
- 检索片段和 Tool 输出先重排、裁剪,不做无上限拼接。
- 只有额外证据确实值得约 20-31 秒 prefill 时,才向 8K 扩展。
- 始终至少保留 512 tokens,用于输出和恢复指令。
左右滑动查看完整图表
图表暂时无法生成,下方仍可查看源代码。
查看图表源代码
flowchart TD
A[组合 System 历史 证据与 Tool 输出] --> B[统计渲染后 tokens]
B --> C{是否在 2K-4K 工作预算内}
C -->|是| D[普通推理]
C -->|否| E[压缩历史并重排证据]
E --> F{保留输出空间后是否低于 8K}
F -->|是且任务值得等待| G[长上下文推理]
F -->|否| H[拆分任务或缩小范围]
关键选择并不是永久固定成“4K 或 8K”,而是:8K 可用,默认只激活 2K-4K。
复杂任务因此仍有空间容纳本地文档、检索证据和 Tool 结果,同时普通请求不必承担 30 秒的首 token 延迟。
这轮测试不能证明什么
这是一组确定性的合成计算负载,用来测量选定模型在目标手机上处理明确 token 数量的成本。它还不能证明:
- 7.5K 自然多文档内容的语义准确率;
- 持续工作过程中的耗电量;
- 20 轮连续对话的表现;
- 完整的温度恢复时间;
- 多 App 内存压力下的 Jetsam 行为;
- 更低内存设备能获得相同性能。
每档也只有三次样本。因此文中同时公开范围和热状态计数,让这个限制保持可见。
这些是下一轮需要单独验证的问题,不能从当前数据中顺带推导。
最终判断
更细的测试没有推翻模型选型。Qwen3.5-2B Q4_K_M 仍是默认本地路由模型,因为它在本轮对比里同时提供了最强的结构化 Agent 结果,以及低于 2 GiB 的 8K Metal 分配。
但部署方式需要明确:
- 8K 是 8 GB 设备上的可用上限。
- 2K-4K 是普通活跃 Prompt 的目标范围。
- 接近填满 8K 是显式慢路径,不是默认路径。
“预留容量”和“实际计算”之间的区别,是这次补测最有价值的结论。在手机上,context window 不只意味着内存,也意味着首 token 延迟、发热、能耗,以及模型在执行前必须读完多少无效信息。
完整评测方法和全部 96 条 Agent 预期路由,可以继续阅读《我们如何为手机本地 Agent 选择模型》。