LangChain实战:Prompt提示词调优

前置体验

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/月)价格(元/月)适用人群
经济套餐1050无限制
畅游套餐100180无限制
无限套餐1000300无限制
校园套餐200150在校生

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

导入依赖

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米的跑步成绩表现出了很好的速度和耐力,这意味着他具有良好的爆发力和持久力,对于进行跆拳道的快速出拳和长时间持续搏斗有着很大的帮助。

此外,小明在铅球项目中也取得了不错的成绩,显示出了一定的力量水平。在跆拳道中,强大的肌肉力量可以让运动员更好地控制自己的身体,并且在进行技术动作时能够更加稳健和有力。

综上所述,根据小明的身体素质和运动成绩,他非常适合进行跆拳道训练。他的速度和耐力优势将为他在比赛中赢得先机,而力量水平也为他提供了良好的技术基础。希望小明能够在跆拳道的训练中不断进步,取得更加辉煌的成绩!
****
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值