4.prompt

Prompt engineering提示词工程:

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

1.Prompt调优:

1.如果知道训练数据,可以根据训练数据,参考训练数据来构造prompt

2.不知道训练数据,根据已知的例如:openai对markdown格式友好对xml友好,未知的只能不断尝试

(高质量prompt核心:具体、丰富、少歧义

若:底层大模型替换了,prompt需要进行重新调优)

2.Prompt构成:

角色:给ai定义一个任务角色

指示:对任务进行描述

上下文:给出与任务相关的其他背景信息

例子:提供可能的输入示例,以及模型期望的输出

输入:任务的输入信息

输出:输出格式描述

(大模型对prompt开头和结尾的内容更敏感,先定义角色把问题域收窄)

实战项目:

例:智能客服根据用户的咨询,推荐产品(流量包)

思路:

语音识别(语音转为文字)->语义理解NLU:做出行为,例如进行排序->DST:由DST生成的结果去到数据库中进行查询->决策:查询得到值后返回->自然语言生成->输出给用户

NLU->DST:把输入的自然语言转成结构化的表示

DST->Policy:从结构化的表示生成策略

用prompt实现DST不是唯一的选择

有点:节省开发量

缺点:调优相对复杂,最好用动态例子

也可以用prompt实现NLU,用传统方法维护DST

优点:DST环节可控性更高

缺点:需要结合业务know-how设计状态更新机制(解冲突)

实现:

前置:导入依赖、加载环境变量、初始化openai、调用大模型

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

#加载.env文件中定义的环境变量
_=load_dotenv(find_dotenv())
#初始化OpenAI客户端
client=OpenAI()#默认使用环境变量中的apikey
#基于prompt生成文本
def get_completion(prompt,model="gpt-3.5-turbo"):
    messages=[{"role":"user","content":prompt}]
    response=clent.chat.completion.create(
        model=model,
        messages=messages,
        temperature=0
    )
    return response.choices[0].message.content

demo1(简单prompt):

从用户的一句话里,自动提取出他想要的流量套餐条件,并输出成程序能直接用的 JSON 结果。

复制代码
#任务描述
instruction="""
你的任务是识别用户对手机流量套餐产品的选择条件。
每种流量套餐产品包含三个属性:名称,月费价格,月流量。
根据用户输入,识别用户在上述三种属性上的倾向。
"""
#输出格式
output_format="""
以json格式输出
"""
#用户输入
input_text="""
办个100G的套餐
"""
#prompt模板,instruction和input_text会被替换为上面的内容
prompt=f"""
{instruction}
{output_format}
用户输入:
{input_text}
"""
#调用大模型
response=get_completion(prompt)
print(response)

目的以及作用:把口语化需求转为规范数据,方便系统处理业务

demo2(精准描述):

把用户说的口语化套餐需求,精准转换成后端系统能直接使用的、严格结构化的查询条件。

复制代码
#把输出格式定义的更精细
#任务描述增加了字段的英文标识符
instruction="""
你的任务是识别用户对手机流量套餐产品的选择条件。
每种流量套餐产品包含三个属性:名称(name),月费价格(price),月流量(data)。
根据用户输入,识别用户在上述三种属性上的倾向。
"""
output_format="""
以json格式输出。
1.name字段取值为string类型,取值必须为以下之一:经济套餐、畅游套餐、无限套餐
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的字段
"""
input_text="""
办个100G以上的套餐
"""
prompt=f"""
{instruction}
{output_format}
用户输入:
{input_text}
"""
#调用大模型
response=get_completion(prompt)
print(response)

demo3(加入例子):

通过提供例子,来展示希望达到的输出效果

复制代码
#加入例子
examples="""
便宜的套餐:{"sort":{"ordering"="ascend","value"="price"}}
有没有不限流量的:{data":{"operator":"==","value":"无上限"}}
流量大的:{"sort":{"ordering"="descend","value"="data"}}
100G以上流量的套餐最便宜的是哪个:{"sort":{"ordering"="ascend","value"="price"},"data":}
月费不超过200的:{"price":{"operator":"<=","value":200}}
就要月费180那个套餐:{"price":{"operator":"==","value":180}}
经济套餐:{"name":"经济套餐}
"""
input_text="有没有便宜的套餐"
prompt=f"""
{instruction}
{output_format}
例如:
{examples}
用户输入:
{input_text}
"""
response=get_completion(prompt)
print(response)

demo4(多轮对话):

