前言

在AI大模型行业中,我越来越深刻地感受到一个趋势:大模型本身正在迅速商品化,而真正拉开差距的,是“如何让模型在具体业务场景中稳定、精准、高效地工作” 。这恰恰是“大模型微调与RAG落地工程师”这个岗位存在的意义——它不只是一个技术Title,更是一套完整的方法论:从模型微调让模型“懂行”,到RAG让模型“有据可依”,再到推理优化让系统“扛得住生产流量”。

这篇文章,我想从微调、RAG、推理部署三个维度,聊一聊这个岗位究竟在做什么,以及怎么做。

一、微调:让通用模型“懂行”

1.1 为什么需要微调?

通用大模型(如Qwen、DeepSeek)虽然能力强大,但在垂直领域往往表现不佳。比如让模型回答“某公司现金流为负是否一定代表经营恶化”,它可能给出泛泛而谈的答案。原因很简单:模型的预训练数据里没有你所在行业的专有知识

微调的核心目标,就是在特定任务数据集上更新模型参数,最小化任务损失函数。但全参数微调对算力要求极高——一个7B模型全参数微调需要几十GB显存。于是,参数高效微调(PEFT) 成为工业界的主流选择。

1.2 LoRA:只加不改

LoRA(Low-Rank Adaptation)的核心思想是:冻结原模型参数,在每一层中插入低秩矩阵进行训练

通俗理解就是:给模型发一本“行业知识小手册”,不用改写模型已有的知识,只记住手册里的内容

以下是基于Qwen-7B-Chat进行LoRA微调的核心代码:

from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model, TaskType
import torch

# 加载基座模型
model_name = "Qwen/Qwen-7B-Chat"
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.bfloat16,
    device_map="auto",
    trust_remote_code=True
)

# 配置LoRA
lora_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,
    r=8,  # 低秩矩阵的秩
    lora_alpha=32,
    lora_dropout=0.1,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],  # 作用的目标模块
)

# 应用LoRA
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()  # 查看可训练参数量

1.3 QLoRA:让普通显卡也能微调大模型

QLoRA在LoRA的基础上更进一步:先对基座模型做4-bit量化,再叠加LoRA微调。QLoRA = 4-bit量化基座 + 全精度LoRA适配器。量化后模型显存占用可下降约65%,7B模型在单张RTX 4090(24GB)上即可运行。

from transformers import BitsAndBytesConfig

# 4-bit量化配置
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_use_double_quant=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16
)

# 加载量化模型
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    quantization_config=bnb_config,
    device_map="auto",
    trust_remote_code=True
)
# 然后叠加LoRA配置(同上)

1.4 数据准备:指令微调的核心

微调数据的格式通常为 {instruction, input, output} 三元组。instruction是任务指令,input是用户输入,output是期望输出。针对金融场景,可以构造如下数据:

train_data = [
    {
        "instruction": "你是一位金融分析师,请基于专业知识回答以下问题。",
        "input": "什么是净息差?它对银行盈利有什么影响?",
        "output": "净息差是银行生息资产收益率与付息负债成本率之差,直接反映银行核心信贷业务的盈利能力。"
    }
]

二、RAG:让模型“有据可依”

如果说微调解决的是模型“懂不懂行”的问题,那RAG解决的就是模型“说话有没有依据”的问题。

2.1 RAG的核心流程

RAG(Retrieval-Augmented Generation)的核心流程是:用户提问 → 从知识库检索相关文档 → 将检索结果作为上下文输入模型 → 模型生成回答。这就像让学生开卷考试——不是靠死记硬背,而是基于提供的权威材料做推理。

2.2 基于LangChain实现RAG

LangChain是实现RAG的“工具箱”,提供了文档加载器、文本分割器、向量数据库、提示模板等全套组件。

from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
from langchain.chains import RetrievalQA
from langchain.llms import HuggingFacePipeline

# 1. 加载文档
loader = PyPDFLoader("./knowledge_base.pdf")
documents = loader.load()

# 2. 文本分割
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50
)
texts = text_splitter.split_documents(documents)

# 3. 向量化与存储
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-large-zh")
vectorstore = FAISS.from_documents(texts, embeddings)

# 4. 构建RAG检索链
qa_chain = RetrievalQA.from_chain_type(
    llm=model,  # 微调后的模型
    chain_type="stuff",
    retriever=vectorstore.as_retriever(search_kwargs={"k": 3})
)

# 5. 问答
response = qa_chain.run("公司年假政策是什么?")

2.3 对话式RAG:LangGraph的进阶方案

传统RAG只是简单的“检索-生成”流程,但在对话场景下会遇到几个棘手的问题:用户问题经常模糊不清或是追问;检索到的文档可能压根不相关。

LangGraph 通过状态化流程编排,让RAG系统具备了处理多轮对话、自适应查询优化的能力。它可以做到:把问题改写成独立完整的形式,判断问题是否属于可回答范畴,检索相关文档并评估质量,没找到合适文档时自动优化查询重新检索。

from langgraph.graph import StateGraph, END
from typing import TypedDict, List

class RAGState(TypedDict):
    question: str
    chat_history: List[dict]
    retrieved_docs: List[str]
    answer: str
    retry_count: int

def retrieve(state: RAGState):
    # 向量检索
    docs = vectorstore.similarity_search(state["question"], k=5)
    return {"retrieved_docs": docs, "retry_count": state.get("retry_count", 0)}

def evaluate_relevance(state: RAGState):
    # 评估检索结果相关性
    if not state["retrieved_docs"] or len(state["retrieved_docs"]) < 2:
        return "rewrite"  # 相关性不足,触发查询改写
    return "generate"

