使用 LangChain 框架和 Ollama 开源大模型搭建一个问答系统,根据提供的文档内容回答用户提出的问题。
系统主要由以下几个部分组成:
- 文档加载模块: 负责从指定的路径加载文档。
- 文档切分模块: 将加载的文档按照指定的大小和重叠度进行切分,以便 LLM 处理。
- LLM 加载模块: 负责加载指定的 Ollama 模型。
- 问答链创建模块: 使用 LangChain 表达式语言 (LCEL) 构建问答链,将用户问题和文档内容输入到 LLM 中,并解析 LLM 的输出。
- 提问模块: 接收用户输入的问题,调用问答链生成答案,并返回给用户。
- 重试机制: 使用 Tenacity 库,当提问模块出现错误时,自动进行重试,以提高系统的稳定性。
1. 创建一个虚拟环境,名字为langqa
conda create -n langqa python=3.12
2. 激活虚拟环境
conda activate langqa
3. 安装依赖
pip install langchain langchain_community langchain-ollama ollama tenacity
4. 部署代码
替换为你自己的文件、模型和地址
- 文件:tcpdump.1-4.4.0.txt
- 模型:llama3.1:8b
- OLLAMA :http://localhost:11434
配置完成后使用命令行进行运行对话
python langqa.py
代码
"""
Author: Qixinlee
Description: 基于LangChain和Ollama的本地文档问答系统
"""
import os
from langchain_community.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_ollama import OllamaLLM
from langchain.prompts import PromptTemplate
import ollama
from tenacity import retry, stop_after_attempt, wait_random_exponential
from langchain_core.runnables import chain
from langchain_core.output_parsers import StrOutputParser
# 常量定义
DOCUMENT_PATH = os.path.join(os.path.dirname(__file__), "tcpdump.1-4.4.0.txt")
CHUNK_SIZE = 1000
CHUNK_OVERLAP = 100
LLM_MODEL_NAME = "llama3.1:8b"
OLLAMA_HOST = "http://localhost:11434"
# 1. 设置 Ollama 连接信息
os.environ["OLLAMA_HOST"] = OLLAMA_HOST
ollama.DEFAULT_BASE_URL = OLLAMA_HOST
# 2. 加载文档
def load_document(document_path: str):
try:
loader = TextLoader(document_path)
documents = loader.load()
print(f"Successfully loaded document from {document_path}")
return documents
except Exception as e:
print(f"Error loading document: {e}")
exit()
# 3. 切分文档
def split_document(documents, chunk_size: int, chunk_overlap: int):
text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
texts = text_splitter.split_documents(documents)
return texts
# 4. 加载 LLM
def load_llm(llm_model_name: str):
try:
llm = OllamaLLM(model=llm_model_name)
print(f"Successfully loaded LLM model: {llm_model_name}")
return llm
except Exception as e:
print(f"Error loading LLM model: {e}")
exit()
# 5. 创建问答链 (使用 LangChain 表达式语言)
def create_qa_chain(llm):
try:
template = """你是一个问答机器人。请根据以下已知信息,用简洁和专业的中文回答用户的问题。
如果无法从中得到答案,请清晰地输出 “根据已知信息无法回答该问题”。
已知信息:
{context}
问题: {question}
答案:"""
QA_CHAIN_PROMPT = PromptTemplate(
input_variables=["context", "question"], template=template
)
# 使用 Runnable 序列
qa_chain = QA_CHAIN_PROMPT | llm | StrOutputParser()
print("Successfully created QA chain.")
return qa_chain
except Exception as e:
print(f"Error creating QA chain: {e}")
exit()
# 6. 提问 (使用 Tenacity 实现重试机制)
@retry(stop=stop_after_attempt(3), wait=wait_random_exponential(multiplier=1, min=4, max=10))
def generate_answer(qa_chain, document_content: str, query: str):
try:
result = qa_chain.invoke({"context": document_content, "question": query})
print("Successfully generated answer.")
return result
except Exception as e:
print(f"Error during QA: {e}")
raise
# 主程序
if __name__ == "__main__":
# 1. 加载文档
documents = load_document(DOCUMENT_PATH)
# 2. 切分文档
texts = split_document(documents, CHUNK_SIZE, CHUNK_OVERLAP)
# 3. 加载 LLM (使用常量)
llm = load_llm(LLM_MODEL_NAME)
# 4. 将所有文本块连接成一个字符串
document_content = "\n".join([text.page_content for text in texts])
# 5. 创建问答链
qa_chain = create_qa_chain(llm)
# 6. 循环提问
while True:
query = input("请输入你的问题 (输入 'exit' 退出): ")
if query.lower() == "exit":
break
try:
result = generate_answer(qa_chain, document_content, query)
print("Question (中文):", query)
print("Answer (中文):", result)
except Exception as e:
print(f"Failed to generate answer: {e}")
exit()
发表回复