Skip to content

Skill Discovery, Selection and Routing

Why do we need skill discovery?

With one skill, there is no routing problem:

request → one known skill

As the system grows:

Agent Runtime
├── PR Review Skill
├── Test Failure Analysis Skill
├── Documentation Skill
├── Incident Triage Skill
├── Deployment Health Skill
├── Support Ticket Skill
└── ...

we need to answer:

How does the runtime know which capability is relevant to a request?

This is the skill discovery and routing problem.

Routing is not only a model problem. A good system applies deterministic filters and policies before a model ever selects a skill.

Skill registry / catalog

A useful mental model:

Skill Registry
├── name
├── version
├── description
├── input contract
├── output contract
├── capabilities / tags
├── risk level
├── required permissions
├── environment constraints
└── status: experimental | stable | deprecated

Example:

name: review_pull_request
version: 2.1
description: Review a pull request for correctness, architecture, regressions and missing tests.
tags: [github, code-review, read-only]
risk: low
requires:
  - repository.read

The registry may be static configuration, a code registry, a database, or a runtime discovery service.

The important point is that the runtime needs more than skill names; it needs metadata required for selection.

Skill descriptions are interface metadata

If an LLM router selects from descriptions, their quality matters.

Weak:

Handles code stuff.

Better:

Reviews an existing pull request for correctness, architecture risks, regressions and missing tests. Read-only. Does not modify code.

This says:

  • what the skill does,
  • what it does not do,
  • its scope,
  • its side-effect profile.

A description is therefore not marketing text; it is part of the routing contract.

Routing pipeline: filter first, semantic selection second

A safer flow:

All registered skills
        ↓
status filter
        ↓
environment filter
        ↓
permission filter
        ↓
risk / policy filter
        ↓
relevant candidate skills
        ↓
router
        ↓
selected skill / no skill / ambiguity

Avoid:

LLM sees every skill
 ↓
chooses dangerous production skill
 ↓
application later discovers user has no permission

Prefer:

user permissions
 ↓
capability filtering
 ↓
LLM never sees unauthorized skill

This is least privilege at discovery time.

Routing strategies

There is no single best strategy.

1. Deterministic/static routing

Example:

HTTP endpoint /review-pr
       ↓
PR Review Skill

or:

request.type == INCIDENT
       ↓
Incident Triage Skill

Benefits:

  • fast,
  • cheap,
  • deterministic,
  • easy to debug.

If the caller already knows which skill is needed, there is no reason to insert an LLM router.

2. Rule-based routing

For example:

source == github && object == pull_request → PR skills
source == pagerduty → incident skills

This is a useful pre-filter.

3. Semantic / embedding routing

A shortlist can be built from embedding similarity between the user intent and skill descriptions.

user request
 ↓
embedding similarity
 ↓
top 3 candidate skills

This can be an efficient first-stage filter for a large registry.

Similarity, however, is not the same as authorization or task correctness.

4. Model-assisted routing

The model receives a shortlist:

Available:
- review_pull_request
- analyze_test_failure
- update_documentation

and returns a structured decision:

{
  "skill": "analyze_test_failure",
  "confidence": 0.86,
  "reason": "The request is about a failing integration test."
}

This is flexible but probabilistic.

5. Hybrid routing

A common production pattern is:

Deterministic permission/environment filters
                ↓
Semantic shortlist
                ↓
LLM selection among 3-5 candidates
                ↓
validation

The model does not need to reason over the entire capability universe.

Router output should be more than a skill name

Useful structured result:

{
  "decision": "SELECT_SKILL",
  "skill": "review_pull_request",
  "confidence": 0.88,
  "missing_inputs": ["pull_request_number"]
}

Other outcomes can include:

{
  "decision": "NO_MATCH"
}

or:

{
  "decision": "AMBIGUOUS",
  "candidates": ["incident_triage", "deployment_health"]
}

This is better than forcing a selection every time.

“No skill” is a legitimate decision

The router must be able to say that no skill applies.

If the contract says:

choose exactly one of A, B, C

then the model is forced to select something.

User request:

Write a short birthday poem.

Available skills:

PR Review
Incident Triage
Deployment Health

Correct routing:

NO_MATCH

