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

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

ruisui883个月前 (02-08)技术分析18

你是否想过,如果你能和人工智能对话,你会说什么?你会问它一些简单的问题,比如“今天天气怎么样?”或者“谁是美国总统?”吗?还是你会让它帮你完成一些稍微复杂的任务,比如“帮我写一篇关于环境保护的文章”或者“给我推荐一个适合我去旅行的地方”呢?如果你仅仅是需要大语言模型(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” 的相关文章

体检刷卡收费管理系统

体检刷卡收费管理系统headerfooter《体检刷卡收费管理系统》是针对各医院进行体检刷卡收费管理的一套系统。软件集办卡、充值、刷卡消费、体检登记与一体。主要功能:1.基本信息:科室设置、套餐设置、单项设置、本院信息;2.体检卡管理:单位人员办卡、个人办卡、体检卡充值、体检卡禁用、体检卡开通、体检...

Excel VBA 收费单据打印/一步一步带你设计【收费管理系统】12

本文于2023年6月10日首发于本人同名公众号:Excel活学活用,更多文章案例请搜索关注!☆本期内容概要☆用户窗体设置:收费结算模块设置(7)单据打印大家好,我是冷水泡茶,前几期我们分享了【收费管理系统】的设计,最近一期是(Excel VBA 收费结算模块/一步一步带你设计【收费管理系统】11),...

vue3父子组件传对象,子组件访问修改父组件对象中的属性值

在Vue 3中,父子组件之间的数据传输通常通过props和emit进行。父组件可以通过props向下传递数据给子组件,子组件则可以通过emit向上通知父组件更新数据。如果需要在子组件中修改父组件对象中的属性值,可以使用一个名为ref的Vue 3新特性。以下是一个示例,演示了如何在Vue 3中实现父子...

2021最全VUE面试题,奥利给

1. vue-router用过没,哪些常用的钩子函数路由钩子的执行流程,钩子函数种类有:全局守卫、路由守卫、组件守卫。首页可以控制导航跳转, beforeEach , afterEach 等,?般?于页? title 的修改。?些需要登录才能调整??的重定向功能。beforeEach 主要有3个参数...

虚幻引擎5.5现已发布 手游开发、动画制作重大改进

Epic在今天发布了虚幻引擎5.5,现可通过Epic Launcher下载。此版本在动画创作、渲染、虚拟制片、移动端游戏开发和开发人员迭代工具集等方面做出了重大改进。 官方博客:虚幻引擎5.5现已发布,在动画创作、虚拟制作和移动游戏开发方面取得了显著进步,渲染、摄像机内视觉特效和开发人员迭代等领域的...

Vue真是太好了 壹万多字的Vue知识点 超详细!

1??、Vue和其他两大框架的区别Angular 学习成本太高React 代码可读性差Vue 学习成本较低 很容易上手VUE官方: https://cn.vuejs.org/v2/guide/comparison.html?2??、Vue是什么Vue是一套用于构建用户界面的渐进式框架 "前端...