Phi-4 接入 LangChain 实战:通过自定义 LLM 类封装本地模型完成统一调用
Phi-4 接入 LangChain 实战通过自定义 LLM 类封装本地模型完成统一调用【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调全参数/Lora、部署国内外开源大模型LLM/多模态大模型MLLM教程项目地址: https://gitcode.com/datawhalechina/self-llm本文是《开源大模型食用指南》中 Phi-4 系列的 LangChain 接入教程讲解如何将本地部署的 Phi-4 模型封装成 LangChain 自定义 LLM 类从而以统一接口驱动模型推理。读完本文你将掌握从环境准备、魔搭模型下载、自定义 LLM 类编写到调用与报错排查的完整流程并理解 LangChain 底层对 LLM 的抽象机制。一、整体思路为什么需要自定义 LLM 类LangChain 的核心价值之一是提供一套与具体模型解耦的统一接口。无论底层是 OpenAI API、Hugging Face 模型还是本地权重上层应用只需面向同一个LLM抽象调用即可。要让本地部署的 Phi-4 也能享受这一便利就需要基于本地模型自定义一个 LLM 类从langchain.llms.base.LLM继承子类重写构造函数与_call函数。构造函数在实例化时一次性加载模型与分词器避免每次调用都重新加载_call是 LangChain 调用模型的核心入口负责把提示词交给模型生成并返回文本结果。完成封装后Phi-4 就能以与任何 LangChain 大模型完全一致的方式被调用上层代码无需关心底层是哪个模型、走什么推理管线。这也为后续接入向量库、构建知识库助手、串联 Agent 等能力打下基础。二、环境准备本文基础环境如下---------------- ubuntu 22.04 python 3.12 cuda 12.1 pytorch 2.3.0 ----------------本文默认学习者已安装好以上 Pytorch(cuda) 环境如未安装请自行安装。pip 换源加速下载并安装依赖包# 升级pip python -m pip install --upgrade pip # 更换 pypi 源加速库的安装 pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple pip install transformers4.44.2 pip install huggingface-hub0.25.0 pip install accelerate0.34.2 pip install modelscope1.18.0 pip install langchain0.3.0这里各依赖的版本均与本文示例代码验证过transformers负责加载模型与分词器accelerate提供device_mapauto的设备自动分配能力modelscope用于从魔搭社区下载模型权重langchain0.3.0提供LLM基类与调用框架。若部分组件版本不一致最可能的影响是接口签名变化建议优先复现本文固定版本组合。三、下载 Phi-4 模型魔搭社区使用魔搭社区中的modelscope的snapshot_download函数下载模型。第一个参数为模型名称可在魔搭社区搜索该模型获取如下图所框参数cache_dir为模型的下载路径参数revision一般默认为master。在/root/autodl-tmp新建model_download.py文件并输入以下内容保存后运行python model_download.py执行下载。模型大小约 28 GB下载大概需要 10 到 20 分钟import torch from modelscope import snapshot_download, AutoModel, AutoTokenizer import os model_dir snapshot_download(LLM-Research/phi-4, cache_dir/root/autodl-tmp, revisionmaster)注意记得修改cache_dir为你的模型下载路径。在魔搭社区中搜索并进入phi-4模型页确认模型名称为LLM-Research/phi-4分支为master即可获得与上方代码一致的下载参数四、编写自定义 LLM 类LLM.py在当前路径新建一个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 #Hugging Face 提供的库用于加载预训练的 NLP 模型 import torch class Phi_4_LLM(LLM): # 基于本地 Phi_4 自定义 LLM 类 tokenizer: AutoTokenizer None #tokenizer:用于将输入文本转换为模型可以理解的 token model: AutoModelForCausalLM None #model:预训练的语言模型 def __init__(self, mode_name_or_path :str): #__init__ 方法初始化模型和分词器 super().__init__() print(正在从本地加载模型...) self.tokenizer AutoTokenizer.from_pretrained(mode_name_or_path, use_fastFalse) #使用 AutoTokenizer.from_pretrained 加载分词器 self.tokenizer.pad_token_id self.tokenizer.eos_token_id 100265 self.model AutoModelForCausalLM.from_pretrained(mode_name_or_path, torch_dtypetorch.bfloat16, device_mapauto) #使用 AutoModelForCausalLM.from_pretrained 加载预训练的因果语言模型并设置数据类型为 bfloat16使用自动设备分配策略。 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): #_call 方法用于生成文本响应 messages [{role: user, content: prompt }] #构造消息列表包含用户的角色和提示内容 input_ids self.tokenizer.apply_chat_template(messages, tokenizeFalse, add_generation_promptTrue) #使用 apply_chat_template 方法应用聊天模板并获取输入 ID model_inputs self.tokenizer([input_ids], return_tensorspt).to(self.model.device) #将输入 ID 转换为 PyTorch 张量并移动到 GPU 上 generated_ids self.model.generate(model_inputs.input_ids, attention_maskmodel_inputs[attention_mask], max_new_tokens512) #使用 generate 方法生成新的 token generated_ids [ output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids) ] #处理生成的 token移除输入部分只保留新生成的部分 response self.tokenizer.batch_decode(generated_ids, skip_special_tokensTrue)[0] return response #将生成的 token 解码为文本响应并返回 property def _llm_type(self) - str: return Phi_44.1 构造函数一次加载处处复用构造函数在对象实例化时完成两件事加载分词器AutoTokenizer.from_pretrained(mode_name_or_path, use_fastFalse)加载 Phi-4 的分词器。use_fastFalse使用经典实现避免 fast tokenizer 与模型特殊 token 之间的兼容问题。加载模型AutoModelForCausalLM.from_pretrained(mode_name_or_path, torch_dtypetorch.bfloat16, device_mapauto)以bfloat16半精度加载因果语言模型并通过device_mapauto依赖accelerate自动将权重分配到可用设备GPU/CPU无需手动指定cuda:0。两个细节值得注意self.tokenizer.pad_token_id self.tokenizer.eos_token_id 100265将 padding token 与结束符统一设置为100265。从调用链看这保证generate时attention_mask构造与停止符判定一致避免因 pad token 缺失或不一致导致的生成异常。self.model.generation_config GenerationConfig.from_pretrained(mode_name_or_path)显式从模型目录加载生成配置temperature、top_p 等让模型按官方默认生成策略推理。把模型加载放在构造函数中意味着后续每次_call都不再需要重新加载权重大幅降低多轮调用的时延。4.2 _call 函数LangChain 与模型之间的桥梁_call是LLM类的核心函数LangChain 会调用该函数来调用 LLM。其内部流程分为四步构造消息列表将用户提示词包装为[{role: user, content: prompt }]与 Chat 模型的对话格式保持一致。应用聊天模板apply_chat_template(messages, tokenizeFalse, add_generation_promptTrue)把消息列表按 Phi-4 的对话模板拼装成完整输入串并追加生成提示符generation prompt这是让模型正确理解对话结构的关键。生成 tokenself.model.generate(model_inputs.input_ids, attention_mask..., max_new_tokens512)控制最多新生成 512 个 token可通过修改max_new_tokens调节回答长度上限。裁剪与解码将生成结果中与输入部分重叠的 token 裁掉仅保留新生成的部分再通过batch_decode(..., skip_special_tokensTrue)解码为纯文本返回skip_special_tokensTrue用于剔除|endoftext|等特殊 token。4.3 _llm_type 属性_llm_type是LLM基类要求子类实现的只读属性用于标识当前 LLM 的类型。这里返回字符串Phi_4在日志、序列化与调试时用于区分不同的模型实现。五、源码级剖析LangChain 对 LLM 的抽象与调用机制了解 LangChain 内部如何驱动这个自定义类有助于排查问题。从langchain.llms.base.LLM基类看_call是抽象方法子类必须实现它。基类通过generate_prompt/__call__等公开入口最终路由到_call完成真正的文本生成。_llm_type是抽象属性子类必须提供用于标识模型类型。回调机制run_manager: CallbackManagerForLLMRun参数允许在生成过程中触发on_llm_start、on_llm_new_token等事件回调。本文示例中未显式使用但保留该参数即可保持与 LangChain 事件体系的兼容。__call__的演进在新版 LangChain 中BaseLLM.__call__已被标记为弃用LangChainDeprecationWarning官方推荐改用invoke方法。示例运行输出中出现的弃用警告即源于此不影响功能但新项目建议直接用llm.invoke(你是谁)或llm.invoke({prompt: ...})风格调用。在整体项目中我们将上述代码封装为LLM.py后续直接从该文件中引入自定义的 LLM 类即可。六、调用自定义 LLM封装完成后就可以像使用任何其他 LangChain 大模型功能一样使用它了from LLM import Phi_4_LLM llm Phi_4_LLM(mode_name_or_path /root/autodl-tmp/LLM-Research/phi-4) print(llm(你是谁))注意记得修改模型路径为你的路径。在 Jupyter Notebook 中执行上述代码先看到模型加载进度条与完成本地模型的加载提示随后模型返回回答输出中可能附带LangChainDeprecationWarning弃用警告属正常现象七、常见报错与排查7.1 ImportError: cannot import name Phi_4_LLM from LLM在调用阶段最容易遇到的报错如下报错原因LLM.py文件中定义的类名与导入语句不一致。例如一开始类名写成了Phi_4而from LLM import Phi_4_LLM这行代码是从LLM模块中导入Phi_4_LLM类两者必须保持一致。将LLM.py中的类名统一改为Phi_4_LLM后即可调用成功该报错同时提示了一个排查思路ImportError出现时先核对模块文件名LLM.py与类名Phi_4_LLM是否与导入语句完全一致再检查两个文件是否位于同一路径下。7.2 其他易踩坑点模型路径错误mode_name_or_path必须指向包含config.json、分词器文件与权重文件的完整模型目录否则from_pretrained会报文件缺失错误。显存不足28 GB 权重的 Phi-4 在 bfloat16 下仍需约 14~16 GB 显存若device_mapauto无法全部放入 GPU可接受其将部分层分配到 CPU但推理会变慢显存紧张的场景建议参考仓库中 04-Phi-4-Lora 微调 使用低秩适配缩减占用。生成结果含特殊符号若未使用skip_special_tokensTrue解码结果会包含|endoftext|等 token注意保留该参数。八、举一反三仓库中的延伸实践本文的自定义 LLM 封装思路在《开源大模型食用指南》中具有通用性同一套模式被应用于多个模型的 LangChain 接入教程中例如 Qwen2.5 Langchain 接入、GLM-4 langchain 接入、InternLM3 Langchain 接入区别仅在于各模型的聊天模板、特殊 token 与加载参数核心的继承LLM 重写_call结构完全一致。进一步地封装好的自定义 LLM 可直接用于更复杂的应用构建知识库助手参考 Atom-7B-Chat 接入 langchain 搭建知识库助手 中的 LLM.py、creat_db.py与run_gradio.py用自定义 LLM 结合向量库实现 RAG 问答。串联 FastAPI 服务如需把模型封装为 HTTP 服务供其他系统调用可参考同系列的 01-Phi-4 FastApi 部署调用其中tokenizer.pad_token_id tokenizer.eos_token_id 100265、bfloat16与device_mapauto等加载逻辑与本文完全一致。交互式 Web Demo参考 03-Phi-4 WebDemo部署 将封装好的模型接入 Gradio 前端。相关文档在仓库 support_model.md 的 phi4 小节中均有索引可以按需查阅同系列的全部教程部署、WebDemo、LoRA 微调、GRPO 微调等形成从模型接入到应用落地的完整链路。【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调全参数/Lora、部署国内外开源大模型LLM/多模态大模型MLLM教程项目地址: https://gitcode.com/datawhalechina/self-llm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考