基于 InternLM 与 LangChain 搭建本地知识库助手:从向量库构建到 Gradio Web Demo 全流程实战
【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm
本文以开源大模型 InternLM(InternLM-Chat-7B-V1.1)为核心,完整讲解如何将本地部署的 InternLM 接入 LangChain 框架,实现"文档加载 → 文本分块 → 向量化 → 向量数据库 → 检索问答链 → Gradio Web Demo"的知识库助手全链路搭建。读完本文,你将掌握 LangChain 自定义 LLM 类的封装方法、RAG 检索增强问答的核心实现,以及如何用三份脚本复现一个可对话、可部署的本地知识库问答系统。
本文配套代码位于仓库 models/InternLM/06-InternLM接入LangChain搭建知识库助手/,其中 readme.md 对代码文件做了如下组织说明:
creat_db.py负责构建向量数据库,LLM.py将 InternLM 封装为自定义 LLM,run_gradio.py负责启动 Gradio 服务。三个脚本分工明确,正好对应本文的三条主线:知识库搭建、LLM 接入、Web 部署。
环境配置与依赖安装
本教程建立在已完成 InternLM 本地部署的前提之上(Transformers 方式加载模型,模型参数存放于本地/root/autodl-tmp/model)。若尚未部署,可参考仓库中的 01-InternLM-Chat-7B Transformers 部署调用.md:通过魔塔社区snapshot_download下载internlm-chat-7b权重,并使用AutoTokenizer/AutoModelForCausalLM配合trust_remote_code=True完成加载与对话调用。
在完成模型部署的基础上,还需要安装以下依赖包:
pip install langchain==0.0.292 pip install gradio==4.4.0 pip install chromadb==0.4.15 pip install sentence-transformers==2.2.2 pip install unstructured==0.10.30 pip install markdown==3.3.7各依赖在链路中的职责如下:
| 依赖包 | 版本 | 在本项目中的职责 |
|---|---|---|
| langchain | 0.0.292 | 提供文档加载器、文本分割器、Embeddings 接口、向量库封装与 RetrievalQA 链 |
| gradio | 4.4.0 | 构建 Web 聊天界面(Chatbot、Textbox、Button 组件) |
| chromadb | 0.4.15 | 本地持久化向量数据库,存储语料向量并支持相似度检索 |
| sentence-transformers | 2.2.2 | 运行开源词向量模型,完成文本到向量的编码 |
| unstructured | 0.10.30 | 解析 Markdown / TXT 等非结构化文档 |
| markdown | 3.3.7 | UnstructuredMarkdownLoader 解析 .md 文件的底层依赖 |
同时,我们需要使用开源词向量模型 Sentence Transformer(paraphrase-multilingual-MiniLM-L12-v2,一个多语言句向量模型)。可以仿照下载 InternLM 模型参数的方式,将其下载到本地/root/autodl-tmp/embedding_model。这样,后续所有向量化操作都在本地完成,不依赖外部 API。
第一步:语料准备与知识库搭建
知识库助手的核心价值在于"让模型基于私有/指定语料回答"。因此第一步是把选定的文档集切分成片段、向量化并持久化到向量数据库中。
1.1 语料来源与获取
本项目选用上海人工智能实验室开源的一系列大模型工具仓库作为语料库来源,包括:OpenCompass(评测)、lmdeploy(部署)、Xtuner(微调)、InternLM-XComposer(多模态)、Lagent(智能体)、InternLM(大模型本体)。
将这些远程开源仓库 Clone 到本地(以 AutoDL 数据盘/root/autodl-tmp为例):
# 进入到数据库盘 cd /root/autodl-tmp # 打开学术资源加速 source /etc/network_turbo # clone 上述开源仓库(依次执行 git clone 对应仓库地址) git clone <OpenCompass 仓库地址> git clone <lmdeploy 仓库地址> git clone <xtuner 仓库地址> git clone <InternLM-XComposer 仓库地址> git clone <lagent 仓库地址> git clone <InternLM 仓库地址> # 关闭学术资源加速 unset http_proxy && unset https_proxy为便于语料处理,本文选用上述仓库中所有的 Markdown、TXT 文件作为示例语料。注意:也可以将代码文件纳入知识库,但需要针对代码文件格式额外做处理(例如更换对应的文档加载器),本文不展开。
1.2 递归收集目标文件路径
首先定义一个函数,递归遍历指定文件夹,返回其中所有后缀名为.md或.txt的文件绝对路径:
import os def get_files(dir_path): # args:dir_path,目标文件夹路径 file_list = [] for filepath, dirnames, filenames in os.walk(dir_path): # os.walk 函数将递归遍历指定文件夹 for filename in filenames: # 通过后缀名判断文件类型是否满足要求 if filename.endswith(".md"): # 如果满足要求,将其绝对路径加入到结果列表 file_list.append(os.path.join(filepath, filename)) elif filename.endswith(".txt"): file_list.append(os.path.join(filepath, filename)) return file_list这里使用os.walk自顶向下递归目录树,endswith只保留.md与.txt两类文件。
1.3 文档加载与解析
拿到文件路径列表后,借助 LangChain 的文档加载器(Loader)将磁盘文件解析为统一的纯文本对象。不同文件类型对应不同的 Loader,需要根据后缀名分发:
from tqdm import tqdm from langchain.document_loaders import UnstructuredFileLoader from langchain.document_loaders import UnstructuredMarkdownLoader def get_text(dir_path): # args:dir_path,目标文件夹路径 # 首先调用上文定义的函数得到目标文件路径列表 file_lst = get_files(dir_path) # docs 存放加载之后的纯文本对象 docs = [] # 遍历所有目标文件 for one_file in tqdm(file_lst): file_type = one_file.split('.')[-1] if file_type == 'md': loader = UnstructuredMarkdownLoader(one_file) elif file_type == 'txt': loader = UnstructuredFileLoader(one_file) else: # 如果是不符合条件的文件,直接跳过 continue docs.extend(loader.load()) return docsloader.load()返回的是一个Document对象列表,每个Document包含page_content(纯文本正文)与metadata(来源路径等元信息)。
1.4 文本分块(Text Splitting)
直接对整篇文档做向量化既不经济也不利于精准检索,因此需要先将长文档切成块。LangChain 提供了多种文本分割器,此处使用RecursiveCharacterTextSplitter(字符串递归分割器),并选择分块大小为 500 字符、块与块之间重叠 150 字符:
from langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=150) split_docs = text_splitter.split_documents(docs)chunk_size=500:每个文本块的最大字符数。块越小检索越精准,但会丢失上下文连贯性;chunk_overlap=150:相邻块之间重叠的字符数,用于保留跨块上下文的衔接,避免关键信息恰好被切在边界处。
两个参数可根据语料长度和检索效果动态调整,是影响 RAG 效果的重要超参数。
1.5 向量化与向量数据库构建
文本块需要转换为向量才能被语义检索。LangChain 提供了直接引入 HuggingFace 开源社区模型的向量化接口:
from langchain.embeddings.huggingface import HuggingFaceEmbeddings embeddings = HuggingFaceEmbeddings(model_name="/root/autodl-tmp/embedding_model")向量数据库选择 Chroma(本地持久化方案)。基于分块后的文档与上述词向量模型,将语料写入指定路径:
from langchain.vectorstores import Chroma # 定义持久化路径 persist_directory = 'data_base/vector_db/chroma' # 加载数据库 vectordb = Chroma.from_documents( documents=split_docs, embedding=embeddings, persist_directory=persist_directory # 允许我们将persist_directory目录保存到磁盘上 ) # 将加载的向量数据库持久化到磁盘上 vectordb.persist()1.6 完整脚本 creat_db.py
将上述步骤整合,即得到完整的知识库构建脚本(与仓库中 creat_db.py 一致)。需要说明的是,仓库脚本中为快速验证流程,对split_documents(docs[:10])仅切分前 10 个文档,实际构建完整知识库时应去掉切片,直接处理全部文档:
# 首先导入所需第三方库 from langchain.document_loaders import UnstructuredFileLoader from langchain.document_loaders import UnstructuredMarkdownLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.vectorstores import Chroma from langchain.embeddings.huggingface import HuggingFaceEmbeddings from tqdm import tqdm import os # 获取文件路径函数 def get_files(dir_path): # args:dir_path,目标文件夹路径 file_list = [] for filepath, dirnames, filenames in os.walk(dir_path): # os.walk 函数将递归遍历指定文件夹 for filename in filenames: # 通过后缀名判断文件类型是否满足要求 if filename.endswith(".md"): # 如果满足要求,将其绝对路径加入到结果列表 file_list.append(os.path.join(filepath, filename)) elif filename.endswith(".txt"): file_list.append(os.path.join(filepath, filename)) return file_list # 加载文件函数 def get_text(dir_path): # args:dir_path,目标文件夹路径 # 首先调用上文定义的函数得到目标文件路径列表 file_lst = get_files(dir_path) # docs 存放加载之后的纯文本对象 docs = [] # 遍历所有目标文件 for one_file in tqdm(file_lst): file_type = one_file.split('.')[-1] if file_type == 'md': loader = UnstructuredMarkdownLoader(one_file) elif file_type == 'txt': loader = UnstructuredFileLoader(one_file) else: # 如果是不符合条件的文件,直接跳过 continue docs.extend(loader.load()) return docs # 目标文件夹 tar_dir = [ "/root/autodl-tmp/InternLM", "/root/autodl-tmp/InternLM-XComposer", "/root/autodl-tmp/lagent", "/root/autodl-tmp/lmdeploy", "/root/autodl-tmp/opencompass", "/root/autodl-tmp/xtuner" ] # 加载目标文件 docs = [] for dir_path in tar_dir: docs.extend(get_text(dir_path)) # 对文本进行分块 text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=150) split_docs = text_splitter.split_documents(docs) # 加载开源词向量模型 embeddings = HuggingFaceEmbeddings(model_name="/root/autodl-tmp/embedding_model") # 构建向量数据库 # 定义持久化路径 persist_directory = 'data_base/vector_db/chroma' # 加载数据库 vectordb = Chroma.from_documents( documents=split_docs, embedding=embeddings, persist_directory=persist_directory # 允许我们将persist_directory目录保存到磁盘上 ) # 将加载的向量数据库持久化到磁盘上 vectordb.persist()运行该脚本后,本地即生成已持久化的 Chroma 向量数据库(位于data_base/vector_db/chroma)。后续使用时直接导入该数据库即可,无需重复构建。这也是把向量库构建独立成creat_db.py脚本的价值所在——一次构建、多次复用。
第二步:将 InternLM 封装为自定义 LLM 接入 LangChain
LangChain 的应用构建依赖统一的LLM抽象接口。InternLM 并未原生提供 LangChain 封装,因此我们需要基于本地部署的 InternLM 自定义一个 LLM 类,将其接入 LangChain 框架。完成自定义之后,就可以用完全一致的方式调用 LangChain 的接口,而不必考虑底层模型调用的差异。
自定义过程并不复杂:从langchain.llms.base.LLM继承一个子类,重写构造函数与_call函数即可(对应仓库中的 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 import torch class InternLM_LLM(LLM): # 基于本地 InternLM 自定义 LLM 类 tokenizer : AutoTokenizer = None model: AutoModelForCausalLM = None def __init__(self, model_path :str): # model_path: InternLM 模型路径 # 从本地初始化模型 super().__init__() print("正在从本地加载模型...") self.tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) self.model = AutoModelForCausalLM.from_pretrained(model_path, trust_remote_code=True).to(torch.bfloat16).cuda() self.model = self.model.eval() print("完成本地模型的加载") def _call(self, prompt : str, stop: Optional[List[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any): # 重写调用函数 response, history = self.model.chat(self.tokenizer, prompt , history=[]) return response @property def _llm_type(self) -> str: return "InternLM"2.1 构造函数:模型一次性加载
在__init__中,通过AutoTokenizer.from_pretrained与AutoModelForCausalLM.from_pretrained加载本地模型,并以torch.bfloat16精度搬移到 CUDA,最后调用eval()切换到推理模式。这里的trust_remote_code=True表示信任并加载模型自带的远程自定义代码(InternLM 的modeling_internlm.py等),这与仓库中 01-InternLM-Chat-7B Transformers 部署调用.md 的模型加载方式完全一致。
将模型加载放在构造函数中的关键意义在于:整个应用生命周期内模型只加载一次,后续每次提问直接复用,避免了每次调用都重新加载 14GB 权重带来的巨大时间开销。
2.2_call函数:LLM 类的核心调用入口
_call是 LangChainLLM基类要求子类实现的核心方法,LangChain 会通过它来完成对模型的真正调用。在该函数中,我们调用已实例化模型的chat方法(InternLM 自带的对话生成接口),传入 prompt 与空历史history=[],取返回的response作为结果返回。
此外,_llm_type属性返回自定义类型名"InternLM",用于标识该 LLM 实例的类型。
在整体项目中,这段代码封装为LLM.py,后续通过from LLM import InternLM_LLM直接引入自定义的 LLM 类。
第三步:构建检索问答链 RetrievalQA
知识库与 LLM 就绪后,LangChain 通过RetrievalQA检索问答链对象封装 RAG(检索增强生成)全流程:给定用户提问 → 语义检索相关文档 → 拼装为 Prompt → 交给 LLM 生成答案。RetrievalQA自动完成上述全部环节,我们只需在初始化时填入已构建的向量数据库和自定义 LLM。
3.1 加载已构建的向量数据库
通过 Chroma 与上文定义的词向量模型直接加载持久化数据库:
from langchain.vectorstores import Chroma from langchain.embeddings.huggingface import HuggingFaceEmbeddings import os # 定义 Embeddings embeddings = HuggingFaceEmbeddings(model_name="/root/autodl-tmp/embedding_model") # 向量数据库持久化路径 persist_directory = 'data_base/vector_db/chroma' # 加载数据库 vectordb = Chroma( persist_directory=persist_directory, embedding_function=embeddings )这里与构建阶段不同的是传入persist_directory直接读取磁盘上的向量库,embedding_function需要与建库时使用同一个词向量模型,否则查询向量与库内向量分布不一致,检索会失真。
vectordb对象可针对用户的query进行语义向量检索,返回与提问相关的知识片段(as_retriever()会将其包装为 LangChain 标准的 Retriever 接口)。
3.2 实例化自定义 LLM
接着实例化基于 InternLM 自定义的 LLM 对象:
from LLM import InternLM_LLM llm = InternLM_LLM(model_path = "/root/autodl-tmp/model") llm.predict("你是谁")predict是 LangChain LLM 基类提供的方法,内部会调用我们重写的_call。这行代码可以验证 InternLM 已成功接入 LangChain 接口体系。
3.3 构造 Prompt Template
检索到相关文档后,需要把它们与用户提问拼装成一个带知识的 Prompt。Prompt Template 本质上是一个带变量的字符串模板,LangChain 会在检索后把文档片段与问题分别填入变量:
from langchain.prompts import PromptTemplate # 我们所构造的 Prompt 模板 template = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答案。尽量使答案简明扼要。总是在回答的最后说“谢谢你的提问!”。 {context} 问题: {question} 有用的回答:""" # 调用 LangChain 的方法来实例化一个 Template 对象,该对象包含了 context 和 question 两个变量,在实际调用时,这两个变量会被检索到的文档片段和用户提问填充 QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],template=template)该模板同时约束了模型的回答风格与事实边界:要求模型基于{context}(检索到的知识片段)作答、不知道时明确承认而不是编造,并在回答末尾附加固定礼貌用语。这些约束对知识库助手的可靠性至关重要。
3.4 组装 RetrievalQA 检索问答链
最后调用 LangChain 的检索问答链构造函数,基于自定义 LLM、Prompt Template 和向量知识库构建完整的检索问答链:
from langchain.chains import RetrievalQA qa_chain = RetrievalQA.from_chain_type( llm, retriever=vectordb.as_retriever(), return_source_documents=True, chain_type_kwargs={"prompt": QA_CHAIN_PROMPT} )关键参数说明:
llm:传入自定义的InternLM_LLM实例;retriever:向量库的检索器,负责根据 query 召回 Top-K 相关文档片段;return_source_documents=True:在结果中同时返回命中的源文档,便于核查答案出处;chain_type_kwargs={"prompt": QA_CHAIN_PROMPT}:指定链内使用的 Prompt 模板,覆盖默认模板。
得到的qa_chain对象即实现了核心功能——基于 InternLM 的专业知识库助手。
3.5 效果对比:检索问答链 vs 纯 LLM
可以对比检索问答链与纯 LLM 的问答效果,直观感受知识库的价值:
# 检索问答链回答效果 question = "什么是InternLM" result = qa_chain({"query": question}) print("检索问答链回答 question 的结果:") print(result["result"]) # 仅 LLM 回答效果 result_2 = llm(question) print("大模型回答 question 的结果:") print(result_2)注意调用方式的差异:qa_chain({"query": question})使用链的标准输入键query,通过result["result"]取答案;纯 LLM 则直接llm(question)。检索问答链的回答会以语料库中的真实文档为依据,而纯 LLM 只能依赖训练时学到的知识——这正是 RAG 能够消除幻觉、补充私有知识的原因。
第四步:基于 Gradio 部署 Web Demo
核心功能验证通过后,使用 Gradio 将其部署为 Web 页面,搭建一个小型 Demo 便于测试与使用(对应仓库中的 run_gradio.py)。
4.1 封装问答链加载函数
先将上文的组装逻辑封装为一个返回检索问答链对象的函数load_chain,并在 Gradio 启动的第一时间调用,后续直接复用该对象进行问答,避免重复加载模型:
from langchain.vectorstores import Chroma from langchain.embeddings.huggingface import HuggingFaceEmbeddings import os from LLM import InternLM_LLM from langchain.prompts import PromptTemplate from langchain.chains import RetrievalQA def load_chain(): # 加载问答链 # 定义 Embeddings embeddings = HuggingFaceEmbeddings(model_name="/root/autodl-tmp/embedding_model") # 向量数据库持久化路径 persist_directory = 'data_base/vector_db/chroma' # 加载数据库 vectordb = Chroma( persist_directory=persist_directory, # 允许我们将persist_directory目录保存到磁盘上 embedding_function=embeddings ) # 加载自定义 LLM llm = InternLM_LLM(model_path = "/root/autodl-tmp/model") # 定义一个 Prompt Template template = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答 案。尽量使答案简明扼要。总是在回答的最后说“谢谢你的提问!”。 {context} 问题: {question} 有用的回答:""" QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],template=template) # 运行 chain qa_chain = RetrievalQA.from_chain_type( llm, retriever=vectordb.as_retriever(), return_source_documents=True, chain_type_kwargs={"prompt": QA_CHAIN_PROMPT} ) return qa_chain4.2 Model_center 问答中心类
定义一个类负责加载并存储检索问答链,同时响应 Web 界面的问答动作:
class Model_center(): """ 存储检索问答链的对象 """ def __init__(self): # 构造函数,加载检索问答链 self.chain = load_chain() def qa_chain_self_answer(self, question: str, chat_history: list = []): """ 调用问答链进行回答 """ if question == None or len(question) < 1: return "", chat_history try: chat_history.append( (question, self.chain({"query": question})["result"])) # 将问答结果直接附加到问答历史中,Gradio 会将其展示出来 return "", chat_history except Exception as e: return e, chat_history def clear_history(self): self.chain.clear_history()qa_chain_self_answer:接收用户问题与聊天历史,调用问答链生成答案并追加到历史中,返回给 Gradio 渲染;对空输入直接返回,对异常则把错误信息反馈到输入框;clear_history:清空后端存储的聊天记录。
4.3 Gradio 界面搭建
按 Gradio 的框架用法实例化 Web 界面,并将按钮点击动作绑定到上述类的回答方法:
import gradio as gr # 实例化核心功能对象 model_center = Model_center() # 创建一个 Web 界面 block = gr.Blocks() with block as demo: with gr.Row(equal_height=True): with gr.Column(scale=15): # 展示的页面标题 gr.Markdown("""<h1><center>InternLM</center></h1> <center>书生蒲语</center> """) with gr.Row(): with gr.Column(scale=4): # 创建一个聊天机器人对象 chatbot = gr.Chatbot(height=450, show_copy_button=True) # 创建一个文本框组件,用于输入 prompt。 msg = gr.Textbox(label="Prompt/问题") with gr.Row(): # 创建提交按钮。 db_wo_his_btn = gr.Button("Chat") with gr.Row(): # 创建一个清除按钮,用于清除聊天机器人组件的内容。 clear = gr.ClearButton( components=[chatbot], value="Clear console") # 设置按钮的点击事件。当点击时,调用上面定义的 qa_chain_self_answer 函数,并传入用户的消息和聊天历史记录,然后更新文本框和聊天机器人组件。 db_wo_his_btn.click(model_center.qa_chain_self_answer, inputs=[ msg, chatbot], outputs=[msg, chatbot]) # 点击后清空后端存储的聊天记录 clear.click(model_center.clear_history) gr.Markdown("""提醒:<br> 1. 初始化数据库时间可能较长,请耐心等待。 2. 使用中如果出现异常,将会在文本输入框进行展示,请不要惊慌。 <br> """) gr.close_all() # 直接启动 demo.launch()界面元素说明:gr.Chatbot渲染对话气泡并支持复制;gr.Textbox承接用户输入;gr.Button("Chat")的click事件把文本框与聊天记录作为输入,交给qa_chain_self_answer,输出更新回文本框与聊天组件;ClearButton一键清屏。底部 Markdown 则给出两条使用提醒。
4.4 启动与访问
将上述代码整体保存为run_gradio.py(与仓库中的脚本一致),直接运行:
python run_gradio.py启动成功后,默认会在 7860 端口运行(demo.launch()默认行为)。在 AutoDL 等云服务器上,可使用类似于模型部署的方式将服务器端口映射到本地端口即可通过浏览器访问。交互效果如上文截图所示:输入"什么是InternLM",系统会基于本地语料给出回答。
若需指定端口或以分享链接方式启动,可参考仓库脚本中被注释的备选写法demo.launch(share=True, server_port=int(os.environ['PORT1']))。
常见问题与排查思路
- 依赖版本不匹配:本教程的导入路径(如
langchain.llms.base.LLM、langchain.embeddings.huggingface)与 LangChain 0.0.292 版本对应,新版 LangChain 对模块路径有较大调整,建议按教程锁定版本安装; - 首次加载耗时:启动时会依次加载词向量模型与 14GB 左右的 InternLM 权重,初始化数据库/模型时间较长,属正常现象,请耐心等待;
- 检索效果不佳:优先调整
chunk_size与chunk_overlap,或确认查询时使用的embedding_function与建库时一致; - 显存不足:
InternLM_LLM.__init__中以torch.bfloat16精度加载模型,若仍显存不足,可考虑量化部署方式; - 运行异常:Demo 会将异常信息展示在文本输入框中,可据此定位问题,无需惊慌。
总结
本文基于仓库 models/InternLM/06-InternLM接入LangChain搭建知识库助手/ 的完整代码,走通了"语料收集 → 文档解析 → 文本分块 → 向量化 → Chroma 持久化 → 自定义 LLM 接入 → RetrievalQA 检索问答 → Gradio Web Demo"的完整链路。三份脚本分工清晰、层层递进:
- creat_db.py:知识库构建,产出可复用的持久化向量数据库;
- LLM.py:InternLM 自定义 LLM 封装,打通 LangChain 生态;
- run_gradio.py:检索问答链 + Gradio 界面,交付可交互的知识库助手。
理解这条链路后,你可以将语料替换为任意领域的私有文档(产品文档、论文、公司知识库等),即可快速复刻出一个基于 InternLM 的专属知识库问答系统。
【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考