Route Queries with SemanticRouter

RedisVL provides a SemanticRouter interface that uses Redis' built-in search and aggregation to perform KNN-style classification over a set of Route references to determine the best match.

This guide covers how to use Redis as a Semantic Router for your applications.

Prerequisites

Before you begin, ensure you have:

What You'll Learn

By the end of this guide, you will be able to:

  • Define routes with references and distance thresholds
  • Initialize and configure a SemanticRouter
  • Route queries to single or multiple matching routes
  • Serialize and restore router configurations
  • Manage route references dynamically

Define the Routes

Below we define 3 different routes. One for technology, one for sports, and another for entertainment. Now for this example, the goal here is surely topic "classification". But you can create routes and references for almost anything.

Each route has a set of references that cover the "semantic surface area" of the route. The incoming query from a user needs to be semantically similar to one or more of the references in order to "match" on the route.

Additionally, each route has a distance_threshold which determines the maximum distance between the query and the reference for the query to be routed to the route. This value is unique to each route and uses Redis COSINE distance units (0-2], where lower values require stricter matching.

from redisvl.extensions.router import Route

# Define routes for the semantic router
technology = Route(
    name="technology",
    references=[
        "what are the latest advancements in AI?",
        "tell me about the newest gadgets",
        "what's trending in tech?"
    ],
    metadata={"category": "tech", "priority": 1},
    distance_threshold=0.71
)

sports = Route(
    name="sports",
    references=[
        "who won the game last night?",
        "tell me about the upcoming sports events",
        "what's the latest in the world of sports?",
        "sports",
        "basketball and football"
    ],
    metadata={"category": "sports", "priority": 2},
    distance_threshold=0.72
)

entertainment = Route(
    name="entertainment",
    references=[
        "what are the top movies right now?",
        "who won the best actor award?",
        "what's new in the entertainment industry?"
    ],
    metadata={"category": "entertainment", "priority": 3},
    distance_threshold=0.7
)

Initialize the SemanticRouter

SemanticRouter will automatically create an index within Redis upon initialization for the route references. By default, it uses the HFTextVectorizer to generate embeddings for each route reference.

import os
from redisvl.extensions.router import SemanticRouter
from redisvl.utils.vectorize import HFTextVectorizer

os.environ["TOKENIZERS_PARALLELISM"] = "false"

# Initialize the SemanticRouter
router = SemanticRouter(
    name="topic-router",
    vectorizer=HFTextVectorizer(),
    routes=[technology, sports, entertainment],
    redis_url="redis://localhost:6379",
    overwrite=True # Blow away any other routing index with this name
)
# look at the index specification created for the semantic router
!rvl index info -i topic-router
Index Information:
╭──────────────────┬──────────────────┬──────────────────┬──────────────────┬──────────────────╮
│ Index Name       │ Storage Type     │ Prefixes         │ Index Options    │ Indexing         │
├──────────────────┼──────────────────┼──────────────────┼──────────────────┼──────────────────┤
| topic-router     | HASH             | ['topic-router'] | []               | 0                |
╰──────────────────┴──────────────────┴──────────────────┴──────────────────┴──────────────────╯
Index Fields:
╭─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────╮
│ Name            │ Attribute       │ Type            │ Field Option    │ Option Value    │ Field Option    │ Option Value    │ Field Option    │ Option Value    │ Field Option    │ Option Value    │
├─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┤
│ reference_id    │ reference_id    │ TAG             │ SEPARATOR       │ ,               │                 │                 │                 │                 │                 │                 │
│ route_name      │ route_name      │ TAG             │ SEPARATOR       │ ,               │                 │                 │                 │                 │                 │                 │
│ reference       │ reference       │ TEXT            │ WEIGHT          │ 1               │                 │                 │                 │                 │                 │                 │
│ vector          │ vector          │ VECTOR          │ algorithm       │ FLAT            │ data_type       │ FLOAT32         │ dim             │ 768             │ distance_metric │ COSINE          │
╰─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────╯
router._index.info()["num_docs"]
11

Simple routing

# Query the router with a statement
route_match = router("Can you tell me about the latest in artificial intelligence?")
route_match
RouteMatch(name='technology', distance=0.419146001339)
# Query the router with a statement and return a miss
route_match = router("are aliens real?")
route_match
RouteMatch(name=None, distance=None)

We can also route a statement to many routes and order them by distance:

# Perform multi-class classification with route_many() -- toggle the max_k and the distance_threshold
route_matches = router.route_many("How is AI used in basketball?", max_k=3)
route_matches
[RouteMatch(name='technology', distance=0.556494116783),
 RouteMatch(name='sports', distance=0.671060025692)]
# Toggle the aggregation method -- note the different distances in the result
from redisvl.extensions.router.schema import DistanceAggregationMethod

route_matches = router.route_many("How is AI used in basketball?", aggregation_method=DistanceAggregationMethod.min, max_k=3)
route_matches
[RouteMatch(name='technology', distance=0.556494116783),
 RouteMatch(name='sports', distance=0.629264295101)]

Note the different route match distances. This is because we used the min aggregation method instead of the default avg approach.

Update the routing config

from redisvl.extensions.router import RoutingConfig

router.update_routing_config(
    RoutingConfig(aggregation_method=DistanceAggregationMethod.min, max_k=3)
)
route_matches = router.route_many("Lebron James")
route_matches
[RouteMatch(name='sports', distance=0.663253962994)]

Router serialization

router.to_dict()
{'name': 'topic-router',
 'routes': [{'name': 'technology',
   'references': ['what are the latest advancements in AI?',
    'tell me about the newest gadgets',
    "what's trending in tech?"],
   'metadata': {'category': 'tech', 'priority': 1},
   'distance_threshold': 0.71},
  {'name': 'sports',
   'references': ['who won the game last night?',
    'tell me about the upcoming sports events',
    "what's the latest in the world of sports?",
    'sports',
    'basketball and football'],
   'metadata': {'category': 'sports', 'priority': 2},
   'distance_threshold': 0.72},
  {'name': 'entertainment',
   'references': ['what are the top movies right now?',
    'who won the best actor award?',
    "what's new in the entertainment industry?"],
   'metadata': {'category': 'entertainment', 'priority': 3},
   'distance_threshold': 0.7}],
 'vectorizer': {'type': 'hf',
  'model': 'sentence-transformers/all-mpnet-base-v2'},
 'routing_config': {'max_k': 3, 'aggregation_method': 'min'}}
router2 = SemanticRouter.from_dict(router.to_dict(), redis_url="redis://localhost:6379")

assert router2.to_dict() == router.to_dict()
router.to_yaml("router.yaml", overwrite=True)
router3 = SemanticRouter.from_yaml("router.yaml", redis_url="redis://localhost:6379")

assert router3.to_dict() == router2.to_dict() == router.to_dict()

Add route references

router.add_route_references(route_name="technology", references=["latest AI trends", "new tech gadgets"])
['topic-router:technology:f243fb2d073774e81c7815247cb3013794e6225df3cbe3769cee8c6cefaca777',
 'topic-router:technology:7e4bca5853c1c3298b4d001de13c3c7a79a6e0f134f81acc2e7cddbd6845961f']

Get route references

# by route name
refs = router.get_route_references(route_name="technology")
refs
[{'id': 'topic-router:technology:f243fb2d073774e81c7815247cb3013794e6225df3cbe3769cee8c6cefaca777',
  'reference_id': 'f243fb2d073774e81c7815247cb3013794e6225df3cbe3769cee8c6cefaca777',
  'route_name': 'technology',
  'reference': 'latest AI trends'},
 {'id': 'topic-router:technology:851f51cce5a9ccfbbcb66993908be6b7871479af3e3a4b139ad292a1bf7e0676',
  'reference_id': '851f51cce5a9ccfbbcb66993908be6b7871479af3e3a4b139ad292a1bf7e0676',
  'route_name': 'technology',
  'reference': 'what are the latest advancements in AI?'},
 {'id': 'topic-router:technology:7e4bca5853c1c3298b4d001de13c3c7a79a6e0f134f81acc2e7cddbd6845961f',
  'reference_id': '7e4bca5853c1c3298b4d001de13c3c7a79a6e0f134f81acc2e7cddbd6845961f',
  'route_name': 'technology',
  'reference': 'new tech gadgets'},
 {'id': 'topic-router:technology:149a9c9919c58534aa0f369e85ad95ba7f00aa0513e0f81e2aff2ea4a717b0e0',
  'reference_id': '149a9c9919c58534aa0f369e85ad95ba7f00aa0513e0f81e2aff2ea4a717b0e0',
  'route_name': 'technology',
  'reference': "what's trending in tech?"},
 {'id': 'topic-router:technology:85cc73a1437df27caa2f075a29c497e5a2e532023fbb75378aedbae80779ab37',
  'reference_id': '85cc73a1437df27caa2f075a29c497e5a2e532023fbb75378aedbae80779ab37',
  'route_name': 'technology',
  'reference': 'tell me about the newest gadgets'}]
# by reference id
refs = router.get_route_references(reference_ids=[refs[0]["reference_id"]])
refs
[{'id': 'topic-router:technology:f243fb2d073774e81c7815247cb3013794e6225df3cbe3769cee8c6cefaca777',
  'reference_id': 'f243fb2d073774e81c7815247cb3013794e6225df3cbe3769cee8c6cefaca777',
  'route_name': 'technology',
  'reference': 'latest AI trends'}]

Delete route references

# by route name
deleted_count = router.delete_route_references(route_name="sports")
deleted_count
5
# by id
deleted_count = router.delete_route_references(reference_ids=[refs[0]["reference_id"]])
deleted_count
1

Clean up the router

# Use clear to flush all routes from the index
router.clear()
# Use delete to clear the index and remove it completely
router.delete()

Next Steps

Now that you understand semantic routing, explore these related guides:

RATE THIS PAGE
Back to top ↑