AI 学习资料 · 生成式 AI 入门(精选 7 课)
创建高级提示词
先回顾一下上一课学到的内容:
提示词工程是通过提供更有用的指令或上下文,引导模型给出更相关回答的过程。
写提示词也分两步:构造提示词,即提供相关的上下文;以及优化,即逐步改进提示词。
到这里,我们对怎样写提示词已经有了一些基本的了解,但还需要更深入。在本课中,你将从尝试各种提示词,进步到理解为什么一个提示词比另一个更好。你会学到如何按照一些基本技术来构造提示词,这些技术适用于任何 LLM。
简介
本课将涵盖以下主题:
- 通过在提示词中运用不同的技术,扩展你对提示词工程的了解。
- 配置提示词,让输出产生变化。
学习目标
完成本课后,你将能够:
- 运用提示词工程技术,改善提示词得到的结果。
- 按需要让提示词的输出有变化,或保持确定。
提示词工程
提示词工程是编写能产生期望结果的提示词的过程。提示词工程不只是写一段文字提示词那么简单。它并不是一门工程学科,更像是一组技术,你可以运用它们来得到期望的结果。
一个提示词的例子
来看一个这样的基本提示词:
生成 10 个关于地理的问题。
在这个提示词中,你其实已经运用了几种不同的提示词技术。
我们来拆解一下:
- 上下文:你指明了问题要和“地理”有关。
- 限制输出:你希望问题不超过 10 个。
简单提示词的局限
你未必能得到想要的结果。问题会生成出来,但地理是个很大的主题,你可能得不到想要的内容,原因如下:
- 主题太大:你不知道问题会是关于国家、首都、河流,还是别的什么。
- 格式:如果你希望问题按某种特定格式排列呢?
可见,写提示词时要考虑的东西很多。
到目前为止,我们只看了一个简单的提示词例子,但生成式 AI 能做的远不止这些,可以帮助各种岗位、各个行业的人。接下来,我们来了解一些基本技术。
提示词技术
首先要明白,LLM 能够响应提示词,这是它的一种涌现特性,也就是说,这不是模型内置的功能,而是我们在使用模型的过程中发现的。
给 LLM 写提示词有一些基本技术,我们逐一来看:
- 零样本提示:最基本的提示形式。只用一条提示词,让 LLM 仅凭训练数据给出回答。
- 少样本提示:提供一个或多个示例,让 LLM 参照这些示例生成回答。
- 思维链:告诉 LLM 怎样把一个问题拆分成若干步骤。
- 生成知识:为了改进回答,在提示词中额外提供生成出来的事实或知识。
- 由少到多:和思维链类似,把问题拆分成一系列步骤,再要求按顺序执行这些步骤。
- 自我完善:对 LLM 的输出提出批评,再要求它改进。
- 助产式提示(maieutic prompting):目的是确保 LLM 的答案正确,做法是让它解释答案的各个部分。这是自我完善的一种形式。
零样本提示
这种提示方式非常简单,只有一条提示词。你刚开始接触 LLM 时用的,多半就是这种技术。例如:
- 提示词:“什么是代数?”
- 回答:“代数是数学的一个分支,研究数学符号以及操作这些符号的规则。”
少样本提示
这种提示方式在请求中附上几个示例,帮助模型完成任务。它由一条提示词加上与任务相关的额外数据组成。例如:
-
提示词:
“用莎士比亚的风格写一首诗。下面是几首莎士比亚十四行诗的例子:
十四行诗第 18 首:‘我可否把你比作夏日?你比夏日更可爱、更温和……’
十四行诗第 116 首:‘我绝不承认两颗真心的结合会有任何障碍。爱若一遇变化就改变,那就不是爱……’
十四行诗第 132 首:‘我爱你的眼睛,它们仿佛怜悯我,知道你的心正用轻蔑折磨我……’
现在,写一首赞美月亮之美的十四行诗。”
-
回答:“月亮在天空中柔柔地闪耀,银色的光洒下温柔的优雅……”
示例向 LLM 提供了期望输出的上下文、格式或风格,帮助模型理解具体任务,生成更准确、更相关的回答。
思维链
思维链是一种很有意思的技术,它带着 LLM 一步一步地走。思路是用一种能让 LLM 明白事情该怎么做的方式来给它指令。看下面这个例子,分别是没有和使用了思维链的情况。
没有思维链时:
- 提示词:“爱丽丝有 5 个苹果,扔掉了 3 个,给了鲍勃 2 个,鲍勃又还给她 1 个,爱丽丝现在有几个苹果?”
- 回答:5
LLM 回答 5,这是错的。正确答案是 1 个苹果,算式为 5 - 3 - 2 + 1 = 1。
那么,我们怎样教 LLM 算对呢?
试试思维链。运用思维链的意思是:
- 给 LLM 一个类似的例子。
- 展示算式,以及怎样正确地计算。
- 再给出原来的提示词。
做法如下:
-
提示词:
“丽莎有 7 个苹果,扔掉了 1 个,给了巴特 4 个,巴特又还给她 1 个:
7 - 1 = 6
6 - 4 = 2
2 + 1 = 3
爱丽丝有 5 个苹果,扔掉了 3 个,给了鲍勃 2 个,鲍勃又还给她 1 个,爱丽丝现在有几个苹果?”
-
回答:1
注意,我们写了一条长得多的提示词,其中包含另一个例子、一组算式,最后才是原来的问题,结果得到了正确答案 1。
可见,思维链是一种非常强大的技术。
生成知识
很多时候,你在构造提示词时,希望用上自己公司的数据:提示词的一部分来自公司,另一部分才是你真正要问的问题。
举个例子,如果你从事保险业务,提示词可能是这样的:
{{company}}: {{company_name}}
{{products}}:
{{products_list}}
请根据以下预算和需求推荐保险:
预算:{{budget}}
需求:{{requirements}}
上面的提示词是用模板构造的。模板中有若干变量,用 {{variable}} 表示,它们会被公司 API 返回的实际值替换。
下面是变量被公司的内容替换后,提示词可能的样子:
保险公司:ACME 保险
保险产品(每月费用):
- 车险,便宜,500 美元
- 车险,昂贵,1100 美元
- 家财险,便宜,600 美元
- 家财险,昂贵,1200 美元
- 寿险,便宜,100 美元
请根据以下预算和需求推荐保险:
预算:1000 美元
需求:车险、家财险和寿险
把这个提示词交给 LLM,会得到类似这样的回答:
根据您的预算和需求,我们推荐 ACME 保险的以下保险组合:
- 车险,便宜,500 美元
- 家财险,便宜,600 美元
- 寿险,便宜,100 美元
总费用:1,200 美元
可以看到,它还推荐了寿险,而这是不应该的。
编者注:英文原文如此。从示例看,更明显的问题是推荐组合的总价 1200 美元超出了 1000 美元的预算。
这个结果说明我们需要优化提示词,把允许的范围说得更清楚。经过一番试错,我们得到了下面这个提示词:
保险公司:ACME 保险
保险产品(每月费用):
- 类型:车险,便宜,费用:500 美元
- 类型:车险,昂贵,费用:1100 美元
- 类型:家财险,便宜,费用:600 美元
- 类型:家财险,昂贵,费用:1200 美元
- 类型:寿险,便宜,费用:100 美元
请根据以下预算和需求推荐保险:
预算:1000 美元,选择仅限以下类型:车险、家财险
注意,加上类型和费用字段,再用上仅限(restrict)这个关键词,能帮助 LLM 理解我们想要什么。
现在得到的回答是:
根据您的预算和需求,我们推荐“车险,便宜”这款产品,每月费用为 500 美元。
这个例子想说明的是:即使用的是生成知识这样的基本技术,大多数情况下仍需要优化提示词,才能得到期望的结果。
由少到多
由少到多提示的思路,是把一个较大的问题拆分成若干子问题,以此引导 LLM 一步步“攻克”这个大问题。数据科学就是一个很好的例子,你可以让 LLM 像这样拆分问题:
提示词:如何用 5 个步骤完成数据科学工作?
AI 助手回答:
- 收集数据
- 清洗数据
- 分析数据
- 绘制数据图表
- 展示数据
自我完善:对结果提出批评
对于生成式 AI 和 LLM,你不能直接相信它的输出,而要加以核实。毕竟,LLM 只是告诉你接下来最可能说什么,而不是什么才是正确的。所以,一个好办法是让 LLM 批评自己,这就引出了自我完善技术。
具体步骤如下:
- 用最初的提示词让 LLM 解决一个问题
- LLM 给出回答
- 你对回答提出批评,并要求 AI 改进
- LLM 再次回答,这次会考虑你的批评,并给出它想到的改进方案
这个过程可以重复任意多次。
下面是使用这种技术的一个例子:
提示词:“创建一个 Python Web API,包含 products(产品)和 customers(客户)两个路由”
AI 回答:
import flask
app = Flask(__name__)
@app.route('/products')
def products():
return 'Products'
@app.route('/customers')
def customers():
return 'Customers'
提示词:对上面的代码提出 3 条改进建议
AI 回答:
当然,以下是改进这段代码的三条建议:
导入正确的模块:第一行应该导入 Flask,而不是 flask。正确的导入语句应为 from flask import Flask。
添加 main 函数:在文件末尾添加 if __name__ == '__main__': 代码块,以便在执行该文件时运行应用。
使用 f-string:在路由函数的 return 语句中使用 f-string 进行格式化。例如,可以把 return 'Products' 改为 return f'This is the {request.path} page'。
from flask import Flask, request
app = Flask(__name__)
@app.route('/products')
def products():
return f'This is the {request.path} page'
@app.route('/customers')
def customers():
return f'This is the {request.path} page'
if __name__ == '__main__':
app.run()
可以看到,由于对第一次的回答提出了批评,上面这次 AI 的回答改进了最初给出的代码。
助产式提示
助产式提示和自我完善类似,但它更侧重于让 LLM 解释自己的回答。目的是减少 LLM 输出中前后不一致的地方,确保它得出正确的答案。流程如下:
- 让 LLM 回答一个问题
- 针对答案的每一部分,让 LLM 更深入地解释
- 如果有不一致的地方,就舍弃这些不一致的部分
重复第 2 步和第 3 步,直到检查完所有部分,并对答案感到满意。
下面是一个提示词示例:
提示词:如何用 5 个步骤制定一份应对疫情的危机预案?
LLM 回答:
1. 识别风险
2. 识别利益相关方
3. 识别资源
4. 识别行动
5. 识别沟通渠道
它给出了 5 个步骤,但我们能确定这是对的吗?让 LLM 逐一解释每个步骤:
提示词:更详细地解释第一步,疫情具体有哪些风险?
风险如下:
- 生命损失
- 业务损失
- 声誉损失
- 客户流失
- 员工流失
这时,我们要确认 LLM 说得对,于是让它更详细地解释第一个风险,并希望它的说法与上面的回答一致:
提示词:在疫情中,最大的风险是什么?为什么?
最大的风险是生命损失,因为生命是最需要保护的。
提示词:疫情中最大的两个风险是什么?
最大的两个风险是生命损失和业务损失。
这时,LLM 的说法前后一致,都把“生命”和“业务”列为最大的两个风险。现在可以比较放心地进入下一步了。不过,我们不应盲目相信 LLM,始终要核实它的输出。
让输出产生变化
LLM 本质上是非确定性的,也就是说,同一个提示词每次运行都会得到不同的结果。比如试试下面这个提示词:
编者注:本节和下一节的几段代码都是模型生成的 Python 程序,看不懂代码也没关系,只需对比每次输出之间差别有多大。
“为一个 Python Web API 生成代码”
# Import necessary modules
import flask
from flask import request, jsonify
# Create a Flask app
app = flask.Flask(__name__)
# Create a route for the API
@app.route('/api', methods=['GET'])
def api():
# Get the data from the request
data = request.args
# Process the data
result = process_data(data)
# Return the result as JSON
return jsonify(result)
# Function to process the data
def process_data(data):
# Do something with the data
result = data + 1
# Return the result
return result
# Run the app
if __name__ == '__main__':
app.run()
再次运行同一个提示词,得到的回答略有不同:
#import necessary packages
import flask
from flask import request, jsonify
#create the Flask application
app = flask.Flask(__name__)
app.config["DEBUG"] = True
#create a list of books
books = [
{'id': 0,
'title': 'A Fire Upon the Deep',
'author': 'Vernor Vinge',
'first_sentence': 'The coldsleep itself was dreamless.',
'year_published': '1992'},
{'id': 1,
'title': 'The Ones Who Walk Away From Omelas',
'author': 'Ursula K. Le Guin',
'first_sentence': 'With a clamor of bells that set the swallows soaring, the Festival of Summer came to the city Omelas, bright-towered by the sea.',
'published': '1973'},
{'id': 2,
'title': 'Dhalgren',
'author': 'Samuel R. Delany',
'first_sentence': 'to wound the autumnal city.',
'published': '1975'}
]
#create an endpoint for the API
@app.route('/', methods=['GET'])
def home():
return '''<h1>Books API</h1>
<p>A prototype API for retrieving books.</p>'''
#create an endpoint to return all books
@app.route('/api/v1/resources/books/all', methods=['GET'])
def api_all():
return jsonify(books)
#create an endpoint to return a single book
@app.route('/api/v1/resources/books', methods=['GET'])
def api_id():
#check if an ID was provided as part of the URL
#if ID is provided, assign it to a variable
#if no ID is provided, display an error
if 'id' in request.args:
id = int(request.args['id'])
else:
return "Error: No id field provided. Please specify an id."
#create an empty list for our results
results = []
#loop through the data and match results that fit the requested ID
#IDs are unique, but other fields might return many results
for book in books:
if book['id'] == id:
results.append(book)
#use the jsonify function from Flask to convert our list of
#Python dictionaries to the JSON format
return jsonify(results)
app.run()
那么,输出会变化,这是问题吗?
这取决于你想做什么。如果你需要一个特定的回答,那就是问题;如果你能接受输出有变化,比如“随便生成 3 个关于地理的问题”,那就不是问题。
用温度来控制输出的变化
好,假设我们决定限制输出,让它更可预测,也就是更确定。该怎么做呢?
温度是一个介于 0 和 1 之间的值,0 表示最确定,1 表示变化最大,默认值是 0.7。我们来看看把温度设为 0.1 时,同一个提示词运行两次会怎样:
编者注:温度的取值范围和默认值因模型和服务而异,请以你所用服务的文档为准。
“为一个 Python Web API 生成代码”
#import necessary libraries
import flask
from flask import request, jsonify
#create an instance of the Flask class
app = flask.Flask(__name__)
#create an endpoint for the API
@app.route('/api/v1/endpoint', methods=['GET'])
def api_endpoint():
#get the request data
data = request.get_json()
#process the data
result = process_data(data)
#return the result
return jsonify(result)
#function to process the data
def process_data(data):
#process the data
result = {'result': 'success'}
#return the result
return result
#run the Flask app
if __name__ == '__main__':
app.run()
再运行一次,得到这样的结果:
#import necessary libraries
import flask
from flask import request, jsonify
#create a Flask app
app = flask.Flask(__name__)
#create a route for the API
@app.route('/api', methods=['GET'])
def api_call():
#get the data from the request
data = request.get_json()
#process the data
result = process_data(data)
#return the result
return jsonify(result)
#define the process_data function
def process_data(data):
#do something with the data
result = data + 1
#return the result
return result
#run the app
if __name__ == '__main__':
app.run()
这两次输出之间只有很小的差别。这次我们反过来,把温度设为 0.9:
# Import necessary libraries
import flask
from flask import request, jsonify
# Create a Flask app
app = flask.Flask(__name__)
# Create a route for the API
@app.route('/api', methods=['GET'])
def api_call():
# Get the data from the request
data = request.args
# Process the data
result = process_data(data)
# Return the result
return jsonify(result)
# Function to process the data
def process_data(data):
# Do something with the data
result = data + 1
# Return the result
return result
# Run the app
if __name__ == '__main__':
app.run()
温度为 0.9 时的第二次尝试:
import flask
from flask import request, jsonify
# create the Flask app
app = flask.Flask(__name__)
app.config['DEBUG'] = True
# create some test data
books = [
{'id': 0, 'title': 'A Fire Upon The Deep', 'author': 'Vernor Vinge', 'first_sentence': 'The coldsleep itself was dreamless.', 'year_published': '1992'},
{'id': 1, 'title': 'The Ones Who Walk Away From Omelas', 'author': 'Ursula K. Le Guin', 'first_sentence': 'With a clamor of bells that set the swallows soaring, the Festival of Summer came to the city Omelas, bright-towered by the sea.', 'published': '1973'},
{'id': 2, 'title': 'Dhalgren', 'author': 'Samuel R. Delany', 'first_sentence': 'to wound the autumnal city.', 'published': '1975'}
]
# create an endpoint
@app.route('/', methods=['GET'])
def home():
return '''<h1>Welcome to our book API!</h1>'''
@app.route('/api/v1/resources/books
可以看到,两次结果差别非常大。
注意,还有更多参数可以调整来改变输出,比如 top-k、top-p、重复惩罚、长度惩罚和多样性惩罚,但这些超出了本课程的范围。
好的做法
为了得到想要的结果,你可以采用很多做法。随着你越来越多地使用提示词,你会形成自己的风格。
除了上面介绍的技术,给 LLM 写提示词时还有一些好的做法值得考虑:
- 说明上下文。上下文很重要,领域、主题等说明得越具体越好。
- 限制输出。如果你需要特定数量的条目或特定的长度,就明确说出来。
- 既说要什么,也说怎么做。记得同时说明你想要什么、想要怎样的结果,例如“创建一个包含 products 和 customers 两个路由的 Python Web API,分成 3 个文件”。
- 使用模板。你常常需要用公司的数据来充实提示词,这时可以使用模板。模板里可以有变量,再用实际数据替换。
- 拼写正确。即使有拼写错误,LLM 也可能给出正确的回答,但拼写正确会得到更好的回答。
作业
下面是一段 Python 代码,演示如何用 Flask 构建一个简单的 API:
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def hello():
name = request.args.get('name', 'World')
return f'Hello, {name}!'
if __name__ == '__main__':
app.run()
使用 GitHub Copilot 或 ChatGPT 等 AI 助手,运用“自我完善”技术来改进这段代码。
参考解答
请尝试通过在代码中加入合适的提示词来完成作业。
小贴士:用提示词要求它改进时,最好限制改进的条数。你也可以要求它从某个方面改进,比如架构、性能、安全性等。
参考解答(原课程仓库中的 Python 文件)
知识测试
为什么要使用思维链提示?下面 3 个选项中,1 个正确,2 个错误。
- 教 LLM 如何解决一个问题。
- B,教 LLM 找出代码中的错误。
- C,指示 LLM 想出不同的解决方案。
答:1。因为思维链的做法,是通过提供一系列步骤、类似的问题以及这些问题是怎样解决的,向 LLM 演示如何解决问题。
🚀 挑战
你刚刚在作业中用到了自我完善技术。拿你做过的任意一个程序,想想你希望对它做哪些改进,然后用自我完善技术实施这些改进。你觉得结果是变好了还是变差了?
出色的工作!继续学习
学完本课后,可以看看原课程推荐的生成式 AI 学习合集,继续提升你对生成式 AI 的了解。
原课程接下来的第 6 课会运用提示词工程知识构建文本生成应用(本库未收录)。本库的下一课是保护你的生成式 AI 应用。
免责声明:本课中文原由 AI 翻译服务 Co-op Translator 机器翻译,本库已对照英文原文校订,并有删节。中英文如有出入,以英文原版为准。