LangChain整合维基百科实现问答系统
引言
随着大语言模型(LLM)的快速发展,结合外部知识源的问答系统变得越来越受欢迎。本教程将介绍如何使用LangChain框架整合维基百科数据,构建一个能够回答有关特定主题问题的问答系统。通过这个系统,用户可以输入关键词获取维基百科内容,然后针对这些内容提出问题,获得准确的回答。
1. 系统架构
我们将构建的问答系统包含以下几个核心组件:
- 数据获取:从维基百科爬取特定主题的内容
- 文本处理:将获取的内容分割成适当大小的块
- 向量化存储:使用FAISS向量数据库存储文本嵌入
- 问答链:利用LangChain的问答链处理用户查询
- 用户界面:使用Streamlit构建简单友好的交互界面
2. 环境准备
首先,我们需要安装必要的依赖库:
pip install langchain langchain-openai faiss-cpu bs4 wikipedia streamlit python-dotenv requests
3. 导入依赖库
接下来,我们导入所需的库:
import os
import requests
from bs4 import BeautifulSoup
import wikipedia
import streamlit as st
from dotenv import load_dotenv
from langchain_openai import OpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_openai import ChatOpenAI
from langchain.chains.question_answering import load_qa_chain
from langchain.prompts import PromptTemplate
# 加载环境变量
load_dotenv()
4. 爬取维基百科内容
我们首先实现从维基百科获取内容的功能:
def get_wiki(query):
"""
获取维基百科内容
Args:
query: 搜索关键词
Returns:
tuple: (页面内容, 摘要)
"""
try:
# 获取维基百科摘要
summary = wikipedia.summary(query)
# 获取维基百科页面URL
page = wikipedia.page(query)
url = page.url
# 使用requests获取完整页面内容
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
# 提取正文内容
content_div = soup.find('div', {'id': 'mw-content-text'})
paragraphs = content_div.find_all('p')
# 合并所有段落文本
full_content = '\n'.join([p.text for p in paragraphs])
return full_content, summary
except wikipedia.exceptions.DisambiguationError as e:
# 处理歧义词条
st.error(f"找到多个匹配项,请尝试更具体的搜索词: {', '.join(e.options[:5])}")
return None, None
except wikipedia.exceptions.PageError:
# 处理页面不存在的情况
st.error(f"找不到与'{query}'相关的维基百科页面")
return None, None
except Exception as e:
st.error(f"发生错误: {str(e)}")
return None, None
5. 设置用户界面
使用Streamlit创建简单易用的用户界面:
def main():
# 设置页面标题
st.title("基于维基百科的问答系统")
# 侧边栏 - 输入OpenAI API密钥
with st.sidebar:
st.header("配置")
api_key = st.text_input("输入您的OpenAI API密钥", type="password")
if api_key:
os.environ["OPENAI_API_KEY"] = api_key
# 主界面 - 搜索维基百科
st.subheader("第一步: 搜索维基百科")
query = st.text_input("请输入要检索的关键词")
if st.button("获取维基百科内容"):
if not query:
st.warning("请输入搜索关键词")
return
with st.spinner("正在从维基百科获取内容..."):
full_content, summary = get_wiki(query)
if full_content and summary:
st.session_state.wiki_content = full_content
st.session_state.wiki_summary = summary
st.success("内容获取成功!")
# 显示摘要
st.subheader("维基百科摘要")
st.write(summary)
# 处理文本并创建向量存储
process_text_and_create_index(full_content)
# 如果已经创建了向量存储,显示问答界面
if 'vectorstore' in st.session_state:
st.subheader("第二步: 提问")
question = st.text_input("请输入您的问题")
if st.button("提交问题"):
if not question:
st.warning("请输入问题")
return
if not api_key:
st.warning("请先输入OpenAI API密钥")
return
with st.spinner("正在思考..."):
answer = answer_question(question)
st.subheader("回答")
st.write(answer)
6. 文本处理和向量存储
接下来,我们需要处理获取的文本并创建向量存储:
def process_text_and_create_index(text):
"""
处理文本并创建向量索引
Args:
text: 要处理的文本内容
"""
try:
# 文本分割
text_splitter = CharacterTextSplitter(
separator="\n",
chunk_size=1000,
chunk_overlap=200,
length_function=len
)
chunks = text_splitter.split_text(text)
st.info(f"文本已分割成 {len(chunks)} 个块")
# 创建向量存储
with st.spinner("正在创建向量索引..."):
embeddings = OpenAIEmbeddings()
vectorstore = FAISS.from_texts(chunks, embeddings)
st.session_state.vectorstore = vectorstore
st.success("向量索引创建成功!")
except Exception as e:
st.error(f"创建索引时出错: {str(e)}")
7. 实现问答功能
最后,我们实现问答功能:
def answer_question(question):
"""
回答问题
Args:
question: 用户问题
Returns:
str: 回答内容
"""
try:
# 获取相关文档
vectorstore = st.session_state.vectorstore
docs = vectorstore.similarity_search(question, k=4)
# 创建自定义提示模板
template = """
你是一个有帮助的AI助手,专门回答关于维基百科内容的问题。
使用以下上下文来回答问题。如果你不知道答案,就说你不知道,不要编造答案。
尽量给出简洁、准确的回答。
上下文: {context}
问题: {question}
回答:
"""
PROMPT = PromptTemplate(
template=template,
input_variables=["context", "question"]
)
# 创建问答链
chain = load_qa_chain(
ChatOpenAI(temperature=0, model_name="gpt-3.5-turbo"),
chain_type="stuff",
prompt=PROMPT
)
# 获取回答
response = chain.run(input_documents=docs, question=question)
return response
except Exception as e:
return f"回答问题时出错: {str(e)}"
8. 运行应用程序
最后,我们添加主函数入口:
if __name__ == "__main__":
main()
将上述代码保存为app.py,然后运行:
streamlit run app.py
9. 完整代码
下面是完整的应用程序代码:
import os
import requests
from bs4 import BeautifulSoup
import wikipedia
import streamlit as st
from dotenv import load_dotenv
from langchain_openai import OpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_openai import ChatOpenAI
from langchain.chains.question_answering import load_qa_chain
from langchain.prompts import PromptTemplate
# 加载环境变量
load_dotenv()
def get_wiki(query):
"""
获取维基百科内容
Args:
query: 搜索关键词
Returns:
tuple: (页面内容, 摘要)
"""
try:
# 获取维基百科摘要
summary = wikipedia.summary(query)
# 获取维基百科页面URL
page = wikipedia.page(query)
url = page.url
# 使用requests获取完整页面内容
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
# 提取正文内容
content_div = soup.find('div', {'id': 'mw-content-text'})
paragraphs = content_div.find_all('p')
# 合并所有段落文本
full_content = '\n'.join([p.text for p in paragraphs])
return full_content, summary
except wikipedia.exceptions.DisambiguationError as e:
# 处理歧义词条
st.error(f"找到多个匹配项,请尝试更具体的搜索词: {', '.join(e.options[:5])}")
return None, None
except wikipedia.exceptions.PageError:
# 处理页面不存在的情况
st.error(f"找不到与'{query}'相关的维基百科页面")
return None, None
except Exception as e:
st.error(f"发生错误: {str(e)}")
return None, None
def process_text_and_create_index(text):
"""
处理文本并创建向量索引
Args:
text: 要处理的文本内容
"""
try:
# 文本分割
text_splitter = CharacterTextSplitter(
separator="\n",
chunk_size=1000,
chunk_overlap=200,
length_function=len
)
chunks = text_splitter.split_text(text)
st.info(f"文本已分割成 {len(chunks)} 个块")
# 创建向量存储
with st.spinner("正在创建向量索引..."):
embeddings = OpenAIEmbeddings()
vectorstore = FAISS.from_texts(chunks, embeddings)
st.session_state.vectorstore = vectorstore
st.success("向量索引创建成功!")
except Exception as e:
st.error(f"创建索引时出错: {str(e)}")
def answer_question(question):
"""
回答问题
Args:
question: 用户问题
Returns:
str: 回答内容
"""
try:
# 获取相关文档
vectorstore = st.session_state.vectorstore
docs = vectorstore.similarity_search(question, k=4)
# 创建自定义提示模板
template = """
你是一个有帮助的AI助手,专门回答关于维基百科内容的问题。
使用以下上下文来回答问题。如果你不知道答案,就说你不知道,不要编造答案。
尽量给出简洁、准确的回答。
上下文: {context}
问题: {question}
回答:
"""
PROMPT = PromptTemplate(
template=template,
input_variables=["context", "question"]
)
# 创建问答链
chain = load_qa_chain(
ChatOpenAI(temperature=0, model_name="gpt-3.5-turbo"),
chain_type="stuff",
prompt=PROMPT
)
# 获取回答
response = chain.run(input_documents=docs, question=question)
return response
except Exception as e:
return f"回答问题时出错: {str(e)}"
def main():
# 设置页面标题
st.title("基于维基百科的问答系统")
# 侧边栏 - 输入OpenAI API密钥
with st.sidebar:
st.header("配置")
api_key = st.text_input("输入您的OpenAI API密钥", type="password")
if api_key:
os.environ["OPENAI_API_KEY"] = api_key
# 主界面 - 搜索维基百科
st.subheader("第一步: 搜索维基百科")
query = st.text_input("请输入要检索的关键词")
if st.button("获取维基百科内容"):
if not query:
st.warning("请输入搜索关键词")
return
with st.spinner("正在从维基百科获取内容..."):
full_content, summary = get_wiki(query)
if full_content and summary:
st.session_state.wiki_content = full_content
st.session_state.wiki_summary = summary
st.success("内容获取成功!")
# 显示摘要
st.subheader("维基百科摘要")
st.write(summary)
# 处理文本并创建向量存储
process_text_and_create_index(full_content)
# 如果已经创建了向量存储,显示问答界面
if 'vectorstore' in st.session_state:
st.subheader("第二步: 提问")
question = st.text_input("请输入您的问题")
if st.button("提交问题"):
if not question:
st.warning("请输入问题")
return
if not api_key:
st.warning("请先输入OpenAI API密钥")
return
with st.spinner("正在思考..."):
answer = answer_question(question)
st.subheader("回答")
st.write(answer)
if __name__ == "__main__":
main()
10. 使用示例
示例1:查询"黄河"
- 在搜索框中输入"黄河"并点击"获取维基百科内容"
- 系统会显示黄河的维基百科摘要
- 在问题框中输入"黄河为什么是世界上含沙量最高的河流?"
- 系统会根据维基百科内容回答这个问题
示例2:查询其他主题
您可以查询任何维基百科上有的主题,例如:
- 历史人物(如"孔子"、"牛顿")
- 科学概念(如"相对论"、"量子力学")
- 地理位置(如"长江"、"珠穆朗玛峰")
- 文化现象(如"京剧"、"中国功夫")
11. 系统优化
为了进一步提升系统性能,您可以考虑以下优化:
-
多语言支持:通过设置wikipedia库的语言参数,支持不同语言的维基百科
wikipedia.set_lang("zh") # 设置为中文维基百科 -
缓存机制:使用Streamlit的缓存功能减少重复API调用
@st.cache_data(ttl=3600) # 缓存1小时 def get_wiki_cached(query): return get_wiki(query) -
错误处理:增强错误处理机制,提供更友好的用户体验
-
高级提示工程:优化提示模板,使回答更准确、更自然
-
多模型支持:允许用户选择不同的LLM模型
12. 总结
本教程展示了如何使用LangChain框架整合维基百科数据,构建一个功能完善的问答系统。通过这个系统,用户可以轻松获取维基百科内容并提出相关问题,得到准确的回答。
这个项目结合了网络爬虫、文本处理、向量存储和大语言模型等技术,是一个很好的LangChain实践案例。您可以在此基础上进行扩展,例如添加更多数据源、优化用户界面、增强问答能力等,打造更强大的知识问答系统。