开发者实战 | 基于 OpenVINO™ + LangChain + LLama2 打造智能小助手

原创
2023/10/23 17:00
阅读数 126

点击蓝字

关注我们,让开发变得更有趣

作者:刘宜松

中国科学院高能物理研究所 硕士研究生

指导老师:庄建 

英特尔边缘计算创新大使

中国科学院高能物理研究所 研究员





本文目的

LLM 大模型存在很多痛点,包括但不限于数据陈旧,无法和外部组件进行交互,本文旨在使用 OpenVINO™  2023,利用其新版本的特性加速 Llama2 模型。为 Llama2 定制化 Prompt,并用 LangChain 实现可联网获得最新消息的辅助检索查询。在本文中,端到端教授大家基于 OpenVINO™ + LangChain + LLama2 实现具备互联网自动查询的 AI 小助手,代码开源地址:

https://github.com/lewis430/langchain_openvino_llama2

(复制链接到浏览器打开)


OpenVINO™ 2023.1 新特性

OpenVINO™ 2023.1 此版本推出的新功能可简化 AI 的部署和加速。此新版本为开发人员提供更多集成,最大限度减少代码更改。


OpenVINO™ 提供了模型转换工具 OVC,该工具正在取代我们众所周知的离线模型转换任务中的模型优化器(MO)工具。该工具以 OpenVINO™ 包形式提供,依靠内部模型前端来读取框架格式,不需要原始框架来执行模型转换。


将原来的 AutoModelForCausalLM 替换为 OVModelForCausalLM 即可实现模型转换:

-from transformers import AutoModelForCausalLM+from optimum.intel.openvino import OVModelForCausalLMfrom transformers import AutoTokenizer, pipeline model_id = "FlagAlpha/Llama2-Chinese-7b-Chat"-model = AutoModelForCausalLM.from_pretrained(model_id)+model = OVModelForCausalLM.from_pretrained(model_id, export=True)

左滑查看更多



模型选择

因为我们是需要面向中文的,而由于 Llama2 本身的中文对齐较弱,我们需要采用中文指令集,对 meta-llama/Llama-2-7b-chat-hf 进行 LoRA 微调,使其具备较强的中文对话能力。同时也因为 Llama2 模型的获取需要向 meta 提供个人信息,所以选择更易获取的 Llama2-Chinese-7b-Chat 模型,以下是模型的 Huggingface 链接:

https://huggingface.co/FlagAlpha/Llama2-Chinese-7b-Chat

(复制链接到浏览器打开)



模型转换为 ONNX 格式

OpenVINO™ 可用于从 Hugging Face Hub 加载优化模型,并创建管道以使用 Hugging FaceAPI 通过 OpenVINO™ Runtime 运行推理。这意味着我们只需要将 AutoModelForXxx 类替换为相应的 OVModelForXxx 类。就能实现模型格式的转换。

model_dir =  "llama-2-chat-7b/ov_model"ov_model = OVModelForCausalLM.from_pretrained('llama-2-chat-7b', export=True, compile=False)ov_model.half()ov_model.save_pretrained(model_dir)

左滑查看更多



模型部署到 CPU

指定其部署推理的设备为 CPU,让模型在英特尔的 CPU 上进行推理。

ov_model=OVModelForCausalLM.from_pretrained(model_dir,device='cpu',ov_config=ov_config,config=AutoConfig.from_pretrained(model_dir,trust_remote_code=True),trust_remote_code=True)

左滑查看更多



LangChain

LangChain 是一个开源的框架,它可以让 AI 开发人员把像 GPT-4 这样的大型语言模型(LLM)和外部数据结合起来。它提供了 Python 或 JavaScript 的包。它是基于大语言模型这种预测能力的应用开发工具。无论你是想要做一个聊天机器人、个人助理、问答系统,或者自助代理等等,都可以帮助我们快速地实现想法。我可以拍胸脯地说,LangChain 作为新一代 AI 开发框架,必将受到程序员的追捧,点燃 AI 应用开发的新热潮。



LangChain 的精髓是 agent 机制,学习了解 Agent 就像探索迷宫一样有趣。langchain 的 Agent 实在是非常聪明的设计。什么是 Agent,简单讲 Agent 利用 LLM 的理解推理能力来简化原本十分复杂的逻辑判断,决定什么问题由什么工具来解决,最后汇总成一个“答案”返回给用户。LangChain 把这个过程用一个非常经典的 Prompt 模板定义了出来,这个模板中包括四个环节:Thought(LLM 的思考过程和决定)、Action(要采取的行动以及要给他的输入)、Observation(执行 Action 后预计的输出结果)、Tools(Action 行动的调用函数)。




