跟着DW学习大语言模型-大模型开发中prompt技术与艺术,学习通义langchain构建应用

发布于:2024-06-23 ⋅ 阅读:(20) ⋅ 点赞:(0)
  • Prompt 最初是 NLP(自然语言处理)研究者为下游任务设计出来的一种任务专属的输入模板,类似于一种任务(例如:分类,聚类等)会对应一种 Prompt。在 ChatGPT 推出并获得大量应用之后,Prompt 开始被推广为给大模型的所有输入。即,我们每一次访问大模型的输入为一个 Prompt,而大模型给我们的返回结果则被称为 Completion。

  • 大语言模型(LLM)学习路径和资料汇总 | blog (ninehills.tech)

  • LLM 生成是具有随机性的,在模型的顶层通过选取不同预测概率的预测结果来生成最后的结果。我们一般可以通过控制 temperature 参数来控制 LLM 生成结果的随机性与创造性。Temperature 一般取值在 0~1 之间,当取值较低接近 0 时,预测的随机性会较低,产生更保守、可预测的文本,不太可能生成意想不到或不寻常的词。当取值较高接近 1 时,预测的随机性会较高,所有词被选择的可能性更大,会产生更有创意、多样化的文本,更有可能生成不寻常或意想不到的词。对于不同的问题与应用场景,我们可能需要设置不同的 temperature。

  • System Prompt 是随着 ChatGPT API 开放并逐步得到大量使用的一个新兴概念,事实上,它并不在大模型本身训练中得到体现,而是大模型服务方为提升用户体验所设置的一种策略。具体来说,在使用 ChatGPT API 时,你可以设置两种 Prompt:一种是 System Prompt,该种 Prompt 内容会在整个会话过程中持久地影响模型的回复,且相比于普通 Prompt 具有更高的重要性;另一种是 User Prompt,这更偏向于我们平时提到的 Prompt,即需要模型做出回复的输入。一般设置 System Prompt 来对模型进行一些初始化设定。

  • Prompt 设计的原则及使用技巧

    • 首先,Prompt 需要清晰明确地表达需求,提供充足上下文,使语言模型能够准确理解我们的意图。并不是说 Prompt 就必须非常短小简洁,过于简略的 Prompt 往往使模型难以把握所要完成的具体任务,而更长、更复杂的 Prompt 能够提供更丰富的上下文和细节,让模型可以更准确地把握所需的操作和响应方式,给出更符合预期的回复。
    • 使用分隔符清晰地表示输入的不同部分:可以使用各种标点符号作为“分隔符”,将不同的文本部分区分开来。分隔符就像是 Prompt 中的墙,将不同的指令、上下文、输入隔开,避免意外的混淆。你可以选择用 ```,“”",< >, ,: 等做分隔符,只要能明确起到隔断作用即可。
    • 使用分隔符尤其需要注意的是要防止提示词注入(Prompt Rejection)。就是用户输入的文本可能包含与你的预设 Prompt 相冲突的内容,如果不加分隔,这些输入就可能“注入”并操纵语言模型,轻则导致模型产生毫无关联的不正确的输出,严重的话可能造成应用的安全风险。
    • 有时候我们需要语言模型给我们一些结构化的输出,而不仅仅是连续的文本。什么是结构化输出呢?就是按照某种格式组织的内容,例如 JSON、HTML 等。这种输出非常适合在代码中进一步解析和处理,例如,您可以在 Python 中将其读入字典或列表中。
    • 如果任务包含不一定能满足的假设(条件),我们可以告诉模型先检查这些假设,如果不满足,则会指 出并停止执行后续的完整流程。您还可以考虑可能出现的边缘情况及模型的应对,以避免意外的结果或 错误发生。
    • “Few-shot” prompting(少样本提示),即在要求模型执行实际任务之前,给模型提供一两个参考样例,让模型了解我们的要求和期望的输出样式。利用少样本样例,我们可以轻松“预热”语言模型,让它为新的任务做好准备。这是一个让模型快速上手新 任务的有效策略。
    • 在设计 Prompt 时,给予语言模型充足的推理时间非常重要。语言模型与人类一样,需要时间来思考并解决复杂问题。如果让语言模型匆忙给出结论,其结果很可能不准确。例如,若要语言模型推断一本书的主题,仅提供简单的书名和一句简介是不足够的。这就像让一个人在极短时间内解决困难的数学题,错误在所难免。相反,我们应通过 Prompt 引导语言模型进行深入思考。可以要求其先列出对问题的各种看法,说明推理依据,然后再得出最终结论。在 Prompt 中添加逐步推理的要求,能让语言模型投入更多时间逻辑思维,输出结果也将更可靠准确。指定完成任务所需的步骤
    • 指导模型在下结论之前找出一个自己的解法,在设计 Prompt 时,我们还可以通过明确指导语言模型进行自主思考,来获得更好的效果。 举个例子,假设我们要语言模型判断一个数学问题的解答是否正确。仅仅提供问题和解答是不够的,语 言模型可能会匆忙做出错误判断。相反,可以在 Prompt 中先要求语言模型自己尝试解决这个问题,思考出自己的解法,然后再与提供的解答进行对比,判断正确性。这种先让语言模型自主思考的方式,能帮助它更深入理解问题,做出 更准确的判断。
    • 在开发与应用语言模型时,需要注意它们可能生成虚假信息的风险。尽管模型经过大规模预训练,掌握 了丰富知识,但它实际上并没有完全记住所见的信息,难以准确判断自己的知识边界,可能做出错误推断。若让语言模型描述一个不存在的产品,它可能会自行构造出似是而非的细节。这被称为“幻觉” (Hallucination),是语言模型的一大缺陷。
  • Transformers https://github.com/huggingface/transformers 提供了可以轻松地下载并且训练先进的预训练模型的 API 和工具。使用预训练模型可以减少计算消耗和碳排放,并且节省从头训练所需要的时间和资源。这些模型支持不同模态中的常见任务,比如: 自然语言处理:文本分类、命名实体识别、问答、语言建模、摘要、翻译、多项选择和文本生成。 机器视觉:图像分类、目标检测和语义分割。 音频:自动语音识别和音频分类。 多模态:表格问答、光学字符识别、从扫描文档提取信息、视频分类和视觉问答。

  • 这些模型通过训练大量数据来学习并优化其性能,从而在语音识别、自然语言处理、图像识别等领域取得了显著的成果。介绍如何调用大模型API,以及大模型开发的基本流程和架构。首先,调用大模型API是实现大模型应用开发的重要步骤。这些API通常由大型科技公司或开源社区提供,它们已经训练好了强大的模型,并提供了易于使用的接口供开发者调用。调用API的过程通常包括以下几个步骤:

    • 注册账号并获取API密钥:首先需要在相应的平台注册账号,并获取API密钥,以便在调用API时进行身份验证。
    • 选择合适的模型和功能:根据需求选择合适的模型和功能,例如文本分类、图像识别等。
    • 调用API并处理返回结果:使用所选模型的API接口,传入相应的参数,并处理返回的结果。
    • 集成到应用程序中:将API集成到自己的应用程序中,实现大模型的应用。
  • 大模型的开发涉及到数据预处理、模型训练、调优和部署等步骤。

    • 数据预处理:在大模型开发中,数据预处理是非常重要的一步。它涉及到数据清洗、数据增强、数据归一化等技术,目的是提高模型的准确性和泛化能力。
    • 模型训练:在预处理后的数据上训练模型,通常需要使用深度学习框架,如TensorFlow或PyTorch。训练过程中需要调整超参数、选择合适的网络结构等。
    • 调优:在模型训练完成后,需要对模型进行调优,以提高其性能。这包括使用正则化技术、集成学习等技术来降低过拟合和提高泛化能力。
    • 部署:最后,将训练好的模型部署到生产环境中,供实际应用使用。部署过程中需要考虑模型的计算效率、可扩展性和安全性等问题。
  • 在大模型开发的架构上,通常采用微服务架构,将数据处理、模型训练和推理等模块拆分成独立的微服务,以便于管理和扩展。此外,为了提高模型的计算效率,可以采用分布式计算架构,将计算任务分配到多个GPU或CPU上进行并行计算。

  • 在实际应用中,我们可以通过集成大模型API来快速实现应用开发。例如,在自然语言处理领域,我们可以使用开源的大规模预训练语言模型(如BERT、GPT等)来实现文本分类、情感分析等功能。这些模型的API已经进行了高度的封装和优化,我们可以直接调用其API来获取相应的结果,而不需要从零开始构建自己的大模型

  • 通义千问API:让大模型使用各种工具 通义千问API:让大模型使用各种工具-阿里云开发者社区 (aliyun.com)

    • 下载文档代码及安装依赖项

    • git clone https://github.com/AlibabaCloudDocs/llm_learning.git
      cd llm_learning
      pip install -r requirements.txt
      
    • 使用通义千问 API 之前,你需要前往模型服务灵积官网创建 API Key。这样你就可以使用密钥 DASHSCOPE_API_KEY 访问通义千问大模型。Windows 可以在终端使用SET命令设置环境变量

    • set DASHSCOPE_API_KEY=sk-****
      # 或者在PowerShell中使用以下命令行配置环境变量
      $Env:DASHSCOPE_API_KEY = "sk-****"
      
    • 首先我们尝试直接询问大模型关于“阿里云灵积服务”是什么的问题,方式如下:

    • from langchain_community.llms import Tongyi
      llm = Tongyi()
      # 使用更强大的通义千问 max
      llm.model_name = 'qwen-max'
      print(llm.invoke('灵积是什么服务'))
      
    • 下面我们来为大模型添加一些知识,让他能正确回答这个问题。基于Langchain调用自定义工具。首先通过调用langchain来实现自定义工具的调用,然后将手动实现langchain的能力,从而让大家很好的理解其中的奥妙。这里将使用langchain中的AgentExecutor来实现这个能力:

    • from langchain import hub
      from langchain.agents import AgentExecutor
      from langchain.agents import create_react_agent
      from langchain_community.llms import Tongyi
      from langchain_core.tools import BaseTool
      model = Tongyi()
      model.model_name = 'qwen-max'
      class SearchTool(BaseTool):
          """服务查询工具"""
          name: str = "服务查询工具"
          description: str = (
              "当你不确定一个服务是什么,才使用此工具。"
          )
          def _run(self, name: str) -> str:
              if name == '灵积':
                  return '''DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅!'''
              return name + '抱歉,没有查到相关信息。'
      tools = [SearchTool()]
      prompt = hub.pull("hwchase17/react")
      agent = create_react_agent(model, tools, prompt)
      agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
      result = agent_executor.invoke({
         'input': '灵积是什么服务'})
      print(result['output'])
      
    • 第一次执行时,我们直接向大模型提问:灵积是什么服务。 这时大模型并没有在预训练中学习过关于“灵积”服务的知识,因此无法回答。为了解决这个问题,我们可以借助外部工具,比如搜索引擎工具如Elastic Search等,先查询一下灵积是什么服务,然后再向大模型提问。有了准确的信息补充,大模型就能回答的更准确了。

    • 基于上述Langchain的思路我们来研究如何自定义一个工具。为了便于和大模型交互,我们定义一个XML交互工具。当用户向大模型提问时,大模型必须返回XML格式的回复,便于我们后续解析和调用工具。

    • from langchain_community.llms import Tongyi
      llm = Tongyi()
      # 使用更强大的通义千问 max
      llm.model_name = 'qwen-max'
      prompt = '''你是一个可以回答任何问题的助手。
      你可以使用下列工具: 
      find_service: 当你不确定答案时,你可以使用此工具。
      为了使用这个工具,你必须用<tool></tool>和<tool_input></tool_input>标签。
      例如,如果您有一个名为“find_service”的工具,可以查询企业内部服务信息,为了搜索阿里云是什么服务,你会返回:
      <tool>find_service</tool><tool_input>阿里云</tool_input>
      使用工具后你会得到一个形式为<observation></observation>的响应,因此在第二轮你会得到输入为
      <tool>find_service</tool><tool_input>阿里云</tool_input><observation>阿里云服务简介...</observation>
      你需要判断<observation></observation>标签中的内容是不是你需要的答案。如果是最终答案final_answer,你需要返回答案:
      <final_answer>阿里云服务简介...</final_answer>
      如果你不需要使用工具也能回答问题,你也必须要用<final_answer></final_answer>标签来包裹答案。比如
      <final_answer>阿里云服务详细介绍...</final_answer>
      开始任务:
      问题:
      '''
      print(llm.invoke(prompt + '灵积是什么服务'))
      
    • 定义XML解析函数和搜索工具

    • from langchain.output_parsers import XMLOutputParser
      # 从xml中获取tool和tool_input
      def get_tool_and_input(response):
          xml_response = f"<xml>{response}</xml>"
          parsed_xml_response = XMLOutputParser().invoke(xml_response)
          response_dict = {
         }
          for item in parsed_xml_response['xml']:
              for key in item:
                  response_dict[key] = item[key]
          return response_dict
      # 运行tool
      def run_tool(response_dict):
          search_input =''
          search_result = ''
          if response_dict.get('tool') is None:
              search_result = response
          elif response_dict['tool'] == 'find_service':
              search_input = response_dict['tool_input']
              search_result = SearchTool().run(search_input)
          return search_input, search_result
      response="""<tool>find_service</tool><tool_input>灵积</tool_input>"""
      response_dict = get_tool_and_input(response)
      search_input, search_result = run_tool(response_dict)
      search_result
      
    • 为了更好帮助读者理解大模型Agent调用的原理,下面的代码是我们自己定义了一个简单的工具类 SearchTool 类,而不是继承自langchain的 BaseTool。并且我们实现了一个简单的 DIYAgent 类,这个类参考了langchain的接口,实现基本的调用工具的能力。这样读者可以看到大模型是如何理解用户问题并返回调用工具的指令,而Agent又是如何理解这个指令,并启动对应工具的。

    • from langchain_community.llms import Tongyi
      import json
      # 定义模型
      llm = Tongyi()
      llm.model_name = 'qwen-max'
      # 定义搜索工具
      class SearchTool():
          """服务查询工具"""
          name: str = "服务查询工具"
          description: str = (
              "当你不确定一个服务是什么,才使用此工具。"
          )
          def run(self, name: str) -> str:
              if name == '灵积':
                  return '''DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅!'''
              return name + '抱歉,没有查到相关信息。'
      # 定义查询模版
      prompt_template = '''你是一个可以回答任何问题的助手。
      你可以使用下列工具: 
      {tools}: 当你不确定答案时,你可以使用此工具。
      为了使用这个工具,你必须用<tool></tool>和<tool_input></tool_input>标签。
      例如,如果您有一个名为“{tool1}”的工具,可以查询企业内部服务信息,为了搜索阿里云是什么服务,你会返回:
      <tool>{tool1}</tool><tool_input>阿里云</tool_input>
      使用工具后你会得到一个形式为<observation></observation>的响应,因此在第二轮你会得到输入为
      <tool>{tool1}</tool><tool_input>阿里云</tool_input><observation>阿里云服务简介XXX</observation>
      你需要判断<observation></observation>标签中的内容是不是你需要的答案。如果是最终答案final_answer,你需要返回答案:
      <final_answer>阿里云服务简介XXX</final_answer>
      如果你不需要使用工具也能回答问题,你也必须要用<final_answer></final_answer>标签来包裹答案。比如
      <final_answer>阿里云服务详细介绍XXX</final_answer>
      开始任务:
      问题:
      '''
      # 定义一个简单的工具调用Agent
      class DIYAgent():
          def __init__(self,tool,model=None,prompt=None):
              self.tool = tool
              self.model = model
              self.verbose = False
              self.tool_name = self.get_class_name(self.tool) 
              self.prompt = prompt.format(tools=self.tool_name,tool1 = self.tool_name)
          def get_class_name(self,tool):
              return tool.__class__.__name__
          # 从xml中获取tool和tool_input
          def get_tool_and_input(self, response):
              xml_response = f"<xml>{response}</xml>"
              parsed_xml_response = XMLOutputParser().invoke(xml_response)
              response_dict = {
         }
              for item in parsed_xml_response['xml']:
                  for key in item:
                      response_dict[key] = item[key]
              return response_dict
          # 运行tool
          def run_tool(self, response_dict):
              search_input =''
              search_result = ''
              if response_dict.get('tool') is None:
                  if response_dict.get('final_answer') is not None:
                      return search_input, response_dict['final_answer']
                  else:
                      return search_input, "Error in tool"+json.dumps(response_dict)
      
              if response_dict['tool'] ==  self.tool_name:
                  search_input = response_dict['tool_input']
                  search_result = self.tool.run(search_input)
                  return search_input, search_result
          # run agent
          def run(self, request):
              raw_response = self.model.invoke(self.prompt + request)
              response_dict = self.get_tool_and_input(raw_response)
              print("使用工具:", response_dict['tool'])
              print("查询内容:", response_dict['tool_input'])
              search_input, search_result = self.run_tool(response_dict)
              print("搜索结果:", search_result)
              new_prompt = self.prompt + f"<tool>{self.tool_name}</tool><tool_input>{search_input}</tool_input><observation>{search_result}</observation>"
              final_response = self.model.invoke(new_prompt)
              final_response_dict = self.get_tool_and_input(final_response)
              search_input, final_answer = self.run_tool(final_response_dict)
              return final_answer
      # MIAN Function
      tool = SearchTool()
      diyAgent = DIYAgent(tool ,llm, prompt_template)
      result = diyAgent.run("灵积是什么服务")
      print("最终输出:",result)
      
    • 扩展搜索范围时,我们只需要扩展SearchTool函数的能力即可,比如从搜索引擎上下载信息。为了进行对比,我们直接填入一些已知信息。

    • # 扩展支持的服务查询
      class SearchTool2():
          """服务查询工具"""
          name: str = "服务查询工具"
          description: str = (
              "当你不确定一个服务是什么,才使用此工具。"
          )
          def run(self, name: str) -> str:
              if name == '灵积':
                  return '''DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅!'''
              elif name=="百炼":
                  return "百炼,即大模型服务平台,是面向企业客户及合作伙伴的,基于通义大模型、行业大模型以及三方大模型,结合企业专属数据,包含全链路大模型开发工具的一站式大模型商业化平台。提供完整的模型训练、微调、评估等产品工具,预置丰富的应用插件,提供便捷的集成方式,更快更高效地完成大模型应用的构建。"
              elif (name=="PAI") or (name=="阿里云 PAI"):
                  return "人工智能平台 PAI(Platform of Artificial Intelligence)面向企业客户及开发者,提供轻量化、高性价比的云原生人工智能,涵盖DSW交互式建模、Designer拖拽式可视化建模、DLC分布式训练到EAS模型在线部署的全流程。"
              elif name=="OSS":
                  return "阿里云对象存储OSS(Object Storage Service)是一款海量、安全、低成本、高可靠的云存储服务,可提供99.9999999999%(12个9)的数据持久性,99.995%的数据可用性。多种存储类型供选择,全面优化存储成本。"
              return name + '抱歉,没有查到相关信息。'
      # MIAN Function
      tool = SearchTool2()
      diyAgent = DIYAgent(tool ,llm, prompt_template)
      result = diyAgent.run("百炼是什么服务")
      print("最终输出:",result)
      
    • 首先用LangChain的的接口定义了一个Agent应用,为了理解大模型使用Agent的内核,用代码实现了一遍。这段代码包括工具定义类prompt定义模版,工具调用,工具查询等功能。从这里我们看到,LangChain方法主要思路是通过详细定义的Prompt让大模型反复思考“大模型目前获得的信息是否可以回答用户的问题”,如果不能解答用户的问题,大模型应该使用何种工具;如果可以解答用户的问题,那么就输出结果。


网站公告

今日签到

点亮在社区的每一天
去签到