基于 Eino 框架从零搭建一个 RAG

发布时间:2026/7/18 16:44:12
基于 Eino 框架从零搭建一个 RAG 基于 CloudWeGo Eino 从零搭建 Go 语言 RAG 检索增强系统前言大语言模型LLM在落地企业场景时普遍面临知识时效性差、专业领域内容缺失、生成幻觉等问题。检索增强生成RAG通过「先检索外部知识库、再基于参考内容生成答案」的思路低成本且高效地解决了上述痛点已成为构建企业级 AI 应用的核心技术方案。CloudWeGo 推出的Eino是一款 Go 生态的大模型应用开发框架它对主流大模型服务、向量数据库、文档处理工具做了统一抽象与封装开发者无需对接各类异构 SDK仅通过几行代码即可组装出完整的 RAG 链路。本文将基于 Eino 框架从组件拆解到完整落地一步步搭建一个可运行的 RAG 知识库系统。一、RAG 核心组件拆解在 Eino 的设计中一个完整的 RAG 系统由以下核心组件构成各司其职且可灵活插拔组件核心作用通俗类比ChatModel与大语言模型直接交互负责最终答案生成答题的「考生」ChatTemplate结构化 Prompt 模板统一管理系统提示、上下文、检索结果标准化答题卡 答题规则Embedding将文本转换为高维向量是语义检索的基础把文字翻译成「向量语言」Indexer将文档向量化后存入向量数据库建立索引整理并装订「小抄本」Retriever根据用户问题从向量库召回最相关的文档片段从「小抄本」里翻找对应知识点Transformer文档预处理分割、过滤、格式转换、合并等把长文档裁成适合翻阅的小片段Tool大模型可调用的外部工具扩展能力考生可以使用的计算器、词典整条 RAG 的核心链路为原始文档 → Transformer 切片 → Embedding 向量化 → Indexer 入库 → 用户提问 → Retriever 检索 → ChatTemplate 组装 Prompt → ChatModel 生成答案二、前置准备2.1 环境与依赖Go 1.21 及以上版本已部署的 Milvus 向量数据库本文使用 Milvus 2.x 独立部署火山方舟字节跳动大模型平台API Key用于调用豆包大模型与 Embedding 服务2.2 项目初始化创建项目并安装核心依赖mkdirEino_framecdEino_frame go mod init Eino_frame# 安装 Eino 核心与扩展包go get github.com/cloudwego/eino go get github.com/cloudwego/eino-ext go get github.com/milvus-io/milvus-sdk-go/v2 go get github.com/joho/godotenv2.3 环境变量配置在项目根目录创建.env文件统一管理密钥与模型配置# .env ARK_API_KEYyour_ark_api_key_here MODELdoubao-pro-32k # 对话模型名称 EMBEDDERdoubao-embedding-text-240515 # 向量嵌入模型名称2.4 项目结构最终项目目录结构如下Eino_frame/ ├── main.go # 主程序入口串联全流程 ├── .env # 环境变量 ├── RAG_introduction.md # 测试知识库文档 └── rag/ ├── milvus_client.go # Milvus 客户端初始化 ├── embedding.go # 向量嵌入组件 ├── transformer.go # 文档分割转换组件 ├── indexer.go # 索引存储组件 ├── retriever.go # 检索组件 ├── chat_model.go # 大模型对话组件 └── prompt.go # ChatTemplate 提示词模板三、核心组件代码实现我们将所有 RAG 能力封装在rag包下保证职责单一、可复用。3.1 Milvus 客户端初始化所有向量操作都依赖 Milvus 客户端做全局单例初始化。rag/milvus_client.gopackageragimport(contextgithub.com/milvus-io/milvus-sdk-go/v2/clientlog)// MilvusCli 全局 Milvus 客户端实例varMilvusCli client.Client// InitMilvusClient 初始化 Milvus 连接funcInitMilvusClient(){ctx:context.Background()cli,err:client.NewClient(ctx,client.Config{Address:39.105.40.22:19530,// Milvus 服务地址DBName:EinoFrame,// 目标数据库名})iferr!nil{log.Fatalf(初始化 Milvus 客户端失败: %v,err)}MilvusClicli log.Println(Milvus 客户端初始化成功)} 注意正式创建 Indexer 前必须确保 Milvus 服务已启动且对应数据库已提前创建。3.2 Embedding 文本向量化组件Embedding 负责将文本转换为高维向量是实现语义相似度匹配的基础。这里我们使用火山方舟的 Embedding 服务。rag/embedding.gopackageragimport(contextgithub.com/cloudwego/eino-ext/components/embedding/arkoslog)// NewArkEmbedding 创建火山方舟向量嵌入器funcNewArkEmbedding(ctx context.Context)*ark.Embedder{apiType:ark.APITypeMultiModal embedder,err:ark.NewEmbedder(ctx,ark.EmbeddingConfig{APIKey:os.Getenv(ARK_API_KEY),Model:os.Getenv(EMBEDDER),APIType:apiType,})iferr!nil{log.Printf(创建 Embedding 失败: %v,err)returnnil}returnembedder}3.3 Transformer 文档分割组件长文档无法直接向量化和精准检索需要先按语义切分为多个小块。Eino 内置了 Markdown 标题分割器非常适合技术文档、博客等结构化内容。rag/transformer.gopackageragimport(contextgithub.com/cloudwego/eino-ext/components/document/transformer/splitter/markdowngithub.com/cloudwego/eino/components/documentlog)// NewArkTransformer 创建 Markdown 标题分割器// 按 #、##、### 标题层级切割文档保留标题结构funcNewArkTransformer(ctx context.Context)document.Transformer{splitter,err:markdown.NewHeaderSplitter(ctx,markdown.HeaderConfig{Headers:map[string]string{#:h1,##:h2,###:h3,},TrimHeaders:false,// 保留标题在片段内容中})iferr!nil{log.Printf(创建文档分割器失败: %v,err)returnnil}returnsplitter}3.4 Indexer 索引存储组件Indexer 是「做小抄」的核心组件它自动调用 Embedding 将文档片段向量化然后写入 Milvus 集合中。⚠️ 重要正式创建 Indexer 之前一定要先定义好集合的字段结构字段类型、维度必须与 Embedding 输出严格匹配。rag/indexer.gopackageragimport(contextgithub.com/cloudwego/eino-ext/components/embedding/arkgithub.com/cloudwego/eino-ext/components/indexer/milvusgithub.com/milvus-io/milvus-sdk-go/v2/entitylog)// NewArkIndexer 创建 Milvus 索引器funcNewArkIndexer(ctx context.Context,embedder*ark.Embedder)*milvus.Indexer{collectionName:test// 定义集合字段结构fields:[]*entity.Field{{Name:id,DataType:entity.FieldTypeVarChar,TypeParams:map[string]string{max_length:255},PrimaryKey:true,},{Name:vector,// 向量字段名需与 Retriever 配置一致DataType:entity.FieldTypeFloatVector,// 普通浮点向量二值向量使用 BinaryVectorTypeParams:map[string]string{dim:1536},// 维度必须与 Embedding 模型输出匹配},{Name:content,DataType:entity.FieldTypeVarChar,TypeParams:map[string]string{max_length:8192},},{Name:metadata,DataType:entity.FieldTypeJSON,},}indexer,err:milvus.NewIndexer(ctx,milvus.IndexerConfig{Client:MilvusCli,Collection:collectionName,Fields:fields,Embedding:embedder,// 绑定嵌入器入库时自动完成向量化})iferr!nil{log.Printf(创建 Indexer 失败: %v,err)returnnil}log.Println(Indexer 创建成功)returnindexer}3.5 Retriever 检索组件Retriever 是「翻小抄」的组件将用户问题向量化后在 Milvus 中做相似度搜索召回最相关的 Top K 个文档片段。rag/retriever.gopackageragimport(contextgithub.com/cloudwego/eino-ext/components/embedding/arkgithub.com/cloudwego/eino-ext/components/retriever/milvuslog)// NewArkRetriever 创建 Milvus 检索器funcNewArkRetriever(ctx context.Context,embedder*ark.Embedder)*milvus.Retriever{retriever,err:milvus.NewRetriever(ctx,milvus.RetrieverConfig{Client:MilvusCli,Collection:test,VectorField:vector,// 指定用于相似度计算的向量字段OutputFields:[]string{// 检索返回的字段id,content,metadata,},TopK:3,// 召回最相关的 3 个片段Embedding:embedder,})iferr!nil{log.Printf(创建 Retriever 失败: %v,err)returnnil}returnretriever}3.6 ChatModel 对话模型组件ChatModel 封装了与大模型的交互逻辑负责基于组装好的 Prompt 生成最终答案。rag/chat_model.gopackageragimport(contextgithub.com/cloudwego/eino-ext/components/model/arkoslog)// NewArkModel 创建火山方舟对话模型funcNewArkModel(ctx context.Context)*ark.ChatModel{model,err:ark.NewChatModel(ctx,ark.ChatModelConfig{APIKey:os.Getenv(ARK_API_KEY),Model:os.Getenv(MODEL),})iferr!nil{log.Printf(创建 ChatModel 失败: %v,err)returnnil}returnmodel}3.7 ChatTemplate 提示词模板ChatTemplate 用于结构化地管理和组装 Prompt。我们将检索到的知识库内容注入到系统提示词中约束模型仅基于参考内容回答从源头减少幻觉。新增rag/prompt.gopackageragimport(github.com/cloudwego/eino/components/promptgithub.com/cloudwego/eino/schema)// BuildRAGPrompt 构建 RAG 对话模板// 将检索到的参考资料注入 system prompt随用户问题一起喂给大模型funcBuildRAGPrompt()prompt.ChatTemplate{systemPrompt:你是一个专业的知识问答助手请严格基于下面的参考资料回答用户的问题。 如果参考资料中没有相关信息请如实回答暂未找到相关资料不要编造内容。 参考资料 {{.references}}returnprompt.FromMessages(schema.SystemMessage(systemPrompt),schema.UserMessage({{.question}}),).WithVariables(references,question)}四、完整 RAG 流程串联现在我们在main.go中把所有组件串起来实现两个核心流程文档入库加载 Markdown 文档 → 分割 → 向量化 → 存入 Milvus检索问答接收用户问题 → 检索相关片段 → 组装 Prompt → 调用大模型生成答案main.gopackagemainimport(Eino_frame/ragcontextfmtgithub.com/cloudwego/eino/schemagithub.com/joho/godotenvlogosstrconvstrings)funcmain(){ctx:context.Background()// 加载环境变量全局加载一次即可err:godotenv.Load(.env)iferr!nil{log.Fatalf(加载 .env 文件失败: %v,err)}// 1. 初始化 Milvus 客户端rag.InitMilvusClient()// 2. 初始化所有基础组件embedder:rag.NewArkEmbedding(ctx)indexer:rag.NewArkIndexer(ctx,embedder)retriever:rag.NewArkRetriever(ctx,embedder)splitter:rag.NewArkTransformer(ctx)chatModel:rag.NewArkModel(ctx)promptTemplate:rag.BuildRAGPrompt()// 流程一文档入库 log.Println( 开始文档入库 )// 读取本地 Markdown 知识库文档bs,err:os.ReadFile(./RAG_introduction.md)iferr!nil{log.Fatalf(读取文档失败: %v,err)}// 构造 Eino 标准文档对象docs:[]*schema.Document{{ID:doc_rag_intro,Content:string(bs),},}// 按标题分割文档splitDocs,err:splitter.Transform(ctx,docs)iferr!nil{log.Fatalf(文档分割失败: %v,err)}log.Printf(文档分割完成共 %d 个片段,len(splitDocs))// 为每个片段设置唯一 IDfori,doc:rangesplitDocs{doc.IDdocs[0].ID_chunk_strconv.Itoa(i)}// 自动向量化并写入 Milvusids,err:indexer.Store(ctx,splitDocs)iferr!nil{log.Fatalf(文档入库失败: %v,err)}log.Printf(文档入库成功ID 列表: %v,ids)// 流程二RAG 检索问答 log.Println(\n 开始 RAG 问答 )userQuestion:RAG 技术主要解决了大语言模型的哪些问题log.Printf(用户问题: %s,userQuestion)// 1. 从向量库检索相关文档片段results,err:retriever.Retrieve(ctx,userQuestion)iferr!nil{log.Fatalf(检索失败: %v,err)}log.Printf(检索到 %d 条相关片段,len(results))// 2. 拼接参考资料文本varrefBuilder strings.Builderfori,res:rangeresults{refBuilder.WriteString(fmt.Sprintf([片段%d]\n%s\n\n,i1,res.Content))}references:refBuilder.String()// 3. 用模板格式化最终 Promptmessages,err:promptTemplate.Format(ctx,map[string]any{references:references,question:userQuestion,})iferr!nil{log.Fatalf(格式化 Prompt 失败: %v,err)}// 4. 调用大模型生成答案resp,err:chatModel.Generate(ctx,messages)iferr!nil{log.Fatalf(调用模型失败: %v,err)}log.Println(\n 最终答案 )fmt.Println(resp.Content)}五、运行与验证在项目根目录准备测试文档RAG_introduction.md写入 RAG 相关的技术介绍内容执行主程序go run main.go预期输出顺序Milvus 客户端初始化成功文档分割完成输出片段数量文档入库成功输出所有片段 ID输出检索到的相关片段数量打印大模型基于参考资料生成的最终答案六、优化与扩展方向增量入库与去重生产环境增加文档指纹校验避免重复写入支持批量入库提升性能检索后重排序Rerank在向量粗召回后增加重排序模型进一步提升检索准确率多格式文档支持接入 PDF、Word、网页、PPT 等格式的 Transformer扩展知识库来源多轮对话记忆增加会话上下文管理实现带历史记忆的 RAG 对话工具调用能力基于 Eino 的 Tool 组件让大模型可调用数据库、外部 API 等工具七、总结Eino 框架通过统一的组件抽象极大降低了 Go 语言下 RAG 系统的开发门槛。开发者只需关注业务逻辑本身无需花费大量精力对接不同厂商的 SDK、处理向量数据库的底层细节。从文档切片到最终生成答案整条链路清晰可扩展非常适合快速落地企业内部知识库、智能客服、技术问答等场景。