首页 / 资料库 / 微软 · 生成式 AI 入门

资料库25 分钟读完MIT提示词提示工程温度大语言模型

编写进阶提示词

译自《Creating Advanced prompts》 · 查看英文原文

原文出处Creating Advanced prompts 原作者:Microsoft · 许可证:MIT License 中文译本由诸葛AI学院整理,仅供学习参考,版权归原作者与微软所有。

先把上一章的收获复习一下:

提示 工程(prompt engineering)是这样一个过程:我们给出更有用的指令或上下文,从而引导模型给出更贴合需求的回复

写提示词有两个环节:构造(constructing),也就是提供相关的上下文;以及 优化optimization),也就是一点一点把提示词改好。

到这一步,我们对怎么写提示词已经有了基本的理解,但还得往深里走。这一章,你会从"随便试几个提示词"走到"明白为什么这个提示词比那个好"。你会学到怎么按几种基本技巧来构造提示词,这些技巧用在任何大语言模型(Large Language Model,LLM)上都成立。

本章简介

这一章涵盖以下内容:

  • 延伸你的提示工程知识,把不同的技巧用到你的提示词里。
  • 配置提示词,改变输出的变化程度。

学习目标

学完本课,你将能够:

  • 运用提示工程技巧,改善提示词的效果。
  • 写出多样化或确定性(deterministic)的提示词。

提示工程

提示工程是编写提示词的过程,目的是得到你想要的结果。写一段文本提示词只是它的起步。它算不上一个工程学科,更像是一套技巧,你用上它们,就能得到想要的结果。

一个提示词的例子

拿这样一个基础提示词:

生成 10 个关于地理的问题。

这个提示词里,你其实用到了一组不同的提示技巧。

拆开来看:

  • 上下文,你指定了内容要关于"地理"。
  • 限制输出,你要的问题不超过 10 个。

简单提示的局限

你想要的结果,可能拿到,也可能拿不到。问题确实会生成出来,但地理是个很大的话题,你很可能得不到想要的东西,原因有两条:

  • 话题太大,你不知道它会扯到国家、首都、河流还是别的什么。
  • 格式,如果你希望问题按某种方式排版呢?

可见,编写提示词要考虑的东西很多。

到目前为止我们只看了一个简单的提示词例子。生成式 AI 能帮到的角色和行业远不止这些。接下来看几种基本技巧。

提示技巧

首先得明白,提示是大语言模型的一个 涌现 特性(emergent property)。它不是模型里预置的功能,而是我们在使用模型的过程中发现的行为。

提示大语言模型有几种基本技巧,逐个来看。

  • 零样本提示(zero-shot prompting),最基础的提示形式。只给一个提示词,让模型仅凭训练数据给出回复。
  • 少样本提示(few-shot prompting),给模型提供 1 个或更多示例,让它参照这些示例生成回复,从而为模型指明方向。
  • 思维链(chain-of-thought),告诉模型怎么把一个问题拆解成若干步骤。
  • 生成知识(generated knowledge),为了让提示词得到更好的回复,你可以在提示词里额外提供一些生成出来的事实或知识。
  • 由少到多(least to most),和思维链类似,也是把问题拆成一系列步骤,然后要求模型按顺序把这些步骤执行完。
  • 自我精炼(self-refine),对模型的输出提出批评,再要求它改进。
  • 助产士式提示(maieutic prompting)。你要的是确保模型的答案正确,办法是让它把答案的各个部分解释清楚。这算自我精炼的一种形式。

零样本提示

这种提示风格很简单,就是一个提示词。你刚开始接触大语言模型时,用的多半就是它。举个例子:

  • 提示词:"什么是代数?"
  • 回答:"代数是数学的一个分支,研究数学符号以及操作这些符号的规则。"

少样本提示

这种提示风格在提出请求的同时附上几个例子,帮模型一把。它仍然是一个提示词,只是加上了与任务相关的数据。举个例子:

  • 提示词:"写一首莎士比亚风格的诗。下面是几首莎士比亚十四行诗的例子: 第 18 首:'我能否把你比作夏日?你可是更加可爱,更加温婉……' 第 116 首: '两颗真心忠于的结合,我不能承认阻碍。爱并不是爱,如果它随变化而变化……' 第 132 首:'我爱你的眼睛,它们怜我受苦,明知你的心以傲慢折磨我……' 现在,写一首关于月亮之美的十四行诗。"
  • 回答:"月亮在天上柔柔地发光,银色的光洒下温婉的神采……"

示例给到了模型想要的上下文、格式或风格。它们帮模型理解具体任务,生成更准确、更贴切的回复。

