代码结构解释

1. 自定义LLM集成

  • 继承LangChain的LLM基类,集成本地部署的ChatGLM-6B-int4模型

  • 需要提前加载模型和分词器

  • 支持自定义模型参数和类型标识

2. 文档处理流程

  • 使用CharacterTextSplitter进行文本分块

  • 通过SimpleDirectoryReader加载指定目录的文档

  • 将文档解析为可处理的文本节点(nodes)

3. 向量检索系统

  • 使用HuggingFace的多语言句子嵌入模型

  • 构建FAISS索引实现高效相似度搜索

  • 注意:需要确保嵌入维度(768)与实际模型输出一致

4. 问答系统整合

  • 组合自定义LLM和检索系统创建服务上下文

  • 支持自定义问答模板(中文字符需注意编码问题)

  • 提供verbose模式查看检索和生成过程

import os      # 操作系统接口
import faiss   # Facebook的向量相似度搜索库
from transformers import AutoTokenizer, AutoModel
from llama_index import SimpleDirectoryReader, LangchainEmbedding, GPTFaissIndex, ServiceContext
from langchain.embeddings.huggingface import HuggingFaceEmbeddings  # HuggingFace嵌入模型
from langchain.text_splitter import CharacterTextSplitter  # 文本分割器
from llama_index.node_parser import SimpleNodeParser  # 节点解析器

# 自定义LLM类(使用ChatGLM-6B-int4模型)
from langchain.llms.base import LLM
from llama_index import LLMPredictor
from typing import Optional, List, Mapping, Any


# 加载模型和分词器(需要约 8GB GPU 显存)
model_name = "THUDM/chatglm-6b-int4"  # HuggingFace 模型ID

# 加载步骤
tokenizer = AutoTokenizer.from_pretrained(
    model_name,
    trust_remote_code=True  # 必须开启信任远程代码
)
model = AutoModel.from_pretrained(
    model_name,
    trust_remote_code=True
).half().cuda()  # 转换为半精度并加载到GPU
# -------------------------------

class CustomLLM(LLM):
    def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
        # 实际调用本地模型进行推理(需要提前加载模型)
        # 示例中使用的是ChatGLM的对话接口
        response, history = model.chat(tokenizer, prompt, history=[])
        return response

    @property
    def _identifying_params(self) -> Mapping[str, Any]:
        return {"name_of_model": "chatglm-6b-int4"}  # 模型标识参数

    @property
    def _llm_type(self) -> str:
        return "custom"  # 自定义LLM类型

# ----------------- 初始化配置 -----------------
# 文本分割配置(将文档分割为小片段)
text_splitter = CharacterTextSplitter(
    separator="\n\n",  # 按空行分割
    chunk_size=100,     # 每个片段最大100字符
    chunk_overlap=20    # 片段间重叠20字符
)
parser = SimpleNodeParser(text_splitter=text_splitter)

# 加载文档数据(假设文档存储在指定目录)
documents = SimpleDirectoryReader('./drive/MyDrive/colab_data/faq/').load_data()

# 解析文档为节点(处理后的文本片段)
nodes = parser.get_nodes_from_documents(documents)

# ----------------- 模型与索引配置 -----------------
# 初始化自定义LLM预测器
llm_predictor = LLMPredictor(llm=CustomLLM())

# 配置embedding模型(使用多语言句子嵌入)
embed_model = LangchainEmbedding(
    HuggingFaceEmbeddings(
        model_name="sentence-transformers/paraphrase-multilingual-mpnet-base-v2"
    )
)

# 创建服务上下文(组合embedding模型和LLM)
service_context = ServiceContext.from_defaults(
    embed_model=embed_model,
    llm_predictor=llm_predictor
)

# 初始化FAISS索引(需要与embedding维度匹配)
dimension = 768  # 注意:需要确认实际embedding维度是否匹配
faiss_index = faiss.IndexFlatIP(dimension)  # 使用内积作为相似度度量

# 创建GPTFaiss索引(结合文档节点和FAISS索引)
index = GPTFaissIndex(
    nodes=nodes,
    faiss_index=faiss_index,
    service_context=service_context
)

# ----------------- 查询配置 -----------------
# 自定义问答提示模板
from llama_index import QuestionAnswerPrompt, QueryMode

QA_PROMPT_TMPL = (
    "{context_str}"
    "\n\n"
    "根据以上信息,请回答下面的问题:\n"
    "Q: {query_str}\n"
)
QA_PROMPT = QuestionAnswerPrompt(QA_PROMPT_TMPL)

# 执行查询示例
response = index.query(
    "请问你们海南能发货吗?", 
    mode=QueryMode.EMBEDDING,       # 使用嵌入检索模式
    text_qa_template=QA_PROMPT,     # 使用自定义提示模板
    verbose=True,                   # 显示详细过程
)

print(response)

📚 代码流程图

  1. 【书架整理】
    SimpleDirectoryReader → 把文档放进文件夹(就像把书摆上书架)
    CharacterTextSplitter → 把长文章撕成小纸片(方便查找)

  2. 【图书编号】
    HuggingFaceEmbeddings → 给每个纸片生成条形码(用数字表示文字含义)
    Faiss索引 → 把所有条形码贴到检索墙上(按数字顺序排列)

  3. 【训练图书管理员】
    CustomLLM → 雇一个懂中文的AI管理员(用ChatGLM模型)
    ServiceContext → 给管理员配发扫码枪(连接检索系统)

  4. 【提问流程】
    用户问题 → 生成问题条形码 → 在检索墙上找最相似的3个纸片
    → 管理员阅读相关纸片 → 组织语言回答

  5. 【核心口诀】
    「一问三纸片」:每个问题都通过3个最相关的文档片段生成答案

代码组件 作用 功能
SimpleDirectoryReader 图书搬运工 把文档"搬"进系统
Faiss索引 图书馆检索机 根据条形码闪电查找
ChatGLM 资深管理员 看片段就能编合理答案
QA_PROMPT_TMPL 回答模板 "根据这三张纸,应该这样回答..."

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