skip navigation
skip mega-menu

一个聊天应用程序与LangChain和ChatGPT

在这个故事中,我们将探索如何编写一个简单的基于web的聊天应用程序 Python using LangChain, ChromaDB, ChatGPT 3.5 and Streamlit.

这款基于聊天的应用将拥有自己的知识库.e. 它将有自己的背景. 上下文将是十大网博靠谱平台 壹点咨询有限公司该公司是本博客作者工作的公司.

聊天应用程序将允许用户查询有关的信息 壹点咨询有限公司. 以下是一些与聊天应用互动的例子:


本博客中描述的技术允许您创建基于上下文的聊天应用程序,讨论您选择的任何主题.

Chat App Components

基于上下文的聊天应用程序内部有两个主要组件:

向量数据库是一种较新的数据库类型,它使用向量(数字列表)来表示数据,并且非常适合相似度搜索. This is how 微软知识库 定义了什么是矢量数据库:

“矢量数据库是一种将数据存储为高维矢量的数据库, 哪些是特征或属性的数学表示. 每个向量都有一定的维数, 从几十到几千不等, 取决于数据的复杂性和粒度.”

使用大语言模型(LLM)处理由向量数据库生成的上下文问题.

Chat App Workflows

聊天应用程序有两个工作流:

  • Vector DB Creation
  • Chat Workflow

矢量数据库创建工作流可以表示为BPMN:

This Vector DB Creation 描述在后台启动聊天应用程序时发生的情况. 此工作流的目标是生成ChatGPT嵌入 ChromaDB.

它执行以下步骤:

  • 收集指定文件夹下的CSV文件和部分网页
  • 提取每个CSV文件的文本
  • 为CSV文件创建文本记录列表
  • 收集所有CSV文件的所有记录在一个列表中
  • 将收集到的文本列表发送到ChatGPT以收集相应的嵌入
  • 使用ChromaDB将ChatGPT嵌入保存在内存中,最终保存在磁盘上.

聊天工作流描述了当用户在用户界面上提出问题时会发生什么:

  • 用户写一个问题并点击“Enter”按钮.
  • 对向量数据库触发一个相似度搜索查询.
  • 对矢量数据库的查询返回特定数量的记录(与问题最相似的记录)
  • 这些记录与问题一起发送给法学硕士.
  • 然后,LLM回复用户,并在UI上显示响应.

Pre-requisites

We create a Anaconda 环境中使用以下库:

langchain==0.0.215
python-dotenv==1.0.0
streamlit==1.23.1
openai==0.27.8
chromadb==0.3.26
tiktoken==0.4.0

Implementation

聊天引擎的实现可以在下面找到:

GitHub - gilfernandes/onepoint_chat:基于点链的迷你聊天应用程序

基于点链的迷你聊天应用. 通过创建一个帐户为gilfernandes/onepoint_chat的开发做出贡献…

github.com

有一个包含完整代码的文件:

onepoint_chat / chat_main.Py在main·gilfernandes/onepoint_chat

基于点链的迷你聊天应用. 通过创建一个帐户为gilfernandes/onepoint_chat的开发做出贡献…

github.com

应用程序从一个main方法开始. 它执行三个步骤:

  • 从特定文件夹和特定页面加载所有文本
  • 创建矢量数据库
  • 初始化用户界面
Def main(doc_location: STR ='onepoint_chat'):
"""
应用程序的主入口点.
它从特定文件夹和特定网页加载所有文本,
创建矢量数据库并初始化用户界面.
:param doc_location: CSV文件所在的位置
"""
logger.info(f"使用文档位置{doc_location}.")
文本,doc_path = load_texts(doc_location=doc_location)
Website_texts = load_website_texts([
'http://www.onepointltd.com/',
'http://www.onepointltd.com/do-data-better/'
])
texts.扩展(website_texts)
docsearch = extract_embeddings(text =text, doc_path=Path(doc_path))
init_streamlit (docsearch = docsearch =文字)

The method load_texts 加载CSV文件的文本,并将所有文本连接到一个文档对象列表中.

def load_texts(doc_location: str) -> Tuple[List[str], Path]:
"""
加载CSV文件的文本,并将所有文本连接到一个列表中.
:param doc_location:文档位置.
:return:一个包含字符串列表和路径的元组.
"""
doc_path =路径(doc_location)
texts = []
for p in doc_path.glob("*.csv"):
texts.extend(load_csv(p))
logger.info(f"文本长度:{len(文本)}")
返回文本,doc_path