思维链

思维链是很有意思的技巧,核心是带着模型走一串步骤。思路是:把提示词写成能让模型明白"这件事该怎么做"的形式。看下面这个例子,分别是有思维链和没有思维链的写法:

- 提示词:"爱丽丝有 5 个苹果,扔掉 3 个,给鲍勃 2 个,鲍勃又还回 1 个,爱丽丝现在有几个苹果?"
- 回答:5

模型回答 5,这是错的。正确答案是 1 个苹果,算法是 (5 -3 -2 + 1 = 1)。

那怎么教会模型做对?

试试思维链。应用思维链的意思是:

  1. 给模型一个相似的例题。
  2. 展示计算过程,以及怎么算才对。
  3. 提供原始的提示词。

具体写法:

  • 提示词:"丽莎有 7 个苹果,扔掉 1 个,给巴特 4 个,巴特又还回 1 个: 7 -1 = 6 6 -4 = 2 2 +1 = 3 爱丽丝有 5 个苹果,扔掉 3 个,给鲍勃 2 个,鲍勃又还回 1 个,爱丽丝现在有几个苹果?" 回答:1

注意,加了例题、计算过程,再放上原始提示词,提示词写得长得多,最后得到了正确答案 1。

可见思维链是非常有力的技巧。

生成知识

很多时候你要构造的提示词得用自己公司的数据。提示词的一部分来自公司,另一部分是你要问的正题。

举个例子,如果你做保险业务,提示词可以长这样:

text {{company}}:{{company_name}} {{products}}: {{products_list}} 请根据以下预算和要求推荐一份保险: 预算:{{budget}} 要求:{{requirements}}

上面是用模板来构造提示词。模板里有若干变量,写作 {{variable}},它们会被替换成公司 API 提供的实际值。

变量被替换成你公司的内容之后,提示词可能长这样:

```text 保险公司:ACME Insurance 保险产品(每月费用): - 车险,低价,500 美元 - 车险,高价,1100 美元 - 房产险,低价,600 美元 - 房产险,高价,1200 美元 - 人寿险,低价,100 美元

请根据以下预算和要求推荐一份保险: 预算:1000 美元 要求:车险、房产险和人寿险 ```

把这个提示词交给大语言模型,会得到这样的回复:

output 根据预算和要求,我们推荐 ACME Insurance 的以下保险组合: - 车险,低价,500 美元 - 房产险,低价,600 美元 - 人寿险,低价,100 美元 总费用:1,200 美元

可以看到,它把人寿险也推荐了,这不该出现。这个结果表明提示词还需要优化,要改得更明白:哪些条件是允许的。经过多轮尝试,我们得到下面这个提示词:

```text 保险公司:ACME Insurance 保险产品(每月费用): - 类型:车险,低价,费用:500 美元 - 类型:车险,高价,费用:1100 美元 - 类型:房产险,低价,费用:600 美元 - 类型:房产险,高价,费用:1200 美元 - 类型:人寿险,低价,费用:100 美元

请根据以下预算和要求推荐一份保险: 预算:1000 美元,类型只限:车险、房产险 ```

注意,加上"类型"和"费用"这些字段名,再用 restrict(限定)这个关键词,模型就更能明白我们要什么。

现在得到的回复是:

output 根据预算和要求,我们推荐车险的低价产品,每月费用 500 美元。

这个例子想说明的是:即使用的是 生成知识 这类基础技巧,多数情况下你还是得优化提示词,才能拿到想要的结果。

由少到多

由少到多提示的思路,是把大问题拆成若干子问题。这样做,等于在指导模型怎么去"攻克"那个大问题。数据科学是个合适的场景,你可以让模型把问题分步拆开,比如:

提示词:用 5 个步骤完成数据科学工作,该怎么做?

你的 AI 助手会回答:

  1. 收集数据
  2. 清洗数据
  3. 分析数据
  4. 绘制数据图表
  5. 呈现数据

自我精炼,批评结果

面对生成式 AI 和大语言模型,你不能直接相信它们的输出,得验证。毕竟模型只是把它认为下一个最可能说的话摆给你,并没有管那对不对。所以,让模型批评自己是个有用的做法,这就引出了自我精炼技巧。

它的流程是:

  1. 初始提示词,要求模型解决一个问题
  2. 模型作答
  3. 你批评这个答案,要求 AI 改进
  4. 模型再次作答,这一次它会把批评考虑进去,给出它想到的解决办法

这个过程你想重复多少遍都可以。

下面是用这个技巧的例子:

提示词:"创建一个 Python Web API,包含 products 和 customers 两个路由"

