Skip to content

Golang RAG 快速上手

本章节将带领大家使用 Golang 快速构建一个最小化的 RAG 系统。我们将使用 langchaingo 库来简化开发流程。

前置条件

  • Go 1.21+
  • OpenAI API Key (或其他兼容 LLM 的 Key)

1. 初始化项目

创建一个新的 Go 项目并安装必要的依赖:

bash
mkdir go-rag-demo
cd go-rag-demo
go mod init go-rag-demo

# 安装 langchaingo
go get github.com/tmc/langchaingo

2. 核心代码实现

创建一个 main.go 文件,实现以下逻辑:

  1. 加载文档:这里简单模拟一些文本数据。
  2. 切分文本:将文本切分为 chunk。
  3. Embedding:使用 OpenAI Embedding 模型。
  4. 向量存储:使用内存向量库(方便演示)。
  5. 检索与问答:构建 Chain 进行问答。
go
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/tmc/langchaingo/chains"
	"github.com/tmc/langchaingo/documentloaders"
	"github.com/tmc/langchaingo/embeddings"
	"github.com/tmc/langchaingo/llms/openai"
	"github.com/tmc/langchaingo/schema"
	"github.com/tmc/langchaingo/textsplitter"
	"github.com/tmc/langchaingo/vectorstores"
	"github.com/tmc/langchaingo/vectorstores/memory"
)

func main() {
	// 0. 设置环境变量 (请替换为你的真实 Key)
	os.Setenv("OPENAI_API_KEY", "sk-xxxxxxxxxxxxxxxxxxxxxxxx")
	os.Setenv("OPENAI_API_BASE", "https://api.openai.com/v1") // 如果用代理

	ctx := context.Background()

	// 1. 准备 LLM 和 Embedding 模型
	llm, err := openai.New()
	if err != nil {
		log.Fatal(err)
	}

	embedder, err := embeddings.NewEmbedder(llm)
	if err != nil {
		log.Fatal(err)
	}

	// 2. 加载数据 (模拟从文件加载)
	f, err := os.Open("test_data.txt")
	if err != nil {
		// 如果文件不存在,创建一个模拟文件
		content := "Golang 是一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。它由 Google 的 Robert Griesemer, Rob Pike 和 Ken Thompson 开发,于 2009 年 11 月正式开源。Go 语言语法与 C 相近,但功能上有:内存安全,GC(垃圾回收),结构形态及 CSP-style 并发计算。"
		doc := schema.Document{PageContent: content}
		runRAG(ctx, llm, embedder, []schema.Document{doc})
		return
	}
	defer f.Close()
	
	loader := documentloaders.NewText(f)
	docs, err := loader.Load(ctx)
	if err != nil {
		log.Fatal(err)
	}

	runRAG(ctx, llm, embedder, docs)
}

func runRAG(ctx context.Context, llm *openai.LLM, embedder *embeddings.EmbedderImpl, docs []schema.Document) {
	// 3. 文本切分
	splitter := textsplitter.NewRecursiveCharacter()
	splitter.ChunkSize = 100 // 演示用,设小一点
	splitter.ChunkOverlap = 20
	
	splitDocs, err := textsplitter.SplitDocuments(splitter, docs)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("切分出了 %d 个文档片段\n", len(splitDocs))

	// 4. 创建向量存储 (Memory) 并索引文档
	store := memory.NewStore()
	_, err = vectorstores.New(
		ctx, 
		store, 
		embedder, 
		vectorstores.WithDocuments(splitDocs, nil),
	)
	if err != nil {
		log.Fatal(err)
	}

	// 5. 创建检索问答链 (RetrievalQA Chain)
	// 使用 StuffQA 模式:将检索到的所有文档塞入 Prompt
	qaChain := chains.NewRetrievalQAFromLLM(
		llm,
		vectorstores.ToRetriever(store, 3), // 检索 Top 3
	)

	// 6. 提问
	query := "Go 语言是谁开发的?"
	fmt.Printf("\n用户提问: %s\n", query)
	
	answer, err := chains.Call(ctx, qaChain, map[string]any{
		"query": query,
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("AI 回答: %s\n", answer["text"])
}

3. 代码解析

核心组件

  • DocumentLoader: 负责将不同格式的数据源(如 TXT, PDF, HTML)统一转换为 schema.Document 对象。
  • TextSplitter: 负责将长文档切分为适合 LLM 上下文窗口的小块(Chunk)。
  • Embedder: 调用 Embedding 模型将文本转化为向量。
  • VectorStore: 向量数据库接口,这里使用了 memory (内存实现),生产环境通常会替换为 Milvus 或 Weaviate。
  • RetrievalQA Chain: LangChain 提供的标准链,自动完成 "Query -> Retrieval -> Prompt -> LLM -> Answer" 的全过程。

4. 运行结果

运行上述代码,你将看到类似如下输出:

text
切分出了 3 个文档片段

用户提问: Go 语言是谁开发的?
AI 回答: Go 语言是由 Google 的 Robert Griesemer, Rob Pike 和 Ken Thompson 开发的。

这就是一个最基础的 RAG 系统!接下来我们将深入每一个环节,探讨如何构建生产级的 RAG 应用。

🚀 学习遇到瓶颈?想进大厂?

看完这篇技术文章,如果还是觉得不够系统,或者想在实战中快速提升?
王中阳的就业陪跑训练营,提供定制化学习路线 + 企业级实战项目 + 简历优化 + 模拟面试。

了解训练营详情