什么是提示工程

提示工程说白了就是研究如何提高和 AI 的沟通质量及效率的,核心关注提示的开发和优化。具体细节可以学习 OpenAI 官方文档中的《用 API 进行提示工程的最佳实践》一文,里面列举了一些好的提示和不好的提示,同时也总结了以下七点建议:

  1. 使用最新的模型;
  2. 把指令放在提示的开头,并且用 ###""" 来分隔指令和上下文,例如:
将下面的文本总结为最重要点的要点列表。

文本:"""
{文本内容}
"""
  1. 尽可能对上下文和输出的长度、格式、风格等给出具体、描述性、详细的要求;
  2. 通过一些列子来阐明想要的输出格式;
  3. 先从零样本提示开始,效果不好的话再用小样本提示。零样本的意思就是初次不给 AI 任何回答格式的指定,直接把问题扔给 AI 后观察输出结果,如果结果不符合预期,再用小样本提示;
  4. 减少空洞和不严谨的描述;
  5. 与其告知不应该做什么,不如告知应该做什么。

限定输出格式

限定输出格式主要是对应上述第四条建议——“通过一些列子来阐明想要的输出格式”。对 AI 指定输出格式之后,我们可以拿到预期想要的格式数据,从而直接在后续代码中处理,使得我们的工作更加高效,而且新版本的 OpenAI 模型其实也支持了按 JSON 格式返回我们想要的数据。比如下面这个提示:

生成一个由三个虚构的订单信息所组成的列表,以 JSON 格式返回。
JSON 列表里的每个元素包含以下信息:
order_id、customer_name、order_item、phone。
所有信息都是字符串。
除了 JSON 之外,不要输出任何额外的文本。

零样本和小样本

零样本和小样本提示主要是对应上述第五条建议——“先从零样本提示开始,效果不好的话再用小样本提示”。我们很多时候都是直接将问题丢给 AI,不给任何回答示范,这样的话我们得到的结果可能很多时候都不是预期的,但如果我们在 AI 回答前给它几个示例,引导它去学习,这样的话得到的结果就跟我们预期的结果出入不会很大。比如我们可以在调用 client.chat.completions.create() 方法时,往 messages 字段中传入几组示例,AI 往往回答的结果也会遵循我们传入的示例格式:

# 提示词工程 | 零样本 VS 小样本
from openai import OpenAI

client = OpenAI()

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "user", "content": "格式化以下信息:\n姓名 -> 张三\n年龄 -> 27\n客户 ID -> 001"},
        {"role": "assistant", "content": "##客户信息\n- 客户姓名:张三\n- 客户年龄:27 岁\n- 客户 ID:001"},
        {"role": "user", "content": "格式化以下信息:\n姓名 -> 李四\n年龄 -> 38\n客户 ID -> 002"},
        {"role": "assistant", "content": "##客户信息\n- 客户姓名:李四\n- 客户年龄:38 岁\n- 客户 ID:002"},
        {"role": "user", "content": "格式化以下信息:\n姓名 -> 王五\n年龄 -> 88\n客户 ID -> 003"},
    ],
    max_tokens=300,
    temperature=0.1,
    #top_p=1,
    frequency_penalty=0,
    presence_penalty=0
)

print(response)
print(response.choices[0].message.content)

输出结果如下:

## 客户信息
- 客户姓名:王五
- 客户年龄:88 岁
- 客户 ID:003

思维链与分步骤思考

上述我们介绍的小样本方式可以引导 AI 按我们给定的范式输出结果,这在大部分情况下是可行的,但是遇到数学计算这类问题时小样本的方式不再适用,即使我们给出了很多组回答范式,它输出的结果依然是在瞎扯,这是因为 AI 在回答数学计算这类问题时生成每个 token 所花费的时长基本是差不多的,它不会因为某个词去涉及更多的思考,所以它不会花费更多的时间去生成那个 token,进而生成的答案也是不尽人意的。
为了解决上述类似问题,我们可以引入思维链,思维链就是我们在每次的回答范式里面,除了指定输出的格式之外,加上推理过程,这样的话 AI 在每次生成回答时也会模仿着去生成一些中间步骤,把过程进行分解,进而得到准确率较高的答案,甚至是每次都可以生成准确答案。例如:

# 提示词工程 | 思维链与分步骤思考
from openai import OpenAI

client = OpenAI()

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "user", "content": "该数组中的奇数加起来为偶数:4、8、9、15、12、2、1,对吗?"},
        {"role": "assistant", "content": "所有的奇数(9、15、1)相加,9+15+1=25。答案为否。"},
        {"role": "user", "content": "该数组中的奇数加起来为偶数:17、10、19、4、8、12、24,对吗?"},
        {"role": "assistant", "content": "所有的奇数(17、19)相加,17+19=36。答案为是。"},
        {"role": "user", "content": "该数组中的奇数加起来为偶数:15、12、5、3、72、17、1,对吗?"},
    ],
)

print(response)
print(response.choices[0].message.content)

输出结果:

我们先找出数组中的奇数:15、5、3、17、1。

现在将这些奇数相加:
15 + 5 + 3 + 17 + 1 = 41

因为41是奇数,所以该数组中的奇数加起来不是偶数。因此,答案是否定的。

上面思维链的方式可以解决 AI 回答数学计算类似问题时的瞎扯问题,但它本质上还是小样本方式,显得有些许的麻烦,我们其实还有另一种简单的做法,就是在每个问题结尾加上这样一句话:让我们来分步骤思考。这样的话 AI 就会自行生成中间步骤进行推理,从而提高回答的准确性,这种做法就叫做分步骤思考。比如:

# 提示词工程 | 思维链与分步骤思考
from openai import OpenAI

client = OpenAI()

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "user", "content": "该数组中的奇数加起来为偶数:15、12、5、3、72、17、1,对吗?让我们来分步骤思考。"},
    ],
)

print(response)
print(response.choices[0].message.content)

输出结果:

首先,我们需要找出数组中的奇数。给定的数组是:15、12、5、3、72、17、1。

1. 找出数组中的奇数:
   - 15(奇数)
   - 12(偶数)
   - 5(奇数)
   - 3(奇数)
   - 72(偶数)
   - 17(奇数)
   - 1(奇数)

所以,数组中的奇数是:15、5、3、17、1。

2. 将这些奇数相加:
   - 15 + 5 = 20
   - 20 + 3 = 23
   - 23 + 17 = 40
   - 40 + 1 = 41

3. 判断总和:
   - 总和为41,而41是一个奇数。

因此,该数组中的奇数加起来并不是偶数,而是奇数。所以结论是:不对。

总结

本文主要介绍了什么是提示工程,以及一个好的提示应该遵循哪些规范。其次,对 OpenAI 官方提出的七条规范中的限定输出格式、零样本及小样本规范进行了简单的介绍。最后简单介绍了一下思维链和分步骤思考的做法以及适用的场景。下一篇文章我们将通过几个示例继续热身,让大家对 OpenAI API 的调用有进一步的了解。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