全部文章

我们如何为手机本地 Agent 选择模型

公开手机本地模型的测试方法,并列出全部 96 条双语用例及预期路由、结构化指标、固定解码、iPhone 资源实测、复现证据与结论边界。

端侧 AI评测方法Agent 评估手机推理小模型
我们如何为手机本地 Agent 选择模型

这套测试最早不是为了做一张模型排行榜。

我们当时遇到的问题很具体:手机里的本地 Agent 需要一个默认路由模型。它不能只会聊天,还要在很短的时间里判断用户想做什么、选择正确的能力、提取执行参数,并在信息不足时停下来追问。与此同时,它还得装得进手机,不能因为 8K context 就把内存和发热推到不可接受的水平。

所以我们真正想验证的是:

在我们的双语、结构化、手机本地 Agent 场景里,哪个模型能以合理的资源成本,最稳定地完成路由与执行参数生成?

这个限定很重要。我们测到的不是模型的通用智力,也不是写作、数学或知识问答排名。它回答的是一个产品工程问题。

先把 Agent 的工作拆开

用户说一句“明天早上七点叫我起床”,真正执行之前至少要完成两步判断。

第一步从当前可用能力中选出正确的 Skill,例如闹钟、日历、邮件或记忆。第二步再选择具体 Action,提取已知参数、列出缺失参数,并判断当前应该直接执行、先规划,还是向用户追问。

Flow diagram Preparing diagram
View diagram source
flowchart LR
    A[用户请求] --> B[候选 Skill 召回]
    B --> C[Stage 1: Skill 选择]
    C --> D[Stage 2: Action 与参数]
    D --> E{信息是否完整}
    E -->|完整| F[执行或规划]
    E -->|缺失| G[结构化追问]
    F --> H[Swift Runtime 校验]
    G --> H

我们没有让模型直接输出一段看起来合理的自然语言,然后凭感觉打分。评测器要求它返回明确的结构:Skill、Action、Mode、已知 Slots、缺失 Slots 和目标。Swift Runtime 再用确定性规则检查这个结构是否属于当前候选范围、是否符合 Schema。

原因很简单:对于一个会操作系统能力的 Agent,“大概理解了”不够。选错 Action 和少填一个关键参数,后果完全不同。

96 条用例是怎么组成的

当前模型横测使用 v3-96-staged-skill-action 数据集,共 96 条请求:

维度构成
语言中文 48 条,英文 48 条
Skill12 类
难度core 48 条,challenge 48 条
流程两阶段 Skill → Action/Slots/Mode
Context4K 与 8K 分别完整运行

core 用例验证常见表达,例如创建闹钟、查询日程、起草邮件。challenge 用例会加入相近能力干扰、缺少必要信息、跨能力规划和更含糊的说法。

我们刻意保留了短输入。真实手机上的指令经常只有几个字,“八点提醒我”或者“回一下这封邮件”都很常见。一个模型如果只在写得像测试题的完整句子上表现稳定,对实际产品帮助不大。

每条用例都保存期望 Skill、Action、Mode 和相关结构约束。当前公开成绩按完整 96 条作为分母,不会把解析失败的样本从准确率里删掉。

96 条用例和预期结果

下面的“预期结果”不是最终自然语言答案,而是模型必须交给 Runtime 的路由契约。每条用例分别核对 Skill、Action 和 Mode;“附加断言”表示该题是否还检查缺槽为空/非空,或者必须进入规划。

三种具体判分示例

  • 信息完整明天早上七点设置一个起床闹钟

应为 builtin.alarm.manage → create_alarm,Mode 为 actmissing_slots 必须为空。

  • 信息不完整帮我起草一封邮件

应为 builtin.mail.draft → create_draft,Mode 为 clarifymissing_slots 必须非空。邮件 Schema 要求收件人、主题和正文。

  • 多步骤任务先查看明天日程,然后为没有空闲时间的事项创建提醒

应为 builtin.task.plan_recover → orchestrate,Mode 为 act,并满足 requires_plan = true

