447 lines
14 KiB
Markdown
447 lines
14 KiB
Markdown
|
|
---
|
|||
|
|
Description: ""
|
|||
|
|
date: "2026-01-30"
|
|||
|
|
lastmod: ""
|
|||
|
|
tags: []
|
|||
|
|
title: Retriever 使用说明
|
|||
|
|
weight: 6
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## **基本介绍**
|
|||
|
|
|
|||
|
|
Retriever 组件是一个用于从各种数据源检索文档的组件。它的主要作用是根据用户的查询(query)从文档库中检索出最相关的文档。这个组件在以下场景中特别有用:
|
|||
|
|
|
|||
|
|
- 基于向量相似度的文档检索
|
|||
|
|
- 基于关键词的文档搜索
|
|||
|
|
- 知识库问答系统 (rag)
|
|||
|
|
|
|||
|
|
## **组件定义**
|
|||
|
|
|
|||
|
|
### **接口定义**
|
|||
|
|
|
|||
|
|
> 代码位置:eino/components/retriever/interface.go
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
type Retriever interface {
|
|||
|
|
Retrieve(ctx context.Context, query string, opts ...Option) ([]*schema.Document, error)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### **Retrieve 方法**
|
|||
|
|
|
|||
|
|
- 功能:根据查询检索相关文档
|
|||
|
|
- 参数:
|
|||
|
|
- ctx:上下文对象,用于传递请求级别的信息,同时也用于传递 Callback Manager
|
|||
|
|
- query:查询字符串
|
|||
|
|
- opts:检索选项,用于配置检索行为
|
|||
|
|
- 返回值:
|
|||
|
|
- `[]*schema.Document`:检索到的文档列表
|
|||
|
|
- error:检索过程中的错误信息
|
|||
|
|
|
|||
|
|
### **Document 结构体**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
type Document struct {
|
|||
|
|
// ID 是文档的唯一标识符
|
|||
|
|
ID string
|
|||
|
|
// Content 是文档的内容
|
|||
|
|
Content string
|
|||
|
|
// MetaData 用于存储文档的元数据信息
|
|||
|
|
MetaData map[string]any
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **公共 Option**
|
|||
|
|
|
|||
|
|
Retriever 组件使用 RetrieverOption 来定义可选参数, 以下是 Retriever 组件需要实现的公共 option。另外,每个具体的实现可以定义自己的特定 Option,通过 WrapRetrieverImplSpecificOptFn 函数包装成统一的 RetrieverOption 类型。
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
type Options struct {
|
|||
|
|
// Index 是检索器使用的索引,不同检索器中的索引可能有不同含义
|
|||
|
|
Index *string
|
|||
|
|
|
|||
|
|
// SubIndex 是检索器使用的子索引,不同检索器中的子索引可能有不同含义
|
|||
|
|
SubIndex *string
|
|||
|
|
|
|||
|
|
// TopK 是检索的文档数量上限
|
|||
|
|
TopK *int
|
|||
|
|
|
|||
|
|
// ScoreThreshold 是文档相似度的阈值,例如 0.5 表示文档的相似度分数必须大于 0.5
|
|||
|
|
ScoreThreshold *float64
|
|||
|
|
|
|||
|
|
// Embedding 是用于生成查询向量的组件
|
|||
|
|
Embedding embedding.Embedder
|
|||
|
|
|
|||
|
|
// DSLInfo 是用于检索的 DSL 信息,仅在 viking 类型的检索器中使用
|
|||
|
|
DSLInfo map[string]interface{}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
可以通过以下方式设置选项:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 设置索引
|
|||
|
|
WithIndex(index string) Option
|
|||
|
|
|
|||
|
|
// 设置子索引
|
|||
|
|
WithSubIndex(subIndex string) Option
|
|||
|
|
|
|||
|
|
// 设置检索文档数量上限
|
|||
|
|
WithTopK(topK int) Option
|
|||
|
|
|
|||
|
|
// 设置相似度阈值
|
|||
|
|
WithScoreThreshold(threshold float64) Option
|
|||
|
|
|
|||
|
|
// 设置向量生成组件
|
|||
|
|
WithEmbedding(emb embedding.Embedder) Option
|
|||
|
|
|
|||
|
|
// 设置 DSL 信息(仅用于 viking 类型检索器)
|
|||
|
|
WithDSLInfo(dsl map[string]any) Option
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## **使用方式**
|
|||
|
|
|
|||
|
|
### **单独使用**
|
|||
|
|
|
|||
|
|
#### VikingDB 示例
|
|||
|
|
|
|||
|
|
> 代码位置:eino-ext/components/retriever/volc_vikingdb/examples/builtin_embedding
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
import (
|
|||
|
|
"github.com/cloudwego/eino/components/retriever"
|
|||
|
|
"github.com/cloudwego/eino/compose"
|
|||
|
|
"github.com/cloudwego/eino/schema"
|
|||
|
|
|
|||
|
|
"github.com/cloudwego/eino-ext/components/retriever/volc_vikingdb"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
collectionName := "eino_test"
|
|||
|
|
indexName := "test_index_1"
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
* 下面示例中提前构建了一个名为 eino_test 的数据集 (collection),并在此数据集上构建了一个名为 test_index_1 的 hnsw-hybrid 索引 (index)
|
|||
|
|
* 数据集字段配置为:
|
|||
|
|
* 字段名称 字段类型 向量维度
|
|||
|
|
* ID string
|
|||
|
|
* vector vector 1024
|
|||
|
|
* sparse_vector sparse_vector
|
|||
|
|
* content string
|
|||
|
|
* extra_field_1 string
|
|||
|
|
*
|
|||
|
|
* component 使用时注意:
|
|||
|
|
* 1. ID / vector / sparse_vector / content 的字段名称与类型与上方配置一致
|
|||
|
|
* 2. vector 向量维度需要与 ModelName 对应的模型所输出的向量维度一致
|
|||
|
|
* 3. 部分模型不输出稀疏向量,此时 UseSparse 需要设置为 false,collection 可以不设置 sparse_vector 字段
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
cfg := &volc_vikingdb.RetrieverConfig{
|
|||
|
|
// https://api-vikingdb.volces.com (华北)
|
|||
|
|
// https://api-vikingdb.mlp.cn-shanghai.volces.com(华东)
|
|||
|
|
// https://api-vikingdb.mlp.ap-mya.byteplus.com(海外-柔佛)
|
|||
|
|
Host: "api-vikingdb.volces.com",
|
|||
|
|
Region: "cn-beijing",
|
|||
|
|
AK: ak,
|
|||
|
|
SK: sk,
|
|||
|
|
Scheme: "https",
|
|||
|
|
ConnectionTimeout: 0,
|
|||
|
|
Collection: collectionName,
|
|||
|
|
Index: indexName,
|
|||
|
|
EmbeddingConfig: volc_vikingdb.EmbeddingConfig{
|
|||
|
|
UseBuiltin: true,
|
|||
|
|
ModelName: "bge-m3",
|
|||
|
|
UseSparse: true,
|
|||
|
|
DenseWeight: 0.4,
|
|||
|
|
},
|
|||
|
|
Partition: "", // 对应索引中的【子索引划分字段】, 未设置时至空即可
|
|||
|
|
TopK: of(10),
|
|||
|
|
ScoreThreshold: of(0.1),
|
|||
|
|
FilterDSL: nil, // 对应索引中的【标量过滤字段】,未设置时至空即可,表达式详见 https://www.volcengine.com/docs/84313/1254609
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
volcRetriever, _ := volc_vikingdb.NewRetriever(ctx, cfg)
|
|||
|
|
|
|||
|
|
|
|||
|
|
query := "tourist attraction"
|
|||
|
|
docs, _ := volcRetriever.Retrieve(ctx, query)
|
|||
|
|
|
|||
|
|
log.Printf("vikingDB retrieve success, query=%v, docs=%v", query, docs)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Milvus 示例
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
import (
|
|||
|
|
"github.com/cloudwego/eino-ext/components/retriever/milvus2"
|
|||
|
|
"github.com/cloudwego/eino-ext/components/retriever/milvus2/search_mode"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 创建 retriever
|
|||
|
|
retriever, err := milvus2.NewRetriever(ctx, &milvus2.RetrieverConfig{
|
|||
|
|
ClientConfig: &milvusclient.ClientConfig{
|
|||
|
|
Address: addr,
|
|||
|
|
Username: username,
|
|||
|
|
Password: password,
|
|||
|
|
},
|
|||
|
|
Collection: "my_collection",
|
|||
|
|
TopK: 10,
|
|||
|
|
SearchMode: search_mode.NewApproximate(milvus2.COSINE),
|
|||
|
|
Embedding: emb,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 检索文档
|
|||
|
|
documents, err := retriever.Retrieve(ctx, "search query")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ElasticSearch 7 示例
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
import (
|
|||
|
|
"github.com/cloudwego/eino/schema"
|
|||
|
|
elasticsearch "github.com/elastic/go-elasticsearch/v7"
|
|||
|
|
|
|||
|
|
"github.com/cloudwego/eino-ext/components/retriever/es7"
|
|||
|
|
"github.com/cloudwego/eino-ext/components/retriever/es7/search_mode"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
client, _ := elasticsearch.NewClient(elasticsearch.Config{
|
|||
|
|
Addresses: []string{"http://localhost:9200"},
|
|||
|
|
Username: username,
|
|||
|
|
Password: password,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 创建带有稠密向量相似度搜索的检索器
|
|||
|
|
retriever, _ := es7.NewRetriever(ctx, &es7.RetrieverConfig{
|
|||
|
|
Client: client,
|
|||
|
|
Index: "my_index",
|
|||
|
|
TopK: 10,
|
|||
|
|
SearchMode: search_mode.DenseVectorSimilarity(search_mode.DenseVectorSimilarityTypeCosineSimilarity, "content_vector"),
|
|||
|
|
Embedding: emb,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 检索文档
|
|||
|
|
docs, _ := retriever.Retrieve(ctx, "search query")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### OpenSearch 2 示例
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"github.com/cloudwego/eino/schema"
|
|||
|
|
opensearch "github.com/opensearch-project/opensearch-go/v2"
|
|||
|
|
|
|||
|
|
"github.com/cloudwego/eino-ext/components/retriever/opensearch2"
|
|||
|
|
"github.com/cloudwego/eino-ext/components/retriever/opensearch2/search_mode"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
client, err := opensearch.NewClient(opensearch.Config{
|
|||
|
|
Addresses: []string{"http://localhost:9200"},
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 创建检索器组件
|
|||
|
|
retriever, _ := opensearch2.NewRetriever(ctx, &opensearch2.RetrieverConfig{
|
|||
|
|
Client: client,
|
|||
|
|
Index: "your_index_name",
|
|||
|
|
TopK: 5,
|
|||
|
|
// 选择搜索模式
|
|||
|
|
SearchMode: search_mode.Approximate(&search_mode.ApproximateConfig{
|
|||
|
|
VectorField: "content_vector",
|
|||
|
|
K: 5,
|
|||
|
|
}),
|
|||
|
|
ResultParser: func(ctx context.Context, hit map[string]interface{}) (*schema.Document, error) {
|
|||
|
|
// 解析 hit map 为 Document
|
|||
|
|
id, _ := hit["_id"].(string)
|
|||
|
|
source := hit["_source"].(map[string]interface{})
|
|||
|
|
content, _ := source["content"].(string)
|
|||
|
|
return &schema.Document{ID: id, Content: content}, nil
|
|||
|
|
},
|
|||
|
|
Embedding: emb,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 检索文档
|
|||
|
|
docs, err := retriever.Retrieve(ctx, "search query")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **在编排中使用**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 在 Chain 中使用
|
|||
|
|
chain := compose.NewChain[string, []*schema.Document]()
|
|||
|
|
chain.AppendRetriever(retriever)
|
|||
|
|
|
|||
|
|
// 在 Graph 中使用
|
|||
|
|
graph := compose.NewGraph[string, []*schema.Document]()
|
|||
|
|
graph.AddRetrieverNode("retriever_node", retriever)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## **Option 和 Callback 使用**
|
|||
|
|
|
|||
|
|
### **Callback 使用示例**
|
|||
|
|
|
|||
|
|
> 代码位置:eino-ext/components/retriever/volc_vikingdb/examples/builtin_embedding
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
import (
|
|||
|
|
"github.com/cloudwego/eino/callbacks"
|
|||
|
|
"github.com/cloudwego/eino/components/retriever"
|
|||
|
|
"github.com/cloudwego/eino/compose"
|
|||
|
|
"github.com/cloudwego/eino/schema"
|
|||
|
|
callbacksHelper "github.com/cloudwego/eino/utils/callbacks"
|
|||
|
|
"github.com/cloudwego/eino-ext/components/retriever/volc_vikingdb"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 创建 callback handler
|
|||
|
|
handler := &callbacksHelper.RetrieverCallbackHandler{
|
|||
|
|
OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *retriever.CallbackInput) context.Context {
|
|||
|
|
log.Printf("input access, content: %s\n", input.Query)
|
|||
|
|
return ctx
|
|||
|
|
},
|
|||
|
|
OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *retriever.CallbackOutput) context.Context {
|
|||
|
|
log.Printf("output finished, len: %v\n", len(output.Docs))
|
|||
|
|
return ctx
|
|||
|
|
},
|
|||
|
|
// OnError
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 使用 callback handler
|
|||
|
|
helper := callbacksHelper.NewHandlerHelper().
|
|||
|
|
Retriever(handler).
|
|||
|
|
Handler()
|
|||
|
|
|
|||
|
|
chain := compose.NewChain[string, []*schema.Document]()
|
|||
|
|
chain.AppendRetriever(volcRetriever)
|
|||
|
|
|
|||
|
|
// 在运行时使用
|
|||
|
|
run, _ := chain.Compile(ctx)
|
|||
|
|
|
|||
|
|
outDocs, _ := run.Invoke(ctx, query, compose.WithCallbacks(helper))
|
|||
|
|
|
|||
|
|
log.Printf("vikingDB retrieve success, query=%v, docs=%v", query, outDocs)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## **已有实现**
|
|||
|
|
|
|||
|
|
- Volc VikingDB Retriever: 基于火山引擎 VikingDB 的检索实现 [Retriever - VikingDB](/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb)
|
|||
|
|
- Milvus v2.5+ Retriever: 基于 Milvus 实现的向量数据库检索器 [Retriever - Milvus 2 (v2.5+) ](/zh/docs/eino/ecosystem_integration/retriever/retriever_milvusv2)
|
|||
|
|
- Milvus v2.4- Retriever: 基于 Milvus 实现的向量数据库检索器 [Retriever - Milvus (v2.4-)](/zh/docs/eino/ecosystem_integration/retriever/retriever_milvus)
|
|||
|
|
- Elasticsearch 8 Retriever: 基于 ES8 实现的通用搜索引擎检索器 [Retriever - Elasticsearch 8](/zh/docs/eino/ecosystem_integration/retriever/retriever_es8)
|
|||
|
|
- ElasticSearch 7 Retriever: 基于 ES7 实现的通用搜索引擎检索器 [Retriever - Elasticsearch 7](/zh/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch7)
|
|||
|
|
- OpenSearch 3 Retriever: 基于 OpenSearch 3 实现的通用搜索引擎检索器 [Retriever - OpenSearch 3](/zh/docs/eino/ecosystem_integration/retriever/retriever_opensearch3)
|
|||
|
|
- OpenSearch 2 Retriever: 基于 OpenSearch 2 实现的通用搜索引擎检索器 [Retriever - OpenSearch 2](/zh/docs/eino/ecosystem_integration/retriever/retriever_opensearch2)
|
|||
|
|
|
|||
|
|
## **自行实现参考**
|
|||
|
|
|
|||
|
|
实现自定义的 Retriever 组件时,需要注意以下几点:
|
|||
|
|
|
|||
|
|
1. 注意 option 机制的处理,及处理公共的 option.
|
|||
|
|
2. 注意处理 callback
|
|||
|
|
3. 注意需要注入特定的 metadata,以便后续节点使用
|
|||
|
|
|
|||
|
|
### **option 机制**
|
|||
|
|
|
|||
|
|
Retriever 组件提供了一组公共选项,实现时需要正确处理这些选项:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 使用 GetCommonOptions 处理公共 option
|
|||
|
|
func (r *MyRetriever) Retrieve(ctx context.Context, query string, opts ...retriever.Option) ([]*schema.Document, error) {
|
|||
|
|
// 1. 初始化及读取 option
|
|||
|
|
options := &retriever.Options{ // 可设置default值
|
|||
|
|
Index: &r.index,
|
|||
|
|
TopK: &r.topK,
|
|||
|
|
Embedding: r.embedder,
|
|||
|
|
}
|
|||
|
|
options = retriever.GetCommonOptions(options, opts...)
|
|||
|
|
|
|||
|
|
// ...
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **Callback 处理**
|
|||
|
|
|
|||
|
|
Retriever 实现需要在适当的时机触发回调,以下结构体是 retriever 组件定义好的结构:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 定义回调输入输出
|
|||
|
|
type CallbackInput struct {
|
|||
|
|
Query string
|
|||
|
|
TopK int
|
|||
|
|
Filter string
|
|||
|
|
ScoreThreshold *float64
|
|||
|
|
Extra map[string]any
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type CallbackOutput struct {
|
|||
|
|
Docs []*schema.Document
|
|||
|
|
Extra map[string]any
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **完整实现示例**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
type MyRetriever struct {
|
|||
|
|
embedder embedding.Embedder
|
|||
|
|
index string
|
|||
|
|
topK int
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewMyRetriever(config *MyRetrieverConfig) (*MyRetriever, error) {
|
|||
|
|
return &MyRetriever{
|
|||
|
|
embedder: config.Embedder,
|
|||
|
|
index: config.Index,
|
|||
|
|
topK: config.DefaultTopK,
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (r *MyRetriever) Retrieve(ctx context.Context, query string, opts ...retriever.Option) ([]*schema.Document, error) {
|
|||
|
|
// 1. 处理选项
|
|||
|
|
options := &retriever.Options{
|
|||
|
|
Index: &r.index,
|
|||
|
|
TopK: &r.topK,
|
|||
|
|
Embedding: r.embedder,
|
|||
|
|
}
|
|||
|
|
options = retriever.GetCommonOptions(options, opts...)
|
|||
|
|
|
|||
|
|
// 2. 获取 callback manager
|
|||
|
|
cm := callbacks.ManagerFromContext(ctx)
|
|||
|
|
|
|||
|
|
// 3. 开始检索前的回调
|
|||
|
|
ctx = cm.OnStart(ctx, info, &retriever.CallbackInput{
|
|||
|
|
Query: query,
|
|||
|
|
TopK: *options.TopK,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 4. 执行检索逻辑
|
|||
|
|
docs, err := r.doRetrieve(ctx, query, options)
|
|||
|
|
|
|||
|
|
// 5. 处理错误和完成回调
|
|||
|
|
if err != nil {
|
|||
|
|
ctx = cm.OnError(ctx, info, err)
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ctx = cm.OnEnd(ctx, info, &retriever.CallbackOutput{
|
|||
|
|
Docs: docs,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
return docs, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (r *MyRetriever) doRetrieve(ctx context.Context, query string, opts *retriever.Options) ([]*schema.Document, error) {
|
|||
|
|
// 1. 如果设置了 Embedding,生成查询的向量表示 (注意公共option的逻辑处理)
|
|||
|
|
var queryVector []float64
|
|||
|
|
if opts.Embedding != nil {
|
|||
|
|
vectors, err := opts.Embedding.EmbedStrings(ctx, []string{query})
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
queryVector = vectors[0]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 其他逻辑
|
|||
|
|
return docs, nil
|
|||
|
|
}
|
|||
|
|
```
|