当前位置:首页 > 技术分析 > 正文内容

应用提示工程(Prompt Engineering)驾驭ChatGPT

ruisui885个月前 (02-08)技术分析26

你是否想过,如果你能和人工智能对话,你会说什么?你会问它一些简单的问题,比如“今天天气怎么样?”或者“谁是美国总统?”吗?还是你会让它帮你完成一些稍微复杂的任务,比如“帮我写一篇关于环境保护的文章”或者“给我推荐一个适合我去旅行的地方”呢?如果你仅仅是需要大语言模型(LLM)完成上面这些任务,实在是有点小瞧LLM的威力,特别是像ChatGPT这种超大规模的模型。在中文互联网上一直有人觉得LLM不过如此,甚至都不知道《狂飙》这么热门的电视剧的中的人物,这些人大部分是其实先入为主把LLM当成了知识库,其实LLM不是知识库,它更像是一个知识的组织者,它会归纳总结,会逻辑推理,甚至会符号推理。

这里会先简单介绍一下prompt engineering,然后用一个例子来演示如何用prompt engineering 实现对外部系统的调用。

无论你想让LLM做什么,你都需要用一种特殊的方式来表达你的意图。这种方式就叫做prompt engineering(提示工程)。prompt engineering指的是为大语言模型设计高质量、有意义的输入,以产生高质量、连贯的输出。

大语言模型是一种基于深度学习的人工智能技术,它可以通过分析海量的文本数据来学习语言规律,并根据输入生成各种类型的文本。例如,ChatGPT就是一个大语言模型,它可以生成聊天对话、故事、诗歌、歌词等等。

但是,并不是任何输入都可以让大语言模型生成好的输出。有些输入可能太模糊、太复杂或者太难理解,导致大语言模型无法准确地捕捉到用户的意图,或者生成不相关、不合理或者不完整的文本。因此,我们需要通过prompt engineering来优化我们的输入,让大语言模型更好地理解我们想要什么,并给出满意的答案。

