文章目录
- 一、经典AI女友Prompt
- 二、prompt怎么做?
- 1)注重格式:
- 2)prompt经典构成
- 3)简单prompt的python询问代码
- 4)python实现订阅手机流量套餐的NLU
- 5)优化一:加入垂直领域推荐
- 6)优化二:改变语气、口吻等风格。
- 7)优化三:实现统一口径
- 8)纯OpenAI方案
- 9)纯OpenAI和自制问答的对比
- 三、prompt提示工程师进阶技巧
- 1)思维链(Chain of Thoughts, CoT)
- 2)自洽性(Self-Consistency)
一、经典AI女友Prompt
### 1.2、案例:哄哄模拟器> [哄哄模拟器](https://hong.greatdk.com/)基于 AI 技术,你需要使用语言技巧和沟通能力,在限定次数内让对方原谅你,这并不容易它的核心技术就是提示工程。著名提示工程师宝玉[复刻了它的提示词](https://weibo.com/1727858283/ND9pOzB0K):```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/100User: 你闺蜜真的蛮好看的Assistant:
(生气)你怎么这么说,你是不是喜欢她?得分:-10
原谅值:10/100User: 有一点点心动Assistant:
(愤怒)那你找她去吧!得分:-10
原谅值:0/100游戏结束,你的女朋友已经甩了你!
你让女朋友生气原因是:...
新建会话可以重新开始游戏
<生成图片>### Example 2,回复让她开心的话导致通关User: 对象问她的闺蜜谁好看我说都好看,她生气了Assistant:
游戏开始,请现在开始哄你的女朋友开心吧,回复让她开心的话!得分:0
原谅值:20/100User: 在我心里你永远是最美的!Assistant:
(微笑)哼,我怎么知道你说的是不是真的?
得分:+10
原谅值:30/100...恭喜你通关了,你的女朋友已经原谅你了!
新建会话可以重新开始游戏
<生成图片>### Example 3,没有提供对象生气原因,随机生成User: 你好!Assistant:
挑战:对象吃胖了,你想和她一起减肥 ᕙ(`▿´)ᕗ,然后就生气了
请回复让她开心的话!得分:0
原谅值:20/100
二、prompt怎么做?
1)注重格式:
OpenAI GPT 对 Markdown 格式友好
OpenAI 官方出了 Prompt Engineering 教程,并提供了一些示例
Claude 对 XML 友好
2)prompt经典构成
角色
:给 AI 定义一个最匹配任务的角色,比如:「你是一位软件工程师」「你是一位小学老师」指示
:对任务进行描述上下文
:给出与任务相关的其它背景信息(尤其在多轮交互中)例子
:必要时给出举例,学术中称为 one-shot learning, few-shot learning 或 in-context learning;实践证明其对输出正确性有很大帮助输入
:任务的输入信息;在提示词中明确的标识出输入输出
:输出的格式描述,以便后继模块自动解析模型的输出结果,比如(JSON、XML)
3)简单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 生成文本
def get_completion(prompt, model="gpt-3.5-turbo"): # 默认使用 gpt-3.5-turbo 模型messages = [{"role": "user", "content": prompt}] # 将 prompt 作为用户输入response = client.chat.completions.create(model=model,messages=messages,temperature=0, # 模型输出的随机性,0 表示随机性最小)return response.choices[0].message.content # 返回模型生成的文本
4)python实现订阅手机流量套餐的NLU
import json
import copy
from openai import OpenAI
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())client = OpenAI()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"字段存储待排序的字段只输出中只包含用户提及的字段,不要猜测任何用户未直接提及的字段。
DO NOT OUTPUT NULL-VALUED FIELD! 确保输出能被json.loads加载。
"""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":"经济套餐"}
"""class NLU:def __init__(self):self.prompt_template = f"{instruction}\n\n{output_format}\n\n{examples}\n\n用户输入:\n__INPUT__"def _get_completion(self, prompt, model="gpt-3.5-turbo"):messages = [{"role": "user", "content": prompt}]response = client.chat.completions.create(model=model,messages=messages,temperature=0, # 模型输出的随机性,0 表示随机性最小)semantics = json.loads(response.choices[0].message.content)return {k: v for k, v in semantics.items() if v}def parse(self, user_input):prompt = self.prompt_template.replace("__INPUT__", user_input)return self._get_completion(prompt)class DST:def __init__(self):passdef update(self, state, nlu_semantics):if "name" in nlu_semantics:state.clear()if "sort" in nlu_semantics:slot = nlu_semantics["sort"]["value"]if slot in state and state[slot]["operator"] == "==":del state[slot]for k, v in nlu_semantics.items():state[k] = vreturn stateclass MockedDB:def __init__(self):self.data = [{"name": "经济套餐", "price": 50, "data": 10, "requirement": None},{"name": "畅游套餐", "price": 180, "data": 100, "requirement": None},{"name": "无限套餐", "price": 300, "data": 1000, "requirement": None},{"name": "校园套餐", "price": 150, "data": 200, "requirement": "在校生"},]def retrieve(self, **kwargs):records = []for r in self.data:select = Trueif r["requirement"]:if "status" not in kwargs or kwargs["status"] != r["requirement"]:continuefor k, v in kwargs.items():if k == "sort":continueif k == "data" and v["value"] == "无上限":if r[k] != 1000:select = Falsebreakif "operator" in v:if not eval(str(r[k])+v["operator"]+str(v["value"])):select = Falsebreakelif str(r[k]) != str(v):select = Falsebreakif select:records.append(r)if len(records) <= 1:return recordskey = "price"reverse = Falseif "sort" in kwargs:key = kwargs["sort"]["value"]reverse = kwargs["sort"]["ordering"] == "descend"return sorted(records, key=lambda x: x[key], reverse=reverse)class DialogManager:def __init__(self, prompt_templates):self.state = {}self.session = [{"role": "system","content": "你是一个手机流量套餐的客服代表,你叫小瓜。可以帮助用户选择最合适的流量套餐产品。"}]self.nlu = NLU()self.dst = DST()self.db = MockedDB()self.prompt_templates = prompt_templatesdef _wrap(self, user_input, records):if records:prompt = self.prompt_templates["recommand"].replace("__INPUT__", user_input)r = records[0]for k, v in r.items():prompt = prompt.replace(f"__{k.upper()}__", str(v))else:prompt = self.prompt_templates["not_found"].replace("__INPUT__", user_input)for k, v in self.state.items():if "operator" in v:prompt = prompt.replace(f"__{k.upper()}__", v["operator"]+str(v["value"]))else:prompt = prompt.replace(f"__{k.upper()}__", str(v))return promptdef _call_chatgpt(self, prompt, model="gpt-3.5-turbo"):session = copy.deepcopy(self.session)session.append({"role": "user", "content": prompt})response = client.chat.completions.create(model=model,messages=session,temperature=0,)return response.choices[0].message.contentdef run(self, user_input):# 调用NLU获得语义解析semantics = self.nlu.parse(user_input)print("===semantics===")print(semantics)# 调用DST更新多轮状态self.state = self.dst.update(self.state, semantics)print("===state===")print(self.state)# 根据状态检索DB,获得满足条件的候选records = self.db.retrieve(**self.state)# 拼装prompt调用chatgptprompt_for_chatgpt = self._wrap(user_input, records)print("===gpt-prompt===")print(prompt_for_chatgpt)# 调用chatgpt获得回复response = self._call_chatgpt(prompt_for_chatgpt)# 将当前用户输入和系统回复维护入chatgpt的sessionself.session.append({"role": "user", "content": user_input})self.session.append({"role": "assistant", "content": response})return response----------------------------------
prompt_templates = {"recommand": "用户说:__INPUT__ \n\n向用户介绍如下产品:__NAME__,月费__PRICE__元,每月流量__DATA__G。","not_found": "用户说:__INPUT__ \n\n没有找到满足__PRICE__元价位__DATA__G流量的产品,询问用户是否有其他选择倾向。"
}dm = DialogManager(prompt_templates)response = dm.run("300太贵了,200元以内有吗")
# response = dm.run("流量大的")
print("===response===")
print(response)
- 代码解析
①构造DialogManager类对象的构造函数,传入prompt_templates字符串,进而构造NLU、DST、MockedDB类对象作为成员变量
②DialogManager类对象dm调用run函数,调用NLU
获得语义解析,调用DST
更新多轮状态,retrieve
根据状态检索DB获得满足条件的候选话费套餐
③将gpt角色
、内容
、命令
打包成一个prompt
④调用_call_chatgpt将prompt送给GPT,调用返回的消息
⑤保存当前用户输入和系统回复维护入chatgpt的session,也就是保存输入的内容
和返回的内容
5)优化一:加入垂直领域推荐
prompt_templates = {"recommand": "用户说:__INPUT__ \n\n向用户介绍如下产品:__NAME__,月费__PRICE__元,每月流量__DATA__G。","not_found": "用户说:__INPUT__ \n\n没有找到满足__PRICE__元价位__DATA__G流量的产品,询问用户是否有其他选择倾向。"
}dm = DialogManager(prompt_templates)
response = dm.run("300太贵了,200元以内有吗")
# response = dm.run("流量大的")
print("===response===")
print(response)
6)优化二:改变语气、口吻等风格。
# 定义语气要求。"NO COMMENTS. NO ACKNOWLEDGEMENTS."是常用 prompt,表示「有事儿说事儿,别 bb」
ext = "很口语,亲切一些。不用说“抱歉”。直接给出回答,不用在前面加“小瓜说:”。NO COMMENTS. NO ACKNOWLEDGEMENTS."
prompt_templates = {k: v+ext for k, v in prompt_templates.items()}dm = DialogManager(prompt_templates)
# response = dm.run("流量大的")
response = dm.run("300太贵了,200元以内有吗")
print("===response===")
print(response)
7)优化三:实现统一口径
ext = "\n\n遇到类似问题,请参照以下回答:\n问:流量包太贵了\n答:亲,我们都是全省统一价哦。"
prompt_templates = {k: v+ext for k, v in prompt_templates.items()}dm = DialogManager(prompt_templates)
response = dm.run("这流量包太贵了")
print("===response===")
print(response)
8)纯OpenAI方案
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,)msg = response.choices[0].message.content# 把模型生成的回复加入消息历史。很重要,否则下次调用模型时,模型不知道上下文messages.append({"role": "assistant", "content": msg})return msgget_completion("有没有土豪套餐?")
get_completion("多少钱?")
get_completion("给我办一个")
print_json(messages)
[{"role": "system","content": "\n你是一个手机流量套餐的客服代表,你叫小瓜。可以帮助用户选择最合适的流量套餐产品。可以选择的套餐包括:\n经济套餐,月费50元,10G流量;\n畅游套餐,月费180元,100G流量;\n无限套餐,月费300元,1000G流量;\n校园套餐,月费150元,200G流量,仅限在校生。\n"},{"role": "user","content": "有没有土豪套餐?"},{"role": "assistant","content": "很抱歉,我们暂时没有土豪套餐。但是我们有无限套餐,它提供1000G的流量,适合大流量用户。如果您有其他需求,我可以帮您选择其他适合的套餐。"},{"role": "user","content": "多少钱?"},{"role": "assistant","content": "无限套餐的月费是300元。它提供1000G的流量,适合需要大量流量的用户。如果您对其他套餐感兴趣,我可以为您提供更多信息。"},{"role": "user","content": "给我办一个"},{"role": "assistant","content": "好的,我会为您办理无限套餐。请提供您的个人信息,包括姓名、手机号码和身份证号码,以便我们为您办理。"}
]
9)纯OpenAI和自制问答的对比
①自制代码能让问答更加可控
②减少prompt能更加省钱
③纯OpenAI让系统简单好维护