上一个实战项目我们为网销同事做了一个微博Agent,方便了他们快速拓展营销链。欢迎大家点赞,评论区交流,一起快乐学习AI。
前言
我们将为老喻干货店的市场、运营和客服同事,以及我们的客户开发一款Chat Robot
。
项目需求
Chatgpt
是大家最熟悉的聊天机器人,我们学习LangChain
,学习AI, 很多场景下就是为开发出更好的,更适合我们业务的Chat Robot
。
我们先来看下Chat Robot
的架构图,方便我们理解整个流程
- 大模型(Chat)
LangChain
有相关聊天模型的列表,大模型有很多家,也有适合不同需求的模型,这里我们选择ChatOpenAI
这样的大模型。
- 提示词模板
提示词是AI应用的重要组成部分,Prompt Template
我个人把它看成mvc
中的model层。 传统的model层和数据库打交道,prompt
和大模型打交道。Prompt Template
的设计工作非常重要,等同于model层的重要性。
- Memory
大模型每次交流,如同HTTP一样,都是无状态的。怎么样让大模型理解与我们之间的聊天上下文呢?LangChain提供了Memory模块,让我们更好的缓存聊天历史,大家可以查看# LangChain 七 Memory, 讲的挺细的。
- 检索器
可以用LangChain
,也可以用llamaindex,帮助我们以读取文件等方式,将专有知识库带给大模型, 即RAG。
所以,chat robot
在聊天模型、提示词这些公共功能外,记忆和检索模块是它的核心。让我们一起来探索吧。
功能点分析
我们将先开发一个具备聊天功能的robot, 再添加RAG(老喻干货店知识库),使用敏捷开发原则,慢慢叠加功能。AI发展异常迅速,敏捷开发是明智之选。
LangChain
的ConversationChain, 可以快速启动聊天。Memory
,聊天上下文,记住用户之前说的话。LangChain
读取老喻干货店企业资料库,增强LLM能力LangChain
SQLChain, textToSql 查询订单状态、物流等- 部署chat robot
ConversationChain 敏捷聊天
python
# 设置OpenAI API密钥
import os
os.environ["OPENAI_API_KEY"] = 'AIzaSyDlloHNsc634fPkVmmnqaTK_8z-c7J8-wY'
# 系统消息和用户消息schema
from langchain.schema import (
HumanMessage,
SystemMessage
)
# ChatOpenAI openai 聊天的封装
from langchain.chat_models import ChatOpenAI
# 实例化聊天模型
chat = ChatOpenAI()
# 创建消息列表,以后交给memory
messages = [
SystemMessage(content="你是一个干货行家。"),
HumanMessage(content="家人血糖有点高,过年怎么吃既有年味又健康?")
]
# 查看反馈
response = chat(messages)
print(response)
LLM返回结果是:
ini
content='作为一个干货行家,我可以为您提供一些建议,让您的过年既有年味又健康。\n\n1. 合理控制糖分摄入量:选择低糖或无糖的食物和饮品,避免过多摄入高糖食品,如糖果、甜点和糕点等。\n\n2. 增加蔬果摄入:多食用新鲜的蔬菜和水果,它们富含纤维和维生素,有助于控制血糖水平。\n\n3. 控制淀粉类食物摄入:选择全谷物食品,如糙米、全麦面包等,避免过度摄入米饭、面条等高淀粉食物。\n\n4. 控制油脂摄入:选择低脂或无脂的食品,避免过多摄入油炸食品和油腻的肉类。\n\n5. 控制食物的烹饪方式:尽量选择清蒸、煮、炖等健康的烹饪方式,避免过多使用油炸或煎炒。\n\n6. 适量摄入蛋白质:选择瘦肉、禽肉、鱼类和豆类等富含蛋白质的食物,有助于维持饱腹感和血糖稳定。\n\n7. 合理安排餐食时间:保持规律的餐食时间,避免过度饥饿或暴饮暴食,有助于控制血糖水平。\n\n8. 注意饮食搭配:合理搭配各种食物,避免单一摄入某一种食物,如同时搭配蛋白质、蔬菜和粗粮等。\n\n最重要的是,与医生或营养师咨询,根据家人的具体情况制定适合的饮食计划。这样可以确保过年期间既能享受美食,又能保护健康。' additional_kwargs={} example=False
上面的代码稍作修改,就可以让用户持续提问:
python
# 设置OpenAI API密钥
import os
os.environ["OPENAI_API_KEY"] = 'Your OpenAI Key'
# 系统消息和用户消息schema
from langchain.schema import HumanMessage, SystemMessage
from langchain.chat_models import ChatOpenAI
# 定义一个命令行聊天机器人的类
class CommandlineChatbot:
# 在初始化时,设置干货行家的角色并初始化聊天模型
def __init__(self):
self.chat = ChatOpenAI()
self.messages = [SystemMessage(content="你是一个花卉行家。")]
# 定义一个循环来持续与用户交互
def chat_loop(self):
print("Chatbot 已启动! 输入'exit'来退出程序。")
while True:
user_input = input("你: ")
# 如果用户输入"exit",则退出循环
if user_input.lower() == 'exit':
print("再见!")
break
# 将用户的输入添加到消息列表中,并获取机器人的响应
self.messages.append(HumanMessage(content=user_input))
response = self.chat(self.messages)
print(f"Chatbot: {response.content}")
# 如果直接运行这个脚本,启动聊天机器人
if __name__ == "__main__":
bot = CommandlineChatbot()
bot.chat_loop()
我们编写了一个CommandlineChatbot类,让用户可以在命令行一直与LLM交互。当用户输入exit,则退出程序。
Memory出场
上面的代码基于敏捷开发思想,专注于chat的快速实现,已完成聊天功能。虽然self.messages.append(...)
会将用户的输入不断的加入数组,并发送给LLM,但是LLM的返回也是聊天上下文的重要组成部分,需要再之后传给LLM才能拥有完整上下文。再是聊天次数一多,会到达tokens上限... 所以,接下来Memory出场,专门搞定记忆功能。
ini
...
from langchain.memory import ConversationBufferMemory
from langchain.prompts import (
ChatPromptTemplate,
MessagesPlaceholder,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
)
from langhain.chains import LLMChain
....
class ChatbotWithMemory:
def __init__(self):
self.llm = ChatOpenAI()
self.prompt = ChatPromptTemplate(
messages = [
SystemMessagePromptTemplate.from_template(
"你是一个干货行家。你通常的回答不超过30字。"
),
MessagesPlaceHolder(variable_name="chat_history"),
HuamnMessagePromptTemplate.from_template("{question}")
]
)
# 初始化Memory
self.memory = ConversationBufferMemory(memory_key="chat_history",
return_messages=True
)
self.conversation=LLMChain(
llm = self.llm,
prompt=self.prompt,
verbose=True,
memory=self.memory
)
def chat_loop(self):
....
response = self.conversation({"question": user_input})
....
...省略的代码代表使用之前的代码就好。我们使用了LLMChain
链式搭建聊天功能。Chain 是LangChain
的特色,它将各个组件组合起来,让代码模块化更好,更好懂。ConversationBufferMemory实例化的memory对象,key 设置为chat_history
,我们的messages数组由三部分组成:第一条SystemMessage是不变的,中间一条用MessagesPlaceHolder拿对应的chat_history
的memory对象的内容输出,最后一条是最新的对话... ChatPromptTemplate
是这块聊天三明治
的面包店师傅,但愿你和我一样喜欢吃三明治,已经理解,阿里嘎多...
检索机制
在完成了聊天和记忆功能后,我们来将老喻干货店的一些pdf、word等内容,交给LangChain
实现RAG机制。
ini
...
# 文本分割器,Embedding前需要准备好
import langchain.text_splitter import RecursiveCharacterTextSplitter
# 引入Embedding模块,向量检索
from langchain.embeddings import OpenAIEmbeddings
# 使用Qdrant向量数据库
from langchain.vectorstores import Qdrant
# 检索chain
from langchain.chains import ConversationRetrievalChain
from langchain.document_loaders import PyPDFLoader
from langchain.document_loaders import Docx2txtLoader
from langchain.document_loaders import TextLoader
...
class ChatbotWithRetrieval:
def __init__(self, dir):
base_dir = dir
documents = []
for file in os.listdir(base_dir):
file_path = os.path.join(base_dir, file)
if (file.endswith('.pdf')):
loader = PyPDFLoader(file_path)
documents.extend(loader.load())
elif file.endswith('.docx') or file.endswith('.doc'):
loader = Docx2txtLoader(file_path)
documents.extend(loader.load())
elif file.endswith('.txt'):
loader=TextLoader(file_path)
documents.extend(loader.load())
text_splitter=RecursiveCharacterTextSplitter(chunk_size=200,chunk_overlap=0)
all_splits = text_splitter.split_documents(documents)
# 向量数据库
self.vectorstore=Qdrant.drom_documents(
documents=all_splits,
embedding=OpenAIEmbeddings(),
location=":memory:",
collection_name="my_documents"
)
self.llm = ChatOpenAI()
self.memory = ConversationSummaryMemory(
llm = self.llm,
memory_key="chat_history",
return_messages=True
)
retriever=self.vectorstore.as_retriever()
self.qa = ConversationRetrievalChain.from_llm(
self.llm,
retriever=retriever,
memory=self.memory
)
经过文档加载、文本分割、文档向量化以及检索功能,robot除了常规的聊天功能,还能够检索存储在指定目录中的文档,并基于这些文档提供答案。一个专家知识库RAG应用就可以上线了....
总结
- 敏捷开发,focus 唯一功能,快速完成,持续集成....
- chain 让记忆、检索优雅上线
参考资料
- 黄佳老师的LangChain课