Skip to content

Commit 73737dc

Browse files
authored
Merge pull request #38 from pinchtab/chore/benchmark-improvements
chore: expand benchmark corpus and tuning tools
2 parents c47fdfe + 44ce632 commit 73737dc

24 files changed

Lines changed: 2200 additions & 43 deletions

File tree

cmd/semantic/main.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ Flags (find/match):
5656
--threshold <n> Minimum score (default: 0.3)
5757
--top-k <n> Max results (default: 3)
5858
--strategy <name> lexical, embedding, or combined (default: combined)
59+
--lexical-weight <n> Combined strategy lexical weight override
60+
--embedding-weight <n> Combined strategy embedding weight override
5961
--format <fmt> json, table, or refs (default: table)
6062
`)
6163
}
@@ -209,6 +211,8 @@ func runFind(args []string) {
209211
threshold := fs.Float64("threshold", 0.3, "minimum score")
210212
topK := fs.Int("top-k", 3, "max results")
211213
strategy := fs.String("strategy", "combined", "matching strategy")
214+
lexicalWeight := fs.Float64("lexical-weight", 0, "combined strategy lexical weight override")
215+
embeddingWeight := fs.Float64("embedding-weight", 0, "combined strategy embedding weight override")
212216
format := fs.String("format", "table", "output format: json, table, refs")
213217
_ = fs.Parse(args)
214218

@@ -226,8 +230,10 @@ func runFind(args []string) {
226230

227231
matcher := newMatcher(*strategy)
228232
result, err := matcher.Find(context.Background(), query, elements, semantic.FindOptions{
229-
Threshold: *threshold,
230-
TopK: *topK,
233+
Threshold: *threshold,
234+
TopK: *topK,
235+
LexicalWeight: *lexicalWeight,
236+
EmbeddingWeight: *embeddingWeight,
231237
})
232238
if err != nil {
233239
fmt.Fprintf(os.Stderr, "error: %v\n", err)

docs/reference/cli.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ semantic find <query> [flags]
1616
| `--threshold` | 0.3 | Minimum score |
1717
| `--top-k` | 3 | Maximum results |
1818
| `--strategy` | combined | `combined`, `lexical`, or `embedding` |
19+
| `--lexical-weight` | 0 | Combined strategy lexical weight override |
20+
| `--embedding-weight` | 0 | Combined strategy embedding weight override |
1921
| `--format` | table | `table`, `json`, or `refs` |
2022

2123
**Examples:**
@@ -31,6 +33,9 @@ curl -s localhost:9999/snapshot | semantic find "search box"
3133
# Machine-readable
3234
semantic find "login" --snapshot page.json --format json
3335

36+
# Tune combined scoring
37+
semantic find "login" --snapshot page.json --lexical-weight 0.7 --embedding-weight 0.3
38+
3439
# Just refs (for piping)
3540
semantic find "submit" --snapshot page.json --format refs
3641

tests/benchmark/README.md

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ cd tests/benchmark
1414
./scripts/run-corpus-benchmark.sh --strategy lexical
1515
./scripts/run-corpus-benchmark.sh --strategy embedding
1616
./scripts/run-corpus-benchmark.sh --strategy combined
17+
18+
# Sweep combined lexical/embedding weights
19+
./scripts/tune-weights.sh
1720
```
1821

1922
## Metrics
@@ -55,32 +58,35 @@ corpus/
5558
## Current Results (combined strategy)
5659

5760
```
58-
Queries: 50
59-
MRR: 0.88
60-
P@1: 0.87
61-
P@3: 0.34
62-
Latency P50: 31 ms
63-
Latency P95: 52 ms
61+
Queries: 105
62+
MRR: 0.8897
63+
P@1: 0.8762
64+
P@3: 0.3412
65+
Latency P50: 23 ms
66+
Latency P95: 28 ms
6467
6568
By Difficulty:
66-
easy: 34 queries, P@1 = 0.95
67-
medium: 14 queries, P@1 = 0.78
68-
hard: 2 queries, P@1 = 0.00
69+
easy: 76 queries, P@1 = 0.94
70+
medium: 25 queries, P@1 = 0.74
71+
hard: 4 queries, P@1 = 0.50
6972
```
7073

7174
## Optimization Targets
7275

73-
The 6 current misses are "hard" cases requiring:
76+
The current misses cluster around:
7477
- Synonym expansion (save for later → wishlist)
7578
- Implicit actions (clone → Code button)
7679
- Domain knowledge (CI status → Actions tab)
80+
- Form/input intent (type new query → search box)
81+
- Accessibility/navigation shortcuts (skip to content, homepage)
7782

