【2026】AI大模型零基础实战:LangChain、RAG、Agent、大模型微调从入门到项目落地(完整版)
刚开始接触大模型应用开发的同学,最容易遇到这样一个问题:网上教程很多,但要么只讲概念不讲代码,要么只给代码不讲原理,跟着做到一半就被各种依赖、版本、报错卡住,最后项目始终跑不起来。
这篇文章就是为解决这个问题而写。我会把大模型应用开发中最核心的四个方向一次性讲透:LangChain、RAG(检索增强生成)、Agent(智能体)、大模型微调。内容从零基础起步,先讲清楚“它们是什么、解决什么问题”,再给出完整的代码案例、运行过程和踩坑建议。无论你是学生、后端开发,还是想转行 AI 应用的工程师,只要能跟着文章一步步操作,就可以把整套技术路线跑通,并且能迁移到自己的业务项目中。
为了照顾新手,我会尽量少堆术语,每个概念都用大白话解释一遍,然后再给专业定义和代码示例。文章涉及的代码比较多,建议收藏后边看边练。
1. 先搞懂四个关键词:LangChain、RAG、Agent、大模型微调
1.1 大模型(LLM)到底是什么
LLM(Large Language Model,大语言模型)本质上是基于海量文本训练出来的深度神经网络模型。它的核心能力是“根据上下文预测下一个 token(词元)”,从而生成连贯、合理的文本。
我们平时接触到的 ChatGPT、文心一言、通义千问、DeepSeek 等产品,背后都离不开 LLM。开发者可以通过 API 调用这些模型,也可以在本地部署开源模型,例如 Qwen、Llama、ChatGLM 等。
对大模型应用开发来说,我们主要关注三件事:
- 如何让模型理解我们的指令。
- 如何让模型利用外部数据。
- 如何让模型执行复杂任务。
这三件事分别对应提示词工程(Prompt Engineering)、RAG 和 Agent。
1.2 LangChain 是干什么的
LangChain 是一个用于构建大模型应用的开源框架,最早于 2022 年发布,目前已经是 LLM 应用开发领域最流行的工具之一。它把“调用模型、处理输入输出、连接外部数据、管理记忆、构建工具调用”等常见操作封装成了统一接口,让开发者不用从零写大量样板代码。
打个比方:如果用原生的 OpenAI SDK 开发一个聊天机器人,你需要自己管理对话历史、处理上下文长度、编写向量检索逻辑、拼接 Prompt。而 LangChain 把这些步骤都组件化了,你可以像搭积木一样组合不同的模块。
LangChain 的核心模块包括:
- Models(模型封装):统一封装 OpenAI、Anthropic、百度文心、通义千问等模型接口。
- Prompts(提示词管理):提供模板化 Prompt 工具,支持变量替换和自动格式化。
- Chains(链):把多个处理步骤串联起来,例如“先检索、再组装 Prompt、最后调用模型”。
- Memory(记忆):管理多轮对话中的历史信息。
- Indexes(索引):负责文档加载、文本分割、向量化存储,这是 RAG 的基础。
- Agents(智能体):让模型自主决定调用哪些工具,完成多步任务。
1.3 RAG:让大模型“临时查资料”
RAG 的全称是 Retrieval-Augmented Generation,即检索增强生成。它的思路很简单:在模型回答之前,先从外部知识库中检索出与问题相关的内容,拼接到 Prompt 中,再让模型基于这些内容生成答案。
为什么要使用 RAG?因为大模型的知识截止日期固定,无法知道训练之后发生的新消息;而且模型对私有数据、企业内部资料是完全不知道的。RAG 可以在不重新训练模型的情况下,把最新数据或私有数据“喂”给模型参考,从而做到:
- 回答更准确,减少幻觉。
- 支持私有知识库问答。
- 答案可引用来源,方便追溯。
- 数据更新成本低,只需要更新知识库,不需要重训模型。
1.4 Agent:让大模型“动手干活”
Agent(智能体)是当前大模型应用最火热的方向之一。传统的大模型调用是一问一答模式,模型只会“动嘴”。而 Agent 的思路是给模型加上工具(Tool),让模型根据用户意图自动规划步骤、调用工具、处理结果,最终完成一个完整任务。
例如用户问“帮我查一下明天的天气,并顺便提醒我是否需要带伞”。这时候 Agent 会先识别意图,调用天气查询 API,拿到天气数据后,再结合数据生成建议。
Agent 的典型流程是:
- 用户输入。
- LLM 理解意图,并决策需要调用哪个工具。
- 执行工具并返回结果。
- 模型根据工具结果决定是继续调用下一个工具,还是生成最终回答。
1.5 大模型微调:让模型“更懂特定领域”
微调(Fine-tuning)是指在预训练好的大模型基础上,使用特定领域的数据继续训练,让模型适应该领域的语言习惯、输出格式或知识偏好。
区分一下:RAG 是“临时查资料”,微调是“改变模型本身的参数和知识”。实际项目中两者经常结合使用:先微调让模型理解业务语气和格式,再用 RAG 补充实时或私有知识。
2. 环境准备:从零搭建一套可运行的大模型应用环境
在开始写代码之前,先把环境准备好。下面的环境适用于 Windows / macOS / Linux,示例命令以 Linux 和 macOS 为主,Windows 用户可以借助 Git Bash 或 WSL 执行。
2.1 安装 Python
LangChain 目前依赖 Python 3.8+,建议使用 Python 3.10 或 3.11。推荐使用 Anaconda 或 Miniconda 管理虚拟环境,避免依赖冲突。
# 创建虚拟环境 conda create -n llm python=3.10 -y # 激活虚拟环境 conda activate llm安装完成后确认版本:
python --version2.2 安装 LangChain 及常用依赖
这里以 LangChain 0.3.x 为例(版本更新很快,请以官方最新文档为准)。安装时按需选择,不一定要全部装完。
# 核心包 pip install langchain # 如果用 OpenAI 接口 pip install openai # 如果用本地模型或者兼容 OpenAI 的模型服务,例如 Ollama、vLLM pip install langchain-openai # 向量数据库 Chroma(本地轻量级) pip install chromadb # 文档加载、拆分工具 pip install langchain-community pip install pypdf # 环境变量读取 pip install python-dotenv安装完毕后,可以测试一下导入是否正常:
import langchain print(langchain.__version__)2.3 选择模型 API 的方式
本文案例采用「OpenAI 兼容接口」的方式,这样可以同时兼容 OpenAI、DeepSeek、通义千问、Ollama 本地模型等多种后端。你只需要更换base_url和api_key即可。
注意:如果你使用的是国外模型服务,请确保你的网络环境合法合规;如果是企业项目,建议直接使用国内大模型服务或私有化部署模型。
2.4 准备一个示例项目结构
建议所有代码放在一个干净的目录下,比如:
llm-tutorial/ ├── .env ├── requirements.txt ├── 01_llm_basic.py ├── 02_langchain_chain.py ├── 03_rag_basic.py ├── 04_agent_tool.py ├── data/ │ └── 产品手册.pdf └── output/.env文件内容:
OPENAI_API_KEY=你的密钥 OPENAI_API_BASE=https://api.openai.com/v1如果你使用其他兼容服务,可以改成:
OPENAI_API_KEY=你的密钥 OPENAI_API_BASE=https://api.deepseek.com/v1加载.env的通用方式:
from dotenv import load_dotenv load_dotenv()3. LangChain 核心模块与最小案例
LangChain 虽然概念很多,但最常见的用法就是“初始化一个模型,写一个 Prompt,组成一个 Chain”。先从一个最简单的例子入手。
3.1 初始化大模型并直接对话
# 文件:01_llm_basic.py import os from dotenv import load_dotenv load_dotenv() from langchain_openai import ChatOpenAI # 初始化一个聊天模型 llm = ChatOpenAI( model="gpt-4o-mini", # 具体模型名根据你的服务商调整 temperature=0.7, # 控制随机性,0~1 api_key=os.getenv("OPENAI_API_KEY"), base_url=os.getenv("OPENAI_API_BASE"), ) # 直接调用 response = llm.invoke("用一句话介绍 LangChain 是什么?") print(response.content)运行结果大致为:
LangChain 是一个用于开发大语言模型应用的框架,提供了链式调用、提示词管理和外部工具集成能力。这里最关键的方法就是invoke()。LangChain 中所有可调用的组件几乎都实现了invoke()方法,传入字符串或消息对象,返回模型输出。
3.2 Prompt 模板:让输入更规范
直接用invoke只能处理简单问题。实际项目中,我们需要把用户输入和指令模板结合起来。
# 文件:prompt_template_demo.py from langchain.prompts import ChatPromptTemplate prompt = ChatPromptTemplate.from_template( "你是{domain}领域的专家,请用简洁的语言回答用户的问题。\n用户问题:{question}" ) chain = prompt | llm # 管道符表示“先执行左边,再把结果传给右边” result = chain.invoke({ "domain": "法律", "question": "合同违约后一般需要承担哪些责任?" }) print(result.content)ChatPromptTemplate.from_template中的{domain}、{question}都是占位符。使用|运算符把 Prompt 和模型串联起来是 LangChain 推荐的新式写法,比旧版本LLMChain更清晰。
3.3 三种常见 Chain
LangChain 提供了多种内置 Chain,下面列举最常用的三种:
1) LLMChain(旧版写法,但仍兼容)
from langchain.chains import LLMChain chain = LLMChain(llm=llm, prompt=prompt) result = chain.run(domain="历史", question="唐朝开元盛世的主要特征是什么?")2) 带结构化输出的 Chain
from langchain_core.output_parsers import StrOutputParser parser = StrOutputParser() chain = prompt | llm | parser result = chain.invoke({"domain": "医学", "question": "感冒和流感的区别?"})StrOutputParser可以把模型的输出统一转成字符串,方便后续处理。
3) 多步顺序 Chain
# 第一步:生成摘要 summarize_prompt = ChatPromptTemplate.from_template("请为以下内容生成一句话摘要:{text}") # 第二步:根据摘要生成关键词 keywords_prompt = ChatPromptTemplate.from_template("根据摘要生成5个关键词:\n摘要:{summary}") summarize_chain = summarize_prompt | llm | parser keywords_chain = keywords_prompt | llm | parser text = "大语言模型在自然语言处理领域取得了显著的进展,并被广泛应用于机器翻译、文本生成和对话系统。" summary = summarize_chain.invoke({"text": text}) keywords = keywords_chain.invoke({"summary": summary}) print("摘要:", summary) print("关键词:", keywords)这种多步 Chain 适用于“先处理、再分析”的场景。如果你觉得 Chain 不够灵活,可以考虑用 LangGraph 来编排更复杂的流程,这部分在进阶篇会提到。
3.4 对话记忆 Memory
默认情况下,多轮对话中大模型不会记住历史消息。如果需要实现“聊天记录”功能,可以使用 LangChain 的记忆模块。
# 文件:memory_demo.py from langchain.memory import ConversationBufferMemory from langchain_core.messages import HumanMessage, AIMessage memory = ConversationBufferMemory() memory.chat_memory.add_message(HumanMessage(content="我叫小明,今年25岁。")) memory.chat_memory.add_message(AIMessage(content="你好小明,很高兴认识你!")) # 获取历史消息 history = memory.load_memory_variables({}) print(history)实际业务中更推荐把历史消息交给前端管理,后端调用时把消息列表作为参数传入,这样可扩展性和可维护性更好。LangChain 的 Memory 适合快速原型验证。
4. RAG 知识库实战:从文档加载到问答系统
RAG 是当前企业应用落地最广泛的方向。接下来我们实现一个完整的流程:加载 PDF 文档 -> 拆分为小块 -> 向量化 -> 存入向量库 -> 检索 -> 组合 Prompt -> 生成回答。
4.1 RAG 整体流程拆解
可以把 RAG 分成离线索引和在线问答两个阶段。
- 离线索引阶段:把业务文档加载进来,做文本清洗、分块、向量化,最后存储到向量数据库。
- 在线问答阶段:把用户问题向量化,在向量库中检索最相似的文本块,把文本块和问题一起交给大模型,生成最终回答。
图示可以想象为:
用户问题 → 向量化 → 向量检索 → 返回 top-k 文本块 → 拼接 Prompt → LLM 生成回答4.2 安装 RAG 相关依赖
pip install chromadb langchain-community pypdf如果 PDF 是扫描版,还需要 OCR,可以用pytesseract或云 OCR 服务,本文不展开。
4.3 文档加载与文本拆分
# 文件:03_rag_basic.py from dotenv import load_dotenv load_dotenv() from langchain_community.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter # 1. 加载 PDF 文档 loader = PyPDFLoader("data/产品手册.pdf") documents = loader.load() print(f"文档页数:{len(documents)}") print(f"第一页内容预览:{documents[0].page_content[:100]}") # 2. 文本拆分 text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, # 每个文本块的最大字符数 chunk_overlap=50, # 块之间的重叠字符数 separators=["\n\n", "\n", "。", "!", "?", ".", " ", ""] ) chunks = text_splitter.split_documents(documents) print(f"切分后的文本块数量:{len(chunks)}")为什么需要切分?
大模型输入有长度限制,如果整篇文档直接塞进 Prompt 会超出 token 上限。而且从检索角度看,切分成小块后,能够更精准地命中与问题相关的片段。chunk_size和chunk_overlap需要根据实际文档调整:
chunk_size越大,每个块包含的信息越多,但检索精度可能下降。chunk_overlap可以防止关键信息恰好被切分到两个块中导致上下文不连续。
4.4 向量化与存储到 Chroma
向量化是 RAG 的核心。我们需要选择一个 Embedding 模型,把文本转成向量。常见方案:
- OpenAI 的
text-embedding-3-small。 - 智源的
BAAI/bge-small-zh-v1.5。 - 阿里云的
text-embedding-v1。
为了在国内环境更友好,这里演示使用BAAI/bge-small-zh-v1.5,它可以通过langchain_community的HuggingFaceEmbeddings加载,也可以直接安装sentence-transformers。
pip install sentence-transformers示例代码:
from langchain_huggingface import HuggingFaceEmbeddings embedding_model = HuggingFaceEmbeddings( model_name="BAAI/bge-small-zh-v1.5", model_kwargs={"device": "cpu"}, # 如果有 GPU,可改成 "cuda" encode_kwargs={"normalize_embeddings": True} )第一次运行会自动下载模型,需要保证网络可达。如果下载困难,可以使用国内镜像,或者用 OpenAI 兼容的 Embedding 接口:
from langchain_openai import OpenAIEmbeddings embedding_model = OpenAIEmbeddings( model="text-embedding-3-small", api_key=os.getenv("OPENAI_API_KEY"), base_url=os.getenv("OPENAI_API_BASE"), )接下来把切分好的文档存入 Chroma:
from langchain.vectorstores import Chroma vector_store = Chroma.from_documents( documents=chunks, embedding=embedding_model, persist_directory="./output/chroma_db" # 持久化目录 ) vector_store.persist() print("向量库创建成功")知识点:Chroma 是一个轻量级向量数据库,支持本地持久化,适合学习和小型项目。生产环境常用 Milvus、Weaviate、Qdrant、Elasticsearch 向量引擎等。
4.5 基于检索生成回答
在线问答阶段,我们不再重新创建向量库,而是直接加载:
vector_store = Chroma( persist_directory="./output/chroma_db", embedding_function=embedding_model ) retriever = vector_store.as_retriever( search_type="similarity", # 相似度检索 search_kwargs={"k": 3} # 返回最相似的3个块 )提问并检索:
question = "产品的质保政策是什么?" docs = retriever.invoke(question) # 查看检索到的内容 for i, doc in enumerate(docs): print(f"[片段 {i+1}] {doc.page_content}")将检索结果注入 Prompt:
from langchain.prompts import ChatPromptTemplate context = "\n\n".join([doc.page_content for doc in docs]) prompt = ChatPromptTemplate.from_template( """请基于以下资料回答用户问题。如果资料中没有相关信息,请直接说明“资料中未找到相关内容”,不要编造。 资料: {context} 问题:{question} 回答:""" ) chain = prompt | llm | StrOutputParser() answer = chain.invoke({"context": context, "question": question}) print("回答:", answer)这样就完成了一个最基本的 RAG 问答系统。你可以替换任何业务文档,实现企业内部知识库问答。
4.6 RAG 效果提升的 6 个方向
只跑通基础流程还不够,实际项目中常通过以下方法提高 RAG 质量:
- 优化分块策略:长文档先按章节切分,再按段落切分;对代码和表格单独处理。
- 混合检索:同时使用关键词检索(BM25)和向量检索,再进行结果融合,兼顾精确匹配和语义匹配。
- 重排序(Rerank):检索出 Top 50 个候选片段后,用 Rerank 模型重新打分,取 Top 5 送给大模型,能显著提升答案准确率。
- 查询改写:对复杂问题先做意图分解或关键词扩展,再进行多路检索。
- 多轮对话记忆:在 RAG 中加入历史对话,避免用户追问时上下文丢失。
- 引用溯源:让模型在回答中标注来源片段编号,增强可信度。
5. Agent 智能体实战:让模型学会调用工具
5.1 Agent 核心概念
Agent 要解决的核心问题是“模型如何自主决定调用工具”。比如我们给模型提供“查询天气”“计算器”“搜索网页”三个工具,用户说“帮我算一下 23*45,然后查一下北京的天气”。模型需要自己判断先调用哪个工具、后调用哪个工具,并根据工具结果继续生成回答。
LangChain 的 Agent 由三个关键部分组成:
- 模型(LLM):大脑,负责规划和决策。
- 工具(Tools):模型可以调用的外部能力。
- 代理执行器(AgentExecutor):负责控制循环执行,包括调用模型、执行工具、把结果反馈给模型。
5.2 定义一个自定义工具
LangChain 中可以用@tool装饰器快速定义一个工具。下面的示例实现“获取城市的当前温度”和“计算两个数之和”。
# 文件:04_agent_tool.py from dotenv import load_dotenv load_dotenv() from langchain_openai import ChatOpenAI from langchain.agents import tool @tool def get_weather(city: str) -> str: """根据城市名称查询实时天气,返回温度和天气状况。""" # 实际项目中这里应调用天气 API,这里模拟返回固定数据 if city == "北京": return "北京:晴,25℃,微风" elif city == "上海": return "上海:多云,28℃,东南风3级" else: return f"{city}:未知天气,请稍后重试" @tool def calculator(expression: str) -> str: """计算数学表达式,例如 '23*45' 返回计算结果。""" try: return str(eval(expression)) except Exception as e: return f"计算失败:{e}"注意:tool装饰器会把函数名作为工具名,函数文档字符串作为工具描述。LLM 需要根据描述来匹配工具,所以描述写清楚至关重要。
5.3 构建 ReAct 风格的 Agent
ReAct(Reasoning + Acting)是目前最常见的 Agent 实现方式:模型先输出思考过程,再决定行动,然后观察结果,再思考,最终给出答案。
from langchain.agents import AgentExecutor, create_react_agent from langchain_core.prompts import PromptTemplate llm = ChatOpenAI( model="gpt-4o-mini", temperature=0, ) tools = [get_weather, calculator] prompt = PromptTemplate.from_template(""" 尽可能帮助用户回答问题。你可以使用以下工具: {tools} 工具名:工具描述 - get_weather: 获取城市天气 - calculator: 计算数学表达式 回答时请按以下格式: Thought: 我需要调用工具 Action: 工具名 Action Input: 工具的输入 Observation: 工具返回结果 ...(继续 Thought/Action/Observation 循环) 最终回答:给用户的答案 用户问题:{input} 你的工作区: {agent_scratchpad} """) agent = create_react_agent(llm=llm, tools=tools, prompt=prompt) agent_executor = AgentExecutor( agent=agent, tools=tools, verbose=True, # 打印执行过程 max_iterations=5, # 限制最大轮数,防止死循环 ) result = agent_executor.invoke({"input": "帮我计算 23*45 的结果,并查询北京天气"}) print("最终结果:", result["output"])运行后会输出类似下面的过程日志:
Thought: 用户需要计算和查询天气,我需要先调用计算器。 Action: calculator Action Input: 23*45 Observation: 1035 Thought: 现在查询北京天气。 Action: get_weather Action Input: 北京 Observation: 北京:晴,25℃,微风 Thought: 我已经获得所有信息。 最终回答:23*45 的结果是 1035。北京天气为晴,25℃,微风。这就是 Agent 的核心运行机制:模型不是一次性给出答案,而是可以“推理-行动-观察-再推理”地循环,直到完成目标。
5.4 使用 LangGraph 编排更复杂的 Agent
LangChain 本身侧重于“链式组合”,而 LangGraph 是更底层的图编排框架,以“节点”和“边”的方式表达流程,适合需要条件分支、循环、人工审批等复杂场景。
简单对比:
- LangChain:面向简化开发,提供
LCEL(LangChain Expression Language)表达式,适合大多数流水线场景。 - LangGraph:面向图状态管理,适合复杂的 Agent 工作流,比如“先审核数据,再决定是否需要人工介入”。
下面是一个 LangGraph 的最小示例,构建两节点流程:
from langgraph.graph import StateGraph, START, END from typing import TypedDict class State(TypedDict): question: str answer: str def node_1(state: State): return {"answer": f"收到问题:{state['question']}"} def node_2(state: State): return {"answer": f"处理完成,回答:{state['answer']}"} graph = StateGraph(State) graph.add_node("first", node_1) graph.add_node("second", node_2) graph.add_edge(START, "first") graph.add_edge("first", "second") graph.add_edge("second", END) app = graph.compile() result = app.invoke({"question": "LangGraph 和 LangChain 的区别?"}) print(result["answer"])如果你只是做固定流程,用 LangChain 的 Chain 就够了;如果流程里有很多条件判断、循环、并行分支,或者需要人类介入审批,用 LangGraph 会更合适。
5.5 关于 Agent 的安全边界
Agent 可以调用工具有很大的风险。在生产环境中,必须注意:
- 给每个工具设置白名单,只暴露必要的功能。
- 对 Agent 可执行的命令进行严格校验,防止提示词注入。
- 设置
max_iterations,防止模型陷入无限循环。 - 对 Agent 的每一步操作都记录日志,方便审计。
- 涉及数据库或文件删除等高风险操作,需要人工确认。
6. 大模型微调:从数据准备到 LoRA 实战
如果 RAG 和 Agent 都掌握了,下一步就可以尝试微调模型。微调适合以下场景:
- 模型输出格式总是不符合要求。
- 需要模型掌握特定领域术语和表达方式。
- 需要降低幻觉,让模型更“确信”自己的专业知识。
6.1 微调的基本概念
大模型微调分为两种:
- 全参微调(Full Fine-tuning):更新模型全部参数。对 GPU 显存要求极高,通常需要多卡并行。
- 参数高效微调(PEFT):只更新一小部分额外参数,冻结原始模型。LoRA 和 QLoRA 是其中最流行的方法。
LoRA(Low-Rank Adaptation)通过在原始权重旁边增加低秩矩阵,训练时只更新这些矩阵,显存占用大幅降低,单卡 24GB 甚至 16GB 显存就能微调 7B 级别的模型。
6.2 准备微调数据集
微调数据一般使用 JSON 或 JSONL 格式,每条数据包含指令、输入、输出。
一个示例:
{ "instruction": "请按照公司客服语气回答用户问题", "input": "退货流程是什么?", "output": "亲,您可以在订单页面申请退货,审核通过后会有快递员上门取件哦~" }如果是对话模型,数据格式可能会是:
[ {"role": "system", "content": "你是客服助手"}, {"role": "user", "content": "退货流程是什么?"}, {"role": "assistant", "content": "亲,您可以在订单页面申请退货..."} ]数据数量至少要几百条起步,质量比数量更重要。建议先整理 50 条高质量数据进行小规模验证,再逐步扩充。
6.3 LoRA 微调实战(基于 HuggingFace PEFT)
下面的示例使用transformers+peft+datasets框架,以常见的对话模型为例。代码是核心片段,实际运行需要根据你的模型路径、数据集路径和环境调整。
pip install transformers peft datasets accelerate bitsandbytes微调脚本如下:
# 文件:lora_finetune.py import torch from transformers import ( AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer, DataCollatorForSeq2Seq, ) from peft import LoraConfig, get_peft_model, TaskType from datasets import load_dataset # 1. 加载模型和分词器 model_path = "Qwen/Qwen2-7B-Instruct" # 示例模型,请根据实际选择 tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) tokenizer.pad_token = tokenizer.eos_token model = AutoModelForCausalLM.from_pretrained( model_path, torch_dtype=torch.bfloat16, device_map="auto", trust_remote_code=True ) # 2. 配置 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"], # 常见注意力模块 ) model = get_peft_model(model, lora_config) model.print_trainable_parameters() # 打印可训练参数数量 # 3. 加载数据集 dataset = load_dataset("json", data_files="data/train.jsonl", split="train") def format_example(example): prompt = f"指令:{example['instruction']}\n输入:{example['input']}\n回答:" output = example["output"] return {"text": prompt + output + tokenizer.eos_token} dataset = dataset.map(format_example) def tokenize_function(examples): return tokenizer( examples["text"], truncation=True, max_length=512, padding="max_length" ) tokenized_dataset = dataset.map(tokenize_function, batched=True, remove_columns=["text", "instruction", "input", "output"]) # 4. 训练参数 training_args = TrainingArguments( output_dir="./lora_output", per_device_train_batch_size=2, gradient_accumulation_steps=4, learning_rate=2e-4, num_train_epochs=3, logging_steps=10, save_steps=200, evaluation_strategy="no", save_total_limit=2, fp16=False, bf16=True, report_to="none", ) trainer = Trainer( model=model, args=training_args, train_dataset=tokenized_dataset, data_collator=DataCollatorForSeq2Seq(tokenizer), ) # 5. 开始训练 trainer.train() # 6. 保存 LoRA 权重 model.save_pretrained("./lora_adapter") tokenizer.save_pretrained("./lora_adapter") print("微调完成")这段代码的重点:
target_modules指定要注入 LoRA 的模块,不同模型可能不同,需要查看模型结构。bfloat16需要 Ampere 架构及以上 GPU 支持;如果不支持,改用fp16=True。- 显存不足时可使用
bitsandbytes做 4bit 量化,也就是 QLoRA。 - 训练数据最多不超过 512 token,超出部分会被截断。
6.4 加载微调后的模型进行推理
from peft import PeftModel from transformers import AutoModelForCausalLM, AutoTokenizer base_model = AutoModelForCausalLM.from_pretrained(model_path, device_map="auto") model = PeftModel.from_pretrained(base_model, "./lora_adapter") inputs = tokenizer("指令:请按照公司客服语气回答用户问题\n输入:如何开发票?\n回答:", return_tensors="pt") outputs = model.generate( **inputs, max_new_tokens=128, temperature=0.2, do_sample=True, ) print(tokenizer.decode(outputs[0], skip_special_tokens=True))微调后通常需要写测试集进行效果评估,比较微调前后在固定问题上的回答质量。评估指标可以是人工打分,也可以是自动指标(BLEU、ROUGE、GPT 评分等)。
6.5 微调 vs RAG 怎么选
这是开发者在实际项目中经常纠结的问题,可以用下表快速判断:
| 对比维度 | RAG | 微调 |
|---|---|---|
| 更新成本 | 低,替换文档即可 | 高,需要重新训练 |
| 输出格式控制 | 只能靠 Prompt 约束 | 可以训练固定格式 |
| 对私有知识 | 适合大量知识查询 | 适合少量高价值知识内化 |
| 幻觉抑制 | 较好,能引用资料 | 取决于数据质量 |
| 部署成本 | 低,不需要额外显存 | 高,需要更大显存/GPU |
| 推荐场景 | 企业内部知识库、客服问答 | 行业翻译、代码生成、定制语气 |
实际项目中二者常结合使用:先微调模型,让模型理解业务规则和输出风格;再叠加 RAG,让模型可以查询最新数据。
7. 常见问题与排查思路
7.1 “openai.APIConnectionError: Connection error” 调用模型失败
| 可能原因 | 解决思路 |
|---|---|
| 网络无法访问模型服务 | 检查base_url是否配置正确,使用国内合法可访问的服务 |
| API Key 无效 | 检查环境变量是否加载,.env是否存在且权限正确 |
| 使用了不受支持的模型名 | 确认服务商实际提供的模型标识,不同厂商model参数不同 |
| 防火墙或代理冲突 | 检查本地代理设置,必要时取消代理或配置白名单 |
7.2 中文 PDF 加载后乱码
PDF 文本提取依赖原始文件的文字层。如果 PDF 是扫描版或者字体嵌入特殊,PyPDFLoader可能提取出乱码。
排查步骤:
- 先打印
documents[0].page_content,确认是否为乱码。 - 如果是扫描版,需要先 OCR,推荐使用 PaddleOCR。
- 如果是字体问题,可以用
pdfplumber或fitz(PyMuPDF)尝试。
7.3 向量检索结果不相关
RAG 问答效果差,90% 的问题出在检索环节。排查顺序如下:
- 确认 Embedding 模型与文档语言是否匹配,中文文档最好用中文 Embedding 模型。
- 检查分块大小,如果块太大,包含噪声信息过多;如果太小,语义不完整。
- 打印检索到的片段,人工检查是否与问题相关。
- 尝试使用混合检索和重排序。
7.4 Agent 在循环中无法停止
为了安全,AgentExecutor必须设置max_iterations。如果模型反复调用同一个工具,可能是工具描述不清晰或 Prompt 中缺少停止条件。可以在 Prompt 中补充“当tool结果已经满足用户需求时,直接返回最终答案”。
7.5 微调时显存不足(OOM)
| 场景 | 推荐方案 |
|---|---|
| 模型大于 7B | 使用 QLoRA(4bit 量化) |
| 批量大小过大 | 减小per_device_train_batch_size |
| 梯度累积设置不当 | 增加gradient_accumulation_steps |
| 最大长度过长 | 降低max_length到 512 或 256 |
| 多卡空闲 | 使用device_map="auto"自动分卡 |
7.6 微调后模型输出“胡说八道”
- 数据质量问题:检查是否存在指令和输出不匹配。
- 学习率过高:降低学习率到
1e-4或2e-4。 - 过拟合:减少训练轮数或增加数据量。
- 基础模型过新或过旧:选择与目标领域相近的基础模型。
8. 最佳实践与工程建议
8.1 提示词设计规范
- 把“角色、任务、输出格式、约束条件、示例”写清楚。
- 对 RAG 场景,必须在 Prompt 里加“资料中没有就直说”,减少幻觉。
- 对 Agent 场景,必须在工具描述中写清楚参数格式和返回值含义。
- 使用少量示例(Few-shot)比参数调节更稳定。
8.2 配置管理
- 敏感信息(API Key、数据库密码)永远不要硬编码。
- 使用
.env文件管理本地配置,生产环境使用配置中心或环境变量注入。 - 不同环境的模型名称、向量库地址、API地址要分开配置。
8.3 日志与可观测性
大模型应用的日志不能只记录错误,还要记录:
- 用户输入的原始内容。
- 最终 Prompt 组装结果。
- 模型原始输出。
- 使用了哪个模型、温度等参数。
- 每次检索命中的文档片段和得分。
- Agent 每一步的 Thought/Action/Observation。
这样一旦线上出现问题,可以完整复现排查。
8.4 安全与合规
- 调用外部大模型 API 时,严禁上传未脱敏的用户隐私数据。
- 如果数据敏感,建议私有化部署开源模型。
- Agent 工具必须鉴权,不能对未授权用户开放危险操作。
- RAG 知识库要做权限隔离,不同角色只能检索到对应范围的文档。
- 所有涉及生产环境的变更,先在测试环境验证并备份。
8.5 性能优化
- 向量检索时,先过滤元数据条件,再做向量相似度计算。
- 对高频问题和热点内容,增加缓存层。
- 使用异步调用处理并发请求,但注意模型 API 的频率限制。
- 大模型的输出用流式(Streaming)提升用户体验,避免长等待。
8.6 模型评估:不要“感觉行”
- 建立一套固定的测试集,包含正常问题、边界问题和陷阱问题。
- 对 RAG 检索效果评估,可以使用命中率、MRR(Mean Reciprocal Rank)、NDCG 等指标。
- 对最终回答效果,可以让多个模型打分或人工盲评。
- 每次修改 Prompt 或更换模型,都要跑一遍回归测试。
9. 总结与下一步学习路线
这篇文章把大模型应用开发最重要的四个方向都过了一遍:用LangChain串联模型、提示词和流程;用RAG让模型接入外部知识库;用Agent让模型自主调用工具;用微调(LoRA)让模型适配特定领域。
建议的学习路线是:
- 先把 LangChain 基础链式调用练熟。
- 在本地用 PDF 文档做一个 RAG 问答系统。
- 给 Agent 添加两三个工具,尝试完成实际业务问题。
- 积累 500~1000 条领域数据,用 LoRA 微调一个开源模型。
- 把 RAG + Agent + 微调结合起来,做成一个完整的业务应用。
如果你想继续深入,下一个阶段可以重点学习 LangGraph 的图状态编排、推理加速(vLLM)、模型部署、以及 RAG 效果评估体系。技术变化很快,但核心思路不会变:让大模型理解任务、连接数据、安全执行。
动手实践的时候,不要怕报错。每个报错都是一次学习机会,把报错信息、代码版本、运行环境记下来,过一段时间你会发现,自己已经可以独立解决大部分问题了。
如果这篇文章对你有帮助,可以先收藏备用,后续在实际项目中遇到问题时,再回头对照排查思路,能节省不少时间。