这里也能看出当前版本的一项限制:v3 对缺槽只检查“空”或“非空”,还没有逐个给缺失字段名称计分。精确槽位名称和值会进入下一版契约测试。

Core:48 条常规用例

# / 用例 ID语言测试输入预期 Skill → ActionMode附加断言
1 · zh-alarm-create中文明天早上七点设置一个起床闹钟builtin.alarm.managecreate_alarmact缺槽:空
2 · zh-alarm-list中文查看我在 Vesta 里创建的所有闹钟builtin.alarm.managelist_alarmsretrieve
3 · en-alarm-create英文Set an alarm for 7 AM tomorrow.builtin.alarm.managecreate_alarmact缺槽:空
4 · en-alarm-list英文List all alarms currently managed by Vesta.builtin.alarm.managelist_alarmsretrieve
5 · zh-schedule-event中文明天下午三点在 A 会议室安排项目评审,持续一小时builtin.schedule.managecreate_calendar_eventact缺槽:空
6 · zh-schedule-reminder中文提醒我周五下午六点提交周报builtin.schedule.managecreate_reminderact缺槽:空
7 · en-schedule-event英文Create a calendar event for project review tomorrow at 3 PM for one hour.builtin.schedule.managecreate_calendar_eventact缺槽:空
8 · en-schedule-reminder英文Remind me Friday at 6 PM to submit the weekly report.builtin.schedule.managecreate_reminderact缺槽:空
9 · zh-brief-today中文汇总我今天的日程和未完成提醒builtin.daily.briefingsummarize_scheduleretrieve
10 · zh-brief-conflict中文看看本周安排里有没有冲突和逾期事项builtin.daily.briefingsummarize_scheduleretrieve
11 · en-brief-today英文Summarize today's calendar and unfinished reminders.builtin.daily.briefingsummarize_scheduleretrieve
12 · en-brief-overdue英文Show my schedule conflicts and overdue reminders this week.builtin.daily.briefingsummarize_scheduleretrieve
13 · zh-general-explain中文解释一下什么是最终一致性builtin.general.answeransweranswer
14 · zh-general-howto中文如何安全地删除一个文件?只需要解释步骤builtin.general.answeransweranswer
15 · en-general-explain英文Explain eventual consistency in simple terms.builtin.general.answeransweranswer
16 · en-general-howto英文How do I delete a file safely? Explain only.builtin.general.answeransweranswer
17 · zh-knowledge-cite中文根据我的本地项目文档,发布日期是什么?请引用来源builtin.knowledge.answeranswer_from_knowledgeretrieve
18 · zh-knowledge-summary中文总结我导入的合同里关于违约责任的内容builtin.knowledge.answeranswer_from_knowledgeretrieve
19 · en-knowledge-cite英文Search my local documents for the launch date and cite the source.builtin.knowledge.answeranswer_from_knowledgeretrieve
20 · en-knowledge-compare英文Compare the pricing terms in my two imported contracts.builtin.knowledge.answeranswer_from_knowledgeretrieve
21 · zh-connected-contact中文查一下通讯录里 Alex 的电话号码builtin.connected.contextanswer_from_sourceretrieve
22 · zh-connected-web中文总结这个网页的内容:https://example.com/reportbuiltin.connected.contextanswer_from_sourceretrieve
23 · en-connected-mail英文Look in my Gmail for the latest message from Alex.builtin.connected.contextanswer_from_sourceretrieve
24 · en-connected-location英文What city am I currently in?builtin.connected.contextanswer_from_sourceretrieve
25 · zh-document-import中文选择并导入这份 PDF 到本地知识库builtin.document.ingestimport_documentact
26 · zh-document-ocr中文识别我选择的图片文字并录入知识库builtin.document.ingestscan_documentact
27 · en-document-import英文Import the selected PDF into my local knowledge base.builtin.document.ingestimport_documentact
28 · en-document-ocr英文Run OCR on the selected image and add it to the knowledge base.builtin.document.ingestscan_documentact
29 · zh-memory-remember中文记住我不喜欢订阅制软件builtin.memory.governancerememberact缺槽:空
30 · zh-memory-forget中文忘记我之前保存的语言偏好builtin.memory.governanceforgetact
31 · en-memory-list英文Show the long-term preferences you remember about me.builtin.memory.governancelist_memoriesretrieve
32 · en-memory-remember英文Remember that I prefer one-time purchases.builtin.memory.governancerememberact缺槽:空
33 · zh-mail-complete中文给 Alex 起草邮件,主题是周会,正文写明改到周五下午三点builtin.mail.draftcreate_draftact缺槽:空
34 · zh-mail-missing中文帮我起草一封邮件builtin.mail.draftcreate_draftclarify缺槽:非空
35 · en-mail-complete英文Draft an email to Alex with subject Weekly Sync and say it moved to Friday at 3 PM.builtin.mail.draftcreate_draftact缺槽:空
36 · en-mail-missing英文Create an email draft for me.builtin.mail.draftcreate_draftclarify缺槽:非空
37 · zh-runtime-contract中文检查 Vesta 当前的 Runtime 和安全执行契约builtin.runtime.inspectinspect_runtimeretrieve
38 · zh-runtime-mcp中文列出本机 MCP Runtime 当前暴露的能力builtin.runtime.inspectinspect_runtimeretrieve
39 · en-runtime-contract英文Inspect Vesta's current runtime and safety contract.builtin.runtime.inspectinspect_runtimeretrieve
40 · en-runtime-mcp英文Show the capabilities exposed by the local MCP runtime.builtin.runtime.inspectinspect_runtimeretrieve
41 · zh-plan-sequence中文先查看明天日程,然后为没有空闲时间的事项创建提醒builtin.task.plan_recoverorchestrateact必须规划
42 · zh-plan-breakdown中文把准备发布会这件事拆成可验证的步骤并跟踪进度builtin.task.plan_recoverorchestrateact必须规划
43 · en-plan-sequence英文First inspect tomorrow's calendar, then create reminders for unresolved items.builtin.task.plan_recoverorchestrateact必须规划
44 · en-plan-breakdown英文Break down the product launch preparation into verifiable steps and track progress.builtin.task.plan_recoverorchestrateact必须规划
45 · zh-data-export中文导出并加密备份我的全部 Vesta 本地数据builtin.data.portabilityexport_packageact
46 · zh-data-restore中文从这个 vestaexport 数据包选择性恢复记忆和任务builtin.data.portabilityrestore_packageact
47 · en-data-export英文Export an encrypted backup of all my local Vesta data.builtin.data.portabilityexport_packageact
48 · en-data-inspect英文Inspect this Vesta backup package without restoring it.builtin.data.portabilityinspect_packageretrieve

