本地化部署大模型-RAG 叠加

RAG =「先查资料,再写答案」的 AI 工作模式。

它把「检索系统」和「大模型」结合起来,让模型不用全靠记忆,而是实时查找外部知识再生成回答,从而更准确、更可信。为什么建议在本地化的大模型中增加RAG叠加,就是这样原因。在Deepseek使用过程中,也会有联网查询这一个功能,如果本地化部署没有增加数据库训练和RAG叠加,后续的回答只能基于原来训练的内容进行回答,会导致回答空洞、无法接入企业/个人知识,没有达到本地化部署、细分领域专用的效果。

RAG的工作流程可以总结如下:

核心结构:用户问题 → 检索器(Vector DB / BM25)→ 文本片段 → 组装 Prompt → 大模型 → 回答

目前RAG的检索方式主要为以下三种:

举个简单的例子:

问题:“写一个R函数,按组计算均值和95%CI。”

传统模型 → 可能瞎写,也可能不是很准确,存在一定的误差

RAG → 检索数据库中已有的 R 代码片段(如 dplyr / broom / summarise 函数示例)

然后拼接 Prompt:

参考以下代码片段:
[1] file/path/stats_utils.R#3
#' 计算均值和置信区间
calc_ci <- function(vec, conf=0.95) { ... }
现在回答问题:
写一个R函数,按组计算 mean 和 95% CI(输入 dataframe 和 group 列)

→ 模型输出会更加准确 + 风格一致 + 易维护;特别是个人使用时,越用越贴近自己的风格(前提是提供RAG的内容是个人的代码或者个人风格的内容)。

接下来举例LLM-Coder,代码助手的RAG叠加的操作:

流程:语料收集 → 清洗切块 → 向量化(+BM25)→ 检索 → 交叉重排 → 拼接上下文 → 本地小模型生成

第 1 步:准备语料(代码 + 文档)

1.拉你要用的 GitHub 仓库(或拷贝企业内部仓)

2.只保留:.py、.R、.ipynb(抽代码单元)、.md、.Rmd、设计文档 / 指南 PDF(可先转文本)

3.去重:同内容哈希;过滤:dist/ build/ .ipynb_checkpoints/ 之类生成物

代码分块原则(很重要):按“函数/类/文件内标题”切块,而不是盲目固定长度;每块附文件路径+起止行号元数据;R 的 roxygen2 注释、Python 的 docstring 作为上下文优先保留;

第 2 步:创建本地向量库(FAISS)

安装必要的库:

pip install sentence-transformers faiss-cpu rank_bm25 unstructured pypdf

接下来使用python脚本进行创建:

# build_index.py
import os, json, re, faiss
from pathlib import Path
from rank_bm25 import BM25Okapi
from sentence_transformers import SentenceTransformer
from tqdm import tqdm
SRC_DIR = "corpus"          # 你整理好的语料根目录
IDX_DIR = "rag_index"       # 索引输出目录
EMB_NAME = "BAAI/bge-small-zh-v1.5"  # 中文场景;英文用 bge-small-en-v1.5
os.makedirs(IDX_DIR, exist_ok=True)
# -------- 1) 收集并“按函数/段落”切块 --------
def split_code_blocks(text, lang):
# 极简切块:按函数/类/roxygen 标题等;你可换成更强的 AST 切块器
if lang == "python":
pat = r"(?m)^(def |class )"
elif lang == "r":
pat = r"(?m)^(#'|[a-zA-Z0-9_]+\s*<-function)"
else:
pat = r"(?m)^#{1,6}\s|^$"
chunks, buf = [], []
for line in text.splitlines():
if re.search(pat, line) and buf:
chunks.append("\n".join(buf)); buf=[line]
else:
buf.append(line)
if buf: chunks.append("\n".join(buf))
# 限长再切
out=[]
for c in chunks:
if len(c) < 2000:
out.append(c)
else:
for i in range(0, len(c), 1500):
out.append(c[i:i+1800])
return out
def walk_corpus():
docs=[]
for p in Path(SRC_DIR).rglob("*"):
if p.suffix.lower() in [".py",".r",".md",".rmd",".txt"]:
txt = p.read_text(errors="ignore", encoding="utf-8", newline=None)
lang = "python" if p.suffix==".py" else ("r" if p.suffix==".r" else "md")
blocks = split_code_blocks(txt, lang)
for bi, b in enumerate(blocks):
docs.append({
"text": b.strip(),
"meta": {"path": str(p), "block_id": bi, "lang": lang}
})
return docs
docs = walk_corpus()
with open(f"{IDX_DIR}/corpus.jsonl","w",encoding="utf-8") as f:
for d in docs: f.write(json.dumps(d,ensure_ascii=False)+"\n")
print("chunks:", len(docs))
# -------- 2) 向量化 + FAISS --------
model = SentenceTransformer(EMB_NAME)  # 默认走 CPU,可用 .to("cuda")(显存允许)
embs = model.encode([d["text"] for d in docs], batch_size=64, show_progress_bar=True, normalize_embeddings=True)
d = embs.shape[1]
index = faiss.IndexFlatIP(d)           # 归一化后用内积=余弦
index.add(embs)
faiss.write_index(index, f"{IDX_DIR}/faiss.index")
with open(f"{IDX_DIR}/meta.json","w",encoding="utf-8") as f:
json.dump({"emb_model":EMB_NAME, "count":len(docs)}, f, ensure_ascii=False, indent=2)
# -------- 3) BM25(可选混合检索)--------
bm25 = BM25Okapi([d["text"].split() for d in docs])
import pickle; pickle.dump({"bm25":bm25, "docs":docs}, open(f"{IDX_DIR}/bm25.pkl","wb"))
print("done.")