Rapid API

RapidAPI 是一个 API 市场,提供了数千个 API,可以帮助开发人员快速找到并使用需要的 API。它包括多个类别,如人工智能、云计算、区块链、金融、游戏等,每个类别下面都有大量的 API 接口可供选择。在 RapidAPI 的平台上,你可以搜索、筛选、订阅和使用这些 API 接口,还可以查看 API 的文档和使用示例,从而更好地了解和使用它们。


我们利用 Rapid API 平台上 bing 搜索引擎提供的 api 来拿到浏览器上的最新数据,以此来作为大模型的新的数据来源。

url = "https://bing-web-search1.p.rapidapi.com/search"querystring= = {"q":query,"mkt":"en-us","safeSearch":"Off","textFormat":"Raw","freshness":"Day"}headers = {"X-BingApis-SDK": "true","X-RapidAPI-Key": "Your Key","X-RapidAPI-Host": "bing-web-search1.p.rapidapi.com"}response = requests.get(url, headers=headers, params=querystring)

左滑查看更多



CustomLLM

LangChain 对 OpenAI 的 ChatGPT 的支持是最好的。但是在某些对数据安全性要求比较高的场合 ,我们需要将数据放在局域网中的,数据是不能出网关的,所以要在局域网内部搭建本地模型,其实整个应用的成本最高的就是 LLM 的部署的硬件需求。所以最经济的一个方案就是一个局域网共同一个 LLM,它是统一部署后通过 API 的形式给各个应用做支持,这样也保证了硬件的充分利用。同时将 LLM 和 LangChain 分开部署的好处还有就是灵活性,LangChain 对硬件的要求不高,它只是做资源的整合,任何重存储和重计算的服务全部在远端部署,可以将 LangChain 部署在硬件条件不那么好的设备上。


我们可以通过 LangChain 来很方便的使用 OpenAI 的模型接口,同时 LangChain 内部很多逻辑代码都是基于 ChatGPT 这样一个出色的模型来设计的,当我们需要考虑使用自定义的本地模型时,需要做一个适配,其核心在于构建 langchain.llms.base.LLM 的子类 CustomLLM 并重写_call 函数如下:

class CustomLLM(LLM):    def _call(        self,prompt: str,stop: Optional[List[str]] = None,        run_manager: Optional[CallbackManagerForLLMRun] = None,    ) -> str:        response = requests.post(f'http://127.0.0.1:8080/', {        "ask": prompt        })        return response.text

左滑查看更多



Prompt 定制

因为 LangChain 的内部逻辑上基本是为了 ChatGPT 量身定制的,内置的所有 Prompt 都是基于 ChatGPT 的参数量和聪明程度,但是对于我们本地部署的参数量小的 Llama-2-7b 来说就十分不契合了,LangChain 默认的 Agent 中的 Prompt 模版没办法和 Llama-2-7b 起到很好的互动,虽然不是每次都失败,但失败的概率是很大的,约等于不可用,所以我们需要自己来定义 Prompt 来做一个 LangChain 和 Llama-2-7b 模型的适配。


本例子中的 Prompt 结构中 background_infomation = 角色扮演 + 网络检索结果 + 原始问题这三者拼接起来的。其中的“网络检索结果”和“原始问题”比较理解理解,而我发现不得不仿照 LangChain 加上一个角色定义,让 Llama2 在做推理决策时和汇总答案时扮演不同的角色,这样可以显著得提高回答的质量。

agent_template = """你现在是一个{role}。这里是一些已知信息:{related_content}{background_infomation}{question_guide}:{input}{answer_format}"""

左滑查看更多



CustomOutputParser

LangChain 的核心逻辑是 Agent,而 Agent 的执行逻辑其实非常简单,首先把问题和背景知识通过拼装到原始的 Prompt 中,然后发给 LLM ,让他返回一个做决策的“任务表”,然后由 OutputParser 处理这个任务表,它根据预设的格式,总结出 LLM 需要做的下一步行动,也就是选择某个 Tool 继续执行,还是就此结束直接返回答案。


在本项目的实现中,我完全继承了 AgentOutptparser,它原则上是可以做到根据返回来多次向 LLM 询问“任务表”的,但因为 Llama-2-7b 理解力实在有限,不会再多次的询问了,最多问三次。

