返回顶部
热门问答 更多热门问答
技术文章 更多技术文章

Golang 基于 Redis 实现文档向量索引与检索系统(RAG)

[复制链接]
链载Ai 显示全部楼层 发表于 3 小时前 |阅读模式 打印 上一主题 下一主题

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;display: table;padding: 0px 0.2em;color: rgb(255, 255, 255);background: rgb(15, 76, 129);">前言

大家好,这里是白泽。这篇文章将讲解如何使用 Redis 的向量检索与 LLM 构建一个 RAG 知识库,知识库存储内容是 Eino 框架的介绍。每次尝试从 Redis 向量索引中获取 top k 条相关信息,并使用 LLM 进行总结回复;当没有相关知识,则提示未查找到文档,限制大模型自由发挥。

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">使用到的技术栈如下:

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">语言:go1.22

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">工作流框架:Eino(字节开源的大模型工作流开发框架)

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">向量存储与检索:Redis

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">大语言模型:doubao-pro-32k-241215

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">向量化模型:doubao-embedding-large-text-240915

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;font-style: normal;padding: 1em;border-radius: 6px;color: rgba(0, 0, 0, 0.5);background: rgb(247, 247, 247);">

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 1em;display: block;letter-spacing: 0.1em;color: rgb(63, 63, 63);">?项目已经开源,地址如下:https://github.com/BaiZe1998/go-learning

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 1em;display: block;letter-spacing: 0.1em;color: rgb(63, 63, 63);">这里说明一下,当前案例中,索引构建阶段的代码取自:https://github.com/cloudwego/eino-examples

系统架构

系统架构回答生成阶段查询检索阶段索引构建阶段Markdown文件文件加载器文档分割器嵌入模型文档向量Redis向量数据库用户问题嵌入模型查询向量KNN向量搜索TopK相关文档提示构建增强提示大语言模型生成回答检索器\nRetrieverRAG系统生成器\nGenerator参数配置\ntopK等

项目运行

  1. 1. docker 启动默认知识库
cd eino_assistant
docker-compose up -d
#通过这种方式启动的 redis 内置了一部分已经完成向量化的 Eino 文档数据
  1. 2. 环境变量设置
#在知识库构建阶段,需要使用到文档向量化的模型
#在检索增强阶段,需要使用语言大模型进行总结回复
cd eino_assistant
source .env
  1. 3. 启动 rag 系统
#使用 redis 作为文档数据库,同时每次检索3条
go run eino/rag/cmd/main.go --redis=true --topk=3
  1. 4. 测试
问题> Agent 是什么

===== 检索到 3 个相关文档 =====

文档[1] 相似度: 0.7705 标题: 无标题
----------------------------------------
## **Agent 是什么**
Agent(智能代理)是一个能够感知环境并采取行动以实现特定目标的系统。在 AI 应用中,Agent 通过结合大语言模型的理解能力和预定义工具的执行能力,可以自主地完成复杂的任务。是未来 AI 应用到生活生产中...

文档[2] 相似度: 0.7606 标题: 无标题
----------------------------------------
## **总结**
介绍了使用 Eino 框架构建 Agent 的基本方法。通过 Chain、Tool Calling 和 ReAct 等不同方式,我们可以根据实际需求灵活地构建 AI Agent。
Agent 是 AI 技术发展的重要方向。它不仅能够理解用户意图,还能主动采取行动,通过�...

文档[3] 相似度: 0.7603 标题: 无标题
----------------------------------------
## **Agent 是什么**
Agent(智能代理)是一个能够感知环境并采取行动以实现特定目标的系统。在 AI 应用中,Agent 通过结合大语言模型的理解能力和预定义工具的执行能力,可以自主地完成复杂的任务。是未来 AI 应用到生活生产中...

==============================


回答:
Agent(智能代理)是一个能够感知环境并采取行动以实现特定目标的系统。在 AI 应用中,Agent 通过结合大语言模型的理解能力和预定义工具的执行能力,可以自主地完成复杂的任务。是未来 AI 应用到生活生产中主要的形态。

