语义路由

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()
评价本页
返回顶部 ↑