How Codex Routes Skills When Multiple Keywords Match
Codex resolves ambiguous skill requests by scoring keyword matches across candidate skills and applying deterministic tie-breakers, falling back to user clarification when confidence is low.
When a user query triggers multiple installed skills in the OpenAI plugins repository, Codex employs a sophisticated routing algorithm to select the most appropriate handler. The system analyzes keyword overlaps between the user input and each skill’s manifest, calculates match scores, and applies deterministic tie-breaking rules to ensure consistent behavior. This process ensures that even when multiple skills share similar terminology—such as "search" or "report"—the correct skill is invoked reliably.
How Codex Scores and Selects Skills
Candidate Skill Collection
First, Codex scans all installed plugins and extracts skills whose keywords arrays intersect with words from the user query. For example, skills defined in plugins/fyxer/.codex-plugin/plugin.json or plugins/zoom/.codex-plugin/plugin.json become candidates if any of their keywords appear in the request. This initial filtering creates a candidate pool of all potentially relevant skills.
Keyword Match Scoring
For each candidate, Codex computes a match score using the internal score_keyword_match function. The scoring algorithm applies differentiated weights based on match quality:
- Exact matches: When a query word equals a manifest keyword, the skill receives a high weight (+10)
- Partial matches: When a query word contains a keyword as a substring, the skill receives a lower weight (+3)
- Synonym expansion: The internal synonym map can boost scores for semantically related terms
This weighted approach ensures that precise terminology matches outweigh vague substring overlaps.
Deterministic Tie-Breaking
When multiple skills achieve identical scores, Codex applies a strict priority chain to guarantee reproducible selection:
- Explicit priority: The optional
priorityfield in the skill manifest breaks ties in favor of higher values - Alphabetical ordering: Plugin names provide a final fallback, ensuring the same skill wins across identical requests
Confidence Thresholds and Disambiguation
If the highest-scoring skill fails to exceed the configured CONFIDENCE_THRESHOLD, Codex raises a ClarificationNeeded exception rather than executing the match. This safety mechanism triggers a clarifying question when generic terms (e.g., "get" or "list") match too many skills, preventing incorrect invocations on ambiguous queries.
Code Implementation
The routing logic operates through a pipeline that processes the query against installed plugin manifests:
def route_request(query: str, installed_plugins: List[Plugin]) -> Skill:
# 1. Extract keywords from the query
query_words = set(query.lower().split())
# 2. Gather candidates
candidates = [
skill for plugin in installed_plugins
for skill in plugin.skills
if query_words & set(skill.manifest["keywords"])
]
# 3. Score candidates
scores = {}
for skill in candidates:
score = 0
for kw in skill.manifest["keywords"]:
if kw in query_words:
score += 10 # exact match
elif any(kw in w for w in query_words):
score += 3 # partial match
scores[skill] = score
# 4. Pick best skill (with deterministic tie‑break)
best_skill = max(
scores,
key=lambda s: (scores[s],
s.manifest.get("priority", 0),
s.name) # alphabetical fallback
)
# 5. Confidence check
if scores[best_skill] < CONFIDENCE_THRESHOLD:
raise ClarificationNeeded("Which skill did you mean?")
return best_skill
Once selected, Codex forwards the request to the skill’s entrypoint as defined in its manifest.
Key Configuration Files
Skill routing depends on metadata defined in plugin manifests and entry points. Critical files in the OpenAI plugins repository include:
plugins/fyxer/.codex-plugin/plugin.json: Defines thekeywordsarray used for candidate matchingplugins/zoom/.codex-plugin/plugin.json: Illustrates multiple keywords per skill and optional priority configurationplugins/zoom/skills/video-sdk/SKILL.md: The entry point referenced by the manifest’sentrypointfield where routed requests ultimately execute
Summary
- Codex collects candidate skills by intersecting user query words with manifest
keywordsarrays found in files likeplugins/fyxer/.codex-plugin/plugin.json - Match scoring weights exact matches (+10) higher than partial substring matches (+3), with additional boosts from synonym expansion
- Deterministic tie-breakers use the manifest
priorityfield first, then alphabetical plugin names to ensure consistent selection - Low-confidence matches trigger
ClarificationNeededexceptions rather than executing ambiguous commands - Selected skills route to their
entrypointscripts (e.g.,SKILL.mdfiles) for execution
Frequently Asked Questions
How does Codex handle queries that match keywords in multiple skills simultaneously?
Codex scores every matching skill based on keyword overlap quality using the score_keyword_match logic, then selects the highest scorer. If scores tie, it checks the priority field in each skill’s manifest, falling back to alphabetical plugin naming to ensure deterministic selection.
What happens when no skill achieves a high confidence score?
When the top candidate’s score falls below CONFIDENCE_THRESHOLD, Codex raises a ClarificationNeeded exception and prompts the user to specify which skill they intended, preventing incorrect invocations on ambiguous queries.
Can developers influence skill selection priority?
Yes. Developers can set an optional priority field in the skill manifest (e.g., in .codex-plugin/plugin.json). Higher priority values break ties in favor of that skill when multiple candidates achieve identical keyword match scores.
Where does a routed request ultimately execute?
After selection, Codex dispatches the request to the skill’s entrypoint as defined in its manifest. For example, the Zoom Video-SDK skill routes to plugins/zoom/skills/video-sdk/SKILL.md according to the repository structure.
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 →