def rewrite_query(state: RAGState):
    # 基于对话历史改写查询
    state["retry_count"] += 1
    if state["retry_count"] > 3:
        return {"answer": "抱歉,未找到相关信息"}
    # 重新检索...
    return state

# 构建状态图
graph = StateGraph(RAGState)
graph.add_node("retrieve", retrieve)
graph.add_node("rewrite", rewrite_query)
graph.add_node("generate", generate_answer)
graph.add_conditional_edges("retrieve", evaluate_relevance)

三、RAG检索优化:召回率从78%到92%

3.1 混合检索

纯向量检索虽然能捕捉语义,但容易漏掉精确的关键词匹配。混合检索(Hybrid Search)将向量检索(语义)和BM25检索(关键词)结合起来,通过加权融合提升召回率。

在一个项目中做了实测:纯向量检索召回率约83%,加入混合检索后提升到88%。这是性价比最高的优化——不需要换模型,不需要重新训练,改几行代码就能看到效果

from langchain.retrievers import EnsembleRetriever
from langchain.retrievers import BM25Retriever

# 向量检索器
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 10})

# BM25关键词检索器
bm25_retriever = BM25Retriever.from_documents(texts)
bm25_retriever.k = 10

# 混合检索(加权融合)
ensemble_retriever = EnsembleRetriever(
    retrievers=[bm25_retriever, vector_retriever],
    weights=[0.3, 0.7]  # 关键词检索权重0.3,向量检索权重0.7
)

3.2 重排序(Rerank)

检索回来的Top-K文档中,往往只有前1-2个是真正相关的。重排序使用Cross-Encoder模型对候选文档重新打分排序,把最相关的文档排在最前面。

from sentence_transformers import CrossEncoder

reranker = CrossEncoder('BAAI/bge-reranker-large')

def rerank_docs(query, docs, top_k=3):
    pairs = [[query, doc.page_content] for doc in docs]
    scores = reranker.predict(pairs)
    sorted_docs = sorted(zip(docs, scores), key=lambda x: x[1], reverse=True)
    return [doc for doc, _ in sorted_docs[:top_k]]

3.3 文档切分的艺术

文档切分chunk_size和chunk_overlap的选择直接影响检索质量。chunk太大会混入噪声,chunk太小会丢失上下文。我的经验是:chunk_size=500-800,chunk_overlap=50-100,同时要根据文档结构(章节、段落)做语义化切分,而非简单按字符数硬切。

四、推理部署:让模型扛住生产流量

4.1 vLLM:高性能推理引擎

模型微调好了,RAG流程跑通了,但如果在生产环境响应太慢、吞吐太低,一切等于零

vLLM是目前最主流的LLM推理引擎之一,通过 PagedAttention 技术实现高效的显存管理和请求批处理。它通过智能合批和调度,在保证吞吐的同时保持较低的首Token延迟(TTFT)。

# 使用vLLM部署微调后的模型
vllm serve /path/to/fine-tuned-model \
    --tensor-parallel-size 2 \
    --max-model-len 4096 \
    --gpu-memory-utilization 0.9 \
    --port 8000

4.2 性能指标:TTFT与TPOT

在生产环境中,我们需要持续关注两个核心指标:

  • TTFT(Time To First Token) :用户发出请求到收到第一个Token的时间。影响用户体验的第一印象。
  • TPOT(Time Per Output Token) :生成每个后续Token的平均耗时。影响整体响应速度。

vLLM的 PD分离架构(Prefill-Decode Disaggregation)将预填充(Prefill)和解码(Decode)分配到不同的GPU实例上,针对各自特性进行专门优化,从而同时优化TTFT和TPOT。

4.3 量化部署

对于私有化部署场景,模型量化是必经之路。4-bit NF4量化后,7B模型显存占用从14.2GB降至5.1GB,推理延迟从48ms/token升至62ms/token,BLEU-4仅下降0.7。用不到7%的精度损失,换来65%的显存节省——这笔账在大多数场景下都是划算的。

# 使用llama.cpp进行GGUF量化部署
# 先使用convert.py将模型转换为GGUF格式
# 然后使用llama.cpp运行
./llama-cli -m model-q4_K_M.gguf -p "用户问题" -n 512

五、微调 + RAG:1+1 > 2

微调和RAG不是二选一的关系,而是互补关系

  • 微调让模型具备领域知识和特定风格,解决“懂不懂行”的问题。
  • RAG让模型能实时引用最新外部知识,解决“知识过时”和“无据可依”的问题。

在项目中,通常采用 “微调模型做基座 + RAG做知识增强” 的组合方案。先通过LoRA微调让模型理解行业术语和业务逻辑,再通过RAG让模型在回答时引用最新的政策文件、产品手册等动态知识。

六、总结

对于 AI大模型的兴起,我最大的感悟是:大模型开发不是“调参侠”的游戏,而是一套系统工程

  • 微调考验的是对数据和任务的理解——知道什么时候该用LoRA、什么时候该用QLoRA、数据该怎么构造。
  • RAG考验的是对检索系统的把控——文档怎么切、向量怎么存、检索怎么优化、多轮对话怎么处理。
  • 推理部署考验的是工程化能力——vLLM怎么配置、TTFT和TPOT怎么优化、量化方案怎么选。

这三个环节环环相扣,任何一个环节掉链子,整个系统都无法在生产环境稳定运行。而“大模型微调与RAG落地工程师”的价值,恰恰在于能把这整条链路打通——从模型微调让模型“懂行”,到RAG让模型“有据可依”,再到推理优化让系统“扛得住流量”

希望这篇文章能给正在这条路上探索的同行一些启发。技术日新月异,但解决问题的底层逻辑始终相通——理解业务、尊重数据、敬畏工程。共勉。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