class CustomOutputParser(AgentOutputParser):    def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:        match = re.match(r'^[\s\w]*(DeepSearch)\(([^\)]+)\)', llm_output, re.DOTALL)        if not match:            return AgentFinish(                return_values={"output": llm_output.strip()},                log=llm_output,            )        else:            action = match.group(1).strip()            action_input = match.group(2).strip()            return AgentAction(tool=action, tool_input=action_input.strip(" ").strip('"'), log=llm_output)

左滑查看更多



Custom Prompt Template 

和 Custom Search Tool

最后,我们在这里定义新的 CustomPromptTemplate 类,其实只要重写一个 format 函数就行,用它来继承了StringPromptTemplate 类,从而实现对控制推理决策和汇总答案汇总拼装到不同的 Prompt 中。LangChain 内部逻辑会自动把每次 LLM 返回作为参数传递给 format 函数。我只要判断哪是首次向 LLM 询问决策,哪是后续的拼装答案。

if len(intermediate_steps) == 0:    background_infomation = "\n"    role = "傻瓜机器人"    question_guide = "我现在有一个问题"    answer_format = """请你只回答\"DeepSearch("搜索词")\",并将"搜索词"替换为你认为需要搜索的关键词,除此之外不要回答其他任何内容。\n\n下面请回答我上面提出的问题!"""else:       # 根据 intermediate_steps 中的 AgentAction 拼装 background_infomation    background_infomation = "\n\n你还有这些已知信息作为参考:\n\n"    action, observation = intermediate_steps[0]    background_infomation += f"{observation}\n"    role = "聪明的 AI 助手"    question_guide = "请根据这些已知信息回答我的问题"    answer_format = ""

左滑查看更多


前面提到给 LLM 赋予角色,可以很好的提升回答质量,在这里我分别赋予了推理决策和汇总答案两个角色:“傻瓜机器人”和“聪明的 AI 助手”,前者只需要做简单的分类任何,后者才是生成最终答案文本,我目测这样的设定成功率较高,你也可以尝试换成其他的角色,说不定有惊喜。



运行结果

cpu 型号:Intel® Xeon® Gold 6248R CPU @ 3.00GHz


运行环境:操作系统 Ubuntu22.04,Gradio 版本>=3.47.1



从运行效果可以看出,对于一个数据实时性要求高的专业问题,这个 Agent 完全可以做到对互联网检索的结果的理解并返回。整个流程是可以很好执行起来。但作为一个 Demo 还不能做到 100% 的高效回答和足够的智能,最大的挑战来自三个方面,即如果想要做到极致的话,要在这三个方面花更多精力:


1、Prompt 的提炼:我觉得还是要设计一个更优的适配 Llama2 的 Prompt,确保 LLM 指令响应的成功率。


2、模型推理能力:在硬件允许的情况下换成参数更多,推理能力更强的大模型。


3、网络检索的 parser:这里我是基于 bing search API 做联网检索,直接返回结果,还是有不少广告等垃圾信息,所以这些信息干扰让 这个机器人显得不那么聪明。

OpenVINO™

--END--


               
               
               
你也许想了解(点击蓝字查看)⬇️
➡️ 开发者实战 | 介绍OpenVINO™ 2023.1:在边缘端赋能生成式AI
➡️ 基于 ChatGLM2 和 OpenVINO™ 打造中文聊天助手
➡️ 基于 Llama2 和 OpenVINO™ 打造聊天机器人
➡️ OpenVINO™ DevCon 2023重磅回归!英特尔以创新产品激发开发者无限潜能
➡️ 5周年更新 | OpenVINO™  2023.0,让AI部署和加速更容易
➡️ OpenVINO™5周年重头戏!2023.0版本持续升级AI部署和加速性能
➡️ OpenVINO™2023.0实战 | 在 LabVIEW 中部署 YOLOv8 目标检测模型
➡️ 开发者实战系列资源包来啦!
➡️  以AI作画,祝她节日快乐;简单三步,OpenVINO™ 助你轻松体验AIGC
➡️  还不知道如何用OpenVINO™作画?点击了解教程。
➡️   几行代码轻松实现对于PaddleOCR的实时推理,快来get!
➡️   使用OpenVINO 在“端—边—云”快速实现高性能人工智能推理


              
              
              

扫描下方二维码立即体验 

OpenVINO™ 工具套件 2023.1


点击 阅读原文 立即体验OpenVINO 2023.1
文章这么精彩,你有没有“在看”?

本文分享自微信公众号 - OpenVINO 中文社区(openvinodev)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部