File size: 5,909 Bytes
b13914b
 
 
 
c4c24a9
b13914b
c4c24a9
b13914b
 
 
 
 
 
 
 
 
 
 
 
c4c24a9
 
 
 
b13914b
c4c24a9
 
 
b13914b
 
 
 
 
 
 
 
 
 
 
 
c4c24a9
 
 
 
b13914b
c4c24a9
 
 
 
 
 
 
 
1f225fe
c4c24a9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b13914b
 
 
 
 
 
 
 
 
 
 
 
 
c4c24a9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b13914b
 
 
 
 
c4c24a9
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
---
language:
- en
license: mit
library_name: sentence-transformers
pipeline_tag: text-classification
base_model: BAAI/bge-base-en-v1.5
tags:
- intent-classification
- multi-label-classification
- voice-notes
- asr
- routing
- mcp
- agents
---

# Voice-Note-Intent-Classifier

> **v0.1.0 (2026-10-15): placeholder release.** A first, deliberately simple model
> that works end to end and sets a baseline. It is not tuned, and its accuracy on real
> notes is modest (micro F1 0.56). Expect it to be replaced; the
> `taxonomy_version` in every prediction lets a router detect that.

A small multi-label classifier that reads a voice-note **transcript** and predicts
its **intent** (shopping list, to-do, calendar event, email draft, AI prompt and so
on), so that an agent system can route it to the right integration.

## Use case

```
voice note ─► ASR ─► transcript ─► [ this classifier ] ─► router
                                                            β”‚  one dispatch per predicted label
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β–Ό              β–Ό              β–Ό               β–Ό              β–Ό
         shopping-list   to-do         calendar         email          AI prompt  …
           subagent     subagent       subagent        subagent        subagent
```

Each subagent holds an MCP connection to its service and extracts the details it
needs. The classifier only decides where a note goes.

## How to use

```bash
pip install sentence-transformers numpy huggingface_hub
hf download danielrosehill/Voice-Note-Intent-Classifier --local-dir vnic
python vnic/predict.py "um need to grab oat milk eggs and uh dish soap"
```

```json
{"model_version": "0.1.0", "taxonomy_version": "0.1.0",
 "scores": {"shopping_list": 0.88, "todo": 0.54, "...": "..."},
 "labels": ["shopping_list", "todo"], "abstain": false}
```

`abstain: true` means no label cleared its threshold. The router should then fall
back to an LLM or ask the user.

## What it is

- **Encoder:** frozen [`BAAI/bge-base-en-v1.5`](https://huggingface.co/BAAI/bge-base-en-v1.5)
  sentence embeddings (normalised, 512 tokens).
- **Head:** one logistic regression per label, stored as plain coefficients in
  `head.json`. Per-label thresholds were calibrated on real notes.
- **Files:** `head.json` (head, thresholds, config), `predict.py` (inference),
  `labels.json` (taxonomy v0.1.0 with definitions), `eval_results.json`.

## Labels (taxonomy v0.1.0)

| Label | Meaning |
|---|---|
| `shopping_list` | Items to buy |
| `todo` | Tasks to do, with no fixed time slot |
| `note_to_self` | Information to remember; nothing to do |
| `calendar_event` | Something at a specific date/time that occupies a slot |
| `email_draft` | Text to send as an email, or an instruction to write one |
| `idea` | A single discrete idea, stated briefly |
| `brainstorm` | Extended exploratory thinking across options |
| `content_draft` | Material for publication: blog post, article, script, outline |
| `ai_prompt` | Instructions addressed to an AI model or agent |

## Training data

- **Synthetic (1492 transcripts).** LLM-generated voice notes
  styled as raw ASR output: single intents, compound pairs, hard cases on confusable
  labels, and notes that fit no label. Generated by one model family (Gemini) and
  kept only where two other families (Claude, GPT), labelling blind, both agreed with
  the requested labels. This dataset is private for now.
- **Real (226 notes).** The author's own voice notes, screened for
  privacy with personal details replaced by fake names, labelled where two LLM
  families agreed. The notes the author has approved for release are public at
  [`danielrosehill/Voice-Notes-Screened`](https://huggingface.co/datasets/danielrosehill/Voice-Notes-Screened).

## Evaluation

5-fold cross-validation on the 226 real notes: each fold's head was
trained on synthetic data plus the other four folds and scored on notes it never saw.

| Metric | Value |
|---|---|
| Micro precision / recall / F1 | 0.50 / 0.64 / 0.56 |
| Macro F1 (labels present) | 0.48 |
| Exact match | 0.39 |
| Notes with no intent, correctly abstained | 10/26 |

| Label | F1 |
|---|---|
| `shopping_list` | 0.78 |
| `todo` | 0.41 |
| `note_to_self` | 0.23 |
| `email_draft` | 0.50 |
| `idea` | 0.36 |
| `brainstorm` | 0.45 |
| `content_draft` | 0.35 |
| `ai_prompt` | 0.76 |

`calendar_event` had no examples among the real notes, so it is unscored on real data.

**Caveat:** the real labels are agreement between two LLMs, not hand labels. The two
LLMs gave identical labels on only 63% of the real notes, so these numbers are a
rough guide, not a benchmark.

## Fit and limitations

- **Built around one person's notes.** The real data comes from a single speaker
  whose notes are dominated by long spoken prompts to AI coding agents. Shopping
  lists and AI prompts work best. Other intents, and other people's dictation
  habits, are weaker.
- **Long agent-style prompts are sometimes labelled `todo`**, because the
  instructions describe tasks.
- **English only.** Hebrew and English-Hebrew code-switched notes are out of scope.
- **Adapting it:** retrain the head on your own transcripts. That takes seconds on
  a CPU because the encoder is frozen. Or swap in the planned fine-tuned encoder
  when it ships.

## Roadmap

| # | Phase | Status |
|---|---|---|
| 0 | Taxonomy definition | draft v0.1.0 |
| 1 | Baselines on real notes | v0.1.0 (this release) |
| 2 | Synthetic data v2 (length-matched to real notes) | generated, not yet trained on |
| 3 | Fine-tuned encoder (ModernBERT-base) | planned |
| 4 | Hand-labelled real evaluation set | planned |
| 5 | ONNX export | planned |
| 6 | Integration into a voice-to-action router | planned |