检索增强生成(Retrieval-Augmented Generation, RAG)成为解决模型知识局限性和幻觉问题的有效手段。在RAG架构中,向量数据库负责存储和检索文本的向量表示,而qDrant作为高性能的向量数据库,常被选为底层存储。本文将结合Go语言代码示例,详细介绍如何通过langchain-go和qdrant-go库存取qDrant向量,并实现一个完整的RAG流程。
环境:
- qDrant 服务(本地或远程运行)
- Ollama 服务(用于Embedding和文本生成)
- 准备nomic-embed-text模型
- 准备任意支持text的大模型
- Go语言环境及以下库:
github.com/tmc/langchaingo(包含向量存储、文档加载器、文本分割器等)github.com/qdrant/go-client(qDrant官方Go客户端)
新建文件夹config/config文件含config.go,管理配置信息。包含qDrant地址、集合名称、Ollama URL、Embedding模型名和生成模型名等。
1. 集合管理:EnsureCollection 和 resetCollection
确保指定的集合存在,若不存在则创建。向量维度(768)与Embedding模型(如nomic-embed-text)匹配,距离度量采用余弦相似度。创建集合时指定的向量维度(768)必须与Embedding模型输出的维度完全一致。本例使用nomic-embed-text(维度768),若换用其他模型(如all-MiniLM-L6-v2维度384),需相应调整。
resetCollection:先删除指定集合,再重新创建,用于重置整个知识库。
func EnsureCollection(urlStr string, collectionName string) {
myclient, err := qdrant.NewClient(&qdrant.Config{
Host: config.QdrantIP,
Port: 6334,
UseTLS: false,
})
if err != nil {
log.Fatal("连接Qdrant失败:", err)
}
defer myclient.Close()
ctx := context.Background()
exists, err := myclient.CollectionExists(ctx, collectionName)
if err != nil {
log.Fatal("查询集合状态失败:", err)
}
if !exists {
fmt.Printf("正在创建集合: %s...\n", collectionName)
err = myclient.CreateCollection(ctx, &qdrant.CreateCollection{
CollectionName: collectionName,
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 768, // nomic-embed-text 维度是 768:必须与 Embedding 模型一致
Distance: qdrant.Distance_Cosine,
}),
})
if err != nil {
log.Fatal("创建集合失败:", err)
}
fmt.Println("集合创建成功")
}
}
func resetCollection(name string) {
myclient, _ := qdrant.NewClient(&qdrant.Config{
Host: config.QdrantIP, Port: 6334, UseTLS: false,
})
defer myclient.Close()
ctx := context.Background()
_ = myclient.DeleteCollection(ctx, name)
EnsureCollection(config.QdrantIP, name)
fmt.Println("集合已重置,准备重新导入...")
}
2. 数据导入:IngestKnowledge
分块策略直接影响检索效果,需根据文档类型和任务调整块大小和重叠。
func IngestKnowledge(filePath string, shouldReset bool) {
// 1. 重置或确保集合存在
if shouldReset {
resetCollection(config.Collection)
} else {
EnsureCollection(config.QdrantIP, config.Collection)
}
// 2. 根据文件类型选择加载器(PDF或文本)
f, _ := os.Open(filePath)
defer f.Close()
var loader documentloaders.Loader
if strings.HasSuffix(filePath, ".pdf") {
loader = documentloaders.NewPDF(f, fileSize)
} else {
loader = documentloaders.NewText(f)
}
// 3. 加载并分割文档
docs, _ := loader.LoadAndSplit(ctx, textsplitter.NewRecursiveCharacter(
textsplitter.WithChunkSize(300),
textsplitter.WithChunkOverlap(100),
))
// 4. 初始化Embedder和向量存储
embedLLM, _ := ollama.New(ollama.WithModel(config.EmbedModel), ollama.WithServerURL(config.OllamaURL))
embedder, _ := embeddings.NewEmbedder(embedLLM)
store, _ := qdrantl.New(
qdrantl.WithURL(url.URL{Scheme: "http", Host: config.QdrantURL}),
qdrantl.WithCollectionName(config.Collection),
qdrantl.WithEmbedder(embedder),
)
_, err = store.AddDocuments(ctx, docs) // 5. 添加文档到qDrant
}
3. 查询与生成:UpdateRAG
确保集合存在,初始化Embedder和向量存储(与导入时相同)。向集合中添加几个示例文档块(演示用)。执行相似性搜索:将查询文本向量化,在qDrant中检索最相似的3个文档块。将检索结果拼接为上下文,构造提示词,调用生成模型(Ollama)得到最终回答。
注意:实际应用中,文档导入和查询生成通常是分开的步骤,此处合并仅为展示完整流程。
func UpdateRAG() {
// 1. 确保集合存在,初始化Embedder和向量存储
EnsureCollection(config.QdrantIP, config.Collection)
embedLLM, _ := ollama.New(ollama.WithModel(config.EmbedModel), ollama.WithServerURL(config.OllamaURL))
embedder, _ := embeddings.NewEmbedder(embedLLM)
store, _ := qdrantl.New(
qdrantl.WithURL(url.URL{Scheme: "http", Host: config.QdrantURL}),
qdrantl.WithCollectionName(config.Collection),
qdrantl.WithEmbedder(embedder),
)
// 2. 准备示例文档并分割(此处仅为演示,实际可从文件加载)
docs := []schema.Document{ ... }
splitter := textsplitter.NewRecursiveCharacter(textsplitter.WithChunkSize(200), textsplitter.WithChunkOverlap(20))
var chunks []schema.Document
for _, doc := range docs {
texts, _ := splitter.SplitText(doc.PageContent)
for _, t := range texts {
chunks = append(chunks, schema.Document{PageContent: t, Metadata: doc.Metadata})
}
}
store.AddDocuments(ctx, chunks)
// 3. 执行相似性搜索
query := "Qdrant 适合用在什么场景?"
results, _ := store.SimilaritySearch(ctx, query, 3)
// 4. 构建提示词并调用LLM生成回答
contextText := ""
for _, doc := range results {
contextText += doc.PageContent + "\n"
}
llm, _ := ollama.New(ollama.WithModel(config.GenerateModel), ollama.WithServerURL(config.OllamaURL))
prompt := fmt.Sprintf(`
你是一个基于知识库回答问题的助手。
只能根据【知识库】内容作答,如果无法得到答案,请回答“不知道”。
【知识库】
%s
【问题】
%s
`, contextText, query)
answer, _ := llms.GenerateFromSinglePrompt(ctx, llm, prompt)
fmt.Println(answer)
}
4. 运行示例
func main() {
IngestKnowledge("./xxix.pdf", true) //指向需要索引的知识文件
UpdateRAG()
}
输出:
文档已成功写入 Qdrant
Model回答:
Qdrant 是一个高性能的向量数据库,常用于 RAG(检索增强生成)系统,因此适合用于构建基于语义搜索、推荐系统、异常检测等需要向量相似性检索的场景。