一个完整的 Go 版 RAG 示例,基于 go-zero + Milvus + OpenAI,包含:
- 知识库集合初始化
- 文档切分与向量化入库
- 语义检索
- 基于检索上下文的大模型回答
- Go 1.24+
- Milvus,默认地址
127.0.0.1:19530 - OpenAI API Key
编辑 etc/rag-demo-api.yaml:
- 设置
Milvus.Address - 设置
OpenAI.ApiKey - 如有需要调整
OpenAI.ChatModel、OpenAI.EmbeddingModel - 如有需要调整
RAG.CollectionName、ChunkSize、ChunkOverlap
go run ./rag-demo.go -f etc/rag-demo-api.yaml服务默认监听 http://127.0.0.1:8888。
curl -X POST http://127.0.0.1:8888/api/knowledge/init \
-H "Content-Type: application/json" \
-d '{"dropIfExists":true}'curl -X POST http://127.0.0.1:8888/api/knowledge/ingest \
-H "Content-Type: application/json" \
-d '{
"documents":[
{
"id":"doc-rag",
"title":"RAG 简介",
"source":"internal-docs/rag-intro.md",
"content":"RAG 是 Retrieval-Augmented Generation 的缩写。它先从知识库检索相关文档,再把命中的上下文交给大模型生成回答。这样可以降低幻觉并引入私有知识。"
},
{
"id":"doc-milvus",
"title":"Milvus 用途",
"source":"internal-docs/milvus.md",
"content":"Milvus 是向量数据库,适合存储 embedding 并执行相似度检索。在 RAG 里,它通常承担召回相关知识块的职责。"
},
{
"id":"doc-pipeline",
"title":"典型流程",
"source":"internal-docs/pipeline.md",
"content":"一个典型的 RAG 流程包括文档切分、embedding、向量入库、用户提问、向量检索、上下文拼接,以及最终回答生成。"
}
]
}'curl -X POST http://127.0.0.1:8888/api/rag/retrieve \
-H "Content-Type: application/json" \
-d '{
"query":"Milvus 在 RAG 里面负责什么?",
"topK":3
}'curl -X POST http://127.0.0.1:8888/api/rag/answer \
-H "Content-Type: application/json" \
-d '{
"query":"请解释一个完整的 RAG 流程",
"topK":4
}'下面这张图对应“用户发起一次问答请求后,代码实际会怎么走”。
sequenceDiagram
participant U as "User / Client"
participant H as "AnswerHandler"
participant L as "AnswerLogic"
participant R as "rag.Service"
participant E as "embedding.Service"
participant M as "Milvus"
participant G as "llm.Service"
participant O as "OpenAI"
U->>H: POST /api/rag/answer {query, topK, minScore, maxTokens}
Note over H: 解析 HTTP JSON 请求<br/>生成 types.AnswerReq
H->>L: Answer(req)
Note over L: 把 API 参数整理成 rag.SearchOptions
L->>R: Answer(query, searchOptions, maxTokens)
Note over R: 先执行检索,再执行生成
R->>R: Retrieve(query, searchOptions)
R->>E: EmbedText(query)
E->>O: 调用 Embeddings API<br/>把问题转成向量
O-->>E: query vector
E-->>R: []float32
R->>M: Search(vector, topK)<br/>在 vector 字段做相似度搜索
M-->>R: topK hits(doc_id, title, content, score...)
alt 没有命中上下文
R-->>L: 返回“根据当前知识库内容,我还不能确定”
else 命中相关 chunk
R->>R: buildAnswerPrompt(query, hits)
Note over R: 把“用户问题 + 命中 chunk”拼成 prompt
R->>G: GenerateAnswer(instructions, prompt, maxTokens)
G->>O: 调用 Responses API<br/>生成最终回答
O-->>G: answer text
G-->>R: answer
R-->>L: AnswerResult{answer, hits, model}
end
L-->>H: CommonResp
H-->>U: JSON 响应
对应代码入口可以按这条链路阅读:
internal/handler/answerhandler.gointernal/logic/answerlogic.gointernal/rag/service.go的Answerinternal/rag/service.go的Retrieveinternal/embedding/embedding.gointernal/llm/generator.go
- 文档会按字符窗口切分,并保留重叠内容,避免上下文在 chunk 边界断裂。
- Milvus 集合固定包含
doc_id、doc_title、chunk_id、source、content、vector。 answer接口会先检索,再将命中的 chunk 拼成提示词,要求模型只依据知识库回答。- 如果没有检索到足够相关的内容,服务会直接返回“根据当前知识库内容,我还不能确定”。
go test ./...
go build ./...