not Incident Triage simply because one choice is required.

Ambiguity and clarification

User:

Find out what is wrong with payment-service.

This could mean:

  • production incident diagnosis,
  • deployment health check,
  • code analysis,
  • test failure.

The router may request clarification:

{
  "decision": "NEEDS_CLARIFICATION",
  "question": "Do you want to investigate the running production service, a deployment, or the source code?"
}

This is better than launching the wrong capability automatically.

Confidence is not policy by itself

A value such as:

confidence = 0.84

is not automatically a calibrated probability.

It can be a routing signal, but thresholds should come from evaluation.

For example:

high-confidence routing → auto select
medium → ask clarification / second-stage router
low → no match

should use measured boundaries rather than intuition.

Permission-aware discovery

Suppose the registry contains:

read_deployment_health
restart_deployment

A read-only user should receive a registry projection containing only:

available skills:
- read_deployment_health

The restart skill is not merely rejected at execution time; it is not selectable in the first place.

Global registry
      ↓
user/environment policy
      ↓
Authorized skill view
      ↓
router

This reduces both accidental and malicious routing opportunities.

Environment-aware routing

A skill can declare:

environments: [dev, staging]

or:

production: approval-required

The runtime can filter accordingly.

For a local coding agent, a capability such as:

run_shell_in_sandbox

may be available, while a production support agent should not receive it.

Skill selection vs tool selection

These are different layers.

User request
 ↓
Skill routing
 ↓
Deployment Health Skill
 ↓
Tool selection inside skill
 ↓
query_metrics / get_deployment / read_events

The router selects a task-level capability.

Skill execution selects tool-level capabilities.

Flattening both levels can produce an enormous tool list for the model.

Hierarchical capability discovery

For large systems, hierarchy can help:

Domain Router
├── Engineering
├── Support
└── Finance

Engineering Router
├── PR Review
├── Test Analysis
└── Deployment Health

Every model call does not need to see 300 skill descriptions.

This is similar to namespace and modularization problems in normal software.

Example: support-agent routing

Available skills:

invoice_lookup
refund_recommendation
account_access_diagnosis
subscription_change_explanation

User:

You charged the same amount twice.

Possible flow:

1. permission filter
2. all support skills remain
3. semantic router → invoice/refund candidates
4. LLM router → invoice_lookup first
5. execute read-only lookup
6. based on result, workflow/agent may later invoke refund recommendation

One routing decision does not necessarily solve the entire user problem.

Routing and skill composition

The router should not try to plan the entire workflow when it only needs to select the next capability.

For example:

request
 ↓
select invoice_lookup
 ↓
new evidence
 ↓
select refund_recommendation

This approaches an agentic loop.

Skill routing and multi-step planning are separate concepts.

Anti-pattern: every skill is always available

300 skills
+ every tool
+ every permission
→ one prompt

This is:

  • noisy,
  • expensive,
  • harder to choose from,
  • a larger security attack surface,
  • more prone to tool confusion.

Prefer candidate filtering and shortlists.

Anti-pattern: routing by skill name only

skill_42
helper_x
smart_fix

The router has no stable semantic contract.

Names and descriptions should be explicit and discriminative.

Anti-pattern: delegating authorization to the router

Weak:

Prompt:
"Do not select admin skills for normal users."

Better:

application removes unauthorized skills
          ↓
router only sees permitted candidates

Anti-pattern: forced selection

If exactly one skill must always be selected, the system manufactures false competence.

Support outcomes such as:

NO_MATCH
AMBIGUOUS
NEEDS_CLARIFICATION

Takeaways

  • A skill registry is more than a list; selection may require metadata, contracts, risk, and permission information.
  • Skill descriptions are part of the routing interface, so make them precise and discriminative.
  • Before routing, deterministically filter by permission, environment, status, and policy.
  • If the caller already knows the skill, use direct routing.
  • A strong pattern for larger systems is hybrid routing: filter → shortlist → model-assisted selection.
  • NO_MATCH and AMBIGUOUS are legitimate outcomes.
  • Use confidence thresholds only when backed by evaluation.
  • Skill selection and tool selection are different abstraction levels.
  • Hierarchical routing can reduce context and confusion in large registries.
  • The router must never be the authorization authority.