Challenge:48 条边界用例

# / 用例 ID语言测试输入预期 Skill → ActionMode附加断言
49 · challenge-zh-alarm-recurring中文每个工作日早上八点半叫我起床builtin.alarm.managecreate_alarmact缺槽:空
50 · challenge-zh-alarm-query中文我明早有设叫醒铃吗?builtin.alarm.managelist_alarmsretrieve
51 · challenge-en-alarm-recurring英文Wake me at 6:45 every weekday.builtin.alarm.managecreate_alarmact缺槽:空
52 · challenge-en-alarm-query英文Do I have a wake-up alarm tomorrow morning?builtin.alarm.managelist_alarmsretrieve
53 · challenge-zh-schedule-create中文下周一上午十点留半小时和产品经理同步builtin.schedule.managecreate_calendar_eventact缺槽:空
54 · challenge-zh-schedule-delete中文删除明天下午由 Vesta 创建的牙医提醒builtin.schedule.managedelete_reminderact
55 · challenge-en-schedule-create英文Put lunch with Sam on my calendar at noon Tuesday.builtin.schedule.managecreate_calendar_eventact缺槽:空
56 · challenge-en-schedule-delete英文Cancel the Vesta reminder to pay rent.builtin.schedule.managedelete_reminderact
57 · challenge-zh-brief-tomorrow中文我明天有哪些会和待办,按时间排一下builtin.daily.briefingsummarize_scheduleretrieve
58 · challenge-zh-brief-load中文这周哪几天的安排最满?builtin.daily.briefingsummarize_scheduleretrieve
59 · challenge-en-brief-morning英文Give me a morning briefing from my calendar and reminders.builtin.daily.briefingsummarize_scheduleretrieve
60 · challenge-en-brief-load英文Which days this week are overloaded?builtin.daily.briefingsummarize_scheduleretrieve
61 · challenge-zh-general-rewrite中文把这句话改得更礼貌:你写错了builtin.general.answeransweranswer
62 · challenge-zh-general-calculate中文计算 240 的 18% 是多少builtin.general.answeransweranswer
63 · challenge-en-general-rewrite英文Rewrite this more politely: You are wrong.builtin.general.answeransweranswer
64 · challenge-en-general-howto英文How can I set an alarm on iPhone? Explain the steps only.builtin.general.answeransweranswer
65 · challenge-zh-knowledge-policy中文从已经收录的报销制度里找出打车标准builtin.knowledge.answeranswer_from_knowledgeretrieve
66 · challenge-zh-knowledge-compare中文对比知识库中两份方案的风险章节builtin.knowledge.answeranswer_from_knowledgeretrieve
67 · challenge-en-knowledge-policy英文According to documents already in my library, what is the reimbursement limit?builtin.knowledge.answeranswer_from_knowledgeretrieve
68 · challenge-en-knowledge-summary英文Summarize the termination clause from the contract I previously imported.builtin.knowledge.answeranswer_from_knowledgeretrieve
69 · challenge-zh-connected-web中文打开并概括这个链接:https://example.com/briefbuiltin.connected.contextanswer_from_sourceretrieve
70 · challenge-zh-connected-outlook中文在 Outlook 里找 Alex 最新发来的邮件builtin.connected.contextanswer_from_sourceretrieve
71 · challenge-en-connected-contact英文Find Jordan's phone number in my contacts.builtin.connected.contextanswer_from_sourceretrieve
72 · challenge-en-connected-web英文Read and summarize https://example.com/brief.builtin.connected.contextanswer_from_sourceretrieve
73 · challenge-zh-document-scan中文把我刚选的扫描件 OCR 后加入资料库builtin.document.ingestscan_documentact
74 · challenge-zh-document-word中文导入这个 Word 文件并建立本地索引builtin.document.ingestimport_documentact
75 · challenge-en-document-photo英文Extract text from this selected photo and save it to my local library.builtin.document.ingestscan_documentact
76 · challenge-en-document-sheet英文Add the selected spreadsheet to my local documents.builtin.document.ingestimport_documentact
77 · challenge-zh-memory-remember中文以后回答尽量简短,请记住这个偏好builtin.memory.governancerememberact缺槽:空
78 · challenge-zh-memory-list中文列出你长期保存的关于我的偏好builtin.memory.governancelist_memoriesretrieve
79 · challenge-en-memory-forget英文Forget that I prefer dark mode.builtin.memory.governanceforgetact
80 · challenge-en-memory-update英文Change my saved language preference to Chinese.builtin.memory.governancerememberact
81 · challenge-zh-mail-complete中文给财务起草邮件,说明发票将在周五补交builtin.mail.draftcreate_draftact缺槽:空
82 · challenge-zh-mail-missing中文写封邮件通知项目延期builtin.mail.draftcreate_draftclarify缺槽:非空
83 · challenge-en-mail-complete英文Draft an email to finance saying the invoice will arrive Friday.builtin.mail.draftcreate_draftact缺槽:空
84 · challenge-en-mail-missing英文Write an email announcing the delay.builtin.mail.draftcreate_draftclarify缺槽:非空
85 · challenge-zh-runtime-sources中文检查 Vesta 当前授权了哪些数据源builtin.runtime.inspectinspect_runtimeretrieve
86 · challenge-zh-runtime-tools中文查看本地执行器现在注册了哪些工具builtin.runtime.inspectinspect_runtimeretrieve
87 · challenge-en-runtime-tools英文Which tools are currently registered in the local runtime?builtin.runtime.inspectinspect_runtimeretrieve
88 · challenge-en-runtime-write英文Check whether this runtime is allowed to execute write operations.builtin.runtime.inspectinspect_runtimeretrieve
89 · challenge-zh-task-sequence中文先找出今天逾期的事项,再逐项建立新的提醒builtin.task.plan_recoverorchestrateact必须规划
90 · challenge-zh-task-resume中文继续上次中断的发布准备任务并从失败步骤恢复builtin.task.plan_recoverrecoveract必须规划
91 · challenge-en-task-sequence英文Review today's overdue items first, then create a reminder for each one.builtin.task.plan_recoverorchestrateact必须规划
92 · challenge-en-task-resume英文Resume the interrupted launch task from its failed step.builtin.task.plan_recoverrecoveract必须规划
93 · challenge-zh-data-inspect中文只检查这个备份包里有什么,不要恢复builtin.data.portabilityinspect_packageretrieve
94 · challenge-zh-data-export中文把知识库和长期偏好导出成加密数据包builtin.data.portabilityexport_packageact
95 · challenge-en-data-restore英文Restore only memories from this Vesta export package.builtin.data.portabilityrestore_packageact
96 · challenge-en-data-inspect英文Verify this backup archive without importing anything.builtin.data.portabilityinspect_packageretrieve

我们分别看哪些指标

我们不使用一个可以互相抵消问题的总分。

指标它回答的问题
Parse两阶段输出能否完整解析并进入契约
Skill是否选中了正确能力
Action是否选中了能力中的正确操作
Mode应该执行、规划还是追问
Missing Slots必填信息缺失时,是否真的停下来询问
Scope Validity输出是否仍在候选能力和 Schema 范围内
TTFT / E2E多快开始回答,以及整次路由耗时

Parse 高不代表语义正确。一个模型可以非常稳定地输出合法 JSON,同时稳定地选错 Skill。反过来,模型可能理解了任务,却因为输出格式漂移而无法被 Runtime 使用。这两类失败必须分开看。

最终是否适合作为执行路由模型,主要看 Skill、Action、Mode 和缺槽行为。只在 Skill 上表现好、Action 明显偏低的模型,可以继续研究为第一阶段粗选器,但不能直接获得系统操作权。

尽量公平,不等于所有 Prompt 一模一样

10 个模型统一使用 GGUF Q4_K_M,相同 evaluator、相同数据集、相同 4K/8K 配置,以及固定解码参数:

temperature = 0
top_p = 1
top_k = 1
seed = 42
candidate_limit = 3

但我们没有把同一段 ChatML 文本硬塞给所有模型。Llama 3 使用自己的 header tokens,SmolLM3 显式关闭 extended thinking,LFM2.5 使用要求的 start-of-text token。Prompt 内容和输出契约保持一致,外层模板遵循模型原生格式。

