Views
No views yet
.aimodel) for on-device inference on iOS 27+.| base Qwen3-0.6B (prompted) | this model | |
|---|---|---|
| exact match (tool + every arg) | 26.2% | 90.0% |
| tool choice | 53.8% | 97.5% |
| title correct | 11.2% | 96.7% |
| due correct | 7.7% | 83.8% |
| target id correct | 68.3% | 96.9% |
| no_op recall (noise rejected) | 97.4% | 97.4% |
| false no_op (real to-dos dropped) | 102 | 2 |
| latency / generation (M-series, greedy) | 278 ms | 206 ms |
| prompt size | ~676 tok | ~219 tok |
no_op tax disappeared: prompting the base model into a
"do nothing" option cost it 102 false-positives on real to-dos; trained from data instead,
that is 2, with identical noise rejection. That is the thing prompting could not buy at
any price.You turn one spoken line into exactly one tool call for a to-do app. Reply with a single <tool_call> and nothing else.
- create_todo — the user wants a new task recorded.
- update_todo — change an existing task's title and/or due time. Target it by its [id].
- delete_todo — remove an existing task. Target it by its [id].
- no_op — the line is not a to-do instruction (greeting, question, chatter, or cut-off speech).
Titles are bare and imperative: drop openers and drop any date/time words, which go in `due` as ISO-8601. Omit `due` when no time was given.
Now: 2026-07-27T15:30 (Monday)
To-dos:
[1] Call mom
[2] Go on a run — due 2026-07-27T19:00To-dos: (none).| tool | args |
|---|---|
create_todo | title, due? |
update_todo | id, title?, due? |
delete_todo | id |
no_op | — |
<tool_call>{"name": …, "arguments": {…}}</tool_call>. due is
ISO-8601 yyyy-MM-dd'T'HH:mm.enable_thinking — Qwen3's template injects
<think></think> when you do, which the fine-tune never saw (measured 91.7% → 90.0%).coreai-models:1let bundle = try LanguageBundle(at: modelDirectory)
2let engine = try await CoreAIRunner(from: bundle).makeInferenceEngine()
3let tokenizer = try await bundle.loadTokenizer()
4
5let ids = try tokenizer.applyChatTemplate(
6 messages: [["role": "system", "content": systemPrompt],
7 ["role": "user", "content": utterance]],
8 tools: [], // deliberately empty
9 additionalContext: nil // deliberately nil
10)
11let stream = try engine.generate(
12 with: ids.map { Int32($0) },
13 samplingConfiguration: .greedy,
14 inferenceOptions: InferenceOptions(maxTokens: 64, includeLogits: false)
15)due is the only metric under 90%, and
roughly half of all residual failures are off-by-one on relative days ("this weekend" →
Friday, "next monday" → a week early). A 0.6B doing calendar math is the limitation. The
fix is to have it emit the phrase and resolve it in application code; the model is
excellent at spotting the time expression and only bad at converting it.```http fence instead of <tool_call>. Parse with a
first-{-to-last-} fallback.qwen3_0_6b_ramble_sft_8bit.aimodel/ Core AI graph (8-bit weights)
tokenizer/ Qwen3 tokenizer + chat template
metadata.json Core AI bundle manifest