
eino整合RAG
Milvus 官方文档:https://milvus.io/docs
支持的向量数据库
根据 Eino-ext 文档,目前已集成以下方案:
| 数据库 | Eino 集成 | 特点 | 适用场景 |
|---|---|---|---|
| Milvus | ✅ Indexer + Retriever | 云原生、分布式、GPU 加速 | 大规模生产环境(千万级+) |
| Elasticsearch | ✅ Indexer + Retriever | 支持向量+全文混合检索 | 已有 ES 基础设施的企业 |
| OpenSearch | ✅ Indexer + Retriever | 成熟的混合查询能力 | 日志+搜索+向量的统一平台 |
| Qdrant | ✅ Retriever | Rust 实现、高性能过滤 | 需要复杂过滤条件的场景 |
| Redis | ✅ Retriever | 超低延迟、KNN + Range 搜索 | 实时推荐、缓存场景 |
| PgVector | 社区支持 | PostgreSQL 扩展、ACID 事务 | 已有 PG 主存储的场景 |
数据规模评估
├── <10万条 → Chroma / 内存方案(原型验证)
├── 10万~1000万 → Qdrant / PgVector / 单机 Milvus
└── >1000万条 → 分布式 Milvus / Elasticsearch / OpenSearch查询模式评估
├── 纯向量检索 → Milvus / Qdrant(性能最优)
├── 混合检索(向量+全文) → Elasticsearch / OpenSearch / Weaviate
└── 复杂元数据过滤 → Qdrant / Weaviate(支持多级嵌套条件)Eino 集成示例
docker安装Milvus
shell
# 下载 standalone 模式配置文件(以 v2.5.5 为例)
wget https://github.com/milvus-io/milvus/releases/download/v2.5.5/milvus-standalone-docker-compose.yml -O docker-compose.yml
# 后台启动所有容器
docker compose up -d
## Attu 图形界面管理 Milvus 打开浏览器,访问 http://localhost:3000。
docker run -d --name attu -p 3000:3000 zilliz/attu:latest| 容器名称 | 角色定位 | 核心功能 | 类比说明 |
|---|---|---|---|
milvus-standalone | 数据库引擎 | 负责处理所有核心的向量搜索、索引构建和数据管理请求,是整个系统的“大脑”和“执行者”。 | 相当于 MySQL 服务本身 |
milvus-etcd | 元数据管理 | 存储Milvus所有的“元数据”,比如:有哪些集合(Collection)、集合的 Schema 定义、分区信息等。 | 相当于 MySQL 的 information_schema 系统表 |
milvus-minio | 数据存储 | 负责真正存储所有向量数据和日志文件,是系统的“数据仓库”。 | 相当于你的电脑硬盘 |
使用 Milvus(推荐)
由代码自动创建表,没有指定数据库,默认是库是用的default
go
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"strings"
"github.com/cloudwego/eino-ext/components/embedding/ollama"
milvusretriever "github.com/cloudwego/eino-ext/components/retriever/milvus"
"github.com/cloudwego/eino/components/retriever"
einoschema "github.com/cloudwego/eino/schema"
"github.com/milvus-io/milvus-sdk-go/v2/client"
milvusclient "github.com/milvus-io/milvus-sdk-go/v2/client"
"github.com/milvus-io/milvus-sdk-go/v2/entity"
)
func main() {
ctx := context.Background()
// --- 1. 初始化 Embedder ---
embedder, err := ollama.NewEmbedder(ctx, &ollama.EmbeddingConfig{
BaseURL: "http://localhost:11434",
Model: "nomic-embed-text",
})
if err != nil {
log.Fatalf("创建 Embedder 失败: %v", err)
}
fmt.Println("✅ Embedder 初始化成功")
// 2. 创建 Milvus 客户端
milvusAddr := "192.168.154.131:19530"
milvusCli, err := milvusclient.NewGrpcClient(ctx, milvusAddr)
if err != nil {
log.Fatalf("创建 Milvus 客户端失败: %v", err)
}
defer milvusCli.Close()
fmt.Printf("✅ Milvus 客户端连接成功 (%s)\n", milvusAddr)
// 删除已存在的集合
collectionName := "my_knowledge"
err = milvusCli.DropCollection(ctx, collectionName)
if err == nil {
fmt.Printf("已删除旧集合: %s\n", collectionName)
}
// 3. 创建集合
fmt.Println("正在创建集合...")
// 定义字段
collSchema := &entity.Schema{
CollectionName: collectionName,
Fields: []*entity.Field{
entity.NewField().
WithName("id").
WithDataType(entity.FieldTypeInt64).
WithIsPrimaryKey(true).
WithIsAutoID(true),
entity.NewField().
WithName("content").
WithDataType(entity.FieldTypeVarChar).
WithMaxLength(65535),
entity.NewField().
WithName("vector").
WithDataType(entity.FieldTypeFloatVector).
WithDim(768),
entity.NewField().
WithName("metadata").
WithDataType(entity.FieldTypeJSON),
},
}
// 创建集合
err = milvusCli.CreateCollection(ctx, collSchema, 1)
if err != nil {
log.Fatalf("创建集合失败: %v", err)
}
fmt.Printf("✅ 集合 %s 创建成功\n", collectionName)
// 创建 FLAT 索引
idx, err := entity.NewIndexFlat(entity.L2)
if err != nil {
log.Fatalf("创建索引失败: %v", err)
}
err = milvusCli.CreateIndex(ctx, collectionName, "vector", idx, false)
if err != nil {
log.Fatalf("创建索引失败: %v", err)
}
fmt.Println("✅ FLAT 索引创建成功")
// 加载集合
err = milvusCli.LoadCollection(ctx, collectionName, false)
if err != nil {
log.Fatalf("加载集合失败: %v", err)
}
fmt.Println("✅ 集合加载成功")
// 4. 准备数据
docs := []*einoschema.Document{
{
Content: "Go语言由Google开发,于2009年正式发布,是一种静态强类型、编译型、并发型编程语言。",
MetaData: map[string]any{
"category": "基础",
"topic": "Go语言历史",
"tags": []string{"google", "编程语言", "静态类型"},
"create_at": "2024-01-15",
},
},
{
Content: "Go语言的goroutine是轻量级线程,只需要2KB栈空间,可以轻松创建数百万个并发任务。",
MetaData: map[string]any{
"category": "并发",
"topic": "goroutine",
"tags": []string{"goroutine", "并发", "轻量级线程"},
"create_at": "2024-02-20",
},
},
{
Content: "Eino是CloudWeGo开源的AI应用开发框架,专为Go语言设计,提供流式处理、可观测性等特性。",
MetaData: map[string]any{
"category": "框架",
"topic": "Eino框架",
"tags": []string{"Eino", "CloudWeGo", "AI框架"},
"create_at": "2024-03-10",
},
},
}
// 5. 生成向量
texts := make([]string, len(docs))
for i, doc := range docs {
texts[i] = doc.Content
}
vectors, err := embedder.EmbedStrings(ctx, texts)
if err != nil {
log.Fatalf("生成向量失败: %v", err)
}
fmt.Printf("✅ 成功生成 %d 个向量,维度: %d\n", len(vectors), len(vectors[0]))
// 6. 插入数据
contentData := make([]string, len(docs))
vectorData := make([][]float32, len(docs))
metadataData := make([][]byte, len(docs))
for i, doc := range docs {
contentData[i] = doc.Content
vectorData[i] = make([]float32, len(vectors[i]))
for j, v := range vectors[i] {
vectorData[i][j] = float32(v)
}
metadataBytes, _ := json.Marshal(doc.MetaData)
metadataData[i] = metadataBytes
}
contentCol := entity.NewColumnVarChar("content", contentData)
vectorCol := entity.NewColumnFloatVector("vector", 768, vectorData)
metadataCol := entity.NewColumnJSONBytes("metadata", metadataData)
_, err = milvusCli.Insert(ctx, collectionName, "", contentCol, vectorCol, metadataCol)
if err != nil {
log.Fatalf("插入数据失败: %v", err)
}
fmt.Printf("✅ 成功插入 %d 条数据\n", len(docs))
// 刷新数据
err = milvusCli.Flush(ctx, collectionName, false)
if err != nil {
log.Fatalf("刷新数据失败: %v", err)
}
fmt.Println("✅ 数据刷新成功")
// 7. 初始化 Retriever
ret, err := milvusretriever.NewRetriever(ctx, &milvusretriever.RetrieverConfig{
Client: milvusCli,
Collection: collectionName,
Embedding: embedder,
MetricType: entity.L2,
OutputFields: []string{"content", "metadata"},
VectorConverter: func(ctx context.Context, vectors [][]float64) ([]entity.Vector, error) {
result := make([]entity.Vector, len(vectors))
for i, v := range vectors {
float32Vec := make([]float32, len(v))
for j, val := range v {
float32Vec[j] = float32(val)
}
result[i] = entity.FloatVector(float32Vec)
}
return result, nil
},
DocumentConverter: func(ctx context.Context, sr client.SearchResult) ([]*einoschema.Document, error) {
var results []*einoschema.Document
contentCol := sr.Fields.GetColumn("content")
metadataCol := sr.Fields.GetColumn("metadata")
for i := 0; i < sr.ResultCount; i++ {
content := ""
if contentCol != nil {
content, _ = contentCol.GetAsString(i)
}
metadata := make(map[string]any)
if metadataCol != nil {
metadataStr, _ := metadataCol.GetAsString(i)
if metadataStr != "" {
json.Unmarshal([]byte(metadataStr), &metadata)
}
}
doc := &einoschema.Document{
Content: content,
MetaData: metadata,
}
doc.WithScore(float64(sr.Scores[i]))
results = append(results, doc)
}
return results, nil
},
})
if err != nil {
log.Fatalf("创建 Retriever 失败: %v", err)
}
fmt.Println("✅ Retriever 初始化成功")
// 8. 执行检索测试
testQueries := []string{
"Go语言的并发有什么特点?",
"Go适合AI开发吗?",
"Eino是什么框架?",
}
fmt.Println("\n" + strings.Repeat("=", 60))
fmt.Println("开始检索测试")
fmt.Println(strings.Repeat("=", 60))
for _, query := range testQueries {
fmt.Printf("\n📝 查询: %s\n", query)
results, err := ret.Retrieve(ctx, query, retriever.WithEmbedding(embedder))
if err != nil {
log.Printf("检索失败: %v\n", err)
continue
}
fmt.Printf("检索到 %d 条相关文档:\n", len(results))
for i, doc := range results {
fmt.Printf("\n [%d] 相似度得分: %.4f\n", i+1, doc.Score())
fmt.Printf(" Content: %s\n", truncateString(doc.Content, 80))
if len(doc.MetaData) > 0 {
fmt.Printf(" MetaData: %+v\n", doc.MetaData)
}
}
fmt.Println("\n" + strings.Repeat("-", 60))
}
}
func truncateString(s string, maxLen int) string {
if len(s) <= maxLen {
return s
}
return s[:maxLen] + "..."
}测试
用的是本地map的方式快速验证
所需要的模型
shell
# Embedding 模型(必需)
ollama pull nomic-embed-text
# LLM 模型(用于生成回答)
ollama pull qwen2.5:3b测试代码
go
package main
import (
"context"
"fmt"
"math"
"sort"
"strings"
ollamaembed "github.com/cloudwego/eino-ext/components/embedding/ollama"
ollamachat "github.com/cloudwego/eino-ext/components/model/ollama"
"github.com/cloudwego/eino/schema"
)
// Document 文档结构
type Document struct {
Content string
Metadata map[string]string
Embedding []float64
}
// VectorStore 简单的向量存储
type VectorStore struct {
documents []Document
}
func main() {
ctx := context.Background()
// 1. 初始化 Embedder(用于文本转向量)
fmt.Println("正在初始化 Embedder...")
embedder, err := ollamaembed.NewEmbedder(ctx, &ollamaembed.EmbeddingConfig{
BaseURL: "http://localhost:11434",
Model: "nomic-embed-text", // 专业的 embedding 模型
})
if err != nil {
fmt.Printf("初始化 Embedder 失败: %v\n", err)
fmt.Println("请先运行: ollama pull nomic-embed-text")
return
}
// 2. 准备真实数据(关于 Go 语言和 AI 的知识库)
documents := []Document{
{
Content: "Go语言由Google开发,于2009年正式发布,是一种静态强类型、编译型、并发型编程语言。",
Metadata: map[string]string{"category": "基础", "topic": "Go语言历史"},
},
{
Content: "Go语言的goroutine是轻量级线程,只需要2KB栈空间,可以轻松创建数百万个并发任务。",
Metadata: map[string]string{"category": "并发", "topic": "goroutine"},
},
{
Content: "Go语言的channel是goroutine之间通信的主要方式,遵循'不要通过共享内存来通信,而要通过通信来共享内存'的理念。",
Metadata: map[string]string{"category": "并发", "topic": "channel"},
},
{
Content: "在AI开发中,Python拥有TensorFlow、PyTorch等丰富框架,而Go在部署和高性能服务方面有优势。",
Metadata: map[string]string{"category": "AI", "topic": "AI开发对比"},
},
{
Content: "Eino是CloudWeGo开源的AI应用开发框架,专为Go语言设计,提供流式处理、可观测性等特性。",
Metadata: map[string]string{"category": "框架", "topic": "Eino框架"},
},
{
Content: "Go语言在Web服务方面性能优秀,标准库net/http简单易用,常用于构建微服务和API网关。",
Metadata: map[string]string{"category": "Web", "topic": "Web开发"},
},
{
Content: "RAG(检索增强生成)结合了信息检索和LLM,可以基于私有知识库回答问题,避免幻觉问题。",
Metadata: map[string]string{"category": "AI", "topic": "RAG技术"},
},
{
Content: "使用Eino框架构建RAG应用时,可以使用Indexer组件管理向量索引,Retriever组件进行检索。",
Metadata: map[string]string{"category": "框架", "topic": "Eino RAG"},
},
}
// 3. 生成所有文档的向量并存储
fmt.Println("\n正在生成文档向量...")
vectorStore := &VectorStore{
documents: make([]Document, len(documents)),
}
for i, doc := range documents {
embeddings, err := embedder.EmbedStrings(ctx, []string{doc.Content})
if err != nil {
fmt.Printf("生成文档 %d 向量失败: %v\n", i+1, err)
return
}
vectorStore.documents[i] = doc
vectorStore.documents[i].Embedding = embeddings[0]
fmt.Printf("✓ 已处理文档 %d/%d: %s\n", i+1, len(documents), truncateString(doc.Content, 40))
}
// 4. 初始化 LLM 模型(用于生成回答)
fmt.Println("\n正在初始化 LLM 模型...")
chatModel, err := ollamachat.NewChatModel(ctx, &ollamachat.ChatModelConfig{
BaseURL: "http://localhost:11434",
Model: "qwen2.5:3b",
})
if err != nil {
fmt.Printf("初始化 LLM 失败: %v\n", err)
return
}
// 5. 测试查询
testQueries := []string{
"Go语言的并发有什么特点?",
"什么是RAG技术?",
"Go适合AI开发吗?",
"Eino是什么框架?",
}
fmt.Println("\n" + strings.Repeat("=", 60))
fmt.Println("开始 RAG 查询测试")
fmt.Println(strings.Repeat("=", 60))
for _, query := range testQueries {
fmt.Printf("\n📝 用户问题: %s\n", query)
fmt.Println(strings.Repeat("-", 60))
// 6. 将查询问题转向量
queryEmbedding, err := embedder.EmbedStrings(ctx, []string{query})
if err != nil {
fmt.Printf("生成查询向量失败: %v\n", err)
continue
}
// 7. 检索最相关的 Top-2 文档
topDocs := vectorStore.Search(queryEmbedding[0], 2)
fmt.Println("📚 检索到的相关文档:")
for i, doc := range topDocs {
fmt.Printf(" [%d] 相似度: %.4f | 分类: %s | 内容: %s\n",
i+1, doc.Score, doc.Doc.Metadata["category"],
truncateString(doc.Doc.Content, 50))
}
// 8. 构建 RAG Prompt
context := ""
for i, doc := range topDocs {
context += fmt.Sprintf("文档%d: %s\n", i+1, doc.Doc.Content)
}
prompt := fmt.Sprintf(`基于以下已知信息,回答问题。如果无法从提供的信息中找到答案,请说明信息不足。
已知信息:
%s
问题:%s
请基于上述信息给出准确、简洁的回答:`, context, query)
// 9. 调用 LLM 生成回答
messages := []*schema.Message{
schema.SystemMessage("你是一个专业的AI助手,请基于提供的上下文回答问题。"),
schema.UserMessage(prompt),
}
resp, err := chatModel.Generate(ctx, messages)
if err != nil {
fmt.Printf("生成回答失败: %v\n", err)
continue
}
fmt.Printf("🤖 AI 回答:\n%s\n", resp.Content)
fmt.Println(strings.Repeat("=", 60))
}
}
// Search 向量检索(余弦相似度)
func (vs *VectorStore) Search(queryEmbedding []float64, topK int) []SearchResult {
results := make([]SearchResult, len(vs.documents))
for i, doc := range vs.documents {
similarity := cosineSimilarity(queryEmbedding, doc.Embedding)
results[i] = SearchResult{
Doc: doc,
Score: similarity,
Index: i,
}
}
// 按相似度排序
sort.Slice(results, func(i, j int) bool {
return results[i].Score > results[j].Score
})
// 返回 Top-K
if topK > len(results) {
topK = len(results)
}
return results[:topK]
}
// SearchResult 检索结果
type SearchResult struct {
Doc Document
Score float64
Index int
}
// 余弦相似度计算
func cosineSimilarity(a, b []float64) float64 {
if len(a) != len(b) {
return 0
}
var dotProduct, normA, normB float64
for i := 0; i < len(a); i++ {
dotProduct += a[i] * b[i]
normA += a[i] * a[i]
normB += b[i] * b[i]
}
if normA == 0 || normB == 0 {
return 0
}
return dotProduct / (math.Sqrt(normA) * math.Sqrt(normB))
}
// 辅助函数:截断字符串
func truncateString(s string, maxLen int) string {
if len(s) <= maxLen {
return s
}
return s[:maxLen] + "..."
}
// 注意:需要在文件顶部导入 strings 包
// import "strings"结果:
text
正在初始化 Embedder...
正在生成文档向量...
✓ 已处理文档 1/8: Go语言由Google开发,于2009年正�...
✓ 已处理文档 2/8: Go语言的goroutine是轻量级线程�...
✓ 已处理文档 3/8: Go语言的channel是goroutine之间通�...
✓ 已处理文档 4/8: 在AI开发中,Python拥有TensorFlow�...
✓ 已处理文档 5/8: Eino是CloudWeGo开源的AI应用开发�...
✓ 已处理文档 6/8: Go语言在Web服务方面性能优秀�...
✓ 已处理文档 7/8: RAG(检索增强生成)结合了信�...
✓ 已处理文档 8/8: 使用Eino框架构建RAG应用时,可...
正在初始化 LLM 模型...
============================================================
开始 RAG 查询测试
============================================================
📝 用户问题: Go语言的并发有什么特点?
------------------------------------------------------------
📚 检索到的相关文档:
[1] 相似度: 0.8040 | 分类: 框架 | 内容: Eino是CloudWeGo开源的AI应用开发框架,�...
[2] 相似度: 0.7114 | 分类: AI | 内容: 在AI开发中,Python拥有TensorFlow、PyTorch�...
🤖 AI 回答:
信息不足。提供的信息没有提及关于Go语言并发的具体特点。
============================================================
📝 用户问题: 什么是RAG技术?
------------------------------------------------------------
📚 检索到的相关文档:
[1] 相似度: 0.8763 | 分类: 框架 | 内容: Eino是CloudWeGo开源的AI应用开发框架,�...
[2] 相似度: 0.7911 | 分类: 框架 | 内容: 使用Eino框架构建RAG应用时,可以使用I...
🤖 AI 回答:
RAG技术是使用Eino框架中的Indexer和Retriever组件来管理向量索引并进行检索的AI应用开发方法。
============================================================
📝 用户问题: Go适合AI开发吗?
------------------------------------------------------------
📚 检索到的相关文档:
[1] 相似度: 0.7392 | 分类: AI | 内容: RAG(检索增强生成)结合了信息检索�...
[2] 相似度: 0.6796 | 分类: 框架 | 内容: Eino是CloudWeGo开源的AI应用开发框架,�...
🤖 AI 回答:
信息不足。
============================================================
📝 用户问题: Eino是什么框架?
------------------------------------------------------------
📚 检索到的相关文档:
[1] 相似度: 0.8763 | 分类: 框架 | 内容: Eino是CloudWeGo开源的AI应用开发框架,�...
[2] 相似度: 0.7911 | 分类: 框架 | 内容: 使用Eino框架构建RAG应用时,可以使用I...
🤖 AI 回答:
Eino是CloudWeGo开源的AI应用开发框架,专为Go语言设计。
============================================================
