You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Langchain+RAG基于Llama检索FAQ PDF内容出错问题排查

问题描述

我用Langchain、RAG结合Llama,基于带编号问答列表的PDF格式FAQ文档实现问答功能,代码能正常运行,但检索存在问题——即便提问文档里已有的相同问题,Langchain也匹配不到正确的问答对,仅返回相似但无法解答问题的文档片段。

代码如下:

from langchain.document_loaders import PyPDFLoader

loader = PyPDFLoader("doc.pdf")
data = loader.load_and_split()

from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(chunk_size=1500, chunk_overlap=100)
all_splits = text_splitter.split_documents(data)

# Embed and store
from langchain.embeddings import (
    GPT4AllEmbeddings,
    OllamaEmbeddings,  # We can also try Ollama embeddings
)
from langchain.vectorstores import Chroma

vectorstore = Chroma.from_documents(documents=all_splits, embedding=GPT4AllEmbeddings())

from langchain.prompts import PromptTemplate

template = "..."

QA_CHAIN_PROMPT = PromptTemplate.from_template(template)# Run chain

# LLM
from langchain.callbacks.manager import CallbackManager
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
import requests
import json

# QA chain
from langchain.chains import RetrievalQA

qa_chain = RetrievalQA.from_chain_type(
    llm,
    retriever=vectorstore.as_retriever(),
    return_source_documents=True,
    chain_type_kwargs={"prompt": QA_CHAIN_PROMPT}
)

question = "..."
print(qa_chain({"query": question}))
问题原因及优化方案

一、核心问题定位

  1. 文档拆分不合理:使用固定字符数的RecursiveCharacterTextSplitter会破坏FAQ的问答对完整性,把一个完整的Q&A拆成多个片段,导致检索时无法获取完整上下文,自然匹配不到正确结果。
  2. 嵌入模型适配性不足:GPT4AllEmbeddings对中文FAQ的语义匹配精度有限,或与Llama的适配性不佳,导致相同问题的向量相似度计算偏差,无法召回正确片段。
  3. 检索器参数未优化:默认检索器未设置相似度阈值、召回数量等参数,可能返回低相关度片段;且未采用FAQ场景适配的混合检索策略。
  4. Prompt缺失关键引导:空模板未明确要求LLM优先匹配检索到的FAQ问答对,导致模型忽略正确内容,自行生成或选取无关片段。

二、具体优化措施

1. 按问答对拆分文档

替换文本拆分器,基于FAQ的编号格式(如1. 问题:XXX\n回答:XXX)自定义拆分逻辑,确保每个片段是完整的Q&A:

from langchain.text_splitter import CharacterTextSplitter

# 以编号(如"1. "、"2. ")作为拆分边界,保留完整问答对
text_splitter = CharacterTextSplitter(
    separator=r"\n\d+\. ",
    chunk_size=1,
    chunk_overlap=0,
    is_separator_regex=True
)
all_splits = text_splitter.split_documents(data)
# 拆分后补回编号,确保每个片段的完整性
processed_splits = []
for i, doc in enumerate(all_splits):
    if i == 0:
        processed_splits.append(doc)
    else:
        doc.page_content = f"{i+1}. {doc.page_content}"
        processed_splits.append(doc)
all_splits = processed_splits

2. 更换适配的嵌入模型

改用Ollama嵌入模型(与Llama适配性更好)或中文专用嵌入模型:

# 使用Ollama的Llama2嵌入
vectorstore = Chroma.from_documents(
    documents=all_splits,
    embedding=OllamaEmbeddings(model="llama2")
)

# 中文场景可选BGE中文嵌入
from langchain.embeddings import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-base-zh-v1.5")
vectorstore = Chroma.from_documents(documents=all_splits, embedding=embeddings)

3. 优化检索器配置

调整检索器参数,或使用多查询检索提升召回率:

# 基础优化:设置相似度阈值和召回数量
retriever = vectorstore.as_retriever(
    search_type="similarity_score_threshold",
    search_kwargs={"k": 5, "score_threshold": 0.7}
)

# 进阶:用MultiQueryRetriever生成多个相关查询,提升召回精度
from langchain.retrievers.multi_query import MultiQueryRetriever
retriever = MultiQueryRetriever.from_llm(
    retriever=vectorstore.as_retriever(),
    llm=llm
)

4. 完善Prompt模板

给LLM明确指令,要求优先匹配检索到的FAQ内容:

template = """你是FAQ专属问答助手,必须严格遵循以下规则回答:
1. 优先在提供的FAQ片段中查找完全匹配或高度相似的问题
2. 找到匹配问题后,直接使用对应的回答内容,不得自行创作
3. 若未找到匹配问题,直接告知用户"暂无相关解答"

FAQ片段:
{context}

用户问题:{question}
你的回答:"""

QA_CHAIN_PROMPT = PromptTemplate.from_template(template)

5. 改用结构化FAQ检索方案

针对结构化FAQ,可先做精确/模糊匹配,向量检索作为兜底:

# 预处理FAQ为字典:{问题: 回答}
faq_dict = {}
for doc in all_splits:
    content = doc.page_content
    # 假设格式为"X. 问题:XXX\n回答:XXX"
    q_part, a_part = content.split("\n回答:", 1)
    question = q_part.split("问题:", 1)[1].strip()
    faq_dict[question] = a_part.strip()

# 优先精确匹配,再模糊匹配,最后向量检索兜底
def get_faq_answer(question):
    # 精确匹配
    if question in faq_dict:
        return faq_dict[question]
    # 模糊匹配
    from difflib import get_close_matches
    close_matches = get_close_matches(question, faq_dict.keys(), n=1, cutoff=0.8)
    if close_matches:
        return faq_dict[close_matches[0]]
    # 向量检索兜底
    result = qa_chain({"query": question})
    return result["result"]

三、无法匹配正确问答对的核心原因

  1. 拆分破坏了问答对的完整性,向量存储的是不完整语义,无法匹配完整的Q&A上下文;
  2. 嵌入模型对FAQ场景的语义捕捉精度不足,导致相同问题的向量相似度偏低,被其他片段覆盖;
  3. 检索策略和Prompt缺乏针对性,未优先匹配精确问题,也未引导LLM使用正确的FAQ内容。

内容的提问来源于stack exchange,提问作者ABinx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 19:28:40