You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

本地全私有化RAG搭建:使用自定义本地Embedding构建FAISS向量库报错求助

本地全私有化RAG搭建:使用自定义本地Embedding构建FAISS向量库报错求助

我太懂你这种想全本地搭建RAG的执念了——跟Hugging Face模型下载的哈希错误死磕真的会让人崩溃!你的思路完全没问题:用LM Studio本地接口生成embeddings,不依赖任何外部服务,现在卡在FAISS这一步,咱们来一步步把问题解决掉。

首先看你的报错:

TypeError: FAISS.from_texts() missing 1 required positional argument: 'embedding'

虽然你贴的代码里写的是FAISS.from_embeddings(),但报错信息显示你实际调用的是from_texts()——大概率是你运行时的代码和贴的版本有出入。不过不管怎样,核心问题是:LangChain的FAISS方法对参数格式有严格要求,而且你还缺了一个关键的「Embeddings接口对象」,用来处理后续查询时的embedding生成

问题根源拆解

LangChain的向量库(包括FAISS)依赖统一的Embeddings抽象类来处理embedding生成逻辑——哪怕你已经提前生成了文档的embeddings,FAISS也需要这个类来把用户的查询文本转换成embedding,才能进行相似性检索。你之前只手动生成了文档的embeddings,但没给FAISS提供这个接口对象,而且参数传递也不符合要求。

解决方案:自定义Embeddings和LLM类适配本地服务

我们需要做两个关键自定义:一是包装LM Studio的embedding接口成LangChain认可的Embeddings类,二是把LM Studio的LLM接口包装成LangChain的LLM类(你后面的RetrievalQA也会用到这个)。

1. 完整修正后的代码

import requests
from langchain_community.document_loaders import PyPDFLoader
from langchain.chains import RetrievalQA
from langchain_community.vectorstores.faiss import FAISS
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_core.embeddings import Embeddings
from langchain_core.llms import LLM
from typing import Optional, List, Mapping, Any

lm_studio_endpoint = "http://127.0.0.1:1234"

# 自定义Embeddings类:适配LM Studio的embedding接口
class LMStudioEmbeddings(Embeddings):
    def __init__(self, endpoint: str):
        self.endpoint = endpoint
        self.model_name = "text-embedding-granite-embedding-278m-multilingual"  # 对应你LM Studio加载的embedding模型

    def embed_documents(self, texts: list[str]) -> list[list[float]]:
        print(f"Generating embeddings for {len(texts)} chunks...")
        try:
            response = requests.post(f"{self.endpoint}/v1/embeddings", json={
                "input": texts,
                "model": self.model_name
            })
            response.raise_for_status()
            return [item['embedding'] for item in response.json()['data']]
        except requests.exceptions.RequestException as e:
            print(f"Embedding request failed: {e}")
            return []

    def embed_query(self, text: str) -> list[float]:
        # 复用批量embedding接口处理单查询
        return self.embed_documents([text])[0]

# 自定义LLM类:适配LM Studio的completion接口
class LMStudioLLM(LLM):
    def __init__(self, endpoint: str):
        self.endpoint = endpoint
        self.model_name = "你的LM Studio加载的LLM模型名"  # 比如:llama-3.2-3b-instruct-q4_K_M
        self.max_tokens = 150

    @property
    def _llm_type(self) -> str:
        return "lm_studio_local"

    def _call(
        self,
        prompt: str,
        stop: Optional[List[str]] = None,
        **kwargs: Any,
    ) -> str:
        try:
            response = requests.post(f"{self.endpoint}/v1/completions", json={
                "prompt": prompt,
                "max_tokens": self.max_tokens,
                "model": self.model_name,
                **kwargs
            })
            response.raise_for_status()
            return response.json()['choices'][0]['text'].strip()
        except requests.exceptions.RequestException as e:
            print(f"LLM request failed: {e}")
            return ""

    @property
    def _identifying_params(self) -> Mapping[str, Any]:
        return {"endpoint": self.endpoint, "model_name": self.model_name}

def setup_qa_system(file_path):
    # 加载并分割PDF
    try:
        loader = PyPDFLoader(file_path)
        docs = loader.load_and_split()
    except Exception as e:
        print(f"PDF加载失败: {e}")
        return None

    # 文本分块
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=50)
    chunks = text_splitter.split_documents(docs)

    # 初始化自定义Embeddings
    local_embeddings = LMStudioEmbeddings(lm_studio_endpoint)

    # 方法1:用from_documents自动生成embeddings(推荐,代码更简洁,保留元数据)
    vector_store = FAISS.from_documents(chunks, local_embeddings)

    # 如果你想手动用已生成的embeddings,用下面的代码替换上面一行:
    # texts = [chunk.page_content for chunk in chunks]
    # embeddings = local_embeddings.embed_documents(texts)
    # vector_store = FAISS.from_embeddings(list(zip(texts, embeddings)), local_embeddings)

    retriever = vector_store.as_retriever()

    # 初始化自定义LLM
    local_llm = LMStudioLLM(lm_studio_endpoint)

    # 构建RetrievalQA链
    qa_chain = RetrievalQA.from_chain_type(
        llm=local_llm,
        chain_type="stuff",  # 根据需求选择chain_type:stuff/map_reduce/refine等
        retriever=retriever,
        return_source_documents=True  # 可选:返回检索到的源文档
    )
    return qa_chain

if __name__ == '__main__':
    qa_chain = setup_qa_system('Documents/OfMiceAndMen.pdf')

    if qa_chain:
        query = "What is the main theme of 'Of Mice and Men'?"
        result = qa_chain.invoke({"query": query})
        
        print("=== 回答 ===")
        print(result['result'])
        print("\n=== 参考源文档 ===")
        for doc in result['source_documents']:
            print(f"- 页码: {doc.metadata['page']}, 内容片段: {doc.page_content[:200]}...")

2. 关键修改点说明

  • 自定义Embeddings类:实现LangChain的Embeddings抽象方法,把LM Studio的接口封装起来,FAISS会用它来生成文档embeddings和查询embeddings,完美适配LangChain的生态。
  • 自定义LLM类:同样实现LangChain的LLM抽象方法,这样RetrievalQA就能正确调用你的本地LLM,而不是像之前那样传一个普通函数(这也是你后续会踩的坑)。
  • FAISS初始化:推荐用from_documents(),LangChain会自动处理分块、生成embeddings的流程,代码更简洁,还能保留PDF的页码等元数据;如果坚持手动生成embeddings,用from_embeddings()时第二个参数必须传自定义的Embeddings对象,而不是embeddings列表。
  • RetrievalQA调用:用invoke()方法统一调用,而不是手动去获取retriever再调用LLM,这是LangChain的标准用法,更稳定。

额外注意事项

  • 确保LM Studio里加载的embedding模型和LLM模型名和代码里的一致,端口1234没错。
  • 如果分块后embedding请求失败,可能是LM Studio的embedding模型不支持批量请求,可以在embed_documents里把文本分批处理。
  • 调试时可以在自定义类里加日志,查看请求的参数和返回的结果,方便定位问题。

备注:内容来源于stack exchange,提问作者aceofjohnonlone

火山引擎 最新活动