这是我们对公平性的理解:任务相同、信息相同、规则相同,但不要故意用错误模板让某个模型吃亏。

每个权重还记录发布方、仓库、commit、文件名、字节数、SHA-256、许可证和 Prompt Profile。只写一个模型名称远远不够;同名模型的不同转换或量化文件,结果可能并不一样。

能力测试和真机测试为什么分开

这里有一个容易误读的地方。

96 条能力用例使用相同模型权重,在桌面 Metal evaluator 上运行。这样做便于批量执行、保存完整原始输出,并让不同模型在稳定环境中重复比较。

资源数据则来自 iPhone 15 Pro Max 真机,配置为 A17 Pro、8 GB 物理内存、SwiftLlama 和 llama.cpp b8668。每个模型分别测试 4K 与 8K,每个组合独立启动三次,共 60 次。

真机记录:

  • 模型加载时间;
  • 短 Prompt 的 TTFT;
  • 进程内存;
  • Metal 内存;
  • 热状态;
  • 是否成功完成基础生成。
Flow diagram Preparing diagram
View diagram source
flowchart TB
    A[固定模型文件与 SHA-256] --> B1[桌面 Metal 能力评测]
    A --> B2[iPhone 真机资源评测]
    B1 --> C1[96 条双语请求]
    C1 --> D1[Parse Skill Action Mode]
    B2 --> C2[4K 与 8K 各三次独立启动]
    C2 --> D2[Load TTFT Memory Thermal]
    D1 --> E[选型判断]
    D2 --> E

