当前位置:   article > 正文

基于OpenVINO+LangChain+LLama2打造互联网自动查询的AI助手_openvino llama2

openvino llama2

作者:刘宜松 中国科学院高能物理研究所 硕士研究生

指导老师:庄建 英特尔边缘计算创新大使,中国科学院高能物理研究所 研究员

本文目的

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即可实现模型转换

  1. -from transformers import AutoModelForCausalLM
  2. +from optimum.intel.openvino import OVModelForCausalLM
  3. from transformers import AutoTokenizer, pipeline
  4. model_id = "FlagAlpha/Llama2-Chinese-7b-Chat"
  5. -model = AutoModelForCausalLM.from_pretrained(model_id)
  6. +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类。就能实现模型格式的转换。

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

模型部署到CPU

指定其部署推理的设备为CPU,让模型在intel的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来拿到浏览器上的最新数据,以此来作为大模型的新的数据来源。

  1. url = "https://bing-web-search1.p.rapidapi.com/search"
  2. querystring= = {"q":query,"mkt":"en-us","safeSearch":"Off","textFormat":"Raw","freshness":"Day"}
  3. headers = {
  4. "X-BingApis-SDK": "true",
  5. "X-RapidAPI-Key": "Your Key",
  6. "X-RapidAPI-Host": "bing-web-search1.p.rapidapi.com"
  7. }
  8. 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 函数如下:

  1. class CustomLLM(LLM):
  2.     def _call(
  3.         self,prompt: str,stop: Optional[List[str]] = None,
  4.         run_manager: Optional[CallbackManagerForLLMRun] = None,
  5.     ) -> str:
  6.         response = requests.post(f'http://127.0.0.1:8080/', {
  7.         "ask": prompt
  8.         })
  9.         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}。这里是一些已知信息:

  1. {related_content}
  2. {background_infomation}
  3. {question_guide}:{input}
  4. {answer_format}
  5. """

CustomOutputParser

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

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

  1. class CustomOutputParser(AgentOutputParser):
  2.     def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:
  3.         match = re.match(r'^[\s\w]*(DeepSearch)\(([^\)]+)\)', llm_output, re.DOTALL)
  4.         if not match:
  5.             return AgentFinish(
  6.                 return_values={"output": llm_output.strip()},
  7.                 log=llm_output,
  8.             )
  9.         else:
  10.             action = match.group(1).strip()
  11.             action_input = match.group(2).strip()
  12.             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 询问决策,哪是后续的拼装答案。

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

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

运行结果

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

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

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

  1. Prompt 的提炼:我觉得还是要设计一个更优的适配Llama2的 Prompt,确保 LLM 指令响应的成功率。
  2. 模型推理能力,在硬件允许的情况下换成参数更多,推理能力更强的大模型。
  3. 网络检索的 parser,这里我是基于 bing search API 做联网检索,直接返回结果,还是有不少广告等垃圾信息,所以这些信息干扰让 这个机器人显得不那么聪明。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/106043?site
推荐阅读
相关标签
  

闽ICP备14008679号