老喻干货店的聊天robot

上一个实战项目我们为网销同事做了一个微博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课
相关推荐
神奇夜光杯几秒前
Python酷库之旅-第三方库Pandas(202)
开发语言·人工智能·python·excel·pandas·标准库及第三方库·学习与成长
正义的彬彬侠3 分钟前
《XGBoost算法的原理推导》12-14决策树复杂度的正则化项 公式解析
人工智能·决策树·机器学习·集成学习·boosting·xgboost
Debroon13 分钟前
RuleAlign 规则对齐框架:将医生的诊断规则形式化并注入模型,无需额外人工标注的自动对齐方法
人工智能
羊小猪~~20 分钟前
神经网络基础--什么是正向传播??什么是方向传播??
人工智能·pytorch·python·深度学习·神经网络·算法·机器学习
AI小杨21 分钟前
【车道线检测】一、传统车道线检测:基于霍夫变换的车道线检测史诗级详细教程
人工智能·opencv·计算机视觉·霍夫变换·车道线检测
晨曦_子画25 分钟前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
道可云27 分钟前
道可云人工智能&元宇宙每日资讯|2024国际虚拟现实创新大会将在青岛举办
大数据·人工智能·3d·机器人·ar·vr
人工智能培训咨询叶梓36 分钟前
探索开放资源上指令微调语言模型的现状
人工智能·语言模型·自然语言处理·性能优化·调优·大模型微调·指令微调
zzZ_CMing36 分钟前
大语言模型训练的全过程:预训练、微调、RLHF
人工智能·自然语言处理·aigc
newxtc37 分钟前
【旷视科技-注册/登录安全分析报告】
人工智能·科技·安全·ddddocr