我们没有把这两类数据混在一起。桌面上的能力准确率不能冒充 iPhone 端到端任务成功率;iPhone 上成功加载也不能证明模型已经理解了 96 条请求。

4K 和 8K 到底测了什么

本轮真机 Prompt 很短,生成也很短。因此 4K/8K 数据主要回答三件事:

  1. 模型能否在目标设备上按该 context 配置成功加载;
  2. context 扩容带来多少内存增量;
  3. 短请求下的启动、首 token 和热状态是否可接受。

它没有证明模型在填满 8K token 后仍然保持同样速度,也没有测长文 prefill 吞吐。要验证后者,需要实际放入约 3.5K 和 7.5K token,并记录 prefill、生成速度、能耗和持续热曲线。

换句话说,本轮的 8K 是容量与资源测试,不是“8K 长文本能力认证”。

一次结果如何留下证据

每次能力评测都会保存模型原始输出及其 SHA-256,而不只保留最后的百分比。汇总 JSON 同时记录:

  • 数据集版本与 digest;
  • 模型文件 SHA-256;
  • Prompt 配置 SHA-256;
  • Prompt Profile;
  • context 与候选数量;
  • 每条用例的期望值、实际值和各项判断;
  • TTFT、E2E 延迟和 token 数;
  • 是否触发格式修复。

真机侧保留每次独立启动的原始文本、CSV 汇总和单模型校验文件。整个证据目录另有统一 SHA256SUMS.txt

这套做法不能阻止所有人为错误,但至少可以回答三个基本问题:当时测的是哪个文件、使用了什么配置、最后一个百分比是由哪些原始样本算出来的。

我们不会用这套结果证明什么

目前这套测试可以支持“为当前手机本地 Agent 选择路由模型”,但不能直接支持以下说法:

  • 某个模型的通用智力更强;
  • 某个模型在所有语言、所有 Tool 场景都更好;
  • 8K 长文吞吐已经得到验证;
  • 真实闹钟、日历或邮件操作的端到端成功率已经达到某个数字;
  • 这是一份学术意义上的行业排名。

当前 96 条中,48 条 core 用例参与过开发反馈,不能视为完全独立的最终留出集。clarify、planning 等关键切片的样本仍然偏少。真机每个配置只有三次启动,也没有完成随机运行顺序、统一冷却、耗电和 20 轮持续会话。

这些限制不是附在文章末尾的客套话,而是结论的一部分。

下一版怎么补强

我们已经为下一版准备了更严格的方向:

  1. 建立不少于 300 条的独立 holdout,并与 tuning 集隔离;
  2. 每条案例由两名非作者按版本化 Rubric 复核;
  3. 每案例至少运行三次,报告稳定通过、波动案例和 95% Wilson 下界;
  4. 加入真实 3.5K/7.5K prefill、持续会话、耗电、热曲线与 Jetsam 恢复;
  5. 在隔离 Tool fixture 中验证参数、调用顺序、审批、追问和最终环境状态;
  6. 最后再用真机系统能力验证少量高风险端到端路径。

其中 300 条 tuning/holdout 草案和 Harness 已经存在,但尚未完成人工双审,所以我们不会把它们包装成正式成绩。

回到最初的问题

这套方法没有试图找出“世界上最聪明的小模型”。它做的是一件更窄、也更有用的事:在明确的设备、Runtime 和 Agent 契约下,找出最适合当前场景的默认模型,并知道它为什么赢、会在哪里失败。

完整的 10 模型结果、4K/8K 真机资源数据和逐模型判断,可以继续阅读《iPhone 真机横测 10 款小模型》

来自 MonoWare

在点击 App Store 前先建立信任。

探索产品组合,或订阅后续隐私与产品工程笔记。

通讯

隐私与产品工程笔记,偶尔发送。