AI技术文章

Embedding相似度不是万金油,电商、 新闻场景如何按时效性做rerank

作者:Zilliz0 次浏览 来源:Zilliz
Embedding相似度不是万金油,电商、 新闻场景如何按时效性做rerank

同样是语义相似度结合时效性做rerank,指数衰减、高斯衰减、线性衰减怎么选? 假设你要在一个新闻应用中落地语义检索功能,让用户搜索雷军的投资版图盘点时,能自动关联顺为资本、小米战投等核心关联信息。

同样是语义相似度结合时效性做rerank,指数衰减、高斯衰减、线性衰减怎么选?

假设你要在一个新闻应用中落地语义检索功能,让用户搜索雷军的投资版图盘点时,能自动关联顺为资本、小米战投等核心关联信息。

那么在确定了向量数据库应该用Milvus之后,要怎么对搜索结果排序呢?

是直接根据基于embedding的语义相似度,根据语义进行内容排序,还是引入更多与新闻相关的变量,比如时间?

答案是后者,在类似新闻、电商这样的场景,我们做检索既要考虑相似度,也需要让新鲜内容自动浮出水面,而旧闻悄然后移。

针对这一需求,Milvus 2.6中推出了一个备受关注的亮点功能Time-aware Ranking Functions(时间感知排名函数),也称为 Time-Aware Decay Functions(时间感知衰减函数)。

这个功能让搜索结果不再仅依赖向量相似度,而是巧妙融入时间因素,实现更智能的排名调整。

本文将深入探讨这一功能的细节、意义、应用场景,并通过一个实际案例展示其使用方式。

01

Time-aware Ranking Functions 具体介绍

Time-aware Ranking Functions 通过在搜索结果重排序(re-ranking)阶段应用时间衰减,动态调整文档的相关性分数。 传统向量搜索仅基于相似度(如L2COSINE等)排名,但现实中信息价值往往随时间衰减(如新闻的时效性)。该功能使用集合中的时间戳字段(支持INT8/16/32/64FLOATDOUBLE类型),计算一个衰减分数(0 到 1 之间),然后与归一化相似度相乘,生成最终排名,从而平衡语义相似度和时间相关性。对于时间相关的衰减,确保参数单位(如秒、毫秒)与集合的时间戳一致。

如何工作

该功能通过三个阶段计算最终分数:

1.归一化相似度分数:将向量相似度分数标准化到 0-1 范围。对于 L2 和 JACCARD 指标(较低值表示更高相似度),使用公式:normalized_score = 1.0 - (2 × arctan(score))/π对于 IP、COSINE 和 BM25 指标,直接使用原始分数。

2.计算衰减分数:基于选择的衰减函数,将数值字段(如时间戳)转换为 0-1 范围的衰减分数,反映与理想点(如当前时间)的“距离”。

3.计算最终分数:final_score = normalized_similarity_score × decay_score在混合搜索中,使用多个向量字段的最大归一化分数:final_score = max(normalized_scores) × decay_score

支持的衰减函数

Milvus 支持三种衰减模型,每种适合不同曲线形状:

  • Exponential Decay(指数衰减):初始快速衰减,后有长尾,适合新闻等急需新鲜度的场景。

  • Gaussian Decay(高斯衰减):铃形曲线,渐进式衰减,适用于平衡时效和全面性的通用搜索。如餐厅推荐中允许中等距离的场所。

  • Linear Decay(线性衰减):直线衰减,有明确截止点,适合有过期阈值的应用。如事件搜索中超出两周的活动不显示。

配置参数

(通过 Python SDK 的Function对象实现):

Embedding相似度不是万金油,电商、 新闻场景如何按时效性做rerank

在搜索时,将函数传入ranker参数即可应用,支持标准向量搜索和混合搜索。

示例配置(Gaussian):

