语义路由
RedisVL 提供了一个SemanticRouter
利用Redis的内置搜索和聚合功能执行的接口
KNN 样式分类Route
引用来确定最佳匹配。
此笔记本将介绍如何将 Redis 用作应用程序的语义路由器
定义路由
下面我们定义 3 种不同的路线。一个用于technology
,一个用于sports
和
另一个用于entertainment
.现在,对于此示例,此处的目标是
当然是 “classification” 这个话题。但是您可以为
几乎任何东西。
每个路由都有一组引用,这些引用覆盖了 路线。来自用户的传入查询需要在语义上类似于 1 或 更多的引用,以便在路由上 “匹配”。
此外,每条路由都有一个distance_threshold
它确定查询与要路由到路由的查询的引用之间的最大距离。此值对于每个路由都是唯一的。
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=1.0
)
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.5
)
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
)
/Users/robert.shelton/.pyenv/versions/3.11.9/lib/python3.11/site-packages/huggingface_hub/file_download.py:1142: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
warnings.warn(
/Users/robert.shelton/.pyenv/versions/3.11.9/lib/python3.11/site-packages/huggingface_hub/file_download.py:1142: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
warnings.warn(
14:07:31 redisvl.index.index INFO Index already exists, overwriting.
router.vectorizer
HFTextVectorizer(model='sentence-transformers/all-mpnet-base-v2', dims=768)
# 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 │
├────────────┼─────────────┼────────┼────────────────┼────────────────┼────────────────┼────────────────┼────────────────┼────────────────┼─────────────────┼────────────────┤
│ route_name │ route_name │ TAG │ SEPARATOR │ , │ │ │ │ │ │ │
│ reference │ reference │ TEXT │ WEIGHT │ 1 │ │ │ │ │ │ │
│ vector │ vector │ VECTOR │ algorithm │ FLAT │ data_type │ FLOAT32 │ dim │ 768 │ distance_metric │ COSINE │
╰────────────┴─────────────┴────────┴────────────────┴────────────────┴────────────────┴────────────────┴────────────────┴────────────────┴─────────────────┴────────────────╯
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.119614303112)
# Query the router with a statement and return a miss
route_match = router("are aliens real?")
route_match
RouteMatch(name=None, distance=None)
# Toggle the runtime distance threshold
route_match = router("Which basketball team will win the NBA finals?")
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("Lebron James", max_k=3)
route_matches
[]
# Toggle the aggregation method -- note the different distances in the result
from redisvl.extensions.router.schema import DistanceAggregationMethod
route_matches = router.route_many("Lebron James", aggregation_method=DistanceAggregationMethod.min, max_k=3)
route_matches
[]
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
[]
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': 1.0},
{'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.5},
{'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': {'distance_threshold': 0.5,
'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()
14:07:34 redisvl.index.index INFO Index already exists, not overwriting.
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()
14:07:34 redisvl.index.index INFO Index already exists, not overwriting.
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()
On this page