7883
## Scripts
7984

8085
| Script | Purpose |
8186
|--------|---------|
8287
| `run-corpus-benchmark.sh` | Main benchmark with MRR/P@K metrics |
8388
| `run-benchmark.sh` | Simple pass/fail test runner |
89+
| `tune-weights.sh` | Grid search combined matcher lexical/embedding weights |
8490

8591
## Adding to Corpus
8692

@@ -95,6 +101,13 @@ The 6 current misses are "hard" cases requiring:
95101

96102
3. Run benchmark to establish baseline
97103

104+
4. Add several related queries for the same behavior, not one isolated case.
105+
Include easy, medium, hard, and at least one near-miss or partial match where
106+
ambiguity matters.
107+
108+
5. Re-run `./scripts/tune-weights.sh` after larger corpus changes to see whether
109+
the best combined weights moved.
110+
98111
## CI Integration
99112

100113
```yaml
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
[
2+
{
3+
"id": "neg-001",
4+
"query": "xyzzy plugh qwerty",
5+
"snapshot": "login-page.json",
6+
"expect_no_match": true,
7+
"threshold": 0.3,
8+
"tags": ["no-match", "nonsense"]
9+
},
10+
{
11+
"id": "neg-002",
12+
"query": "upload spreadsheet to cloud",
13+
"snapshot": "login-page.json",
14+
"expect_no_match": true,
15+
"threshold": 0.4,
16+
"tags": ["no-match", "absent-control"]
17+
},
18+
{
19+
"id": "neg-003",
20+
"query": "open video player",
21+
"snapshot": "dashboard.json",
22+
"expect_no_match": true,
23+
"threshold": 0.4,
24+
"tags": ["no-match", "absent-control"]
25+
},
26+
{
27+
"id": "neg-004",
28+
"query": "print receipt",
29+
"snapshot": "login-page.json",
30+
"expect_no_match": true,
31+
"threshold": 0.4,
32+
"tags": ["no-match", "absent-control"]
33+
},
34+
{
35+
"id": "neg-005",
36+
"query": "submit button",
37+
"snapshot": "multi-form.json",
38+
"expect_ref": "e11",
39+
"expect_ref_alt": ["e3", "e7"],
40+
"threshold": 0.3,
41+
"min_score": 0.5,
42+
"tags": ["threshold", "duplicate-labels"]
43+
},
44+
{
45+
"id": "neg-006",
46+
"query": "enter",
47+
"snapshot": "login-page.json",
48+
"expect_has_matches": true,
49+
"threshold": 0.1,
50+
"min_score": 0.15,
51+
"tags": ["threshold", "weak-match"]
52+
},
53+
{
54+
"id": "neg-007",
55+
"query": "click",
56+
"snapshot": "ecommerce-product.json",
57+
"expect_has_matches": true,
58+
"threshold": 0.1,
59+
"tags": ["threshold", "generic-verb"]
60+
},
61+
{
62+
"id": "neg-008",
63+
"query": "the thing",
64+
"snapshot": "dashboard.json",
65+
"expect_has_matches": true,
66+
"threshold": 0.05,
67+
"tags": ["threshold", "vague-query"]
68+
},
69+
{
70+
"id": "neg-009",
71+
"query": "asdfghjkl",
72+
"snapshot": "multi-form.json",
73+
"expect_no_match": true,
74+
"threshold": 0.3,
75+
"tags": ["no-match", "keyboard-mash"]
76+
},
77+
{
78+
"id": "neg-010",
79+
"query": "stale element e999",
80+
"snapshot": "login-page.json",
81+
"expect_no_match": true,
82+
"threshold": 0.3,
83+
"tags": ["no-match", "stale-ref"]
84+
},
85+
{
86+
"id": "neg-011",
87+
"query": "a b c d e f",
88+
"snapshot": "dashboard.json",
89+
"expect_no_crash": true,
90+
"threshold": 0.3,
91+
"tags": ["threshold", "noise-tokens"]
92+
},
93+
{
94+
"id": "neg-012",
95+
"query": "configure webhook endpoint",
96+
"snapshot": "login-page.json",
97+
"expect_no_match": true,
98+
"threshold": 0.4,
99+
"tags": ["no-match", "absent-control", "domain-intent"]
100+
},
101+
{
102+
"id": "neg-013",
103+
"query": "invoice download",
104+
"snapshot": "ecommerce-product.json",
105+
"expect_no_match": true,
106+
"threshold": 0.4,
107+
"tags": ["no-match", "absent-control"]
108+
},
109+
{
110+
"id": "neg-014",
111+
"query": "share on twitter",
112+
"snapshot": "login-page.json",
113+
"expect_no_match": true,
114+
"threshold": 0.4,
115+
"tags": ["no-match", "absent-control"]
116+
}
117+
]

tests/benchmark/cases/visual.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,46 @@
11
[
22
{
3-
"id": "visual-001",
3+
"id": "vcase-001",
44
"query": "button in top right",
55
"snapshot": "visual-layout.json",
66
"expect_ref": "e1",
77
"min_score": 0.5,
88
"tags": ["visual", "position", "directional"]
99
},
1010
{
11-
"id": "visual-002",
11+
"id": "vcase-002",
1212
"query": "button on the left",
1313
"snapshot": "visual-layout.json",
1414
"expect_ref": "e0",
1515
"min_score": 0.4,
1616
"tags": ["visual", "position", "directional"]
1717
},
1818
{
19-
"id": "visual-003",
19+
"id": "vcase-003",
2020
"query": "button at bottom",
2121
"snapshot": "visual-layout.json",
2222
"expect_ref": "e7",
2323
"min_score": 0.4,
2424
"tags": ["visual", "position", "directional"]
2525
},
2626
{
27-
"id": "visual-004",
27+
"id": "vcase-004",
2828
"query": "link on left side",
2929
"snapshot": "visual-layout.json",
3030
"expect_ref": "e3",
3131
"min_score": 0.4,
3232
"tags": ["visual", "position", "directional"]
3333
},
3434
{
35-
"id": "visual-005",
35+
"id": "vcase-005",
3636
"query": "top left menu button",
3737
"snapshot": "visual-layout.json",
3838
"expect_ref": "e0",
3939
"min_score": 0.5,
4040
"tags": ["visual", "position", "compound"]
4141
},
4242
{
43-
"id": "visual-006",
43+
"id": "vcase-006",
4444
"query": "settings in upper right corner",
4545
"snapshot": "visual-layout.json",
4646
"expect_ref": "e1",

tests/benchmark/corpus/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,30 @@ Each corpus entry is a directory containing:
3838
- **P@3**: How many of top-3 are relevant?
3939
- **Margin**: Score gap between relevant and irrelevant
4040

41+
## Expansion Groups
42+
43+
### Expansion 1: Complex Query Patterns (2026-04)
44+
45+
Added corpora for underrepresented query types:
46+
47+
- **implicit-domain-intent/**: GitHub-like repo page with 56 elements. Tests implicit intents like "clone this repo", "check CI status", "switch branch", "save for later". 18 queries, 8 hard.
48+
49+
- **form-state-controls/**: Settings page with checkboxes, radios, toggles, comboboxes. Tests stateful controls like "keep me logged in", "enable 2FA", "subscribe to newsletter". 18 queries, 8 hard.
50+
51+
- **ambiguous-layout-context/**: Multi-section page with duplicate labels (3x Search, 2x Save, 2x Cancel, 2x Login, 2x Home, 2x Help). Tests positional and section disambiguation. 17 queries, 7 hard.
52+
53+
Also added `tests/benchmark/cases/negative-threshold.json` with 14 no-match and threshold calibration cases.
54+
55+
### Expansion 2: Enterprise UI Patterns (2026-04)
56+
57+
Added corpora for complex enterprise UI scenarios:
58+
59+
- **table-grid/**: Invoice table with 50+ elements. Tests row-level context, repeated buttons (Edit, Delete, More), ordinal references ("second invoice", "last row"), and bulk operations. 24 queries, 8 hard.
60+
61+
- **overlays-menus-dialogs/**: Multi-layer UI with modal dialogs, dropdown menus, context menus, notifications. Tests duplicate controls across scopes ("cancel in modal", "save on page not dialog"), menu item selection, and overlay disambiguation. 24 queries, 8 hard.
62+
63+
- **icon-aria-labels/**: Icon-only controls across toolbar, media player, navigation. Tests sparse accessible names, icon descriptions ("kebab menu", "hamburger", "pencil edit"), and section context for repeated icons. 25 queries, 6 hard.
64+
4165
## Sources
4266

4367
Snapshots should be captured from real websites using pinchtab:

0 commit comments

Comments
 (0)