from pymilvus import Function, FunctionType from datetime import datetime decay_ranker = Function(     name="time_decay",     input_field_names=["timestamp"],     function_type=FunctionType.RERANK,     params={         "reranker": "decay",         "function": "gauss",         "origin": int(datetime.now().timestamp()),         "scale": 7 * 24 * 60 * 60,  # 7 天         "offset": 24 * 60 * 60,     # 1 天         "decay": 0.5     } )

search()hybrid_search()中应用ranker参数。

02

引入该功能的意义和应用场景

引入 Time-aware Ranking Functions 的核心意义在于解决传统向量搜索的“时效盲区”。

在动态数据环境中(如社交媒体或实时推荐),旧信息往往淹没新内容,导致用户体验下降。 该功能通过内置衰减机制,直接在 Milvus 引擎中处理时间因素,避免了后处理(如客户端排序)的额外开销,提高了查询效率和精度。

此外,它与 Milvus 的文本分析管道无缝整合,支持全文本搜索与向量嵌入的结合,进一步桥接传统信息检索和现代 AI。 在 v2.6.3 更新中,还优化了分数合并逻辑,提升了性能。 总体而言,这一功能让 Milvus 更适合亿级规模的实时应用,降低了开发复杂度和运维成本,推动 AI 搜索的民主化。

应用场景:

Time-aware Ranking Functions 适用于任何需要时效优先的向量搜索场景:

  • 新闻和内容推荐:在新闻 App 中,优先显示昨日热点,而非数月前的旧文。

  • 电商搜索:新上架商品排名更高,结合位置衰减(类似时间)实现“附近新鲜货”。

  • 社交媒体 feeds:新鲜帖子主导 feed,Exponential 模型确保高相关旧帖仍有曝光。

  • 事件查找:Linear 模型设置过期阈值,如仅显示未来两周活动。

这些场景中,该功能通过 configurable 衰减率,确保结果既相关又及时。

03

实际案例

为了快速了解功能,我们通过一个新闻文章搜索系统案例演示,使用 Milvus 构建时间感知排名。 假设我们有一个包含 7 篇 AI 相关新闻的集合,发布日期从 1 天前到 120 天前,包括相同内容但不同日期的文章对。

实施步骤

(1)连接 Milvus 并创建集合:使用 pymilvus 连接,定义 schema 包括 headline、content、dense(语义向量)、sparse_vector(BM25)和 publish_date。

(2)设置嵌入函数:使用 Openai text embedding 模型生成 dense 向量,BM25 生成 sparse 向量。

(3)插入数据:添加文章,publish_date 为时间戳。

(4)配置衰减排名器:定义 Gaussian、Exponential 和 Linear 排名器,以当前时间为 origin。

(5)执行搜索:查询"artificial intelligence advancements",比较无衰减和有衰减结果。

代码片段

(1)连接Milvus和创建schema:

import datetime import matplotlib.pyplot as plt import numpy as np from pymilvus import (     MilvusClient,     DataType,     Function,     FunctionType,     AnnSearchRequest, ) # Create connection to Milvus milvus_client = MilvusClient("http://localhost:19530")  # Define collection name collection_name = "articles_tutorial" # Clean up any existing collection with the same name milvus_client.drop_collection(collection_name) # Create schema with fields for content and temporal information schema = milvus_client.create_schema(enable_dynamic_field=False, auto_id=True) schema.add_field("id", DataType.INT64, is_primary=True) schema.add_field("headline", DataType.VARCHAR, max_length=200, enable_analyzer=True) schema.add_field("content", DataType.VARCHAR, max_length=2000, enable_analyzer=True) schema.add_field("dense", DataType.FLOAT_VECTOR, dim=3072)  # For dense embeddings schema.add_field("sparse_vector", DataType.SPARSE_FLOAT_VECTOR)  # For sparse (BM25) search schema.add_field("publish_date", DataType.INT64)  # Timestamp for decay ranking # Create embedding function for semantic search text_embedding_function = Function(     name="openai_embedding",     function_type=FunctionType.TEXTEMBEDDING,     input_field_names=["content"],     output_field_names=["dense"],     params={         "provider": "openai",                               "model_name": "text-embedding-3-large"       } ) schema.add_function(text_embedding_function) # Create BM25 function for keyword search bm25_function = Function(     name="bm25",     input_field_names=["content"],     output_field_names=["sparse_vector"],     function_type=FunctionType.BM25, ) schema.add_function(bm25_function) index_params = milvus_client.prepare_index_params() index_params.add_index(field_name="dense", index_type="AUTOINDEX", metric_type="L2") index_params.add_index(     field_name="sparse_vector",     index_name="sparse_inverted_index",     index_type="AUTOINDEX",     metric_type="BM25", ) milvus_client.create_collection(     collection_name,     schema=schema,     index_params=index_params,     consistency_level="Bounded" ) print(f"Collection {collection_name} is created successfully")

(2)插入数据:

current_time = int(datetime.datetime.now().timestamp()) current_date = datetime.datetime.fromtimestamp(current_time) print(f"Current time: {current_date.strftime('%Y-%m-%d %H:%M:%S')}") # Sample news articles spanning different dates articles = [     {         "headline": "AI Breakthrough Enables Medical Diagnosis Advancement",         "content": "Researchers announced a major breakthrough in AI-based medical diagnostics, enabling faster and more accurate detection of rare diseases.",         "publish_date": int((current_date - datetime.timedelta(days=120)).timestamp())  # ~4 months ago     },     {         "headline": "Tech Giants Compete in New AI Race",         "content": "Major technology companies are investing billions in a new race to develop the most advanced artificial intelligence systems.",         "publish_date": int((current_date - datetime.timedelta(days=60)).timestamp())  # ~2 months ago     },     {         "headline": "AI Ethics Guidelines Released by International Body",         "content": "A consortium of international organizations has released new guidelines addressing ethical concerns in artificial intelligence development and deployment.",         "publish_date": int((current_date - datetime.timedelta(days=30)).timestamp())  # 1 month ago     },     {         "headline": "Latest Deep Learning Models Show Remarkable Progress",         "content": "The newest generation of deep learning models demonstrates unprecedented capabilities in language understanding and generation.",         "publish_date": int((current_date - datetime.timedelta(days=15)).timestamp())  # 15 days ago     },     # Articles with identical content but different dates     {         "headline": "AI Research Advancements Published in January",         "content": "Breakthrough research in artificial intelligence shows remarkable advancements in multiple domains.",         "publish_date": int((current_date - datetime.timedelta(days=90)).timestamp())  # ~3 months ago     },     {         "headline": "New AI Research Results Released This Week",         "content": "Breakthrough research in artificial intelligence shows remarkable advancements in multiple domains.",         "publish_date": int((current_date - datetime.timedelta(days=5)).timestamp())  # Very recent - 5 days ago     },     {         "headline": "AI Development Updates Released Yesterday",         "content": "Recent developments in artificial intelligence research are showing promising results across various applications.",         "publish_date": int((current_date - datetime.timedelta(days=1)).timestamp())  # Just yesterday     }, ] # Insert articles into the collection milvus_client.insert(collection_name, articles) print(f"Inserted {len(articles)} articles into the collection")

(3)排名器配置:

# Create a Gaussian decay ranker gaussian_ranker = Function(     name="time_decay_gaussian",     input_field_names=["publish_date"],     function_type=FunctionType.RERANK,     params={         "reranker": "decay",         "function": "gauss",           # Gaussian/bell curve decay         "origin": current_time,        # Current time as reference point         "offset": 7 * 24 * 60 * 60,    # One week (full relevance)         "decay": 0.5,                  # Articles from two weeks ago have half relevance          "scale": 14 * 24 * 60 * 60     # Two weeks scale parameter     } ) # Create an exponential decay ranker with different parameters exponential_ranker = Function(     name="time_decay_exponential",     input_field_names=["publish_date"],     function_type=FunctionType.RERANK,     params={         "reranker": "decay",         "function": "exp",             # Exponential decay         "origin": current_time,        # Current time as reference point         "offset": 3 * 24 * 60 * 60,    # Shorter offset          "decay": 0.3,                  # Steeper decay          "scale": 10 * 24 * 60 * 60     # Different scale      } ) # Create a linear decay ranker linear_ranker = Function(     name="time_decay_linear",     input_field_names=["publish_date"],     function_type=FunctionType.RERANK,     params={         "reranker": "decay",         "function": "linear",          # Linear decay         "origin": current_time,        # Current time as reference point         "offset": 7 * 24 * 60 * 60,    # One week (full relevance)         "decay": 0.5,                  # Articles from two weeks ago have half relevance         "scale": 14 * 24 * 60 * 60     # Two weeks scale parameter     } )

(4)搜索:

# Helper function to format search results with dates and scores def print_search_results(results, title):     print(f"\n=== {title} ===")     for i, hit in enumerate(results[0]):         publish_date = datetime.datetime.fromtimestamp(hit.get('publish_date'))         days_from_now = (current_time - hit.get('publish_date')) / (24 * 60 * 60)         print(f"{i+1}. {hit.get('headline')}")         print(f"   Published: {publish_date.strftime('%Y-%m-%d')} ({int(days_from_now)} days ago)")         print(f"   Score: {hit.score:.4f}")         print() # Define our search query query = "artificial intelligence advancements" # 1. Search without decay ranking (purely based on semantic relevance) standard_results = milvus_client.search(     collection_name,     data=[query],     anns_field="dense",     limit=7,  # Get all our articles     output_fields=["headline", "content", "publish_date"],     consistency_level="Bounded" ) print_search_results(standard_results, "SEARCH RESULTS WITHOUT DECAY RANKING") # Store original scores for later comparison original_scores = {} for hit in standard_results[0]:     original_scores[hit.get('headline')] = hit.score # 2. Search with each decay function # Gaussian decay gaussian_results = milvus_client.search(     collection_name,     data=[query],     anns_field="dense",     limit=7,     output_fields=["headline", "content", "publish_date"],     ranker=gaussian_ranker,     consistency_level="Bounded" ) print_search_results(gaussian_results, "SEARCH RESULTS WITH GAUSSIAN DECAY RANKING") # Exponential decay exponential_results = milvus_client.search(     collection_name,     data=[query],     anns_field="dense",     limit=7,     output_fields=["headline", "content", "publish_date"],     ranker=exponential_ranker,     consistency_level="Bounded" ) print_search_results(exponential_results, "SEARCH RESULTS WITH EXPONENTIAL DECAY RANKING") # Linear decay linear_results = milvus_client.search(     collection_name,     data=[query],     anns_field="dense",     limit=7,     output_fields=["headline", "content", "publish_date"],     ranker=linear_ranker,     consistency_level="Bounded" ) print_search_results(linear_results, "SEARCH RESULTS WITH LINEAR DECAY RANKING")

(5)结果

=== SEARCH RESULTS WITHOUT DECAY RANKING === 1. AI Research Advancements Published in January    Published: 2025-07-23 (90 days ago)    Score: 0.7090 2. New AI Research Results Released This Week    Published: 2025-10-16 (5 days ago)    Score: 0.7090 3. AI Development Updates Released Yesterday    Published: 2025-10-20 (1 days ago)    Score: 0.7317 4. Tech Giants Compete in New AI Race    Published: 2025-08-22 (60 days ago)    Score: 1.0101 5. AI Breakthrough Enables Medical Diagnosis Advancement    Published: 2025-06-23 (120 days ago)    Score: 1.1065 6. Latest Deep Learning Models Show Remarkable Progress    Published: 2025-10-06 (15 days ago)    Score: 1.1649 7. AI Ethics Guidelines Released by International Body    Published: 2025-09-21 (30 days ago)    Score: 1.3030 === SEARCH RESULTS WITH GAUSSIAN DECAY RANKING === 1. New AI Research Results Released This Week    Published: 2025-10-16 (5 days ago)    Score: 0.6074 2. AI Development Updates Released Yesterday    Published: 2025-10-20 (1 days ago)    Score: 0.5979 3. Latest Deep Learning Models Show Remarkable Progress    Published: 2025-10-06 (15 days ago)    Score: 0.3601 4. AI Ethics Guidelines Released by International Body    Published: 2025-09-21 (30 days ago)    Score: 0.0642 5. Tech Giants Compete in New AI Race    Published: 2025-08-22 (60 days ago)    Score: 0.0000 6. AI Research Advancements Published in January    Published: 2025-07-23 (90 days ago)    Score: 0.0000 7. AI Breakthrough Enables Medical Diagnosis Advancement    Published: 2025-06-23 (120 days ago)    Score: 0.0000 === SEARCH RESULTS WITH EXPONENTIAL DECAY RANKING === 1. AI Development Updates Released Yesterday    Published: 2025-10-20 (1 days ago)    Score: 0.5979 2. New AI Research Results Released This Week    Published: 2025-10-16 (5 days ago)    Score: 0.4774 3. Latest Deep Learning Models Show Remarkable Progress    Published: 2025-10-06 (15 days ago)    Score: 0.1065 4. AI Ethics Guidelines Released by International Body    Published: 2025-09-21 (30 days ago)    Score: 0.0161 5. Tech Giants Compete in New AI Race    Published: 2025-08-22 (60 days ago)    Score: 0.0005 6. AI Research Advancements Published in January    Published: 2025-07-23 (90 days ago)    Score: 0.0000 7. AI Breakthrough Enables Medical Diagnosis Advancement    Published: 2025-06-23 (120 days ago)    Score: 0.0000 === SEARCH RESULTS WITH LINEAR DECAY RANKING === 1. New AI Research Results Released This Week    Published: 2025-10-16 (5 days ago)    Score: 0.6074 2. AI Development Updates Released Yesterday    Published: 2025-10-20 (1 days ago)    Score: 0.5979 3. Latest Deep Learning Models Show Remarkable Progress    Published: 2025-10-06 (15 days ago)    Score: 0.3226 4. AI Research Advancements Published in January    Published: 2025-07-23 (90 days ago)    Score: 0.3037 5. Tech Giants Compete in New AI Race    Published: 2025-08-22 (60 days ago)    Score: 0.2484 6. AI Breakthrough Enables Medical Diagnosis Advancement    Published: 2025-06-23 (120 days ago)    Score: 0.2339 7. AI Ethics Guidelines Released by International Body    Published: 2025-09-21 (30 days ago)    Score: 0.2084

(1)无衰减排名(纯语义相关性搜索)

这是基准(Baseline),其结果完全由查询artificial intelligence advancements与每篇文章内容之间的语义相似度决定,时间因素被完全忽略。

  • 相同内容,相同得分: 两篇内容完全相同的文章(“AI Research Advancements Published in January” 和 “New AI Research Results Released This Week”)尽管发布日期相差近三个月,但它们的得分完全一样(0.7090)。这完美地证明了在不使用时间衰减器时,publish_date 字段对排名没有任何影响。

  • 相关性决定排名: 排名顺序纯粹基于语义相关性(在这里用L2距离表示,值越小越相关)。得分最低(最相关)的两篇文章内容都包含 "artificial intelligence" 和 "advancements" 的直接同义词或强相关概念。而得分最高的(最不相关)文章 “AI Ethics Guidelines...” 虽然也关于AI,但主题更偏向“伦理”,与“技术进步”的语义距离较远。

  • 时效性被忽略: 最新的文章(1天前、5天前发布)并没有排在最前面。甚至一篇90天前的文章排在了第一位,这在很多需要获取最新资讯的场景下是不可接受的。

(2)高斯衰减排名 (Gaussian Decay)

高斯衰减器引入了时间维度,其衰减曲线像一个“钟形”,对近期内容友好,对中期内容惩罚逐渐加重,对远期内容则给予极大的惩罚。

配置: offset 为7天(一周内不惩罚),scale 为14天,decay 为0.5(两周前的文章,时间权重衰减为50%)。

  • 近期内容优先: 最新的两篇文章(1天前和5天前)现在跃居前两位。因为它们都在7天的 offset 范围之内,时间上没有受到惩罚,排名主要由它们原始的语义相关性决定。

  • 平滑的惩罚曲线: 15天前的文章(略超出 scale 范围)排名第三,得分被显著降低(0.3601),但依然在榜。30天前的文章得分更低(0.0642)。

  • 远期内容被“过滤”: 所有超过60天的文章,其最终得分都变成了 0.0000。这表明高斯衰减函数对超过一定时间阈值(在此配置下约为1-2个月)的内容施加了极大的惩罚,使其几乎不可能出现在靠前的位置。

(3)指数衰减排名 (Exponential Decay)

指数衰减是最“严厉”的时间衰减方式,时间越久,权重下降得越快。

配置: offset 仅3天,scale 为10天,decay 为0.3。这些参数比高斯衰减的配置更为“激进”。

  • 极致的时效性: 1天前的文章排名第一,5天前的文章虽然也很新,但因为它已经超出了3天的 offset,其分数(0.4774)相比1天前的文章(0.5979)有了明显的下降。这体现了指数衰减对“最新”的极致追求。

  • 剧烈的分数下降: 15天前的文章得分骤降至 0.1065,远低于高斯衰减下的得分。30天前的文章得分几乎为零。这显示了指数衰减的惩罚力度非常大。

  • 快速“遗忘”: 和高斯衰减类似,超过一定时间的内容得分都趋近于零,但这个“遗忘”速度更快

(4)线性衰减排名 (Linear Decay)

线性衰减的惩罚是恒定的,随着时间的推移,分数呈直线下降,是最“温和”的衰减方式。

  • 旧内容仍有价值: 最显著的区别在于,旧文章依然保留了可观的分数。90天前和120天前的文章得分分别为 0.3037 和 0.2339,并且仍然排在榜单上。而在高斯和指数衰减中,它们的得分都是0。

  • 平衡相关性与时间: 虽然最新的文章依然排名靠前,但线性衰减给了语义相关性更高的权重。例如,90天前的文章(原始语义分很高)排在了30天前的文章(原始语义分较低)之前,这说明线性衰减虽然惩罚了它的“旧”,但不足以完全抵消其内容上的“高相关性”。

  • 温和的惩罚: 分数的下降不像其他两种衰减那样剧烈,提供了一个更加平滑的过渡。

对比总结

Embedding相似度不是万金油,电商、 新闻场景如何按时效性做rerank

在实践中,我们可以根据以上排名器各自的特点,进行选择性的配置。

如有更多相关问题,欢迎评论区分享交流。

作者介绍

Embedding相似度不是万金油,电商、 新闻场景如何按时效性做rerank

Zilliz 黄金写手:臧伟

文章来自于微信公众号 “Zilliz”,作者 “Zilliz”

相关主题

读完之后

加入 AINRK 交流群

了解加入交流群的方式,与更多 AI 从业者交流。

查看交流群说明(在新窗口打开)