基于 LangChain 接入 Qwen2.5-Coder-7B-Instruct:自定义 LLM 类实现本地代码生成应用
【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm
本指南以《开源大模型食用指南》(self-llm)项目中的 LangChain 接入教程 为主体,介绍如何把本地部署的 Qwen2.5-Coder-7B-Instruct 代码模型接入 LangChain 框架。通过继承langchain.llms.base.LLM并重写_call函数,即可用统一的 LangChain 接口调用本地模型,从而在对话问答、代码生成等应用中复用整个 LangChain 生态。读完本文,你将掌握:本地模型下载、自定义 LLM 类的完整实现原理,以及调用模型完成对话与代码生成任务的实战方法。
1. 方案概述:为什么要把本地模型接入 LangChain
LangChain 是当下主流的 LLM 应用开发框架,提供了链(Chain)、检索(Retriever)、记忆(Memory)、Agent 等大量开箱即用的组件。但 LangChain 原生的模型调用大多面向云端 API,本地部署的开源模型(如 Qwen2.5-Coder-7B-Instruct)无法直接接入。
解决思路并不复杂:LangChain 的模型抽象层提供了一个基类LLM,只要基于本地模型实现一个子类并重写核心方法,LangChain 就能像调用其他大模型一样调用本地模型,上层应用无需关心底层调用差异。本项目(self-llm)正是基于这一思路,给出了一个可复制的通用模板,整个仓库中 Qwen、ChatGLM、InternLM 等数十个模型的 LangChain 接入教程均采用了同一套设计模式。
说明:本文聚焦"接入 LangChain"这一应用场景。若需要将模型封装为 HTTP API 服务,可参考同目录下的 FastApi 部署教程;若需要图形化聊天界面,可参考 WebDemo 部署教程。
2. 环境准备
2.1 基础环境
本文的验证环境如下:
---------------- ubuntu 22.04 python 3.12 cuda 12.1 pytorch 2.3.0 ----------------本文默认学习者已安装好以上 Pytorch(cuda) 环境,如未安装请先自行安装。
2.2 依赖安装
由于国内网络环境,先升级 pip 并切换 PyPI 源加速下载,再安装本教程所需的依赖包:
# 升级pip python -m pip install --upgrade pip # 更换 pypi 源加速库的安装 pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple pip install transformers==4.46.2 pip install modelscope==1.20.0 pip install langchain==0.3.7 pip install accelerate==1.1.1四个依赖包的分工如下:
| 依赖包 | 版本 | 作用 |
|---|---|---|
transformers | 4.46.2 | 加载模型权重与分词器,执行生成推理 |
modelscope | 1.20.0 | 国内模型下载通道,提供snapshot_download下载模型 |
langchain | 0.3.7 | LLM 应用框架,本文接入的目标框架 |
accelerate | 1.1.1 | 配合device_map="auto"自动分配设备、管理显存 |
3. 模型下载
使用modelscope中的snapshot_download函数下载模型。第一个参数为模型名称,参数cache_dir为模型的本地存储路径(建议使用绝对路径,相关细节可参考项目中的 模型下载通用指南)。
在项目工作目录下新建model_download.py文件,输入以下内容并保存:
import torch from modelscope import snapshot_download, AutoModel, AutoTokenizer import os model_dir = snapshot_download('Qwen/Qwen2.5-Coder-7B-Instruct', cache_dir='/root/autodl-tmp', revision='master')运行下载:
python model_download.py模型大小约 16 GB,下载大概需要 12 分钟(视网络状况而定)。
注意:记得修改
cache_dir为你的模型下载路径哦。下载完成后,模型权重会落在cache_dir/Qwen/Qwen2___5-Coder-7B-Instruct目录下(modelscope 会将模型名中的.转写为___,该路径格式在仓库的 LoRA 微调教程 中也被同样使用),后续自定义 LLM 类将直接加载这一路径。
4. 代码准备:自定义 LLM 类
4.1 设计思路
为便捷构建 LLM 应用,我们需要基于本地部署的 Qwen2.5-Coder 自定义一个LLM类,将其接入 LangChain 框架。完成自定义 LLM 类之后,可以以完全一致的方式调用 LangChain 的接口,而无需考虑底层模型调用的不一致。
基于本地模型自定义 LLM 类并不复杂,只需从langchain.llms.base.LLM继承一个子类,并重写以下两个部分:
- 构造函数
__init__:在对象实例化时一次性加载本地模型与分词器,避免每次调用都重新加载模型导致耗时过长; _call函数:LLM 类的核心函数,LangChain 在真正调用模型时会调用它;在函数内调用已实例化模型的generate方法完成推理并返回结果。
4.2 完整实现
在当前路径新建LLM.py文件,输入以下内容并保存:
from langchain.llms.base import LLM from typing import Any, List, Optional from langchain.callbacks.manager import CallbackManagerForLLMRun from transformers import AutoTokenizer, AutoModelForCausalLM, GenerationConfig, LlamaTokenizerFast import torch class Qwen2_5_Coder(LLM): # 基于本地 Qwen2_5-Coder 自定义 LLM 类 tokenizer: AutoTokenizer = None model: AutoModelForCausalLM = None def __init__(self, mode_name_or_path :str): super().__init__() print("正在从本地加载模型...") self.tokenizer = AutoTokenizer.from_pretrained(mode_name_or_path, use_fast=False) self.model = AutoModelForCausalLM.from_pretrained(mode_name_or_path, torch_dtype=torch.bfloat16, device_map="auto") self.model.generation_config = GenerationConfig.from_pretrained(mode_name_or_path) print("完成本地模型的加载") def _call(self, prompt : str, stop: Optional[List[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any): messages = [{"role": "user", "content": prompt }] input_ids = self.tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) model_inputs = self.tokenizer([input_ids], return_tensors="pt").to('cuda') generated_ids = self.model.generate(model_inputs.input_ids, attention_mask=model_inputs['attention_mask'], max_new_tokens=512) generated_ids = [ output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids) ] response = self.tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0] return response @property def _llm_type(self) -> str: return "Qwen2_5_Coder"在整体项目中,我们将上述代码封装为LLM.py,后续将直接从该文件中引入自定义的 LLM 类。
4.3 关键代码逐段解析
(1)构造函数:一次加载,多次复用
self.tokenizer = AutoTokenizer.from_pretrained(mode_name_or_path, use_fast=False) self.model = AutoModelForCausalLM.from_pretrained(mode_name_or_path, torch_dtype=torch.bfloat16, device_map="auto") self.model.generation_config = GenerationConfig.from_pretrained(mode_name_or_path)use_fast=False:使用经典分词器实现,避免部分模型与 fast tokenizer 的兼容性问题;torch_dtype=torch.bfloat16:以 bfloat16 半精度加载权重,显著降低显存占用(该 7B 模型在单卡环境下更易部署);device_map="auto":由 accelerate 自动将各层分配到可用设备上,单卡时全部加载到 GPU;GenerationConfig.from_pretrained:加载模型自带的生成配置(如 temperature、top_p 等默认值),保证生成行为与模型发布时的设定一致。
(2)_call函数:从 prompt 到回复的完整链路
messages = [{"role": "user", "content": prompt }] input_ids = self.tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) model_inputs = self.tokenizer([input_ids], return_tensors="pt").to('cuda') generated_ids = self.model.generate(model_inputs.input_ids, attention_mask=model_inputs['attention_mask'], max_new_tokens=512) generated_ids = [ output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids) ] response = self.tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0] return response- 第 1 行:把用户输入包装成 OpenAI 风格的
messages对话结构; - 第 2 行:
apply_chat_template按 Qwen2.5-Coder 的 Chat 模板将 messages 拼装为模型输入格式,add_generation_prompt=True会在末尾追加生成提示符; - 第 3 行:通过分词器将文本转为 token id 张量并搬运到 CUDA 设备;
- 第 4 行:调用
model.generate执行自回归生成,max_new_tokens=512限制新生成的最大 token 数; - 第 5-7 行:从完整输出中裁掉输入部分,只保留新生成的 token;
- 第 8 行:
batch_decode将 token id 解码回文本,skip_special_tokens=True去除<|im_end|>等特殊符号。
这里的apply_chat_template+generate调用链与仓库中 FastApi 部署教程 的实现完全一致,说明该模板在不同应用形态(HTTP API / LangChain / WebDemo)之间是可复用的。
(3)_llm_type属性
@property def _llm_type(self) -> str: return "Qwen2_5_Coder"该属性是 LangChain LLM 基类要求的抽象接口,用于标识 LLM 类型,返回自定义的名称即可。
5. 调用:像使用任何 LangChain 大模型一样使用
封装完成后,就可以像使用任何其他 LangChain 大模型一样使用本地模型了。
注意:记得修改模型路径为你的路径哦。若按上文
cache_dir=/root/autodl-tmp下载,模型实际路径为autodl-tmp/Qwen/Qwen2___5-Coder-7B-Instruct。
5.1 基础对话测试
from LLM import Qwen2_5_Coder llm = Qwen2_5_Coder(mode_name_or_path = "autodl-tmp/Qwen/Qwen2___5-Coder-7B-Instruct") print(llm.invoke("你是谁"))首次实例化时终端会输出模型加载进度,加载完成后llm.invoke("你是谁")即可得到模型的自我介绍,验证了模型已能通过 LangChain 接口正常对话:
5.2 代码生成实战
既然是 Coder 模型,当然要试着让它编写代码。让模型用 Python 写一个"三局两胜"的猜拳小游戏:
text = llm.invoke("为我用python写一个简单的猜拳小游戏,三局两胜") print(text)模型生成了完整可运行的 Python 代码,包含玩家输入、电脑随机出拳、胜负判定与比分统计等完整逻辑:
将生成的代码保存后直接运行,可以看到游戏交互流程正常:用户与电脑依次出拳,模型实时判断每局结果并累计比分,验证了生成的代码可以直接运行:
值得注意:
max_new_tokens=512会限制单次生成长度。如果需求生成的代码较长,可适当调大该参数,否则输出可能在中途被截断——这一点在仓库 FastApi 教程 中同样被明确指出。
6. 扩展:接入 LangChain 后的下一步
完成自定义 LLM 类后,该对象已具备 LangChain 标准LLM接口,可以直接参与 LangChain 的链式调用、检索问答、Agent 编排等高级用法。同时,self-llm 仓库围绕 Qwen2.5-Coder-7B-Instruct 提供了完整的使用闭环,可作为后续学习路线:
- FastApi 部署调用:将模型封装为 HTTP API 服务,供多客户端调用;
- WebDemo 部署:基于 Streamlit 搭建带流式输出的图形化聊天界面;
- vLLM 部署调用:通过 PagedAttention 等优化实现高吞吐推理服务;
- LoRA 微调:基于 peft 对模型进行高效微调,并借助 SwanLab 可视化训练过程。
将本教程的"自定义 LLM 类"模板与上述任一部署形态结合,即可快速搭建出属于自己的本地代码助手应用。
【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考