大家好,我是 AI 研习者轻寒。在之前的文章《解密AI客服:LangChain+ChatGPT打造智能客服新时代》中,我基于 LangChain + ChatGPT 实现了简易公众号的 AI 客服,主要是基于文本回答用户提出的问题,但缺陷在于无法回答文本之外的内容,且回答较为生硬,同时没有根据用户隔离会话。本文通过优化,使客服能够在回答业务问题之外,还能与用户闲聊,同时隔离用户会话的同时保留上下文缓存。
LangChain 是一个用于开发由语言模型驱动的应用程序的框架,LangChain 灵活的抽象和广泛的工具包使开发人员能够利用 LLM 的力量。由于其版本迭代的太快,作者也是在不断的探索。
AI 客服旨在提供实用的辅助和支持,通过与用户的互动,自动分析用户的输入的问题,并根据用户的需求响应适当的回答或建议。
AI 客服可以为企业提高高效、降低成本,通过快速响应以提升用户满意度,并减少人工客服的工作负担,如原本可能需要 10 人的客服团队,现在在 AI 的辅助下可能只需要 2 人就能胜任工作。
目前 AI 客服还没法完全自主解决所有问题,但如何提升 AI 客服解决问题的能力还有很多探索的空间,比如基于 LangChain 实现日常客服(如售前、售后)流程阶段自动化,通过 LangChain 的代理及工具可以和其他业务系统紧密结合,利用 LangChain 的链可以对会话或阶段进行分析和评估等等。
这是之前实现简易 AI 客服,有兴趣的可以去看看:《解密AI客服:LangChain+ChatGPT打造智能客服新时代》。
本文是基于前文的实现进行了优化。
在之前的实现中使用了 RetrievalQA,RetrievalQA 是 LangChain 中的一个模块,用于构建基于检索的问答系统。
RetrievalQA 的主要思想是先从文档库中检索出与问题相关的文档,基于相关性对候选答案进行排序和筛选,然后将这些文档和原始问题一起提供给语言模型,让语言模型根据这些上下文来生成答案。
本次优化使用 ConversationalRetrievalChain 去实现,ConversationalRetrievalChain 用于构建会话式的检索问答代理。它会考虑会话的上下文信息,根据之前的问题并回答来改进当前问题的检索结果。相比单轮的 RetrievalQA,可以在会话的过程中不断改进检索性能。
ConversationalRetrievalChain 的主要思想是先构建一个检索工具,可以对用户的问题进行检索,获取相关文档。然后构建一个会话式的代理,内部集成了检索工具。当用户提出一个问题时,代理首先用检索工具获取相关文档。然后将问题、相关文档以及之前的对话历史作为 prompt,提供给语言模型生成回答。这样可以利用检索结果增强语言模型的理解能力,生成更好的回答。
ConversationBufferMemory 是 LangChain 中的一个组件,用于在会话问答系统中存储对话历史。它可以跟踪对话的上下文,在生成回复时提供给语言模型使用。
qa = RetrievalQA.from_chain_type(llm=ChatOpenAI(model_name='gpt-3.5-turbo-0613'), chain_type="stuff", retriever=vector_store.as_retriever())
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True, verbose=True)qa = ConversationalRetrievalChain.from_llm(llm=ChatOpenAI(model_name='gpt-3.5-turbo-0613'), chain_type="stuff", memory=memory, retriever=vector_store.as_retriever(), condense_question_prompt=common_prompt, verbose=True)
系统提示预设可以让模型回答的更可控,通过自定义系统模板,可以让 AI 的回复更加符合交互场景的需要,提高用户体验。它为构建会话式问答系统提供了更多可定制性,比如让系统回复更加人性化,丰富系统和用户之间的交互形式,又如控制 AI 哪些可以回答,哪些不能回答。
把 system_prompt.txt 存在在静态文件中,在 docker 部署时,方便快速调试以及优化 prompt。
# 构建system_template.txt文件的路径system_template_path = os.path.join(base_dir, 'static', 'system_template.txt')with open(system_template_path, 'r') as file: system_template = file.read()common_prompt = PromptTemplate( template=system_template, input_variables=[])
OpenAI 有 Function calling 的功能,LangChain 的代理也是类似,这里我们使用了 OpenAIFunctionsAgent。
LangChain 的代理可以根据用户的提问,结合工具的描述(描述是非常有用的)和自定义参数自动判断是否需要调用工具以及选择什么工具。
基于代理和工具我们还可以把业务和 GPT 模型能力完美的结合起来。
这里把之前写的实时查询天气的工具集成了进来,方便测试。可以阅读之前的文章《基于LangChain实现ChatGPT实时查询天气》来学习如何实现自定义工具(工具即是函数或者接口能力)。
qa = ConversationalRetrievalChain.from_llm(llm=llm, chain_type="stuff", memory=memory, retriever=vector_store.as_retriever(), condense_question_prompt=common_prompt, verbose=True)realWeatherTool = RealWeatherTool()tools = [ Tool( name="xmhc", func=qa.run, description="当您需要回答小猫惠充客服相关问题时,包括话费充值、充值类型(快充/慢充)、到账时间、充值须知、充值规则、充值协议、活动规则、客服联系方式、联系客服等," "这非常有用。输入应该是一个完整的问题。", verbose=True, ), Tool( name="RealWeatherTool", func=realWeatherTool.run, description=realWeatherTool.description, verbose=True, )]chat_prompt = OpenAIFunctionsAgent.create_prompt( system_message=SystemMessage(content=system_template))agent = OpenAIFunctionsAgent(llm=llm, tools=tools, prompt=chat_prompt, verbose=True)agent_executor = AgentExecutor(agent=agent, tools=tools, memory=memory, verbose=True)
我们知道微信公众消息都是基于公众号用户的,所用我们可以采用 openid 来作用用户唯一标识,以此来用于绘画隔离。
同时需要记录用户会话的上下文,这里我采用 redis 来缓存用户会话记录,后续还可以基于会话记录来做一个会话分析功能。
对会话上下文进行限制,以防超出模型最大 token。
@app.route("/xmhc/kf", methods=['POST'])def chat(): # put application's code here # 接口请求参数 json_data = request.get_json() userid = json_data['userid'] question = json_data['question'] # 获取当前用户历史会话内容 history_message = [] if redis_store.exists(userid): history_message_str = redis_store.get(userid) history_message = json.loads(history_message_str) try: answer = agent_executor.run({"input": question, "chat_history": history_message}) current_message = (question, answer) # 缓存当前用户历史对话内容 history_message.append(current_message) if len(history_message) > 10: # 对会话数进行限制 # 删除最旧的数据 history_message.pop(0) # 将元组列表转换为JSON字符串 history_message_str = json.dumps(history_message) redis_store.set(userid, history_message_str) print(answer) except InvalidRequestError: # 调用模型请求失败,可能是额度不足或者请求频繁 return { "err_code": -1, "data_list": [] } return { "err_code": 0, "data_list": [ {"content": answer} ] }
LangChain 给了我很多的惊喜,它让开发 AI 应用变得简单。但它的生态还在不断扩展,版本不断迭代,所以我们只能不断的探索。
理解新范式,拥抱新时代,把握新机会。