本文中示例的代码片段详见:[eino-examples/quickstart/taskagent](https://github.com/cloudwego/eino-examples/blob/master/quickstart/taskagent/main.go)
  1. 5. 提问知识库中不存在的信息
问题> 什么是大数据

===== 检索到 3 个相关文档 =====

文档[1] 相似度: 0.7647 标题: 无标题
----------------------------------------
---
Description: ""
date: "2025-01-07"
lastmod: ""
tags: []
title: Tool
weight: 0
---

文档[2] 相似度: 0.7488 标题: 无标题
----------------------------------------
---
Description: ""
date: "2025-01-06"
lastmod: ""
tags: []
title: Document
weight: 0
---

文档[3] 相似度: 0.7419 标题: 无标题
----------------------------------------
---
Description: ""
date: "2025-01-06"
lastmod: ""
tags: []
title: Embedding
weight: 0
---

==============================


回答:
很抱歉,我不知道什么是大数据,文档中没有提供相关信息。
  1. 6. 在知识库中补充"大数据"相关信息
#在 cmd/knowledgeindexing 目录下新建一个 big_data.md 文档,内容如下:
#大数据
大数据(Big Data)是指规模庞大、结构复杂且无法通过传统数据处理工具在合理时间内进行有效捕捉、管理和处理的数据集合。其核心价值在于通过专业化分析挖掘数据中蕴含的信息,从而提升决策力、优化流程并创造新价值。
  1. 7. 重新生成文档向量,将大数据信息添加到 Redis 索引中
yucong@yucongdeMacBook-Air eino_assistant % cd cmd/knowledgeindexing
yucong@yucongdeMacBook-Air knowledgeindexing % go run ./
[start] indexing file: eino-docs/_index.md
[done] indexing file: eino-docs/_index.md, len of parts: 4
[start] indexing file: eino-docs/agent_llm_with_tools.md
[done] indexing file: eino-docs/agent_llm_with_tools.md, len of parts: 1
[start] indexing file: eino-docs/big_data.md
[done] indexing file: eino-docs/big_data.md, len of parts: 1 # 可以看到被切分了
index success
  1. 8. 再次测试
问题> 什么是大数据

===== 检索到 3 个相关文档 =====

文档[1] 相似度: 0.8913 标题: 大数据
----------------------------------------
#大数据
大数据(Big Data)是指规模庞大、结构复杂且无法通过传统数据处理工具在合理时间内进行有效捕捉、管理和处理的数据集合。其核心价值在于通过专业化分析挖掘数据中蕴含的信息,从而提升决策力、优化流程并创造�...

文档[2] 相似度: 0.7647 标题: 无标题
----------------------------------------
---
Description: ""
date: "2025-01-07"
lastmod: ""
tags: []
title: Tool
weight: 0
---

文档[3] 相似度: 0.7488 标题: 无标题
----------------------------------------
---
Description: ""
date: "2025-01-06"
lastmod: ""
tags: []
title: Document
weight: 0
---

==============================


回答:
大数据(Big Data)是指规模庞大、结构复杂且无法通过传统数据处理工具在合理时间内进行有效捕捉、管理和处理的数据集合。其核心价值在于通过专业化分析挖掘数据中蕴含的信息,从而提升决策力、优化流程并创造新价值。

核心业务流程

索引构建阶段

这一部分参见:eino_assistant/eino/knowledgeindexing 目录代码

流程图:

image-20250505111038955

索引的构建阶段,本质也是一个工作流,因此可以通过 Goland 的 Eino Dev 插件进行可视化绘制,完成之后点击生成流程框架代码,然后填充一些业务实现即可:

image-20250505232622560
  • • 文件加载:从文件系统读取Markdown文档
  • • 文档分割:按标题、段落等逻辑单位将文档分割成小段(根据 # 拆分)
  • • 向量生成:使用嵌入模型,将文本转换为高维向量(4096)
  • • Redis存储:将文档内容、元数据和向量存储到Redis哈希结构中

检索阶段

参见:eino_assistant/eino/rag/retriver.go

  • • 用户输入:用户在终端输入问题
  • • 查询向量化:使用同样的嵌入模型将问题转换为向量
  • • KNN搜索:在Redis中执行KNN(K近邻)向量搜索
  • • 相关文档获取:获取与问题语义最相关的TopK个文档
// Retrieve 检索与查询最相关的文档
func(r *RedisRetriever)Retrieve(ctx context.Context, querystring, topKint) ([]*schema.Document,error) {
// 生成查询向量
queryVectors, err := r.embedder.EmbedStrings(ctx, []string{query})
iferr !=nil{
returnnil, fmt.Errorf("生成查询向量失败: %w", err)
}

iflen(queryVectors) ==0||len(queryVectors[0]) ==0{
returnnil, fmt.Errorf("嵌入模型返回空向量")
}

queryVector := queryVectors[0]

// 构建向量搜索查询
searchQuery := fmt.Sprintf("(*)=>[KNN %d @%s $query_vector AS %s]",
topK,
redispkg.VectorField,
redispkg.DistanceField)

// 执行向量搜索
res, err := r.client.Do(ctx,
"FT.SEARCH", r.indexName,// 执行搜索的索引名称
searchQuery, // 向量搜索查询语句
"PARAMS","2",// 参数声明,后面有2个参数
"query_vector", vectorToBytes(queryVector),// 查询向量的二进制表示
"DIALECT","2",// 查询方言版本
"SORTBY", redispkg.DistanceField,// 结果排序字段
"RETURN","3", redispkg.ContentField, redispkg.MetadataField, redispkg.DistanceField,// 返回字段
).Result()

iferr !=nil{
returnnil, fmt.Errorf("执行向量搜索失败: %w", err)
}

// 将Redis结果转换为Document对象
returnr.parseSearchResults(res)
}

回答生成阶段

参见:eino_assistant/eino/rag/generator.go

  • • 提示构建:将检索到的文档和用户问题组合成增强提示
  • • LLM调用:将增强提示发送给大语言模型(ARK doubao)
  • • 回答生成:模型根据提供的上下文生成针对用户问题的回答
// Generate 生成回答
func(g *ArkGenerator)Generate(ctx context.Context, querystring, documents []*schema.Document) (string,error) {
// 组合上下文信息
context :=""
iflen(documents) >0{
contextParts :=make([]string,len(documents))
fori, doc :=rangedocuments {
// 如果元数据中有标题,添加标题信息
titleInfo :=""
iftitle, ok := doc.MetaData["title"].(string); ok && title !=""{
titleInfo = fmt.Sprintf("标题: %s\n", title)
}
contextParts[i] = fmt.Sprintf("文档片段[%d]:\n%s%s\n", i+1, titleInfo, doc.Content)
}
context = strings.Join(contextParts,"\n---\n")
}

// 构建提示
systemPrompt :="你是一个知识助手。基于提供的文档回答用户问题。如果文档中没有相关信息,请诚实地表明你不知道,不要编造答案。"
userPrompt := query

ifcontext !=""{
userPrompt = fmt.Sprintf("基于以下信息回答我的问题:\n\n%s\n\n问题:%s", context, query)
}

// 构建请求
messages := []chatMessage{
{Role:"system", Content: systemPrompt},
{Role:"user", Content: userPrompt},
}

reqBody := chatRequest{
Model: g.modelName,
Messages: messages,
}

// 序列化请求体
jsonData, err := json.Marshal(reqBody)
iferr !=nil{
return"", fmt.Errorf("序列化请求失败: %w", err)
}

// 创建HTTP请求
endpoint := fmt.Sprintf("%s/chat/completions", g.baseURL)
req, err := http.NewRequestWithContext(ctx,"POST", endpoint, bytes.NewBuffer(jsonData))
iferr !=nil{
return"", fmt.Errorf("创建HTTP请求失败: %w", err)
}

// 添加头信息
req.Header.Set("Content-Type","application/json")
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", g.apiKey))

// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
iferr !=nil{
return"", fmt.Errorf("发送请求失败: %w", err)
}
deferresp.Body.Close()

// 读取响应
body, err := io.ReadAll(resp.Body)
iferr !=nil{
return"", fmt.Errorf("读取响应失败: %w", err)
}

// 检查响应状态
ifresp.StatusCode != http.StatusOK {
return"", fmt.Errorf("API返回错误: %s, 状态码: %d",string(body), resp.StatusCode)
}

// 解析响应
varchatResp chatResponse
iferr := json.Unmarshal(body, &chatResp); err !=nil{
return"", fmt.Errorf("解析响应失败: %w", err)
}

// 提取回答
iflen(chatResp.Choices) >0{
returnchatResp.Choices[0].Message.Content,nil
}

return"", fmt.Errorf("API没有返回有效回答")
}

主循环

funcmain(){
// 定义命令行参数
useRedis := flag.Bool("redis",true,"是否使用Redis进行检索增强")
topK := flag.Int("topk",3,"检索的文档数量")

flag.Parse()

// 检查环境变量
env.MustHasEnvs("ARK_API_KEY")

// 构建RAG系统
ctx := context.Background()
ragSystem, err := rag.BuildRAG(ctx, *useRedis, *topK)
iferr !=nil{
fmt.Fprintf(os.Stderr,"构建RAG系统失败: %v\n", err)
os.Exit(1)
}

// 显示启动信息
if*useRedis {
fmt.Println("启动RAG系统 (使用Redis检索)")
}else{
fmt.Println("启动RAG系统 (不使用检索)")
}
fmt.Println("输入问题或输入'exit'退出")

// 创建输入扫描器
scanner := bufio.NewScanner(os.Stdin)

// 主循环
for{
fmt.Print("\n问题> ")

// 读取用户输入
if!scanner.Scan() {
break
}

input := strings.TrimSpace(scanner.Text())
ifinput ==""{
continue
}

// 检查退出命令
ifstrings.ToLower(input) =="exit"{
break
}

// 处理问题
answer, err := ragSystem.Answer(ctx, input)
iferr !=nil{
fmt.Fprintf(os.Stderr,"处理问题时出错: %v\n", err)
continue
}

// 显示回答
fmt.Println("\n回答:")
fmt.Println(answer)
}

iferr := scanner.Err(); err !=nil{
fmt.Fprintf(os.Stderr,"读取输入时出错: %v\n", err)
}

fmt.Println("再见!")
}

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

链载AI是专业的生成式人工智能教程平台。提供Stable Diffusion、Midjourney AI绘画教程,Suno AI音乐生成指南,以及Runway、Pika等AI视频制作与动画生成实战案例。从提示词编写到参数调整,手把手助您从入门到精通。
  • 官方手机版

  • 微信公众号

  • 商务合作

  • Powered by Discuz! X3.5 | Copyright © 2025-2025. | 链载Ai
  • 桂ICP备2024021734号 | 营业执照 | |广西笔趣文化传媒有限公司|| QQ