如果你有机器学习相关背景,可能会想到让LLM完成特定任务应该是对模型进微调(fine tuning),但是对这种动辄千亿级别参数的模型进行微调代价会非常高,同时也有学术研究(Pre-train, Prompt, and Predict: A Systematic Survey of Prompting Methods in Natural Language Processing
https://arxiv.org/pdf/2107.13586.pdf)证明prompt engineering可以获得比fine tuning更好的效果。

Prompt engineering 相关的研究现在非常热门,这里介绍一种非常简单有效方法上下文提示学习(in-context learning prompt)作为例子。上下文提示学习是一种利用大语言模型进行任务学习的方法,其中模型只根据一些输入输出对(即in-context examples)和一个新的输入(即query input)来生成相应的输出。上下文提示学习只发生在推理时,不涉及对模型参数的更新,所以成本低廉只会占用模型输出输入的token数。上下文提示学习可以让模型在没有显式微调或标注数据的情况下,展示出一定程度的泛化和适应能力。

这里用鸡兔同笼做一个例子,GPT-3.5和New Bing可以正确的求解题干正确的鸡兔同笼问题,但是如果题干错误,GPT-3.5 和 New Bing 都会输出错误的答案。例如:

输入:

“一个笼子关着一些兔子和鸡,一共有42只脚,10个头,请问笼子里有多少只鸡和兔子?

GPT-3.5输出:

“假设笼子里有x只兔子,y只鸡,则有以下方程组:

x + y = 10 (头的数量)

4x + 2y = 42 (脚的数量)

解这个方程组可以得到x = 7,y = 3,所以笼子里有7只兔子和3只鸡。”

明显这是一个错误的答案。

如果采用上下文提示学习

输入:

“Q: 一个笼子关着一些兔子和鸡,一共有40只脚,9个头,请问笼子里有多少只鸡和兔子?

A: 首先判断头的数量乘以4必须大于脚的数量,9*4=36 小于40 题目不正确。

Q: 一个笼子关着一些兔子和鸡,一共有40只脚,8个头,请问笼子里有多少只鸡和兔子?”

输出:

“假设笼子里有x只兔子,y只鸡,则有以下方程组:

x + y = 8 (头的数量)

4x + 2y = 40 (脚的数量)

解这个方程组可以得到x = 10,y = -2,但是y为负数,不符合实际情况。因此,该问题无解。”

通过下文提示学习,模型学会了判断题干是不是正确,还展现一定的迁移能力。

这里是一个比较完整的prompt engineering 教程,对prompt engineering有兴趣的朋友可以看一下。

https://learnprompting.org/docs/intro

接下来我们再举一个复杂的例子,不仅仅让GPT-3.5按给定格式输出内容,还要和Python脚本配合实现对数据操作。我们要使用GPT-3.5开发一个用于聚会报名的程序,让GPT-3.5 作为智能助理通过聊天方式收集用户报名信息保存到内存的一个dict对象里。

我先来设计一个上下文提示训练的样本, 为了方便调用OpenAI的接口这里用Python的对象数组来表示这个样本:

[
    {"role": "system", "content": '''
    你现在是一个家庭聚会智能助理聊天机器人,负责接受参加聚会的用户报名,你需要处理参加和取消两种操作。
    报名操作:家庭聚会参加的每个家庭只需要一个人报名,你负责收集报名人的名字,以及每个家庭参加聚会的人数。
    取消参加的用户,你只需要收集报名人的名字。你完成信息收集后将信息输出为一个json对象, json对象的格式如下:
     {
        "action":"join 或 cancel", //必须收集的字段,要进行的操作
        "name":"报名者名字",//必须收集的字段,报名或取消者的名字
        "number":"参加人数",//取消操作不需要该字段
      }
    '''},
    {"role": "assistant", "content": '''
    您好,您要报名参加聚会吗?
    '''
     },
    {"role": "user", "content": '''
    是的
    '''
     },
    {"role": "assistant", "content": '''
    请问您的名字?
    '''
     },
    {"role": "user", "content": '''
    张三
    '''
     },
    {"role": "assistant", "content": '''
    请问张先生/女士,你们家几个人参加?
    '''
     },
    {"role": "user", "content": '''
    4
    '''
     },
    {"role": "assistant", "content": '''
      {
         "action":"join",
         "name":"张三",
         "number":4
      }
      '''
     },
    {"role": "assistant", "content": '''
    您好,我是您的家庭聚会助理,您要参加聚会吗?
    '''
     }
]

这个训练样本先给出一个整体的提示,定义了输出的json格式,然后使用了一段对话作为样本。

使用上面的样本再配合一个python终端程序,这个程序接收标准输入用户输入的内容通过API发送给OpenAI 的GPT-3.5模型,如果用户键入/list 则打印出内存里的报名数据。下面是测试的结果。

参加聚会的对话

user:你好
Bot:     您要参加聚会吗?
user:是的
Bot:     请问您的名字是?
user:张三
Bot:     张先生/女士,您家有几个人参加聚会呢?
user:3
Bot: 完成参加聚会注册
user:/list
Bot {'张三': 3}

取消聚会的对话

user:你好
Bot:     您好,请问您要参加聚会吗?
user:取消    
Bot:     好的,请问您是哪位报名者?
user:张三
Bot: 已帮您取消聚会注册
user:/list
Bot {}

没有对模型进行任何fine tuning,只是使用了上下文提示学习就可以让ChatGPT-3.5按预想的方式去工作了。完整代码在文后。

下文提示学习(in-context learning prompt)其实是属于小样本学习(Few Shot Learning)中的one shot learning只有一个支持集的小样本学习。

与LLM和传统编程有着很大的不同,Prompt engineering 和 Prompt Learning 是让我们可以和大模型好好聊天和协作的有效工具。

祝大家玩得愉快,未来的提示工程师(Prompt Engineers) 和 模型训练工程师( Model Training Engineers)们。

完整代码:

import openai
import json5
import re

key = "YOUR OPEN AI API KEY"
openai.api_key = key


def send_message(message_log):
    # Use OpenAI's ChatCompletion API to get the chatbot's response
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",  # The name of the OpenAI chatbot model to use
        # The conversation history up to this point, as a list of dictionaries
        messages=message_log,
        # The stopping sequence for the generated response, if any (not used here)
        stop=None,
        # The "creativity" of the generated response (higher temperature = more creative)
        temperature=0.7,
    )
    for choice in response.choices:
        if "text" in choice:
            return choice.text

    # If no response with text is found, return the first response's content (which may be empty)
    return response.choices[0].message.content


prompts = [
    {"role": "system", "content": '''
    你现在是一个家庭聚会智能助理聊天机器人,负责接受参加聚会的用户报名,你需要处理参加和取消两种操作。
    报名操作:家庭聚会参加的每个家庭只需要一个人报名,你负责收集报名人的名字,以及每个家庭参加聚会的人数。
    取消参加的用户,你只需要收集报名人的名字。你完成信息收集后将信息输出为一个json对象, json对象的格式如下:
     {
        "action":"join 或 cancel", //必须收集的字段,要进行的操作
        "name":"报名者名字",//必须收集的字段,报名或取消者的名字
        "number":"参加人数",//取消操作不需要该字段
      }
    '''},
    {"role": "assistant", "content": '''
    您好,您要报名参加聚会吗?
    '''
     },
    {"role": "user", "content": '''
    是的
    '''
     },
    {"role": "assistant", "content": '''
    请问您的名字?
    '''
     },
    {"role": "user", "content": '''
    张三
    '''
     },
    {"role": "assistant", "content": '''
    请问张先生/女士,你们家几个人参加?
    '''
     },
    {"role": "user", "content": '''
    4
    '''
     },
    {"role": "assistant", "content": '''
      {
         "action":"join",
         "name":"张三",
         "number":4
      }
      '''
     },
    {"role": "assistant", "content": '''
    您好,我是您的家庭聚会助理,您要参加聚会吗?
    '''
     }
]

