Best LLMs for Topic Taxonomy Matching
Maps transient input topics to persistent taxonomy topics, reusing existing topics where semantically appropriate and proposing new ones only for durable coverage gaps. Illustrative uses include mapping support-ticket themes to a service taxonomy, customer feedback to product are
Run this task on Fronset — request an invitation
Models
Frontier on this task: Gemini 3.8 Flash at 8.72 / 10. Quality bar at 90%: 7.84.
point-estimate floor (CI low) · upper CI (less certain) · Bars sorted by blended cost; best-value model first. Greyed rows are MEDIUM+ models whose point estimate does not clear the bar.
| Model | Quality score | CI low | Cost / 1k runs | vs best value |
|---|---|---|---|---|
| Qwen 3.7 Plus | 7.86 / 10 | 7.52 | $13.83 | best value |
| Gemini 3.8 Flash | 8.72 / 10 | 8.34 | $24.76 | 1.8x more expensive |
| Meta Muse Spark 1.3 | 8.15 / 10 | 7.75 | $67.43 | 4.9x more expensive |
| Moonshot Kimi K3 | 8.42 / 10 | 8.05 | $187.87 | 14x more expensive |
| Gemini 3.5 Flash Lite | 6.68 / 10 | 6.23 | $5.87 | 58% cheaper |
| MiniMax M3 | 6.49 / 10 | 6.29 | $4.65 | 66% cheaper |
| Tencent Hy3 | 6.40 / 10 | 5.94 | $3.21 | 77% cheaper |
| Thinking Machines Inkling | 7.54 / 10 | 7.17 | $127.86 | 9.2x more expensive |
| Thinking Machines Inkling Small | 7.57 / 10 | 7.14 | $19.74 | 1.4x more expensive |
Cost breakdown
| Model | Quality | Confidence | Cost / 1k runs | Overpay | Mode |
|---|---|---|---|---|---|
| Qwen 3.7 Plus ★ Alibaba Cloud (DashScope) | 7.86 / 10 CI [7.52, 8.20] | MEDIUM | $13.83 | best value | batch |
| Gemini 3.8 Flash best Gemini | 8.72 / 10 CI [8.34, 9.09] | MEDIUM | $24.76 | 1.8x | batch |
| Meta Muse Spark 1.3 OpenRouter | 8.15 / 10 CI [7.75, 8.55] | MEDIUM | $67.43 | 4.9x | batch |
| Moonshot Kimi K3 Moonshot AI | 8.42 / 10 CI [8.05, 8.79] | MEDIUM | $187.87 | 14x | batch |
Overpay shows how much more you pay than the best-value model that clears the quality bar (marked ★) — the best-value good-enough option. "16x" means you overpay 16× — 16× that reference for no quality benefit above the bar. Typical call shape for this task: 26675 input tokens → 6987 output tokens, EMA-tracked from production traffic. Cost is the observed, all-in $ per 1,000 task runs: each model's own measured usage on this task — output verbosity, thinking/reasoning tokens, cache reads and writes, and the spend on its billed failures — priced at current list rates and adjusted by the billing overhead we actually reconcile against provider invoices. Models that answer tersely cost what they actually cost; models that think at length pay for it. Not comparable to providers' advertised $/1M list rates — this is what running the task costs, not a per-token price.
Evaluation rubric
Judge semantic match accuracy, existing-topic reuse, stable granularity, complete identifier coverage, consistency across similar inputs, and restraint in creating new topics.
Output schema
Every answer on this task is checked against this JSON Schema, whichever model wrote it. An answer that doesn't fit counts as a model failure, and the call is retried on another model.
{
"$defs": {
"TopicMatch": {
"description": "One cluster label resolved against the shared Topic vocabulary.\n\nEither `topic_key` names an existing Topic, or the two `proposed_*` fields\ndescribe a genuinely novel theme. Never both, never neither.",
"properties": {
"client_topic_id": {
"description": "The id of the cluster's draft topic being resolved",
"title": "Client Topic Id",
"type": "integer"
},
"proposed_description": {
"anyOf": [
{
"maxLength": 500,
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Only when topic_key is null: what the new Topic covers and where its boundary lies. REQUIRED alongside proposed_name — the description is what the next run discriminates on (max 500 characters).",
"title": "Proposed Description"
},
"proposed_name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Only when topic_key is null: a name for a NEW Topic. It must be reusable across subjects — no company, product, ticker or person names — because it joins a vocabulary every client matches against.",
"title": "Proposed Name"
},
"topic_key": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The key of an existing Topic from the supplied vocabulary, when one covers this cluster. Null only when nothing in the vocabulary fits.",
"title": "Topic Key"
}
},
"required": [
"client_topic_id"
],
"title": "TopicMatch",
"type": "object"
}
},
"description": "One decision per draft supplied.\n\n⚠ Several drafts resolving to the same `topic_key` is the NORMAL and\nwanted case — that is how grouping emerges. Measured across 32 completed runs,\na run's clusters collapse onto a median of 14 distinct Topics (max 51 of 60).",
"properties": {
"assignments": {
"description": "Exactly one entry per draft in the input",
"items": {
"$ref": "#/$defs/TopicMatch"
},
"title": "Assignments",
"type": "array"
}
},
"title": "TopicMatchingOutput",
"type": "object"
}Prompt templates
The published template for this task.
default ·
LLMB_TOPIC_TAXONOMY_MATCHING_SYSTEM +
LLMB_TOPIC_TAXONOMY_MATCHING_USER
(323 calls in window)
System prompt
Match by underlying subject and scope, not exact wording. The existing topics are a persistent vocabulary reused across runs and across subjects, so an input topic that names a particular company, product or period should map onto the general existing topic that covers it rather than minting a near-duplicate beside it — a difference that is only the named entity or the period is a match, not a new boundary. Many input topics may map to one persistent topic; one input topic must not be split unless the schema allows it. Prefer an existing topic when it covers the same durable concept, and create a new topic only when no existing boundary fits: name a new one for the reusable theme, free of company, organisation, product, ticker and person names, and give it a description stating its scope and the boundary separating it from its neighbours, because that description is what later runs match against. Preserve every input identifier. Treat empty optional values as absent and return only the requested result. Your response must conform exactly to this output schema: {schema_json_string}.
User prompt
Inputs — workflow_topics_json: {workflow_topics_json}; existing_client_topics_json: {existing_client_topics_json}. Use only these inputs to complete the task defined by the system prompt.