AI 的回答:

```python import flask app = Flask(name)

@app.route('/products') def products(): return 'Products'

@app.route('/customers') def customers(): return 'Customers' ```

提示词:针对上面的代码提出 3 点改进建议

AI 的回答:

```text 好的,这里有三条改进代码的建议:

导入正确的模块:第一行应该导入 Flask 而不是 flask。正确的导入语句是 from flask import Flask。

添加 main 函数:在文件末尾加一个 if name == 'main': 代码块,让程序在被执行时启动应用。

使用 f-字符串:用 f-字符串来格式化路由函数里的 return 语句。例如,return 'Products' 可以改成 return f'This is the {request.path} page'。 ```

```python 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 上面对最初那版代码做了改进。

助产士式提示

助产士式提示和自我精炼相似,但它的重点不是改进,而是让模型解释自己。目的是减少模型输出里的前后矛盾,确保最后得到正确答案。要走的流程是:

  1. 让模型回答一个问题
  2. 对答案的每一部分,让模型更深入地解释。
  3. 如果出现不一致,把不一致的部分丢弃。

重复第 2 步和第 3 步,直到每一部分都过完,你对答案满意为止。

下面是一个提示词的例子:

提示词:我该怎么分 5 步制定一份疫情危机预案? LLM 回答:

text 1. 识别风险 1. 识别利益相关方 1. 识别资源 1. 识别行动 1. 识别沟通渠道

它给出了 5 步,可我们怎么知道这对不对?让模型把每一步解释一遍:

提示词:详细解释第一步,疫情情况下的风险具体有哪些?

```text 风险有下面这些:

  • 失去生命
  • 失去业务
  • 失去声誉
  • 失去客户
  • 失去员工 ```

到这一步,我们要确认模型说的是对的,于是让它把第一个风险再解释细一点,期望它和上面的答案一致:

提示词:在疫情期间,最大的风险是什么,为什么?

text 最大的风险是失去生命,因为它是最需要保护的东西。

疫情期间最大的两个风险是什么?

text 最大的两个风险是失去生命和失去业务。

到这里,模型的答案是一致的,它把"生命"和"业务"列为头两个风险。我们可以放心往下一步走。不过,不能盲目信任模型,输出始终要验证。

让输出多样化

大语言模型天生是非确定性的(nondeterministic),同一个提示词每次运行,结果都不一样。拿下面这个提示词试试:

"生成一个 Python Web API 的代码"

```python

导入必要的模块

import flask from flask import request, jsonify

创建 Flask 应用

app = flask.Flask(name)

为 API 创建一个路由

@app.route('/api', methods=['GET']) def api(): # 从请求中获取数据 data = request.args

# 处理数据
result = process_data(data)

# 以 JSON 形式返回结果
return jsonify(result)

处理数据的函数

def process_data(data): # 对数据做一些处理 result = data + 1

# 返回结果
return result

运行应用

if name == 'main': app.run()

```

把同一个提示词再跑一遍,生成的回复略有不同:

```python

导入必要的包

import flask from flask import request, jsonify

创建 Flask 应用

app = flask.Flask(name) app.config["DEBUG"] = True

创建一个图书列表

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'} ]

为 API 创建一个端点

@app.route('/', methods=['GET']) def home(): return '''

Books API

A prototype API for retrieving books.

'''

创建一个端点,返回全部图书

@app.route('/api/v1/resources/books/all', methods=['GET']) def api_all(): return jsonify(books)

创建一个端点,返回单本图书

@app.route('/api/v1/resources/books', methods=['GET']) def api_id(): # 检查 URL 里是否带了 ID # 带了 ID,就把它赋给一个变量 # 没带 ID,就显示一个错误 if 'id' in request.args: id = int(request.args['id']) else: return "Error: No id field provided. Please specify an id."

# 创建一个空列表存放结果
results = []

# 遍历数据,匹配出请求的 ID
# ID 是唯一的,但其他字段可能匹配出很多条
for book in books:
    if book['id'] == id:
        results.append(book)

# 用 Flask 的 jsonify 函数,把我们的
# Python 字典列表转成 JSON 格式
return jsonify(results)

app.run() ```

那么,输出不一致是个问题吗?

要看你想做什么。你要的是一个确切的回复,那它就是问题。你接受变化的输出,比如"生成任意 3 个关于地理的问题",那就不是问题。

用温度控制输出的变化

好,我们决定要限制输出,让它更可预测,也就是更确定。怎么做到?

温度(temperature)是 0 到 1 之间的一个值。0 表示最确定,1 表示最多样。默认值是 0.7。看看把温度设成 0.1 时,同一个提示词跑两次的结果:

"生成一个 Python Web API 的代码"

```python

