RAG 从入门到放弃?丐版 demo 实战笔记(go+python)
RAG(检索增强生成)技术结合了搜索引擎和大语言模型,通过向量数据库检索相关数据并构建提示词,提升大模型在特定业务场景下的回答质量。该技术适用于数据规模大、时效性要求高、多租户隔离等场景。文章提供了一个基于Go的本地化RAG实现Demo,包含权限控制、向量检索和智能问答功能,使用Qdrant向量数据库和Ollama+Llama3/DeepSeek本地模型。核心流程包括个性化查询处理、权限过滤的向量
背景
我当前有一个业务系统,希望能添加一个机器人助手。直接使用大模型,由于缺少相关的业务数据,效果并不理想,了解一下 RAG
。
什么是 RAG
RAG(Retrieval Augmented Generation),搜索引擎 + 大模型。
简单来说就是从一个数据源中先捞出一部分数据,有个前置的筛选操作(通常是向量数据库),然后将搜索出的数据组成 prompt
喂给大模型,最终获取大模型的返回值,进行过滤输出。
什么时候要用到 RAG
其实大多数的业务系统可以不上 RAG
,通常情况下的业务系统都是通过数据库记录数据,很少有需要做数据推理、解释、总结的相关功能,如果真遇到了需要语义匹配(例如:任务表中有任务描述)等刚需场景再考虑上。
- 数据规模大:需要参考的知识库太大,超过了模型上下文限制。
- 时效性与准确性:数据经常更新,每次更新都要重新训练模型,成本就太高了。
- 多租户/版本数据隔离:非公开数据(例:常见的SaaS系统都有用户角色控制权限,数据仅某些角色可见)。
- 长尾:出现频率低、种类多的一些罕见任务或小众需求。
题外话:发现了一个开源库 vanna 可以直接和数据库进行对话。
这个我也测试了一下,其原理简单说就是
- 训练:数据库结构(DDL)、字段说明、示例 SQL 等扔进向量库,建成私有知识库。
- 提问:用自然语言问问题时,系统先检索最相关的上下文,再喂给 LLM 生成可直接执行的 SQL,本地运行并返回结果/图表。整个过程数据不出本地,且每次成功查询会自动回注向量库,持续自我优化。
CODE SHOW
使用 AI
编程简单做了一个小 demo
,github源码
技术架构
核心特性
- 完全本地化:使用本地嵌入模型和LLM,无需依赖外部API
- 权限控制:基于用户角色和单位的多级权限管理
- 向量检索:使用Qdrant向量数据库进行语义搜索
- 智能问答:结合检索到的上下文进行个性化回答
核心实现
RAG服务核心逻辑
// 处理用户查询的核心流程
func (r *RAGService) Query(userID int, question string) (string, error) {
// 1. 获取用户信息和权限
user, err := r.authSvc.GetUserByID(userID)
userAccess := r.getUserAccessContext(user)
// 2. 个性化查询重写
personalizedQuery := r.personalizeQuery(question, user)
// 3. 向量检索(带权限过滤)
apkIDs, contexts, err := r.retrieveAPKs(personalizedQuery, userAccess)
// 4. 构建提示词并生成答案
prompt := r.buildPrompt(user, question, contexts)
return r.generateAnswer(prompt)
}
// 个性化查询处理
func (r *RAGService) personalizeQuery(query string, user *User) string {
if strings.Contains(query, "我") || strings.Contains(query, "我的") {
return fmt.Sprintf("%s 上传者ID:%d", query, user.ID)
}
if strings.Contains(query, "我们单位") {
return fmt.Sprintf("%s 单位ID:%d", query, user.UnitID)
}
return query
}
向量检索与权限过滤
// 带权限过滤的向量检索
func (r *RAGService) retrieveAPKs(query string, userAccess []string) ([]int, []string, error) {
vector, err := r.embeddingSvc.GetEmbedding(query)
// 构建权限过滤器
filter := &qdrant.Filter{
Must: []*qdrant.Condition{{
ConditionOneOf: &qdrant.Condition_Field{
Field: &qdrant.FieldCondition{
Key: "access_scope",
Match: &qdrant.Match{
MatchValue: &qdrant.Match_Keywords{
Keywords: &qdrant.RepeatedStrings{Strings: userAccess},
},
},
},
},
}},
}
// 执行向量搜索
resp, err := pointsClient.Search(ctx, &qdrant.SearchPoints{
CollectionName: "apk_vectors",
Vector: vector,
Filter: filter,
Limit: 3,
})
// 处理搜索结果...
}
本地服务集成
// 嵌入服务调用
func (e *EmbeddingService) GetEmbedding(text string) ([]float32, error) {
requestBody, _ := json.Marshal(map[string][]string{"texts": {text}})
resp, _ := http.Post(e.baseURL+"/embed", "application/json", bytes.NewBuffer(requestBody))
var result struct { Embeddings [][]float32 `json:"embeddings"` }
json.NewDecoder(resp.Body).Decode(&result)
return result.Embeddings[0], nil
}
// LLM服务调用
func (r *RAGService) generateAnswer(prompt string) (string, error) {
requestBody, _ := json.Marshal(map[string]interface{}{
"prompt": prompt,
"model": "deepseek-coder:6.7b",
})
resp, _ := http.Post("http://localhost:5001/generate",
"application/json", bytes.NewBuffer(requestBody))
var result struct { Response string `json:"response"` }
json.NewDecoder(resp.Body).Decode(&result)
return result.Response, nil
}
API接口示例
# 添加APK
curl -X POST http://localhost:8080/apks \
-H "Content-Type: application/json" \
-d '{"name": "支付宝", "uploader_id": 1, "visible_units": [101, 102]}'
# 智能问答
curl -X POST http://localhost:8080/query \
-H "Content-Type: application/json" \
-d '{"user_id": 1, "question": "我在哪天上传了支付宝?"}'
快速启动
# 1. 启动依赖服务
docker run -p 6333:6333 -p 6334:6334 qdrant/qdrant
docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=password -e MYSQL_DATABASE=apk_rag -d mysql:latest
# 2. 启动本地模型服务
ollama serve && ollama pull llama3
python local_embedding.py &
python local_llm.py &
# 3. 启动Go服务
go run .
技术栈
组件 | 技术选型 | 作用 |
---|---|---|
后端服务 | Go + Gin | API服务和业务逻辑 |
嵌入模型 | BGE-M3 | 文本向量化 |
LLM服务 | Ollama + Llama3/DeepSeek | 文本生成 |
向量数据库 | Qdrant | 向量存储和检索 |
关系数据库 | MySQL | 结构化数据存储 |
实际做 RAG 开发中的一些感悟
其实大多数业务系统是不需要使用 RAG
的,先搞清楚自己到底要不要上 RAG
。
上述 demo
极为简单,是丐版,离真正的生产使用还差了好远。如果真的考虑做一个 RAG
系统,可以考虑考虑以下问题(实际生产中的问题更多):
RAG
有一步是数据向量化,是不是可以不用向量化,我直接通过elastic search
之类的服务做存储,然后搜出来数据,自己组装prompt
丢给大模型。向量化有什么作用?- 什么是
embedding
? - 向量存储方案选型?
- 模型怎么选,选哪个?
- 文档怎么切?
- 如何同当前系统进行结合?
- 输出结果不理想怎么办,如何调优?
- 如何去评估
RAG
的效果好不好? - 拒答阈值怎么定?
- 生产级加固,成本和延迟,可观测性?
最难的点还是在于如何精准的搜索到最相关的上下文。
总结
实际生产中,首先得再问一下,是否真的有必要上 RAG
。
大模型的 RAG
入门并不难,难的是各种细节的调整(数据处理等)。
撸了一个丐版的 RAG
小 demo
,向量化 → 近似召回 → Prompt 拼装 → 大模型生成。
最后提一嘴,最后调用大模型的参数量越大越好,上下文长度越长越好。
参考
更多推荐
所有评论(0)