文章目录
前置体验
PromptTemplate【字符串模板替换】
from langchain_core.prompts import PromptTemplate
prompt_template = PromptTemplate.from_template("我今天心情{feel},我想{content}")
message = prompt_template.format(feel = "非常开心",content = "去吃一顿火锅好好庆祝一下")
print(message)
我今天心情非常开心,我想去吃一顿火锅好好庆祝一下
给变量feel和content赋值,达到动态输出内容
ChatPromptTemplate【具有角色的模板替换】
注意:需要在.env文件中设置
OPENAI_API_KEY=sk-XXXXXXXXXXXXXXXX
OPENAI_API_BASE=https://xxx
from dotenv import load_dotenv, find_dotenv
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
load_dotenv(find_dotenv())
chat_prompt_template = ChatPromptTemplate.from_messages(
[
("system","你是一个厨师"),
("human","{user_input}")
]
)
model = ChatOpenAI()
chain = chat_prompt_template | model
response = chain.invoke({"user_input":"你会做什么饭?"})
print(response.content)
我可以做各种不同类型的菜肴,包括中式、西式、日式、意式等等。无论是炒菜、烤肉、炖汤、烘培,我都可以应对。我也可以根据你的口味和饮食习惯来调整菜谱,让你的用餐体验更加愉快。
什么是提示工程(Prompt Engineering)
我们发给大模型的指令就叫提示词,比如:
【我今天心情不好,讲一个笑话给我听】
【我女朋友生气了,怎么哄好她】
【我的代码出现问题了,报这个错误,怎么解决】
这些都叫做提示词。
案例体验
哄哄模拟器 ,不知道怎么哄好女朋友的可以提前练习一下。
地址:https://hong.greatdk.com/
Prompt调优
找到好的 prompt 是个持续迭代的过程,需要不断调优。
如果知道训练数据是怎样的,参考训练数据来构造 prompt 是最好的。
「当人看」类比:
你知道 ta 喜欢三国演义,就和 ta 聊三国演义
你知道 ta 是个吃货,就和 ta 说美食
你知道 ta 喜欢健身,就夸 ta 身材好
Prompt 的典型构成
角色:指定具体的角色
指示:对任务进行描述
上下文:给出与任务相关的其它背景信息(尤其在多轮交互中)
例子:必要时给出举例
输入:任务的输入信息;在提示词中明确的标识出输入
输出:输出的格式描述,以便后继模块自动解析模型的输出结果,比如(JSON、XML)
如果换做成生活中,我们如何和别人沟通,同样都是确定好彼此的角色
如果我参观动物园的大熊猫,但是不知道具体位置,就要和工作人员沟通
描述好我的诉求,并且告诉 ta 我希望得到什么样的结果
【推荐流量包的智能客服】调优案例
某运营商的流量包产品:
名称 | 流量(G/月) | 价格(元/月) | 适用人群 |
---|---|---|---|
经济套餐 | 10 | 50 | 无限制 |
畅游套餐 | 100 | 180 | 无限制 |
无限套餐 | 1000 | 300 | 无限制 |
校园套餐 | 200 | 150 | 在校生 |
需求:智能客服根据用户的咨询,推荐最适合的流量包。
导入依赖
from dotenv import find_dotenv, load_dotenv
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
加载.env环境变量设置
load_dotenv(find_dotenv())
任务描述、用户输入、输出格式、模板定义
# 任务描述
instruction = """
你的任务是识别用户对手机流量套餐产品的选择条件。
每种流量套餐产品包含三个属性:名称,月费价格,月流量。
根据用户输入,识别用户在上述三种属性上的需求是什么。
"""
#输出格式
output_format = """
以 JSON 格式输出
"""
# 用户输入
input_text = """
办个100G的套餐。
"""
template = """
{instruction}
{output_format}
用户输入:
{input_text}
"""
设置chain并测试结果
prompt = ChatPromptTemplate.from_template(template)
llm = ChatOpenAI()
out = StrOutputParser()
chain =prompt | llm | out
message = chain.invoke(
{
"instruction": instruction,
"output_format": output_format,
"input_text": input_text
}
)
print(message)
输出结果
{
"需求": {
"名称": null,
"月费价格": null,
"月流量": "100G"
}
}
大模型是懂 JSON 的,但需要对 JSON 结构做严格定义。
把输出格式定义的更精细再试试呢
【改进】增加输出格式
from dotenv import find_dotenv, load_dotenv
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableParallel, RunnablePassthrough
from langchain_openai import ChatOpenAI
load_dotenv(find_dotenv())
# 任务描述
instruction = """
你的任务是识别用户对手机流量套餐产品的选择条件。
每种流量套餐产品包含三个属性:名称(name),月费价格(price),月流量(data)。
根据用户输入,识别用户在上述三种属性上的需求是什么,符合哪个套餐。
已知产品包括:
经济套餐:月费50元,月流量10G
畅游套餐:月费180元,月流量100G
无限套餐:月费300元,月流量1000G
校园套餐:月费150元,月流量200G,限在校学生办理
"""
#输出格式
output_format = """
以JSON格式输出。
1. name字段的取值为string类型,取值必须为以下之一:经济套餐、畅游套餐、无限套餐、校园套餐 或 null;
2. price字段的取值为套餐的价格
3. data字段的取值为取值为一个结构体 或 null,包含两个字段:
(1) operator, string类型,取值范围:'<='(小于等于), '>=' (大于等于), '=='(等于)
(2) value, int类型或string类型,string类型只能是'无上限'
4. 用户的意图可以包含按price或data排序,以sort字段标识,取值为一个结构体:
(1) 结构体中以"ordering"="descend"表示按降序排序,以"value"字段存储待排序的字段
(2) 结构体中以"ordering"="ascend"表示按升序排序,以"value"字段存储待排序的字段
只输出中只包含用户提及的字段,不要猜测任何用户未直接提及的字段。
"""
# 用户输入
input_text = """
办个200G的套餐。
"""
template = """
{instruction}
{output_format}
用户输入:
{input_text}
"""
prompt = ChatPromptTemplate.from_template(template)
llm = ChatOpenAI()
out = StrOutputParser()
chain = prompt | llm | out
print("任务描述:" + instruction)
for c in range(5):
print(f"=================================第 {c+1}次计算================================")
print("提问:" + input_text)
message = chain.invoke(
{
"instruction": instruction,
"output_format": output_format,
"input_text": input_text
}
)
print("结果: " + message)
任务描述:
你的任务是识别用户对手机流量套餐产品的选择条件。
每种流量套餐产品包含三个属性:名称(name),月费价格(price),月流量(data)。
根据用户输入,识别用户在上述三种属性上的需求是什么,符合哪个套餐。
已知产品包括:
经济套餐:月费50元,月流量10G
畅游套餐:月费180元,月流量100G
无限套餐:月费300元,月流量1000G
校园套餐:月费150元,月流量200G,限在校学生办理
=================================第 1次计算================================
提问:
办个200G的套餐。
结果: {
"name": "校园套餐",
"data": {
"operator": ">=",
"value": 200
}
}
=================================第 2次计算================================
提问:
办个200G的套餐。
结果: {
"name": "校园套餐",
"data": {
"operator": ">=",
"value": 200
}
}
=================================第 3次计算================================
提问:
办个200G的套餐。
结果: {
"name": "畅游套餐",
"data": {
"operator": ">=",
"value": 200
}
}
=================================第 4次计算================================
提问:
办个200G的套餐。
结果: {
"name": null,
"price": null,
"data": {
"operator": ">=",
"value": 200
}
}
=================================第 5次计算================================
提问:
办个200G的套餐。
结果: {
"name": null,
"price": null,
"data": {
"operator": ">=",
"value": 200
}
}
只有2次选对了套餐,但是和要求的结构不同
加入例子试试呢 例如 example
例子可以让输出更稳定:
答错的,一定给例子
答对的,也给例子,能更稳定
【改进】增加例子
from dotenv import find_dotenv, load_dotenv
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
load_dotenv(find_dotenv())
examples = """
输入:办个100G的套餐。
输出:
{
"name": "畅游套餐",
"price": 180,
"data": {
"operator": ">=",
"value": 100
}
}
"""
# 任务描述
instruction = """
你的任务是识别用户对手机流量套餐产品的选择条件。
每种流量套餐产品包含三个属性:名称(name),月费价格(price),月流量(data)。
根据用户输入,识别用户在上述三种属性上的需求是什么,符合哪个套餐。
已知产品包括:
经济套餐:月费50元,月流量10G
畅游套餐:月费180元,月流量100G
无限套餐:月费300元,月流量1000G
校园套餐:月费150元,月流量200G,限在校学生办理
"""
#输出格式
output_format = """
以JSON格式输出。
1. name字段的取值为string类型,取值必须为以下之一:经济套餐、畅游套餐、无限套餐、校园套餐 或 null;
2. price字段的取值为套餐价格
3. data字段的取值为取值为一个结构体 或 null,包含两个字段:
(1) operator, string类型,取值范围:'<='(小于等于), '>=' (大于等于), '=='(等于)
(2) value, int类型或string类型,string类型只能是'无上限'
只输出中只包含用户提及的字段,不要猜测任何用户未直接提及的字段。
"""
# 用户输入
input_text = """
办个200G的套餐。
"""
template = """
{instruction}
{output_format}
例如:
{examples}
用户输入:
{input_text}
"""
prompt = ChatPromptTemplate.from_template(template)
llm = ChatOpenAI()
out = StrOutputParser()
chain = prompt | llm | out
print("任务描述:" + instruction)
for c in range(5):
print(f"=================================第 {c+1}次计算================================")
message = chain.invoke(
{
"instruction": instruction,
"output_format": output_format,
"input_text": input_text,
"examples": examples
}
)
print(message)
执行结果
任务描述:
你的任务是识别用户对手机流量套餐产品的选择条件。
每种流量套餐产品包含三个属性:名称(name),月费价格(price),月流量(data)。
根据用户输入,识别用户在上述三种属性上的需求是什么,符合哪个套餐。
已知产品包括:
经济套餐:月费50元,月流量10G
畅游套餐:月费180元,月流量100G
无限套餐:月费300元,月流量1000G
校园套餐:月费150元,月流量200G,限在校学生办理
=================================第 1次计算================================
提问:
办个200G的套餐。
结果: {
"name": "校园套餐",
"price": 150,
"data": {
"operator": ">=",
"value": 200
}
}
=================================第 2次计算================================
提问:
办个200G的套餐。
结果: 输出:
{
"name": "校园套餐",
"price": 150,
"data": {
"operator": ">=",
"value": 200
}
}
=================================第 3次计算================================
提问:
办个200G的套餐。
结果: 输出:
{
"name": "校园套餐",
"price": 150,
"data": {
"operator": ">=",
"value": 200
}
}
=================================第 4次计算================================
提问:
办个200G的套餐。
结果: {
"name": "校园套餐",
"price": 150,
"data": {
"operator": "==",
"value": 200
}
}
=================================第 5次计算================================
提问:
办个200G的套餐。
结果: {
"name": "校园套餐",
"price": 150,
"data": {
"operator": ">=",
"value": 200
}
}
5次计算全部选对了套餐,并且按照要求的格式输出
【改进】思维树
在思维链的每一步,采样多个分支
设计搜索算法
小明 100 米跑成绩:10.5 秒,1500 米跑成绩:3 分 02 秒,铅球成绩:50米。他适合参加哪些搏击运动训练。
import json
from dotenv import load_dotenv, find_dotenv
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
load_dotenv(find_dotenv())
封装调用大模型方法
def get_completion(prompt):
llm = ChatOpenAI()
out = StrOutputParser()
chain = prompt | llm | out
return chain.invoke(
{
}
)
推测适合的运动
def possible_sports(talent, category):
template = f"""
需要{talent}强的{category}运动有哪些。给出5个例子,以array形式输出。确保输出能由json.loads解析。"""
prompt = PromptTemplate.from_template(template)
try:
response = get_completion(prompt)
return json.loads(response)
except Exception as e:
print(e)
分析每种指标的强弱
def performance_analyser(text):
template = f"{text}\n请根据以上成绩,分析候选人在速度、耐力、力量三方面素质的分档。分档包括:强(3),中(2),弱(1)三档。\
\n以JSON格式输出,其中key为素质名,value为以数值表示的分档。"
prompt = PromptTemplate.from_template(template)
response = get_completion(prompt)
return json.loads(response)
执行计算
def evaluate(sports, talent, value):
template = f"分析{sports}运动对{talent}方面素质的要求: 强(3),中(2),弱(1)。\
\n直接输出挡位数字。输出只能是数字且只能是1或者2或者3中的一个,不可以存在文字。例如 输出 1"
prompt = PromptTemplate.from_template(template)
response = get_completion(prompt)
val = int(response)
flag = "符合" if value >= val else "不符合"
print(f"{sports}: {talent} 需要 {val},现在的分数是 {value},结果: {flag}")
return value >= val
生成报告
def report_generator(name, performance, talents, sports):
level = ['弱', '中', '强']
_talents = {k: level[v - 1] for k, v in talents.items()}
_talents = list(_talents.items())
template = f"已知{name}{performance}\n身体素质:\
{_talents}。\n生成一篇{name}适合{sports}训练的分析报告。"
prompt = PromptTemplate.from_template(template)
response = get_completion(prompt)
return response
主方法测试
if __name__ == "__main__":
name = "小明"
performance = "100米跑成绩:10.5秒,1500米跑成绩:3分02秒,铅球成绩:50米。"
category = "搏击"
cache = set()
talents = performance_analyser(name + performance)
print(f"{name}的体能分析结果为:{talents}, 指标不是【强(3)】将不会被分析")
# 第一层节点
for k, v in talents.items():
if v < 3: # 剪枝
continue
leafs = possible_sports(k, category)
print(f"==={k} 指标===")
print(f"推荐的运动包括:{leafs}")
if leafs is not None and len(leafs) > 0:
# 第二层节点
for sports in leafs:
if sports in cache:
continue
cache.add(sports)
suitable = True
for t, p in talents.items():
if t == k:
continue
# 第三层节点
if not evaluate(sports, t, p): # 剪枝
suitable = False
break
if suitable:
report = report_generator(name, performance, talents, sports)
print("****")
print(report)
print("****")
else:
print(f"结论:不适合{sports}运动")
运行结果
小明的体能分析结果为:{'速度': 3, '耐力': 3, '力量': 2}, 指标不是【强(3)】将不会被分析
Expecting value: line 1 column 1 (char 0)
===速度 指标===
推荐的运动包括:None
===耐力 指标===
推荐的运动包括:['拳击', '泰拳', '综合格斗', '散打', '跆拳道']
拳击: 速度 需要 3,现在的分数是 3,结果: 符合
拳击: 力量 需要 3,现在的分数是 2,结果: 不符合
结论:不适合拳击运动
泰拳: 速度 需要 3,现在的分数是 3,结果: 符合
泰拳: 力量 需要 3,现在的分数是 2,结果: 不符合
结论:不适合泰拳运动
综合格斗: 速度 需要 3,现在的分数是 3,结果: 符合
综合格斗: 力量 需要 3,现在的分数是 2,结果: 不符合
结论:不适合综合格斗运动
散打: 速度 需要 3,现在的分数是 3,结果: 符合
散打: 力量 需要 3,现在的分数是 2,结果: 不符合
结论:不适合散打运动
跆拳道: 速度 需要 3,现在的分数是 3,结果: 符合
跆拳道: 力量 需要 2,现在的分数是 2,结果: 符合
****
根据小明的运动成绩和身体素质评价,可以得出他在速度和耐力方面具有较强的优势,力量也处于中等水平。这样的身体素质对于跆拳道训练来说是非常有利的。
在跆拳道中,速度和耐力是非常重要的素质。小明在100米和1500米的跑步成绩表现出了很好的速度和耐力,这意味着他具有良好的爆发力和持久力,对于进行跆拳道的快速出拳和长时间持续搏斗有着很大的帮助。
此外,小明在铅球项目中也取得了不错的成绩,显示出了一定的力量水平。在跆拳道中,强大的肌肉力量可以让运动员更好地控制自己的身体,并且在进行技术动作时能够更加稳健和有力。
综上所述,根据小明的身体素质和运动成绩,他非常适合进行跆拳道训练。他的速度和耐力优势将为他在比赛中赢得先机,而力量水平也为他提供了良好的技术基础。希望小明能够在跆拳道的训练中不断进步,取得更加辉煌的成绩!
****