# Define a global variable data, which is an empty dictionary
global data
data = {}

def extract_json(text):
    # match the content inside curly braces or square brackets
    pattern = r"(\{.*?\}|\[.*?\])"
    # return a list of all matching
    matches = re.findall(pattern, text, re.DOTALL)
    result = []  # create an empty list to store the results for json_str in matches
    for json_str in matches:  # iterate over each string in the list
        # convert the string to a JSON object or array
        data = json5.loads(json_str)
        # append the JSON object or array to the result list
        result.append(data)
    return result  # return the result list

# Define a function named ops, which takes a dictionary as an argument
def ops(action_dict):
    # Declare to use the global variable data
    global data
    # Get the values corresponding to the keys action, name and number from the argument dictionary
    action = action_dict.get("action")
    name = action_dict.get("name")
    number = action_dict.get("number")
    # If the value of action is "join", then add a key-value pair to the data dictionary, with the key as name and the value as number, and return a message
    if action == "join":
        data[name] = number
        return "完成参加聚会注册"
    # If the value of action is "cancel", then delete the key-value pair with name as the key from the data dictionary, and return a message
    elif action == "cancel":
        del data[name]
        return "已帮您取消聚会注册"
    # If the value of action is "list", then return the data dictionary itself
    elif action == "list":
        return data

def main():
    # Initialize an empty list for storing messages
    message_log = [] + prompts
    # Send the initial message to the bot
    send_message(message_log)
    # Start an infinite loop to get user input
    while True:
        # Get user input from the console
        user_input = input("user:")
        # Check if user input starts with /list command
        if user_input.startswith("/list"):
            # Print the bot response for listing operations
            print("Bot", ops({"action":"list"}))
            # Skip the rest of the loop and continue with next iteration
            continue
        # Append user input to message log as a dictionary with role and content keys
        message_log.append(
            {
                "role": "user",
                "content": user_input
            }
        )
        # Send the updated message log to the bot and get its response as a string
        result = send_message(message_log)
        # Extract any JSON data from the bot response using regex
        action_json = extract_json(result)
        # Check if there is any JSON data in the bot response
        if bool(action_json):
            # Perform the operation specified by the JSON data and get its result as a string 
            result = ops(action_json[0])
            # Reset the message log to only contain prompts 
            message_log = [] + prompts 
            # Send the reset message log to update bot state 
            send_message(message_log) 
        else: 
            # Append bot response to message log as a dictionary with role and content keys 
            message_log.append(
               {
                   "role": "assistant",
                   "content": result
               }
            )
        # Print bot response on console 
        print("Bot:", result)

if __name__ == '__main__':
    main()

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/1802.html

标签: json_extract
分享给朋友:

“应用提示工程(Prompt Engineering)驾驭ChatGPT” 的相关文章

Vue2的16种传参通信方式

前言先直入主题列出有哪些传参方式,下面再通过事例一一讲解。props(父传子)$emit与v-on (子传父)EventBus (兄弟传参).sync与update: (父子双向)v-model (父子双向)ref$children与$parent$attrs与$listeners (爷孙双向)pr...

VUE-router

七.Vue-router1、什么是vue-routervue-router是vue.js官方路由管理器。vue的单页应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。传统页面切换是用超链接a标签进行切换。但vue里是用路由,因为我们用Vue做的都是单页应用,就相当于只有一个主的i...

三勾知识付费(PHP+vue3)微信小程序平台+SAAS+前后端源码

项目介绍三勾小程序商城基于thinkphp8+element-plus+uniapp打造的面向开发的小程序商城,方便二次开发或直接使用,可发布到多端,包括微信小程序、微信公众号、QQ小程序、支付宝小程序、字节跳动小程序、百度小程序、android端、ios端。软件架构后端:thinkphp8 管理端...

同步电机和异步电机竟然有这么大区别,看完就理解了

同步电机和异步电机的主要区别是:同步电机能与其定子磁场旋转达到同步转速,异步电机转速达不到定子磁场的同步转速。电机大致分成三种,同步机,异步机(以上两种多与电网相连),还有个直流电机。下面的内容是一个过渡,只作为对电机(同步机、异步机)原理性的知识进行形象的讲解(懂电机的可跳过)。同步机和异步机,这...

准考证打印入口

2023年甘肃省特岗教师、三支一扶、西部计划准考证打印时间6月27日9∶00至7月2日9∶00考试时间7月2日成绩查询时间7月15日以后温馨提示:报考人员需登录“甘肃人事考试网”(http://ks.rst.gansu.gov.cn),进入 “准考证打印”栏目,按照提示打印本人准考证并妥善保管,以备...

52、Vue 怎么实现跨域(必会)

1、什么是跨域跨域指浏览器不允许当前页面的所在的源去请求另一个源的数据。源指协议,端口,域名。只要这个 3 个中有一个不同就是跨域2、使用 vue-cli 脚手架搭建项目时 proxyTable 解决跨域问题,打开 config/index.js,在 proxyTable 中添写如下代码:proxy...