第 3 步:检索 + 重排 + 调用本地 LLM

先装一个轻量重排器(可关掉以换速度):

pip install cross-encoder

查询脚本(支持向量检索 + 可选 BM25 混合 + 重排):

# query_rag.py
import json, faiss, numpy as np, pickle
from sentence_transformers import SentenceTransformer, CrossEncoder
from pathlib import Path
import requests
IDX_DIR = "rag_index"
OLLAMA_URL = "http://localhost:11434/api/generate"   # 先启动 ollama
meta = json.load(open(f"{IDX_DIR}/meta.json","r",encoding="utf-8"))
docs = [json.loads(l) for l in open(f"{IDX_DIR}/corpus.jsonl","r",encoding="utf-8")]
index = faiss.read_index(f"{IDX_DIR}/faiss.index")
emb_model = SentenceTransformer(meta["emb_model"])
try:
bm25_pack = pickle.load(open(f"{IDX_DIR}/bm25.pkl","rb"))
bm25, bm_docs = bm25_pack["bm25"], bm25_pack["docs"]
except:
bm25=None
# 可选重排(CPU 也能跑,慢时可关闭)
try:
reranker = CrossEncoder("BAAI/bge-reranker-base")
except:
reranker = None
def retrieve(query, topk=6, mix_bm25=True):
qv = emb_model.encode([query], normalize_embeddings=True)
D,I = index.search(qv, topk)
cand = [docs[i] for i in I[0]]
if mix_bm25 and bm25:
bm = bm25.get_top_n(query.split(), bm_docs, n=topk)
cand = cand + bm
# 重排
if reranker:
pairs = [(query, c["text"]) for c in cand]
scores = reranker.predict(pairs)
ranked = [c for _,c in sorted(zip(scores,cand), key=lambda x:-x[0])]
return ranked[:topk]
return cand[:topk]
PROMPT = """你是代码助手。请仅依据“参考上下文”,用{lang}给出解决方案,并简短解释。
问题:{question}
参考上下文(可能不完整,请择要引用文件路径):
{context}
要求:
1) 先给核心代码;2) 再解释关键点;3) 如引用到文件,请标注 path#blockid。
"""
def ask_llm(model_tag, prompt):
resp = requests.post(OLLAMA_URL, json={"model": model_tag, "prompt": prompt, "stream": False})
return resp.json()["response"]
if __name__=="__main__":
question = "用R写一个函数:分组计算均值和95%CI,并返回整洁表(tidy)。"
lang = "R"
cands = retrieve(question, topk=8, mix_bm25=True)
ctx = "\n\n---\n".join([f"[{i}] {c['meta']['path']}#b{c['meta']['block_id']}\n{c['text'][:1200]}" for i,c in enumerate(cands)])
prompt = PROMPT.format(lang=lang, question=question, context=ctx)
print(ask_llm("deepseek-r1:1.5b", prompt))

先起 LLM:

# (个人电脑安装的1.5B模型)CMD输入:
ollama pull deepseek-r1:1.5b
ollama run  deepseek-r1:1.5b

第 4 步:提高检索高命中

  • Hybrid 检索:向量 + BM25 混合(上面已示范);代码类问题对关键字很敏感
  • 代码块切分更智能:有余力的话用 AST/语法树分块器(按函数、类、导出对象)
  • 去噪与权重:README、示例/测试、roxygen/docstring 给予更高“候选优先级”
  • 重排器可选:小型交叉编码器离线即可;嫌慢就把重排改成轻量(如 Cosine + Heuristic)
  • Prompt 模板固定:始终要求“先代码、后解释、引用路径”,可极大降低幻觉
  • 缓存:对 embeddings 和检索结果做本地缓存(SQLite/磁盘),多次问答更快

第 5 步:评估与迭代

  • 小题集:自建 50–200 条“问题→期望代码/断言”的集合(Python 用 pytest,R 用 testthat
  • 三类指标
  1. 检索命中率(是否找到包含答案的块)
  2. 重排质量(相关块是否排在前 3)
  3. 执行通过率(生成代码跑单测是否通过)
  • 闭环:错误样本加入“补充语料/FAQ/模板代码” → 重新嵌入 → 索引刷新

最近这几年,经济形式下行,IT行业面临经济周期波动与AI产业结构调整的双重压力,很多人都迫于无奈,要么被裁,要么被降薪苦不堪言。但我想说的是一个行业下行那必然会有上行行业,目前AI大模型的趋势就很不错,大家应该也经常听说大模型,也知道这是趋势,但苦于没有入门的契机,现在他来了,我在本平台找到了一个非常适合新手学习大模型的资源。大家想学习和了解大模型的,可以**点击这里前往查看**

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