混合检索(Hybrid Search)结合向量相似度检索和传统标量过滤,是构建高质量 RAG 应用的关键技术。
传统检索: WHERE status='active' AND category='tech'
向量检索: ORDER BY embedding <=> query_vector
混合检索: WHERE status='active' AND category='tech'
ORDER BY embedding <=> query_vector
func HybridSearch(queryVector []float32, status string, category string) (string, error) {
built := xb.Of(&Document{}).
VectorSearch("embedding", queryVector, 20). // 向量检索,返回 20 条
Eq("status", status). // 标量过滤
Eq("category", category). // 标量过滤
QdrantX(func(qx *xb.QdrantBuilderX) {
qx.ScoreThreshold(0.7)
}).
Build()
return built.ToQdrantJSON()
}
func AdvancedHybridSearch(params SearchParams) (map[string]interface{}, error) {
// ⭐ xb 自动过滤 nil/0/空字符串/time.Time零值,直接传参即可
builder := xb.Of(&Document{}).
VectorSearch("embedding", params.QueryVector, params.TopK).
Eq("status", params.Status). // 自动过滤空字符串
Gte("created_at", params.StartDate). // 自动过滤零值
Lte("created_at", params.EndDate). // 自动过滤零值
In("category", params.Categories...). // 自动过滤空切片
Ne("status", "deleted").
Or(func(cb *xb.CondBuilder) {
for _, tag := range params.Tags {
cb.Like("tags", tag).OR() // ⭐ xb 自动添加 %tag%
}
}) // 空切片时 Or() 会被自动过滤
built := builder.
QdrantX(func(qx *xb.QdrantBuilderX) {
qx.ScoreThreshold(float32(params.MinScore))
}).
Build()
return built.ToQdrantJSON()
}
// 适用于:过滤条件能显著减少候选集
func FilterThenSearch(vector []float32, mustFilters map[string]interface{}) (string, error) {
built := xb.Of(&Document{}).
Eq("status", mustFilters["status"]). // 先过滤
Eq("language", mustFilters["language"]). // 缩小范围
VectorSearch("embedding", vector, 10). // 再向量检索,返回 10 条
Build()
return built.ToQdrantJSON()
}
// 适用于:需要大量候选结果再精筛
func SearchThenFilter(vector []float32, optionalFilters map[string]interface{}) (string, error) {
built := xb.Of(&Document{}).
VectorSearch("embedding", vector, 100). // 粗召回,100 条
QdrantX(func(qx *xb.QdrantBuilderX) {
qx.ScoreThreshold(0.6) // 相似度阈值
}).
Build()
// 注意:后置过滤需要在应用层处理,取前 10 条
return built.ToQdrantJSON()
}
func MultiStageHybridSearch(params SearchParams) ([]Document, error) {
// 阶段 1: 宽松向量检索 + 核心过滤
built1 := xb.Of(&Document{}).
VectorSearch("embedding", params.Vector, 100). // 粗召回 100 条
Eq("language", params.Language). // 核心过滤
QdrantX(func(qx *xb.QdrantBuilderX) {
qx.ScoreThreshold(0.5) // 宽松阈值
}).
Build()
stage1JSON, err := built1.ToQdrantJSON()
if err != nil {
return nil, err
}
// 执行查询获取阶段1结果(伪代码)
stage1Results := executeQdrantQuery(stage1JSON)
// 阶段 2: 应用额外过滤
filtered := applyBusinessFilters(stage1Results, params.Filters)
// 阶段 3: 重排序
reranked := rerankResults(filtered, params.RerankModel)
// 阶段 4: 多样性控制
diverse := applyMMR(reranked, params.Lambda, params.TopK)
return diverse, nil
}
// 优先返回最新内容
func TimeAwareSearch(query string, vector []float32) (string, error) {
sevenDaysAgo := time.Now().AddDate(0, 0, -7)
built := xb.Of(&Document{}).
VectorSearch("embedding", vector, 20). // 返回 20 条
Gte("published_at", sevenDaysAgo). // 最近 7 天
Eq("status", "published"). // 已发布
QdrantX(func(qx *xb.QdrantBuilderX) {
qx.ScoreThreshold(0.65)
}).
Build()
return built.ToQdrantJSON()
}
func MultilingualSearch(vector []float32, preferredLang string) (string, error) {
// 优先返回首选语言,但也包含其他语言
built := xb.Of(&Document{}).
VectorSearch("embedding", vector, 20).
Or(func(cb *xb.CondBuilder) {
cb.Eq("language", preferredLang).OR(). // 首选语言
Eq("language", "en") // 备选语言
}).
Build()
return built.ToQdrantJSON()
}
func PermissionAwareSearch(vector []float32, userID int64, userRoles []string) (map[string]interface{}, error) {
return xb.Of(&Document{}).
VectorSearch("embedding", vector).
Or(func(cb *xb.CondBuilder) {
// 公开文档
cb.Eq("visibility", "public").OR()
// 用户自己的文档
cb.Eq("owner_id", userID).OR()
// 角色可访问的文档
cb.In("required_role", userRoles)
}).
Ne("status", "deleted").
Build()
return built.ToQdrantJSON()
}
// 支持层级分类:科技 > 人工智能 > 机器学习
func HierarchicalSearch(vector []float32, category string) (map[string]interface{}, error) {
return xb.Of(&Document{}).
VectorSearch("embedding", vector).
Or(func(cb *xb.CondBuilder) {
// 精确匹配
cb.Eq("category", category).OR()
// 父类别
cb.Like("category", category+":%").OR()
// 子类别
cb.Like("category", "%:"+category)
}).
Build()
return built.ToQdrantJSON()
}
// 根据文档新鲜度调整相似度分数
func FreshnessWeightedSearch(vector []float32) ([]Document, error) {
built := xb.Of(&Document{}).
VectorSearch("embedding", vector, 50).
Build()
qdrantJSON, _ := built.ToQdrantJSON()
now := time.Now()
for i := range results {
// 计算文档年龄(天)
age := now.Sub(results[i].CreatedAt).Hours() / 24
// 应用时间衰减:score * e^(-age/30)
decayFactor := math.Exp(-age / 30.0)
results[i].Score *= decayFactor
}
// 重新排序
sort.Slice(results, func(i, j int) bool {
return results[i].Score > results[j].Score
})
return results[:10], nil
}
func PersonalizedSearch(vector []float32, userID int64) (map[string]interface{}, error) {
// 获取用户偏好
userPrefs := getUserPreferences(userID)
builder := xb.Of(&Document{}).
VectorSearch("embedding", vector)
// 应用个性化过滤
if len(userPrefs.FavoriteCategories) > 0 {
builder.In("category", userPrefs.FavoriteCategories)
}
if len(userPrefs.BlockedAuthors) > 0 {
builder.NotIn("author_id", userPrefs.BlockedAuthors...)
}
built := builder.Build()
return built.ToQdrantJSON()
}
func SearchWithNegativeFeedback(vector []float32, userID int64) (map[string]interface{}, error) {
// 获取用户已看过/不感兴趣的文档
viewedDocs := getUserViewHistory(userID, 30) // 最近 30 天
dislikedDocs := getUserDislikes(userID)
excludeIDs := append(viewedDocs, dislikedDocs...)
built := xb.Of(&Document{}).
VectorSearch("embedding", vector, 20).
NotIn("id", excludeIDs). // 排除已看过的
Build()
return built.ToQdrantJSON()
}
| 策略 | 延迟 | 准确率 | 适用场景 |
|---|---|---|---|
| 纯向量检索 | 10ms | 75% | 无结构化过滤需求 |
| 先过滤后检索 | 15ms | 85% | 过滤条件强 |
| 先检索后过滤 | 25ms | 90% | 需要大量候选 |
| 多阶段混合 | 50ms | 95% | 高质量要求 |
相关文档: