How reverse-skill Handles Multiple Keyword Matches for Routing: Complete Scoring Algorithm Explained
reverse-skill uses a scoring-based routing system where every keyword match adds a "hit" to a route's candidate score, and ties are broken by a configurable priority list.
The reverse-skill project implements a deterministic router that resolves ambiguous user hints by weighing multiple keyword matches against each other. This article explains exactly how the scoring algorithm works, how ties are resolved, and how to debug routing decisions using the source code in zhaoxuya520/reverse-skill.
Keyword-Based Routing Configuration
All routing logic in reverse-skill is defined in skills/config/routing.json, the single source of truth for route definitions. Each route contains a keywords array where every object specifies matching conditions:
| Field | Purpose |
|---|---|
must |
Regex that must appear in the hint for the rule to match |
mustAll |
Array of regexes where all must match simultaneously |
exclude |
Regex that must not appear (immediate disqualification) |
note |
Human-readable documentation (ignored by the router) |
Consider this excerpt from the APK reverse route (R1) at lines 14-18 of routing.json:
{
"id": "R1",
"name": "APK Reverse",
"keywords": [
{ "must": "apk", "note": "Android package keyword" },
{ "must": "jadx|apktool|baksmali", "note": "APK tool mentions" }
]
}
The Scoring Algorithm for Multiple Matches
When master-route.ps1 receives a hint, it executes a five-step scoring process:
- Scan all routes and evaluate every keyword object against the hint
- Increment hit count — each successful rule adds exactly one hit to that route's candidate score
- Build candidate set — collect all routes with at least one hit
- Select primary route by:
- Choosing the route with the highest hit count
- Breaking ties using the
priorityarray — the route appearing earliest wins
- Fallback to
R0if no route receives any hits
The scoring metadata is documented at lines 5-6 of routing.json: "每条关键字规则命中后计入候选集" (each keyword rule hit is counted toward the candidate set).
Priority Array: The Tiebreaker
When two or more routes share the same hit count, the router consults the priority array at lines 308-313:
"priority": [
"R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8",
"R9", "R10", "R11", "R12", "R13", "R14", "R15"
]
This explicit ordering ensures deterministic routing even with ambiguous hints.
Practical Examples
Single Route Match
.\skills\scripts\master-route.ps1 -Hint "apk jadx reverse root detect"
The hint contains apk, matching R1's first keyword rule, and jadx, matching its second. No other route scores higher. Result: R1 is PRIMARY.
Competing Routes with Equal Scores
.\skills\scripts\master-route.ps1 -Hint "apk jadx and graphql api security"
R1(APK reverse): 1 hit — matchesapkR12(API security): 1 hit — matchesgraphql
Both routes tie with one hit. The priority array lists R1 before R12, so R1 becomes PRIMARY while R12 remains in the candidate set.
Complex Multi-Condition Rule
bash skills/scripts/master-route.sh "jailbreak ipa iOS reverse"
This satisfies R2's (Mobile reverse) mustAll rule: jailbreak must appear together with any token from ios|iphone|ipad|mobile|objection|ipa. The router selects R2 as PRIMARY.
Inspecting Candidate Scores
.\skills\scripts\master-route.ps1 -Hint "nmap sqlmap reverse" -Verbose
Verbose output reveals the scoring breakdown:
[INFO] Candidate routes:
R11 – 2 hits (nmap, sqlmap)
R0 – 1 hit (reverse)
[INFO] Primary route: R11 (higher score)
R11 (Network/Infra reverse) wins with two keyword matches versus R0's one.
Source Files and Their Roles
| File | Function | Key Implementation |
|---|---|---|
skills/config/routing.json |
Central routing definition | Keyword rules, priority array, fallbackId |
skills/scripts/master-route.ps1 |
PowerShell routing entry point | Scoring algorithm, candidate selection |
skills/scripts/master-route.sh |
Bash wrapper | Calls Python backend with identical logic |
skills/MASTER-ROUTING.md |
Documentation | Priority table and routing concepts |
skills/scripts/verify-routing-coherence.ps1 |
Validation | JSON structure and priority consistency checks |
skills/scripts/test-routing.ps1 |
Regression suite | 162 automated routing tests |
Summary
- Every keyword match counts: Each satisfied rule adds one hit to a route's score
- Highest score wins: The route with the most hits becomes PRIMARY
- Priority array breaks ties: Earlier entries win when scores are equal
- Deterministic fallback:
R0handles hints with no matches viafallbackId - Single configuration: All behavior is controlled through
skills/config/routing.json
Frequently Asked Questions
How does reverse-skill handle hints that match zero routes?
The router falls back to the route specified by fallbackId, which defaults to R0 (General reverse). This ensures every hint produces a valid routing decision even without keyword matches.
Can a single hint trigger multiple rules within the same route?
Yes. If a hint satisfies multiple keyword objects in one route's keywords array, each successful match increments that route's hit count. This allows fine-grained confidence scoring rather than binary matching.
What happens if the priority array is modified?
The router respects the updated order immediately on the next execution. The verify-routing-coherence.ps1 script validates that all route IDs in priority match existing routes and that no duplicates exist.
How can I debug why a particular route was selected?
Run master-route.ps1 with the -Verbose flag. This displays the complete candidate set with per-route hit counts and explicitly states which priority tiebreaker was applied if needed.
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 →