语义路由
RedisVL 提供了一个 SemanticRouter
接口,利用 Redis 内置的搜索和聚合功能,对一组 Route
引用执行 KNN 式分类,以确定最佳匹配项。
本 Notebook 将介绍如何将 Redis 用作应用程序的语义路由器
定义路由
下面我们定义了 3 个不同的路由。一个用于 technology
(技术),一个用于 sports
(体育),另一个用于 entertainment
(娱乐)。在本例中,目标显然是主题“分类”。但您可以为几乎任何事物创建路由和引用。
每个路由都有一组涵盖路由“语义表面区域”的引用。用户的输入查询需要与一个或多个引用在语义上相似,才能“匹配”到该路由。
此外,每个路由都有一个 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
)
初始化 SemanticRouter
SemanticRouter
在初始化时将自动在 Redis 中为路由引用创建一个索引。默认情况下,它使用 HFTextVectorizer
为每个路由引用生成嵌入。
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://: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 │
╰────────────┴─────────────┴────────┴────────────────┴────────────────┴────────────────┴────────────────┴────────────────┴────────────────┴─────────────────┴────────────────╯
简单路由
# 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)
我们还可以将一个语句路由到多个路由,并按距离对其进行排序
# 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
[]
注意不同的路由匹配距离。这是因为我们使用了 min
聚合方法,而不是默认的 avg
方法。
更新路由配置
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.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://: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://:6379")
assert router3.to_dict() == router2.to_dict() == router.to_dict()
14:07:34 redisvl.index.index INFO Index already exists, not overwriting.
清理路由器
# Use clear to flush all routes from the index
router.clear()
# Use delete to clear the index and remove it completely
router.delete()