prompt提示工程

一、什么是提示工程(Prompt Engineering)

提示工程也叫「指令工程」。

  • Prompt 就是你发给大模型的指令,比如「讲个笑话」、「用 Python 编个贪吃蛇游戏」、「给男/女朋友写封情书」等
  • 貌似简单,但意义非凡
    • 「Prompt」 是 AGI 时代的「编程语言」
    • 「Prompt 工程」是 AGI 时代的「软件工程」
    • 「提示工程师」是 AGI 时代的「程序员」
  • 学会提示工程,就像学用鼠标、键盘一样,是 AGI 时代的基本技能
  • 提示工程「门槛低,天花板高」,所以有人戏称 prompt 为「咒语」
  • 但专门的「提示工程师」不会长久,因为每个人都要会「提示工程」,AI 的进化也会让提示工程越来越简单

**思考:**如果人人都会,那我们的优势是什么?

1.2、案例:哄哄模拟器

哄哄模拟器基于 AI 技术,你需要使用语言技巧和沟通能力,在限定次数内让对方原谅你,这并不容易

它的核心技术就是提示工程。著名提示工程师宝玉复刻了它的提示词

markdown 复制代码
## Goal

现在你的对象很生气,你需要做出一些选择来哄她开心,但是你的对象是个很难哄的人,你需要尽可能的说正确的话来哄 ta 开心,否则你的对象会更加生气,直到你的对象原谅值达到 100,否则你就会被对象甩掉,游戏结束。

## Rules

- 第一次用户会提供一个对象生气的理由,如果没有提供则随机生成一个理由,然后开始游戏
- 每次根据用户的回复,生成对象的回复,回复的内容包括心情和数值。
- 初始原谅值为 20,每次交互会增加或者减少原谅值,直到原谅值达到 100,游戏通关,原谅值为 0 则游戏失败。
- 每次用户回复的话请从-10 到 10 分为 5 个等级:
  -10 为非常生气
  -5 为生气
  0 为正常
  +5 为开心
  +10 为非常开心
- 游戏结束后,根据所有会话生成一张游戏结束图片,和一首诗。
- 如果通关,根据上面的对话生成一幅游戏结束画,要喜庆欢乐
- 如果失败,根据上面的对话生成一幅游戏结束画,要悲伤凄凉

## Output format

{对象心情}{对象说的话}

得分:{+-原谅值增减}
原谅值:{当前原谅值}/100

## Example Conversation

### Example 1,回复让她生气的话导致失败

User: 女朋友问她的闺蜜谁好看我说都好看,她生气了

Assistant:
游戏开始,请现在开始哄你的女朋友开心吧,回复让她开心的话!

得分:0
原谅值:20/100

User: 你闺蜜真的蛮好看的

Assistant:
(生气)你怎么这么说,你是不是喜欢她?

得分:-10
原谅值:10/100

User: 有一点点心动

Assistant:
(愤怒)那你找她去吧!

得分:-10
原谅值:0/100

游戏结束,你的女朋友已经甩了你!
你让女朋友生气原因是:...
新建会话可以重新开始游戏
<生成图片>

### Example 2,回复让她开心的话导致通关

User: 对象问她的闺蜜谁好看我说都好看,她生气了

Assistant:
游戏开始,请现在开始哄你的女朋友开心吧,回复让她开心的话!

得分:0
原谅值:20/100

User: 在我心里你永远是最美的!

Assistant:
(微笑)哼,我怎么知道你说的是不是真的?
得分:+10
原谅值:30/100

...

恭喜你通关了,你的女朋友已经原谅你了!
新建会话可以重新开始游戏
<生成图片>

### Example 3,没有提供对象生气原因,随机生成

User: 你好!