导入必要的库

import flask from flask import request, jsonify

创建 Flask 类的实例

app = flask.Flask(name)

为 API 创建一个端点

@app.route('/api/v1/endpoint', methods=['GET']) def api_endpoint(): # 获取请求数据 data = request.get_json()

# 处理数据
result = process_data(data)

# 返回结果
return jsonify(result)

处理数据的函数

def process_data(data): # 处理数据 result = {'result': 'success'}

# 返回结果
return result

运行 Flask 应用

if name == 'main': app.run()

```

再跑一次这个提示词,得到的是:

```python

导入必要的库

import flask from flask import request, jsonify

创建 Flask 应用

app = flask.Flask(name)

为 API 创建一个路由

@app.route('/api', methods=['GET']) def api_call(): # 从请求中获取数据 data = request.get_json()

# 处理数据
result = process_data(data)

# 返回结果
return jsonify(result)

定义 process_data 函数

def process_data(data): # 对数据做一些处理 result = data + 1

# 返回结果
return result

运行应用

if name == 'main': app.run()

```

这两份输出之间只差一点点。这次反过来,把温度设成 0.9:

```python

导入必要的库

import flask from flask import request, jsonify

创建 Flask 应用

app = flask.Flask(name)

为 API 创建一个路由

@app.route('/api', methods=['GET']) def api_call(): # 从请求中获取数据 data = request.args

# 处理数据
result = process_data(data)

# 返回结果
return jsonify(result)

处理数据的函数

def process_data(data): # 对数据做一些处理 result = data + 1

# 返回结果
return result

运行应用

if name == 'main': app.run()

```

温度取 0.9 的第二次尝试:

```python import flask from flask import request, jsonify

创建 Flask 应用

app = flask.Flask(name) app.config['DEBUG'] = True

创建一些测试数据

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'} ]

创建一个端点

@app.route('/', methods=['GET']) def home(): return '''

Welcome to our book API!

'''

@app.route('/api/v1/resources/books

```

可以看到,结果的差异大得不能再大了。

另外,还有一些参数可以改用来调整输出,比如 top-k、top-p、重复惩罚(repetition penalty)、长度惩罚(length penalty)、多样性惩罚(diversity penalty)。这些超出了本课程的范围。

最佳实践

能帮你拿到想要的结果的做法很多。提示词写得多了,你会形成自己的风格。

除了已经讲过的技巧,提示大语言模型时还有一些值得考虑的好做法:

  • 指定上下文。上下文很重要,你能指定得多细就多细,领域、话题等等,越具体越好。
  • 限制输出。你要固定数量的条目或固定长度,就明确写出来。
  • 同时说明要什么和怎么要。记住,既要讲清楚你要什么,也要讲清楚你要的方式,例如"创建一个 Python Web API,包含 products 和 customers 两个路由,分成 3 个文件"。
  • 使用模板。你常常需要拿公司的数据来补充提示词,用模板来做这件事。模板里可以有变量,你替换成真实数据即可。
  • 拼写正确。模型可能给你正确的回复,但你拼写正确的话,回复会更好。

作业

下面是 Python 代码,演示怎么用 Flask 构建一个简单的 API:

```python 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() ```

用一个 AI 助手,比如 GitHub Copilot 或 ChatGPT,对它应用"自我精炼"技巧来改进这段代码。

解答

请给这段代码加上合适的提示词,自己完成作业。

[!TIP] 组织提示词让模型做改进时,限制改进的数量是个好主意。你也可以指定改进的方向,比如架构、性能、安全性等。

解答代码

知识检查

我为什么要用思维链提示?给我一个正确答案,两个错误答案。

  1. 为了教模型怎么解决问题。
  2. B,为了教模型在代码里找错误。
  3. C,为了让模型想出不同的解决方案。

A:1。因为思维链做的事,就是给模型提供一串步骤、类似的题目以及它们是怎么被解决的,从而示范这道题该怎么解。

挑战

作业里你正好用到了自我精炼技巧。挑一个你写过程序,想想你想给它做哪些改进。然后用自我精炼技巧把这些改动提出来。你觉得结果更好,还是更差?

很棒!继续学习

学完本课,可以去 生成式 AI 学习合集 看看,把生成式 AI 的知识接着往深里学。

接着去第 6 课,用提示工程的知识 构建文本生成应用

这篇在讲什么,跟咱们的课怎么对?

资料库是大厂公开教材的中文译本,偏原理和工程做法。想看面向中小企业的白话版本,去入门课场景课