复制代码
#把输出格式定义的更精细
#任务描述增加了字段的英文标识符
instruction="""
你的任务是识别用户对手机流量套餐产品的选择条件。
每种流量套餐产品包含三个属性:名称(name),月费价格(price),月流量(data)。
根据用户输入,识别用户在上述三种属性上的倾向。
"""
output_format="""
以json格式输出。
1.name字段取值为string类型,取值必须为以下之一:经济套餐、畅游套餐、无限套餐
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="""
哪个便宜
"""

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

prompt=f"""
{instruction}
{output_format}
{examples}
用户输入:
{input_text}
"""
#调用大模型
response=get_completion(prompt)
print(response)

demo5(客服机器人):

复制代码
#实现对话策略和NLG
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类型,取值必须为以下之一:经济套餐、畅游套餐、无限套餐
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的字段
"""

class NLU:
    def __init__(self):
        self.prompt_template=f"{instruction}\n\n{output_format}\n\n{examples}\n"

    def _get_completion(self,prompt,moddel="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):
        pass

    def update(self, state, nlu_semantics):
        # 如果用户语义里包含"name",清空历史对话状态
        if "name" in nlu_semantics:
            state.clear()

        # 处理"sort"(排序)相关的槽位逻辑
        if "sort" in nlu_semantics:
            # 从NLU结果中取出排序字段和值
            slot = nlu_semantics["sort"]["value"]
            # 如果当前状态中已有该槽位,且操作符是"==",则删除该槽位
            if slot in state and state[slot]["operator"] == "==":
                del state[slot]

        # 将NLU解析出的所有新语义信息,更新到对话状态中
        for k, v in nlu_semantics.items():
            state[k] = v

        # 返回更新后的对话状态
        return state

# 模拟数据库类:负责根据对话状态筛选、排序套餐数据
class MockedDB:
    def __init__(self):
        # 模拟的手机流量套餐数据
        self.data = [
            {"name": "经济套餐", "price": 50, "data": 10, "requirement": None},
            {"name": "畅游套餐", "price": 100, "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 = True

            # 处理特殊用户身份限制(如校园套餐的"在校生"要求)
            if r["requirement"]:
                # 如果用户状态里没有status,或身份不匹配,则跳过该套餐
                if "status" not in kwargs or kwargs["status"] != r["requirement"]:
                    continue

            # 遍历所有筛选条件(排除排序字段)
            for k, v in kwargs.items():
                # 排序字段不参与筛选
                if k == "sort":
                    continue

                # 处理data字段的"无上限"特殊逻辑(对应无限套餐1000G)
                if k == "data" and v["value"] == "无上限":
                    if r[k] != 1000:
                        select = False
                        break

                # 处理带操作符的条件(如 price > 100)
                if isinstance(v, dict) and "operator" in v:
                    if not eval(f"{r[k]} {v['operator']} {v['value']}"):
                        select = False
                        break
                # 普通等值匹配条件
                elif str(r[k]) != str(v):
                    select = False
                    break

            # 所有条件都满足,加入结果列表
            if select:
                records.append(r)

        # 如果结果数量≤1,直接返回,无需排序
        if len(records) <= 1:
            return records

        # 处理排序逻辑:默认按price升序
        key = "price"
        reverse = False
        if "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_templates

    def _wrap(self, user_input, records):
        """根据查询结果,拼接给ChatGPT的提示词"""
        if records:
            # 有匹配结果:使用推荐模板
            prompt = self.prompt_templates["recommand"].replace("__INPUT__", user_input)
            # 用第一条匹配结果填充模板中的占位符(如__NAME__、__PRICE__)
            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)
            # 用对话状态填充模板中的占位符(如__PRICE__、__DATA__)
            for k, v in self.state.items():
                if isinstance(v, dict) and "operator" in v:
                    # 处理带操作符的条件(如 price > 100)
                    prompt = prompt.replace(f"__{k.upper()}__", f"{v['operator']}{str(v['value'])}")
                else:
                    # 普通字段直接替换
                    prompt = prompt.replace(f"__{k.upper()}__", str(v))
        return prompt

    def _call_chatgpt(self, prompt, model="gpt-3.5-turbo"):
        """调用ChatGPT生成回复"""
        # 深拷贝会话历史,避免污染原始数据
        session = copy.deepcopy(self.session)
        # 添加用户当前提示词
        session.append({"role": "user", "content": prompt})
        # 调用API
        response = client.chat.completions.create(
            model=model,
            messages=session,
            temperature=0,
        )
        # 返回模型生成的回复
        return response.choices[0].message.content

    def run(self, user_input):
        """对话系统主流程:串联所有模块"""
        # 1. 调用NLU解析用户输入,得到结构化语义
        semantics = self.nlu.parse(user_input)
        print("===semantics===")
        print(semantics)

        # 2. 调用DST更新对话状态
        self.state = self.dst.update(self.state, semantics)
        print("===state===")
        print(self.state)

        # 3. 调用MockedDB根据对话状态查询匹配的套餐
        records = self.db.retrieve(**self.state)

        # 4. 拼接给ChatGPT的提示词
        prompt_for_chatgpt = self._wrap(user_input, records)
        print("===gpt-prompt===")
        print(prompt_for_chatgpt)

        # 5. 调用ChatGPT生成自然语言回复
        response = self._call_chatgpt(prompt_for_chatgpt)

        # 6. 将当前对话添加到会话历史,用于上下文记忆
        self.session.append({"role": "user", "content": user_input})
        self.session.append({"role": "assistant", "content": response})

        return response

解析:

NLU:自然语言理解:把人话 → JSON

DST:对话状态追踪:记住用户所有要求

MockedDB:模拟数据库

DialogManager:对话管理:串联所有模块

demo6(加入模板):

通过这种方式可以在特定的场景固定回答模板

复制代码
#加入制定情况下的回答模板
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元以内有吗")
print(response)

demo7(增加约束):

通过 ext 变量添加风格约束,控制回复的语气和格式

复制代码
#增加约束(改变语气)
ext="需要口语、亲切一些。不用说"抱歉"。直接回答用户问题并且不需要在前面加自己的身份NO COMMENTS.NO ACKNOW"
prompt_templates={k:v+ext for k,v in dm.state.items()}

dm=DialogManager(prompt_templates)

用OpenAI API实现:

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

def print_json(json_source):
    """把任意对象或数组用排版美观的 JSON 格式打印出来"""
    json_string = ""
    if (not isinstance(json_source, list)):
        json_source = json.loads(json_source.model_dump_json())

    print(json.dumps(
        json_source,
        indent=4,
        ensure_ascii=False
    ))


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 msg


get_completion("有没有土豪套餐?")
get_completion("多少钱?")
get_completion("给我办一个")
print_json(messages)

#我们发给大模型的prompt,不会改变大模型的参数
"""
1.多轮对话,需要每次都把绥化历史带上(很耗费token)
2.对话历史数据可能会被用去训练大模型
"""

进阶:

思维链:提问时以[Let's think step by step]开头,ai会把问题分解为多个步骤,ai生成更多相关内容,构成更丰富的[上文],从而提高[下文]的正确概率

自洽性

思维树

思考:

prompt设计和优化方式:

1.明确目标:

例:

目标不明确:天气变化的影响

目标明确:天气变化的主要原因以及对农业、农产品有什么样的影响

2.提供上下文

例:

无上下文:解释一下微积分

有上下文:我是一名高中生,目前正在学习微积分;请用通俗的语言解释一下微积分的基本概念

3.使用具体指示:

例:

模糊指示:写一篇文章

具体提示:请写一篇关于人工智能在医疗领域的应用文章,包括以下几点:应用场景、有事和挑战

4.提供示例:

例:

无示例:生成一个关于产品的报告

有示例:生成一个关于产品的报告,格式如下:\n\n-产品名称:\n-价格:\n-特点:\n-优点:\n-缺点:

5.使用分步指示:

对于复杂的任务,分解为多个步骤,逐步引导模型完成

6.控制输出长度

7.使用占位符和模板

8.制定输出格式

9.使用多轮对话

10.反复实验以及调整优化

总结:

使用prompt先拆解步骤再具体描述小步骤,通过反复尝试优化prompt;各个环节严谨同时应多维度进行考虑

相关推荐
云 祁11 小时前
从 Prompt 到 Skills:AI 能力工程化的范式跃迁
prompt
南汁bbj12 小时前
从Prompt到Agent:教育错题分析系统的流程编排设计实践
人工智能·prompt
技术小猪猪12 小时前
PromptOps:用Python构建生产级提示词工程体系
人工智能·python·ai·自动化·prompt
李铁蛋zs16 小时前
AI 前端开发 Prompt 模板库
前端·vue.js·prompt
追风的老玩骨17 小时前
Spec Coding:下一代 AI 编程方式,正在替代传统 Prompt 编程
人工智能·prompt
Rauser Mack17 小时前
编程零基础五分钟用AI做了个贪吃蛇(附prompt)
人工智能·python·html·prompt·ai编程
有才不一定有德17 小时前
读完 Anthropic 100+ 个 Skills 和 Agent Prompt 后,我重写了自己的系统
prompt·agent
水木流年追梦1 天前
大模型入门-大模型的推理策略
开发语言·python·算法·正则表达式·prompt
格桑阿sir2 天前
07-大模型智能体开发工程师:提示词工程(Prompt Engineering)
ai·llm·prompt·提示词·context·智能体·提示词工程