The method load_csv 使用LangChain的CSV加载器将CSV内容加载为文档列表.

def load_csv(file_path: Path) -> List[Document]:
"""
使用csv加载器将csv内容加载为文档列表.
:param file_path: CSV文件路径
:返回:提取并拆分所有CSV记录后的文档列表.
"""
loader = CSVLoader(file_path=str(file_path), encoding="utf-8")
doc_list: List[Document] = loader.load()
Doc_list = [d for d in Doc_list.page_content != 'Question: \nAnswer: ']
logger.info(f"第一项:{doc_list[0].page_content}")
logger.info(f" CSV列表的长度:{len(doc_list)}")
返回split_docs (doc_list)

There is also a load_website_texts 从网页加载文本的方法:

def load_website_texts(url_list: List[str]) -> List[Document]:
"""
用于加载网站文本.
:param url_list: url列表
:返回:一个文档列表
"""
documents: List[Document] = []
for url in url_list:
Text = text_from_html(请求.get(url).text)
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=100, separator=".")
文本= text_splitter.split_text(text)
for t in texts:
documents.追加(文档(page_content = t))
return documents

我们有来自CSV和HTML网站的所有文本,我们现在用下面的方法提取嵌入:

def extract_embeddings(texts: List[Document], doc_path: Path) -> Chroma:
"""
要么将Chroma嵌入保存在本地,要么从磁盘(如果存在的话)读取它们.
:返回嵌入周围的色度包装器.
"""
Embedding_dir = f"{cfg.chroma_persist_directory} / {doc_path.stem}"
如果路径(embedding_dir).exists():
shutil.rmtree (embedding_dir ignore_errors = True)
try:
docsearch = Chroma.from_documents(文本,cfg.嵌入,persist_directory = embedding_dir)
docsearch.persist()
例外情况如下:
logger.错误(f"处理{doc_path}: {str(e)}失败")
return None
return docsearch

最后,初始化 Streamlit environment in the init_streamlitfunction. 这段代码需要某种形式的用户问题,然后处理问题. 它还可以处理带有预定义问题的下拉列表中的问题.

def init_streamlit(docsearch: Chroma, text):
"""
创建Streamlit用户界面.
这段代码期待某种形式的用户问题,一旦有了它就会处理
the question.
它还可以处理带有预定义问题的下拉列表中的问题.
像这样使用流光:
streamlit run ./chat_main.py
"""
title =“问十大网博靠谱平台某一点的问题”
st.set_page_config(是page_title =标题)
st.header(title)
st.write(f"Context with {len(text)} entries")
Simple_chat_tab, historical_tab = st.选项卡([“简单聊天”,“历史问题”])
simple_chat_tab:
user_question = st.text_input(你的问题)
with st.spinner('Please wait ...'):
process_user_question (docsearch = docsearch user_question = user_question)
historical_tab:
User_question_2 = st.select ("Ask a previous question", read_history())
with st.spinner('Please wait ...'):
logger.信息(f”问题:{user_question_2}”)
process_user_question (docsearch = docsearch user_question = user_question_2)

Finally, we have the process_user_question 函数,在矢量数据库中执行搜索, 创建问题上下文, 是和问题一起发给法学硕士的吗.

(docsearch: Chroma, user_question: str):
"""
接收用户问题并在向量数据库中搜索相似的文本文档.
使用相似的文本和用户问题从LLM检索响应.
docsearch:对vector数据库对象的引用
user_question:用户输入的问题.
"""
if user_question:
similar_docs: List[Document] = docsearch.(user_question, k = 5)
响应,similar_texts = process_question(similar_docs, user_question)
st.markdown(response)
if len(similar_texts) > 0:
write_history (user_question)
st.text(“相似条目(向量数据库结果)”)
st.写(similar_texts)
else:
st.警告(“这个答案与我们的上下文无关.")

源代码中还有更多的函数,但是,它们只是简单的辅助函数.

Conclusion

我们开发的聊天应用在UI方面非常基础. 更好的UI可以用UI Javascript框架编写,比如 Next.Js or Nuxt. Ideally, 您可以通过围绕聊天功能构建REST接口来分离服务器端和客户端.

如果你有一个矢量数据库,创建基于任何知识领域的智能聊天应用程序现在都很容易, 一个LLM和一个UI库,比如Streamlit. 这是一个非常强大的工具组合,可以真正释放你作为开发人员的创造力.

吉尔·费尔南德斯,Onepoint咨询公司

澳门十大正规赌博娱乐平台

Sign up here