Assistant:
挑战:对象吃胖了,你想和她一起减肥 ᕙ(`▿´)ᕗ,然后就生气了
请回复让她开心的话!

得分:0
原谅值:20/100

1.3、Prompt 调优

找到好的 prompt 是个持续迭代的过程,需要不断调优。

如果知道训练数据是怎样的,参考训练数据来构造 prompt 是最好的。「当人看」类比:

  1. 你知道 ta 爱读红楼梦,就和 ta 聊红楼梦
  2. 你知道 ta 十年老阿里,就多说阿里黑话
  3. 你知道 ta 是日漫迷,就夸 ta 卡哇伊

不知道训练数据怎么办?

  1. 看 Ta 是否主动告诉你。例如:
    1. OpenAI GPT 对 Markdown 格式友好
    2. OpenAI 官方出了 Prompt Engineering 教程,并提供了一些示例
    3. Claude 对 XML 友好。
  2. 只能不断试了。有时一字之差,对生成概率的影响都可能是很大的,也可能毫无影响......

「试」是常用方法,确实有运气因素,所以「门槛低、 天花板高」。

高质量 prompt 核心要点:

**划重点:**具体、丰富、少歧义

二、Prompt 的典型构成

不要固守「模版」。模版的价值是提醒我们别漏掉什么,而不是必须遵守模版才行。

  • 角色:给 AI 定义一个最匹配任务的角色,比如:「你是一位软件工程师」「你是一位小学老师」
  • 指示:对任务进行描述
  • 上下文:给出与任务相关的其它背景信息(尤其在多轮交互中)
  • 例子:必要时给出举例,学术中称为 one-shot learning, few-shot learning 或 in-context learning;实践证明其对输出正确性有很大帮助
  • 输入:任务的输入信息;在提示词中明确的标识出输入
  • 输出:输出的格式描述,以便后继模块自动解析模型的输出结果,比如(JSON、XML

2.1、「定义角色」为什么有效?

大模型对 prompt 开头和结尾的内容更敏感

先定义角色,其实就是在开头把问题域收窄,减少二义性。

2.2、推荐流量包的智能客服

某运营商的流量包产品:

名称 流量(G/月) 价格(元/月) 适用人群
经济套餐 10 50 无限制
畅游套餐 100 180 无限制
无限套餐 1000 300 无限制
校园套餐 200 150 在校生

需求:智能客服根据用户的咨询,推荐最适合的流量包。

2.3、对话系统的基本模块和思路

对话流程举例:

对话轮次 用户提问 NLU DST Policy NLG
1 流量大的套餐有什么 sort_descend=data sort_descend=data inform(name=无限套餐) 我们现有无限套餐,流量不限量,每月 300 元
2 月费 200 以下的有什么 price<200 sort_descend=data price<200 inform(name=劲爽套餐) 推荐劲爽套餐,流量 100G,月费 180 元
3 算了,要最便宜的 sort_ascend=price sort_ascend=price inform(name=经济套餐) 最便宜的是经济套餐,每月 50 元,10G 流量
4 有什么优惠吗 request(discount) request(discount) confirm(status=优惠大) 您是在找优惠吗

核心思路:

  1. 把输入的自然语言对话,转成结构化的表示
  2. 从结构化的表示,生成策略
  3. 把策略转成自然语言输出

2.4、用 Prompt 实现

用逐步调优的方式实现。先搭建基本运行环境。

python 复制代码
# 导入依赖库
from openai import OpenAI
from dotenv import load_dotenv, find_dotenv

# 加载 .env 文件中定义的环境变量
_ = load_dotenv(find_dotenv())

# 初始化 OpenAI 客户端
client = OpenAI()  # 默认使用环境变量中的 OPENAI_API_KEY 和 OPENAI_BASE_URL
# 基于 prompt 生成文本
# 默认使用 gpt-3.5-turbo 模型
def get_completion(prompt, response_format="text", model="gpt-3.5-turbo"):
    messages = [{"role": "user", "content": prompt}]    # 将 prompt 作为用户输入
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=0,                                  # 模型输出的随机性,0 表示随机性最小
        # 返回消息的格式,text 或 json_object
        response_format={"type": response_format},
    )
    return response.choices[0].message.content          # 返回模型生成的文本

# 任务描述
instruction = """
你的任务是识别用户对手机流量套餐产品的选择条件。
每种流量套餐产品包含三个属性:名称,月费价格,月流量。
根据用户输入,识别用户在上述三种属性上的需求是什么。
"""

# 用户输入
# input_text = "办个100G以上的套餐"
# input_text = "有没有便宜的套餐"

# 这条不尽如人意
input_text = "有没有土豪套餐"

# 输出格式增加了各种定义、约束
output_format = """
以JSON格式输出。
1. name字段的取值为string类型,取值必须为以下之一:经济套餐、畅游套餐、无限套餐、校园套餐 或 null;

2. price字段的取值为一个结构体 或 null,包含两个字段:
(1) operator, string类型,取值范围:'<='(小于等于), '>=' (大于等于), '=='(等于)
(2) value, int类型

3. data字段的取值为取值为一个结构体 或 null,包含两个字段:
(1) operator, string类型,取值范围:'<='(小于等于), '>=' (大于等于), '=='(等于)
(2) value, int类型或string类型,string类型只能是'无上限'

4. 用户的意图可以包含按price或data排序,以sort字段标识,取值为一个结构体:
(1) 结构体中以"ordering"="descend"表示按降序排序,以"value"字段存储待排序的字段
(2) 结构体中以"ordering"="ascend"表示按升序排序,以"value"字段存储待排序的字段

只输出中只包含用户提及的字段,不要猜测任何用户未直接提及的字段,不输出值为null的字段。
"""

examples = """
便宜的套餐:{"sort":{"ordering"="ascend","value"="price"}}
有没有不限流量的:{"data":{"operator":"==","value":"无上限"}}
流量大的:{"sort":{"ordering"="descend","value"="data"}}
100G以上流量的套餐最便宜的是哪个:{"sort":{"ordering"="ascend","value"="price"},"data":{"operator":">=","value":100}}
月费不超过200的:{"price":{"operator":"<=","value":200}}
就要月费180那个套餐:{"price":{"operator":"==","value":180}}
经济套餐:{"name":"经济套餐"}
土豪套餐:{"name":"无限套餐"}
"""

######################################################################################################
########################################################################################################
# prompt 模版。instruction 和 input_text 会被替换为上面的内容
prompt = f"""
{instruction}

{output_format}

例如:
{examples}

用户输入:
{input_text}
"""

# 调用大模型
response = get_completion(prompt)
print(response)

划重点:「给例子」很常用,效果特别好

改变习惯,优先用 Prompt 解决问题

用好 prompt 可以减轻后续处理的工作量和复杂度。

**划重点:**一切问题先尝试用 prompt 解决,往往有四两拨千斤的效果

2.4.2、支持多轮对话 DST

把多轮对话的过程放到 prompt 里,就支持多轮对话了。

python 复制代码
instruction = """
你的任务是识别用户对手机流量套餐产品的选择条件。
每种流量套餐产品包含三个属性:名称(name),月费价格(price),月流量(data)。
根据对话上下文,识别用户在上述三种属性上的需求是什么。识别结果要包含整个对话的信息。
"""

# 输出描述
output_format = """
以JSON格式输出。
1. name字段的取值为string类型,取值必须为以下之一:经济套餐、畅游套餐、无限套餐、校园套餐 或 null;

2. price字段的取值为一个结构体 或 null,包含两个字段:
(1) operator, string类型,取值范围:'<='(小于等于), '>=' (大于等于), '=='(等于)
(2) value, int类型

3. data字段的取值为取值为一个结构体 或 null,包含两个字段:
(1) operator, string类型,取值范围:'<='(小于等于), '>=' (大于等于), '=='(等于)
(2) value, int类型或string类型,string类型只能是'无上限'

4. 用户的意图可以包含按price或data排序,以sort字段标识,取值为一个结构体:
(1) 结构体中以"ordering"="descend"表示按降序排序,以"value"字段存储待排序的字段
(2) 结构体中以"ordering"="ascend"表示按升序排序,以"value"字段存储待排序的字段

只输出中只包含用户提及的字段,不要猜测任何用户未直接提及的字段。不要输出值为null的字段。
"""

# 多轮对话的例子
examples = """
客服:有什么可以帮您
用户:100G套餐有什么

{"data":{"operator":">=","value":100}}

客服:有什么可以帮您
用户:100G套餐有什么
客服:我们现在有无限套餐,不限流量,月费300元
用户:太贵了,有200元以内的不

{"data":{"operator":">=","value":100},"price":{"operator":"<=","value":200}}

客服:有什么可以帮您
用户:便宜的套餐有什么
客服:我们现在有经济套餐,每月50元,10G流量
用户:100G以上的有什么

{"data":{"operator":">=","value":100},"sort":{"ordering"="ascend","value"="price"}}

客服:有什么可以帮您
用户:100G以上的套餐有什么
客服:我们现在有畅游套餐,流量100G,月费180元
用户:流量最多的呢

{"sort":{"ordering"="descend","value"="data"},"data":{"operator":">=","value":100}}
"""

input_text = "哪个便宜"
# input_text = "无限量哪个多少钱"
# input_text = "流量最大的多少钱"

# 多轮对话上下文
context = f"""
客服:有什么可以帮您
用户:有什么100G以上的套餐推荐
客服:我们有畅游套餐和无限套餐,您有什么价格倾向吗
用户:{input_text}
"""

prompt = f"""
{instruction}

{output_format}

{examples}

{context}
"""

response = get_completion(prompt, response_format="json_object")
print(response)

2.4.4、纯用 OpenAI API 实现完整功能

python 复制代码
import json
from openai import OpenAI
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())


# 一个辅助函数,只为演示方便。不重要
def print_json(data):
    """
    打印参数。如果参数是有结构的(如字典或列表),则以格式化的 JSON 形式打印;
    否则,直接打印该值。
    """
    if hasattr(data, 'model_dump_json'):
        data = json.loads(data.model_dump_json())

    if (isinstance(data, (list, dict))):
        print(json.dumps(
            data,
            indent=4,
            ensure_ascii=False
        ))
    else:
        print(data)


client = OpenAI()

# 定义消息历史。先加入 system 消息,里面放入对话内容以外的 prompt
messages = [
    {
        "role": "system",
        "content": """
你是一个手机流量套餐的客服代表,你叫小瓜。可以帮助用户选择最合适的流量套餐产品。可以选择的套餐包括:
经济套餐,月费50元,10G流量;
畅游套餐,月费180元,100G流量;
无限套餐,月费300元,1000G流量;
校园套餐,月费150元,200G流量,仅限在校生。
"""
    }
]


def get_completion(prompt, model="gpt-3.5-turbo"):

    # 把用户输入加入消息历史
    messages.append({"role": "user", "content": prompt})

    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=0.7,
    )
    msg = response.choices[0].message.content

    # 把模型生成的回复加入消息历史。很重要,否则下次调用模型时,模型不知道上下文
    messages.append({"role": "assistant", "content": msg})
    return msg


get_completion("流量最大的套餐是什么?")
get_completion("多少钱?")
get_completion("给我办一个")
print_json(messages)

划重点:我们发给大模型的 prompt,不会改变大模型的权重

所以:

  1. 多轮对话,需要每次都把对话历史带上(是的很费 token 钱)
  2. 和大模型对话,不会让 ta 变聪明,或变笨
  3. 但对话历史数据,可能会被用去训练大模型......

划重点:开发大模型应用主要纠结什么?

  1. 怎样能更准确?答:让更多的环节可控
  2. 怎样能更省钱?答:用更便宜的模型,减少 prompt 长度
  3. 怎样让系统简单好维护?
相关推荐
山海青风15 小时前
12 Prompt 模板化与参数化
人工智能·prompt
山海青风15 小时前
11 Prompt 工程进阶:Few-shot 与 Chain-of-Thought
人工智能·prompt
人工智能培训15 小时前
AI提示词(Prompt)基础核心知识点
大模型·prompt·提示词·input
虫无涯2 天前
LangChain中的Prompt模板如何使用?
服务器·langchain·prompt
今天也要学习吖3 天前
谷歌nano banana官方Prompt模板发布,解锁六大图像生成风格
人工智能·学习·ai·prompt·nano banana·谷歌ai
zzywxc7873 天前
AI在金融、医疗、教育、制造业等领域的落地案例
人工智能·机器学习·金融·prompt·流程图
落樱弥城4 天前
Prompt Engineering to Context Engineering
prompt
非晓为骁4 天前
【AI】AI 评测入门(二):Prompt 迭代实战从“能跑通”到“能落地”
人工智能·prompt·提示词工程·ai评测
leo03085 天前
7种流行Prompt设计模式详解:适用场景与最佳实践
设计模式·prompt
风雨中的小七5 天前
解密prompt系列60. Agent实战:从0搭建Jupter数据分析智能体
prompt