Keyword Scoring Mechanism in routing.json: How Reverse-Skill Resolves Routing Conflicts
The routing.json file uses a hit-based scoring system where each matching keyword rule adds one point to a route's score, resolving ties through a declared priority array and falling back to R0 when no rules match.
The routing.json configuration file serves as the single source of truth for the reverse-skill task router in the zhaoxuya520/reverse-skill repository. Unlike weighted routing systems that assign numeric scores within the configuration, this mechanism uses a simple binary hit-counting approach combined with explicit priority ordering to guarantee deterministic skill selection. Understanding this data-driven approach is essential for customizing routing behavior and debugging conflicts between overlapping keyword patterns.
How Keyword Rules Trigger Route Scoring
The scoring engine evaluates every incoming query against keyword rules defined in skills/config/routing.json. Each route contains an array of keyword objects that must satisfy specific matching criteria before contributing to the route's score.
Rule Matching with Must, Exclude, and MustAll
A keyword rule generates a hit only when all active conditions are satisfied:
must– The primary regular expression that must be found in the user query (case-insensitive matching)exclude– An optional pattern that filters out false positives; if matched, the rule is discardedmustAll– An optional array requiring every listed pattern to be present simultaneously
According to the source configuration (lines 5-6), the router processes these conditions sequentially for each keyword object within a route.
Hit-Based Scoring System
When a keyword rule matches successfully, the associated route receives exactly one point added to its score. The file does not store numeric weights—each successful match counts as a single hit regardless of complexity. This design means routes with more granular keyword definitions (multiple must clauses across different rules) can accumulate higher scores than generic routes, naturally preferring specific matches over broad ones.
Conflict Resolution Strategy
After evaluating all routes, the router must select a single primary route when multiple candidates achieve positive scores.
Priority-Based Tie Breaking
When two or more routes share the highest hit count, the router consults the priority array (defined in lines 24-29 of routing.json). The route that appears first in this ordered list among the tied candidates is selected as the PRIMARY route. This explicit ordering prevents ambiguous routing and ensures deterministic behavior even when keyword overlap occurs between skills.
Fallback Handling
If no keyword rules match the incoming query, the router defaults to the route specified by fallbackId in the meta object, which conventionally points to R0. This guarantees that every query receives a skill assignment even when the input falls outside defined keyword patterns.
Implementation Examples
The scoring mechanism is implemented consistently across both JavaScript-like pseudocode and the production PowerShell scripts.
JavaScript-Style Pseudocode
This implementation mirrors the logic found in the routing engine:
// Load routing.json configuration
const routing = require('./skills/config/routing.json');
const priority = routing.priority;
const fallbackId = routing.meta.fallbackId;
function route(query) {
const hits = {};
// Scan each route's keyword rules
for (const [id, info] of Object.entries(routing.routes)) {
for (const kw of info.keywords) {
// Evaluate must clause
if (!new RegExp(kw.must, 'i').test(query)) continue;
// Check exclude clause
if (kw.exclude && new RegExp(kw.exclude, 'i').test(query)) continue;
// Validate mustAll clauses
if (kw.mustAll) {
const all = kw.mustAll.every(p => new RegExp(p, 'i').test(query));
if (!all) continue;
}
// Record hit and move to next route
hits[id] = (hits[id] || 0) + 1;
break;
}
}
// Determine best route by hit count
const maxScore = Math.max(...Object.values(hits), 0);
const candidates = Object.keys(hits).filter(id => hits[id] === maxScore);
// Resolve ties using priority order
for (const pid of priority) {
if (candidates.includes(pid)) return routing.routes[pid];
}
// Return fallback route
return routing.routes[fallbackId];
}
PowerShell Implementation in master-route.ps1
The production entry point skills/scripts/master-route.ps1 consumes this logic:
$routing = Get-Content -Raw 'skills/config/routing.json' | ConvertFrom-Json
$priority = $routing.priority
$fallback = $routing.meta.fallbackId
function Get-Route($query) {
$hits = @{}
foreach($id in $routing.routes.Keys) {
foreach($kw in $routing.routes[$id].keywords) {
if($query -match $kw.must) {
if($kw.exclude -and $query -match $kw.exclude) { continue }
if($kw.mustAll) {
$all = $true
foreach($pat in $kw.mustAll) {
if(-not ($query -match $pat)) {
$all = $false;
break
}
}
if(-not $all) { continue }
}
$hits[$id] = ($hits[$id] ?? 0) + 1
break
}
}
}
$max = ($hits.Values | Measure-Object -Maximum).Maximum
$candidates = $hits.Keys | Where-Object { $hits[$_] -eq $max }
foreach($pid in $priority) {
if($candidates -contains $pid) {
return $routing.routes[$pid]
}
}
return $routing.routes[$fallback]
}
Summary
- Binary Scoring: Each matching keyword rule contributes exactly one point to a route's score, with no configurable weights in
routing.json - Explicit Priority: The
priorityarray (lines 24-29) determines the winner when multiple routes achieve identical hit counts - Fallback Safety: The system defaults to
fallbackId(typicallyR0) when no keyword rules match, ensuring every query receives routing - Pattern Flexibility: Routes support
must,exclude, andmustAllpatterns to handle complex matching scenarios - Data-Driven Design: All routing logic derives from
skills/config/routing.json, making behavior predictable and version-controllable
Frequently Asked Questions
How does routing.json handle multiple keyword matches within the same route?
Each route can contain multiple keyword objects, but only one hit per route is recorded regardless of how many keyword rules match. As implemented in master-route.ps1, the inner loop breaks after the first successful keyword match, ensuring a route receives a maximum score of one per query evaluation cycle.
What happens if two routes have the same keyword score?
When scores are tied, the router examines the priority array and selects the route that appears first in that ordered list. This deterministic resolution prevents random selection and allows administrators to control precedence through configuration rather than adjusting complex regex weights.
Where is the fallback route configured in routing.json?
The fallback route is defined in the meta object at the root of routing.json (lines 5-6) via the fallbackId property, which conventionally points to R0. If no keyword rules trigger a hit across any route, the system automatically returns this default route.
Can I assign different numeric weights to keyword rules?
No. The routing.json schema does not support numeric weights. According to the meta.scoring field (line 6), the system uses simple hit counting where each successful match adds exactly one point. To prioritize routes, use the priority array to establish precedence order rather than attempting to configure decimal scores.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →