在实际的大模型应用开发中,我们经常面临一个核心选择:如何让一个通用的大语言模型(LLM)具备特定领域的知识或遵循特定的回答风格?检索增强生成(RAG)和模型微调(Fine-tuning)是两种主流且互补的技术路径。很多开发者初次接触时,容易混淆两者的适用场景,导致项目初期选型错误,后期维护成本高昂。RAG 通过外挂知识库,在不改动模型本身的情况下,为模型提供实时、精确的外部信息;而微调则是通过额外的训练,直接修改模型的“大脑”(权重参数),使其内化特定的知识或风格。
本文将深入对比 RAG 与微调,从原理、实现、成本、维护性等多个维度进行剖析,并结合一个金融问答机器人的项目案例,展示如何在实际开发中结合使用这两种技术。无论你是希望快速构建一个基于私有知识的问答系统,还是需要定制一个具有特定行业术语和对话风格的 AI 助手,理解这两种技术的差异与结合点都至关重要。
1. 理解 RAG 与微调的核心差异与工作原理
在深入技术实现之前,必须从概念上厘清 RAG 和微调各自解决了什么问题,以及它们是如何工作的。这是后续技术选型和架构设计的基础。
1.1 什么是检索增强生成(RAG)?
RAG 的核心思想是“按需查询,即时补充”。你可以把它想象成一个拥有超强记忆力和快速查阅能力的研究员。当研究员(LLM)被问到一个问题时,他不会仅凭自己的常识回答,而是会立刻去翻阅一个庞大的、定制化的资料库(向量数据库),找到与问题最相关的几份资料,然后结合这些资料和自己的知识,生成最终答案。
技术定义:RAG 是一种架构模式,它将信息检索组件与文本生成模型相结合。其工作流程通常分为三步:检索(Retrieval)、增强(Augmentation)和生成(Generation)。
- 检索:将用户查询(Query)转化为向量表示,然后在向量数据库中进行相似性搜索,找出最相关的文本片段(Chunks)。
- 增强:将检索到的相关文本片段与原始用户查询组合,形成一个包含上下文信息的“增强提示”(Augmented Prompt)。
- 生成:将这个增强提示提交给 LLM,LLM 基于提供的上下文生成最终回答。
关键优势:
- 知识实时性:只需更新向量数据库中的文档,模型就能获取最新信息,无需重新训练。
- 答案可追溯:可以要求模型在回答中引用来源,提高可信度和可审计性。
- 成本相对较低:不修改基础模型,主要成本在于构建检索管道和向量数据库的维护。
- 避免幻觉:通过提供确切的上下文,极大减少了模型“胡编乱造”的可能性。
典型场景:企业知识库问答、法律条文查询、产品手册咨询等需要基于大量、可能频繁更新的结构化或非结构化文档进行回答的场景。
1.2 什么是模型微调(Fine-tuning)?
微调的核心思想是“重塑思维,内化能力”。这好比让一位通才(基础模型)去接受某个领域的专业培训。经过培训后,这位通才不仅掌握了该领域的专业知识,其思考问题和表达方式也会更贴近该领域的专家。
技术定义:微调是指在一个大规模预训练模型的基础上,使用一个较小的、特定领域的数据集进行额外的训练,以调整模型的权重参数,使其更擅长完成特定任务或适应特定领域。
主要类型:
- 全参数微调:更新模型的所有参数。效果通常最好,但计算成本和数据需求极高。
- 参数高效微调:如 LoRA、QLoRA,只训练一小部分新增的参数(适配器),而冻结原始模型的大部分参数。能以极小的成本获得接近全参数微调的效果,是目前的主流选择。
关键优势:
- 风格与格式控制:可以训练模型输出特定格式(如 JSON、SQL)、特定风格(如客服话术、正式报告)的内容。
- 任务泛化能力:让模型学会一种新的任务范式,而不仅仅是记忆知识。例如,训练模型理解“将这段文字改写成小红书风格”的指令。
- 减少提示词长度:通过微调,模型能更好地理解简短指令,无需在每次提问时都提供冗长的上下文和示例。
- 响应速度:由于知识已内化,推理时无需额外的检索步骤,响应更快。
典型场景:让模型学会写特定风格的代码、生成符合公司品牌的营销文案、将自然语言指令转换为 API 调用等需要改变模型“行为模式”的场景。
1.3 RAG 与微调对比速查表
为了更直观地进行选型,可以参考下表:
| 特性维度 | 检索增强生成 | 模型微调 |
|---|---|---|
| 核心目标 | 为模型提供外部、精确、可更新的知识。 | 改变模型的内在能力、风格或任务范式。 |
| 知识更新 | 即时,通过更新向量数据库即可。 | 滞后,需要收集新数据并重新训练模型。 |
| 答案可解释性 | 高,可提供引用来源。 | 低,模型基于内化参数生成,过程如同黑盒。 |
| 计算成本 | 低(推理时),主要成本在检索和向量化。 | 高(训练时),尤其是全参数微调。 |
| 数据需求 | 需要高质量的原始文档,无需标注。 | 需要高质量的指令-输出对(SFT)或偏好数据(RLHF)。 |
| 实现复杂度 | 中等,涉及文档处理、向量化、检索等管道。 | 高,涉及数据准备、训练流程、超参调优等。 |
| 适用场景 | 基于文档的问答、事实查询、实时信息获取。 | 风格迁移、复杂指令跟随、新任务学习、减少提示工程。 |
| 技术代表 | LangChain, LlamaIndex, Chroma, Pinecone | LoRA, QLoRA, PPO, DeepSpeed, Hugging Face Transformers |
2. 环境准备与核心工具栈选择
在开始构建项目前,需要搭建一个稳定的开发环境,并选择合适的技术栈。以下配置基于一个常见的 Python 开发环境。
2.1 基础环境与 Python 包管理
建议使用 Python 3.10 或 3.11,这两个版本在生态兼容性和稳定性上表现最好。使用虚拟环境隔离项目依赖。
# 创建并激活虚拟环境 (Linux/macOS) python3 -m venv venv source venv/bin/activate # 创建并激活虚拟环境 (Windows) python -m venv venv venv\Scripts\activate2.2 核心依赖安装
我们将使用pip安装项目所需的核心库。这里区分了 RAG 相关和微调相关的依赖。
# 升级 pip 和 setuptools pip install --upgrade pip setuptools # 1. 大模型基础与 API 调用 pip install openai # 如需调用 OpenAI API # 国内常用模型,以 Qwen 为例 pip install transformers torch # Hugging Face 模型库 pip install modelscope # 魔搭社区,方便下载国内模型 # 2. RAG 框架与工具链 pip install langchain langchain-community # LangChain 核心 pip install llama-index # LlamaIndex,另一个流行的 RAG 框架 pip install sentence-transformers # 用于生成文本向量的嵌入模型 pip install chromadb # 轻量级本地向量数据库 # pip install faiss-cpu # 高性能向量检索库 (可选) # 3. Web 服务与工具 pip install fastapi uvicorn # 构建 API 服务 pip install pydantic # 数据验证 # 4. 微调相关 (根据需求选择安装) pip install peft accelerate datasets # PEFT (LoRA), 加速训练,数据集处理 pip install trl # Transformer Reinforcement Learning,用于 RLHF pip install bitsandbytes # 用于 4-bit 量化训练 (QLoRA)2.3 模型与工具选择建议
在实际项目中,模型和工具的选择需要权衡效果、成本、部署难度和合规要求。
大语言模型选择:
- 云端 API:OpenAI GPT-4/3.5、Anthropic Claude、国内大厂 API。优点:省心,性能强。缺点:持续付费,数据出境需合规。
- 本地开源模型:Qwen、ChatGLM、Llama、Yi 等。优点:数据可控,可微调。缺点:需要一定的 GPU 资源,效果可能略逊于顶级闭源模型。对于入门和内部场景,Qwen-7B/14B 是优秀的选择。
向量数据库选择:
- 轻量级/本地:ChromaDB、FAISS。适合快速原型、中小规模数据、单机部署。
- 生产级/分布式:Pinecone、Weaviate、Milvus、Qdrant。支持高可用、持久化、多副本,适合企业级应用。
RAG 框架选择:
- LangChain:模块化程度高,生态丰富,学习曲线稍陡,适合构建复杂、定制化的 AI 应用链。
- LlamaIndex:专注于数据连接和检索,对 RAG 流程封装更彻底,上手简单,适合快速构建基于文档的问答系统。
微调方法选择:
- 全参数微调:除非有海量数据和计算资源,否则不推荐。
- LoRA/QLoRA:当前微调开源模型的事实标准,能以极低的显存开销达到接近全参数微调的效果。
3. 项目实战:构建金融知识问答机器人
我们将以一个“金融大模型问答机器人”项目为例,演示如何结合 RAG 和微调。项目目标:机器人能准确回答关于公司内部金融产品手册的问题(RAG),并且能用专业、严谨、符合公司规范的客服语气进行回答(微调)。
3.1 项目架构设计
项目采用分层架构,核心流程如下:
用户提问 | v [FastAPI Web 层] - 接收请求,返回响应 | v [业务逻辑层] - 协调 RAG 检索与 LLM 生成 | | |---> [RAG 模块] --->| 从向量库检索相关产品文档 | | v v [LLM 核心] <--- (增强提示:问题 + 检索上下文) | v 生成最终回答技术栈:
- LLM:Qwen-7B-Chat (本地部署)
- RAG 框架:LangChain + LlamaIndex (用于文档加载)
- 向量数据库:ChromaDB (本地)
- Web 框架:FastAPI
- 微调方法:LoRA (用于风格微调)
3.2 阶段一:实现基础 RAG 问答功能
首先,我们实现不经过微调的基础 RAG 功能,确保知识检索的管道是通的。
步骤 1:准备知识库文档假设我们有一个financial_docs/目录,里面存放着 PDF、Word 或 TXT 格式的金融产品说明书。
步骤 2:文档加载、切分与向量化创建rag_pipeline.py:
import os from langchain_community.document_loaders import DirectoryLoader, TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import Chroma from langchain_community.llms import HuggingFacePipeline from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline import torch # 1. 加载文档 documents_path = "./financial_docs" loader = DirectoryLoader(documents_path, glob="**/*.txt", loader_cls=TextLoader) # 按需更换 loader documents = loader.load() # 2. 分割文本为小块 text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, # 每个块的大小 chunk_overlap=50, # 块之间的重叠,避免上下文断裂 separators=["\n\n", "\n", "。", ";", ",", " ", ""] ) texts = text_splitter.split_documents(documents) print(f"已将文档切分为 {len(texts)} 个文本块。") # 3. 创建嵌入模型(用于将文本转为向量) # 使用一个轻量且效果好的开源模型 embed_model = HuggingFaceEmbeddings( model_name="BAAI/bge-small-zh-v1.5", # 中文嵌入模型 model_kwargs={'device': 'cpu'}, # 无 GPU 可用 'cpu' encode_kwargs={'normalize_embeddings': True} ) # 4. 创建向量数据库并持久化 vector_store_path = "./chroma_db_finance" vectordb = Chroma.from_documents( documents=texts, embedding=embed_model, persist_directory=vector_store_path ) vectordb.persist() print(f"向量数据库已创建并保存至 {vector_store_path}") # 5. 加载本地 LLM (Qwen-7B-Chat) 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.float16, # 半精度节省显存 device_map="auto", # 自动分配 GPU/CPU trust_remote_code=True ) pipe = pipeline( "text-generation", model=model, tokenizer=tokenizer, max_new_tokens=512, temperature=0.1, # 低温度使输出更确定 do_sample=True, ) llm = HuggingFacePipeline(pipeline=pipe) # 6. 构建检索问答链 from langchain.chains import RetrievalQA from langchain.prompts import PromptTemplate # 自定义提示模板,指导模型基于上下文回答 prompt_template = """你是一个专业的金融客服助手。请严格根据以下提供的上下文信息来回答问题。如果上下文信息不足以回答问题,请直接说“根据现有资料,我无法回答这个问题”,不要编造信息。 上下文: {context} 问题:{question} 请根据上下文提供专业、准确的回答:""" PROMPT = PromptTemplate( template=prompt_template, input_variables=["context", "question"] ) # 创建检索器 retriever = vectordb.as_retriever(search_kwargs={"k": 3}) # 检索最相关的3个片段 # 创建问答链 qa_chain = RetrievalQA.from_chain_type( llm=llm, chain_type="stuff", # 简单地将所有检索到的上下文塞进提示词 retriever=retriever, chain_type_kwargs={"prompt": PROMPT}, return_source_documents=True # 返回来源文档 ) # 7. 测试问答 query = "请问贵行的‘稳健增长’理财产品的起购金额是多少?" result = qa_chain.invoke({"query": query}) print("问题:", query) print("回答:", result["result"]) print("\n来源文档:") for i, doc in enumerate(result["source_documents"]): print(f"[{i+1}] {doc.page_content[:200]}...") # 打印前200字符运行此脚本,它会完成从文档处理到向量化存储,再到问答测试的完整流程。这是 RAG 的核心骨架。
3.3 阶段二:使用 LoRA 对模型进行风格微调
基础 RAG 能提供准确信息,但回答可能不够“专业”或“像客服”。接下来,我们使用 LoRA 微调 Qwen 模型,使其输出风格更符合金融客服的要求。
步骤 1:准备微调数据集微调需要指令-输出对数据。我们准备一个finetune_data.jsonl文件,每行是一个 JSON 对象。
{"instruction": "用户询问理财产品风险", "input": "", "output": "尊敬的客户,您好。理财产品的风险等级在产品说明书中均有明确标注,通常分为R1(谨慎型)到R5(激进型)五个等级。投资前请您务必仔细阅读相关条款,并根据自身的风险承受能力进行选择。我行客户经理也可为您提供详细解读。"} {"instruction": "用户抱怨转账未到账", "input": "", "output": "非常理解您焦急的心情。转账延迟可能由收款行处理、节假日或系统清算等原因导致。请您提供转账凭证编号,我将立即为您查询具体进度。同时建议您通过手机银行‘转账记录’功能实时跟踪状态。"} {"instruction": "根据上下文回答问题", "input": "上下文:'稳盈三年期'产品年化收益率区间为3.5%-4.2%。\n问题:该产品收益保底吗?", "output": "根据产品资料显示,‘稳盈三年期’产品的收益率是一个预期区间,并非承诺的保底收益。理财非存款,产品有风险,过往业绩不代表未来表现,请您知悉。"}数据应涵盖各种客服场景,并体现专业、严谨、有礼貌的语气。
步骤 2:编写 LoRA 微调脚本创建finetune_lora.py:
from datasets import load_dataset from transformers import ( AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer, DataCollatorForSeq2Seq, ) from peft import LoraConfig, get_peft_model, TaskType import torch # 1. 加载模型和分词器 model_name = "Qwen/Qwen-7B-Chat" tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True) # 设置 padding token if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype=torch.float16, device_map="auto", trust_remote_code=True ) # 2. 配置 LoRA lora_config = LoraConfig( task_type=TaskType.CAUSAL_LM, # 因果语言模型任务 r=8, # LoRA 的秩,影响参数量,通常 8, 16, 32 lora_alpha=32, # 缩放参数 lora_dropout=0.1, target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], # 针对 Qwen 的注意力模块 bias="none", ) # 将原模型转换为 PEFT 模型 model = get_peft_model(model, lora_config) model.print_trainable_parameters() # 打印可训练参数量,应该只占原模型很小一部分 # 3. 加载并预处理数据集 def preprocess_function(examples): # 构建模型的输入格式: `instruction + input + output` # 这里使用 Qwen 的聊天格式 texts = [] for ins, inp, out in zip(examples['instruction'], examples['input'], examples['output']): message = [ {"role": "system", "content": "你是一个专业、严谨、有礼貌的金融客服助手。"}, {"role": "user", "content": f"{ins}\n{inp}".strip()}, {"role": "assistant", "content": out} ] text = tokenizer.apply_chat_template(message, tokenize=False) texts.append(text) return tokenizer(texts, truncation=True, padding=True, max_length=512) dataset = load_dataset('json', data_files='finetune_data.jsonl', split='train') tokenized_dataset = dataset.map(preprocess_function, batched=True) # 4. 设置训练参数 training_args = TrainingArguments( output_dir="./qwen-7b-chat-finance-lora", per_device_train_batch_size=2, # 根据 GPU 内存调整 gradient_accumulation_steps=4, num_train_epochs=3, logging_steps=10, save_steps=100, learning_rate=2e-4, fp16=True, # 使用混合精度训练 remove_unused_columns=False, push_to_hub=False, # 如需上传到 Hugging Face Hub ) # 5. 创建 Trainer 并开始训练 trainer = Trainer( model=model, args=training_args, train_dataset=tokenized_dataset, data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True), ) trainer.train() trainer.save_model() # 保存 LoRA 权重 tokenizer.save_pretrained(training_args.output_dir) print(f"微调完成,模型保存在 {training_args.output_dir}")步骤 3:加载微调后的模型进行推理训练完成后,加载基础模型和 LoRA 适配器进行推理。
from peft import PeftModel # 加载基础模型 base_model = AutoModelForCausalLM.from_pretrained( "Qwen/Qwen-7B-Chat", torch_dtype=torch.float16, device_map="auto", trust_remote_code=True ) # 加载 LoRA 权重 model = PeftModel.from_pretrained(base_model, "./qwen-7b-chat-finance-lora") model = model.merge_and_unload() # 可选:将 LoRA 权重合并回原模型,加速推理 tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen-7B-Chat", trust_remote_code=True) # 使用微调后的模型进行生成 def generate_response(instruction, input_text=""): messages = [ {"role": "system", "content": "你是一个专业、严谨、有礼貌的金融客服助手。"}, {"role": "user", "content": f"{instruction}\n{input_text}".strip()} ] text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) inputs = tokenizer(text, return_tensors="pt").to(model.device) outputs = model.generate(**inputs, max_new_tokens=200, temperature=0.7) response = tokenizer.decode(outputs[0][inputs['input_ids'].shape[1]:], skip_special_tokens=True) return response # 测试 print(generate_response("用户询问理财产品风险"))3.4 阶段三:集成 RAG 与微调模型
最后,我们将微调后的模型集成到 RAG 管道中,替换掉原来的基础模型。
# 在 rag_pipeline.py 中,修改 LLM 加载部分 from peft import PeftModel # ... [之前的文档加载、向量化代码不变] ... # 加载基础模型和 LoRA 适配器 base_model = AutoModelForCausalLM.from_pretrained( "Qwen/Qwen-7B-Chat", torch_dtype=torch.float16, device_map="auto", trust_remote_code=True ) # 假设我们使用合并后的模型,或者直接加载 PEFT 模型 # 方式一:加载合并后的模型(推理更快) model = AutoModelForCausalLM.from_pretrained("./qwen-7b-chat-finance-merged") # 方式二:加载基础模型 + 分离的 LoRA 权重(更灵活) # model = PeftModel.from_pretrained(base_model, "./qwen-7b-chat-finance-lora") tokenizer = AutoTokenizer.from_pretrained("./qwen-7b-chat-finance-merged", trust_remote_code=True) pipe = pipeline( "text-generation", model=model, tokenizer=tokenizer, max_new_tokens=512, temperature=0.1, do_sample=True, ) llm = HuggingFacePipeline(pipeline=pipe) # ... [后续创建 RetrievalQA 链的代码不变] ...现在,这个问答机器人既能够从金融产品手册中检索精确信息(RAG),又能以专业客服的口吻进行回答(微调)。
4. 部署与 API 服务化
为了提供稳定的服务,我们使用 FastAPI 将上述功能封装成 HTTP API。
创建main.py:
from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import List, Optional # 导入之前封装好的 RAG 问答链 qa_chain # from rag_pipeline import qa_chain app = FastAPI(title="金融知识问答机器人 API") class QueryRequest(BaseModel): question: str top_k: Optional[int] = 3 # 检索文档数量 class QueryResponse(BaseModel): answer: str sources: List[str] # 简化显示来源 # 假设 qa_chain 已全局初始化 # qa_chain = initialize_qa_chain() @app.post("/ask", response_model=QueryResponse) async def ask_question(request: QueryRequest): try: result = qa_chain.invoke({"query": request.question}) # 处理来源文档 source_list = [doc.page_content[:150] + "..." for doc in result.get("source_documents", [])] return QueryResponse(answer=result["result"], sources=source_list) except Exception as e: raise HTTPException(status_code=500, detail=f"处理请求时出错: {str(e)}") @app.get("/health") async def health_check(): return {"status": "healthy"} if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)使用命令python main.py启动服务,即可通过http://localhost:8000/ask接口进行问答。
5. 常见问题排查与优化
在实际开发和运行中,你可能会遇到以下问题:
5.1 RAG 相关问题
| 问题现象 | 可能原因 | 检查与解决 |
|---|---|---|
| 回答与文档内容无关(幻觉) | 1. 检索到的文档不相关。 2. 提示词未强制模型基于上下文回答。 3. 上下文长度超限,被截断。 | 1. 检查向量搜索的相似度阈值 (score_threshold)。2. 强化提示词,如“必须基于以下上下文”。 3. 调整文本切分策略 ( chunk_size) 或使用map_reduce等复杂链类型。 |
| 检索速度慢 | 1. 向量数据库未使用索引。 2. 嵌入模型太大或设备慢。 3. 文档块太多。 | 1. Chroma/FAISS 默认创建索引,确认配置。 2. 换用更小的嵌入模型(如 BAAI/bge-small)。3. 优化 chunk_size,避免过多小块。 |
| 无法回答最新文档内容 | 1. 向量数据库未更新。 2. 文档预处理(如元数据)有问题。 | 1. 实现一个文档更新流程,定期或触发式重建向量库。 2. 检查文档加载器是否正确处理了文件格式和编码。 |
5.2 微调相关问题
| 问题现象 | 可能原因 | 检查与解决 |
|---|---|---|
| 训练损失不下降 | 1. 学习率过高或过低。 2. 数据量太少或质量差。 3. LoRA 参数 r太小。 | 1. 尝试经典学习率如2e-4,1e-4。2. 确保数据是指令-输出对,且输出是期望风格。 3. 逐步增加 r(如 8->16)。 |
| 模型输出乱码或重复 | 1. 生成参数temperature太低。2. 训练数据存在大量重复。 3. 模型过拟合。 | 1. 适当提高temperature(如 0.7)。2. 清洗数据,去重。 3. 减少训练轮次 ( num_train_epochs),增加数据。 |
| 微调后模型“遗忘”通用知识 | 1. 微调数据领域过于狭窄。 2. 使用了全参数微调且数据不足。 | 1. 在数据集中混合少量通用问答数据。 2.优先使用 LoRA 等 PEFT 方法,能极大缓解灾难性遗忘。 |
5.3 部署与性能问题
| 问题现象 | 可能原因 | 检查与解决 |
|---|---|---|
| GPU 内存不足 (OOM) | 1. 模型太大。 2. 未使用量化或 device_map。 | 1. 换用更小模型(如 Qwen-1.8B)。 2. 使用 bitsandbytes进行 4/8-bit 量化加载。3. 使用 vLLM或TGI进行高效推理部署。 |
| API 响应延迟高 | 1. 每次请求都加载模型。 2. RAG 检索慢。 3. 未使用缓存。 | 1. 确保模型在服务启动时一次性加载。 2. 对常见问题建立答案缓存(如 Redis)。 3. 异步处理耗时操作。 |
6. 生产环境最佳实践与扩展方向
将项目从原型推向生产,需要考虑更多因素。
6.1 安全与合规
- 数据脱敏:在将金融文档入库前,需自动或手动脱敏客户姓名、身份证号、银行卡号等敏感信息。
- 访问控制:API 服务需增加认证(如 API Key、JWT)和授权机制。
- 审计日志:记录所有用户问答记录、来源文档,满足合规审查要求。
- 内容过滤:在模型输入输出层增加敏感词过滤,防止生成不当内容。
6.2 性能与可观测性
- 异步处理:对于耗时的文档解析和向量化,使用 Celery 或 Dramatiq 等任务队列异步执行。
- 监控指标:监控 API 响应时间、错误率、GPU 使用率、向量数据库连接数等。
- 链路追踪:集成 OpenTelemetry,追踪一个用户请求在 RAG 检索、LLM 生成等各阶段的耗时。
- 分级缓存:对高频通用问题(如“营业时间”)的答案进行内存缓存,对向量检索结果进行磁盘或 Redis 缓存。
6.3 架构扩展
- 混合检索:结合关键词检索(如 BM25)和向量检索,提升召回率。
- 重排序:检索出 Top K 个文档后,使用一个更精细的交叉编码器模型对结果进行重排序,提升精度。
- Agentic RAG:引入智能体(Agent)概念,让系统能判断何时检索、何时调用工具(如计算器、搜索API)、何时直接回答,处理更复杂的多轮问答。
- 图增强 RAG:对于高度关联的知识(如金融产品关系、公司股权结构),使用图数据库(如 Neo4j)存储关系,利用 GraphRAG 技术进行更深度的推理。
6.4 持续迭代
- 评估体系:建立 RAG 系统评估体系,包括答案相关性、事实准确性、信息完整性等维度,定期用测试集评估。
- 反馈闭环:设计用户对回答的“点赞/点踩”机制,将“踩”的数据收集起来,作为后续优化微调数据集或修正知识库的依据。
- 自动化管道:建立 CI/CD 管道,当知识库文档更新时,自动触发向量库的更新和模型的重新评估。
通过以上步骤,你不仅能够构建一个可用的金融问答机器人,更能建立起一套应对真实业务场景、可持续运维和迭代的大模型应用开发方法论。RAG 与微调不是二选一的关系,而是相辅相成的利器。理解其原理,掌握其工具链,并在项目中灵活运用,是当前大模型应用开发工程师的核心能力。