LLMs Prompt for Developer
一、导读
之前写过《面向使用者的提示工程》,主要是面向普通用户,在日常使用大语言模型聊天或对话的时候应该如何书写提示词,来改善大模型输出的效果。而本篇主要是面向开发者,介绍在开发 RAG 类基于大模型的应用时应该如何优化和改善提示词,针对特定任务构造能充分发挥大模型能力的 Prompt 的技巧
整理自:
二、提示原则
2.1、原则一:编写清晰、具体的指令
2.1.1、使用分隔符清晰地表示输入的不同部分
作用:
- 将不同的部分进行分割,避免混淆
- 避免词注入(用户输入的文本可能与预设的 prompt 冲突,可能导致可以直接通过输入操控语言模型、或者干扰模型而得出较差的效果)
示例:
text = f""" 您应该提供尽可能清晰、具体的指示,以表达您希望模型执行的任务。\ 这将引导模型朝向所需的输出,并降低收到无关或不正确响应的可能性。\ 不要将写清晰的提示词与写简短的提示词混淆。\ 在许多情况下,更长的提示词可以为模型提供更多的清晰度和上下文信息,从而导致更详细和相关的输出。 """ # 需要总结的文本内容 prompt = f""" 把用三个反引号括起来的文本总结成一句话。 ```{text}``` """
2.1.2、寻求结构化的输出
有时候我们需要语言模型给我们一些结构化的输出,如JSON、HTML等,而不仅仅是连续的文本。
示例
prompt = f""" 请生成包括书名、作者和类别的三本虚构的、非真实存在的中文书籍清单,\ 并以 JSON 格式提供,其中包含以下键:book_id、title、author、genre。 """
2.1.3、要求模型检查是否满足条件
如果任务包含不一定能满足的假设(条件),我们可以告诉模型先检查这些假设。
示例
text_1 = f""" 泡一杯茶很容易。首先,需要把水烧开。\ 在等待期间,拿一个杯子并把茶包放进去。\ 一旦水足够热,就把它倒在茶包上。\ 等待一会儿,让茶叶浸泡。几分钟后,取出茶包。\ 如果您愿意,可以加一些糖或牛奶调味。\ 就这样,您可以享受一杯美味的茶了。 """ prompt = f""" 您将获得由三个引号括起来的文本。\ 如果它包含一系列的指令,则需要按照以下格式重新编写这些指令: 第一步 - ... 第二步 - … … 第N步 - … 如果文本中不包含一系列的指令,则直接写“未提供步骤”。" \"\"\"{text_1}\"\"\" """
2.1.4、提供少量示例
给模型一两个已完成的样例,让模型了解我们的要求和期望的输出样式
示例
prompt = f""" 您的任务是以一致的风格回答问题。 <孩子>: 请教我何为耐心。 <祖父母>: 挖出最深峡谷的河流源于一处不起眼的泉眼;最宏伟的交响乐从单一的音符开始;最复杂的挂毯以一根孤独的线开始编织。 <孩子>: 请教我何为韧性。 """
2.2、原则二:给模型时间去思考
如果让语言模型匆忙给出结论,其结果很可能不准确。例如,若要语言模型推断一本书的主题,仅提供简单的书名和一句简介是不足够的。
应通过 Prompt 指引语言模型进行深入思考。可以要求其先列出对问题的各种看法,说明推理依据,然后再得出最终结论。在 Prompt 中添加逐步推理的要求,能让语言模型投入更多时间逻辑思维,输出结果也将更可靠准确。
2.2.1、指定完成任务所需的步骤
示例
text = f""" 在一个迷人的村庄里,兄妹杰克和吉尔出发去一个山顶井里打水。\ 他们一边唱着欢乐的歌,一边往上爬,\ 然而不幸降临——杰克绊了一块石头,从山上滚了下来,吉尔紧随其后。\ 虽然略有些摔伤,但他们还是回到了温馨的家中。\ 尽管出了这样的意外,他们的冒险精神依然没有减弱,继续充满愉悦地探索。 """ # example 1 prompt_1 = f""" 执行以下操作: 1-用一句话概括下面用三个反引号括起来的文本。 2-将摘要翻译成英语。 3-在英语摘要中列出每个人名。 4-输出一个 JSON 对象,其中包含以下键:english_summary,num_names。 请用换行符分隔您的答案。 Text: ```{text}``` """ prompt_2 = f""" 1-用一句话概括下面用<>括起来的文本。 2-将摘要翻译成英语。 3-在英语摘要中列出每个名称。 4-输出一个 JSON 对象,其中包含以下键:English_summary,num_names。 请使用以下格式: 文本:<要总结的文本> 摘要:<摘要> 翻译:<摘要的翻译> 名称:<英语摘要中的名称列表> 输出 JSON:<带有 English_summary 和 num_names 的 JSON> Text: <{text}> """
2.2.2、指导模型在下结论之前找出一个自己的解法
还可以通过明确指导语言模型进行自主思考,来获得更好的效果。
假设我们要语言模型判断一个数学问题的解答是否正确,可以在 Prompt 中先要求语言模型自己尝试解决这个问题,思考出自己的解法,然后再与提供的解答进行对比,判断正确性。这种先让语言模型自主思考的方式,能帮助它更深入理解问题,做出更准确的判断。
示例
prompt = f""" 请判断学生的解决方案是否正确,请通过如下步骤解决这个问题: 步骤: 首先,自己解决问题。 然后将您的解决方案与学生的解决方案进行比较,对比计算得到的总费用与学生计算的总费用是否一致,并评估学生的解决方案是否正确。 在自己完成问题之前,请勿决定学生的解决方案是否正确。 使用以下格式: 问题:问题文本 学生的解决方案:学生的解决方案文本 实际解决方案和步骤:实际解决方案和步骤文本 学生计算的总费用:学生计算得到的总费用 实际计算的总费用:实际计算出的总费用 学生计算的费用和实际计算的费用是否相同:是或否 学生的解决方案和实际解决方案是否相同:是或否 学生的成绩:正确或不正确 问题: 我正在建造一个太阳能发电站,需要帮助计算财务。 - 土地费用为每平方英尺100美元 - 我可以以每平方英尺250美元的价格购买太阳能电池板 - 我已经谈判好了维护合同,每年需要支付固定的10万美元,并额外支付每平方英尺10美元; 作为平方英尺数的函数,首年运营的总费用是多少。 学生的解决方案: 设x为发电站的大小,单位为平方英尺。 费用: 1. 土地费用:100x美元 2. 太阳能电池板费用:250x美元 3. 维护费用:100,000+100x=10万美元+10x美元 总费用:100x美元+250x美元+10万美元+100x美元=450x+10万美元 实际解决方案和步骤: """
大语言模型可能会产生幻觉问题(自行构造出似是而非的细节),由于幻觉信息往往令人无法辨别真伪,开发者必须警惕并尽量避免它的产生。
可以先让语言模型直接引用文本中的原句,然后再进行解答。这可以追踪信息来源,降低虚假内容的风险。
三、迭代优化
以下是一些常见的问题可供参考
- 生成文本太长
- 优化:使用最多50个词
- 问题:精度不会很准,但是接近预设长度,需要多次尝试
- 细节处理错误:细节信息错误,侧重角度不对等
- 请对三个反引号之间的评论文本进行概括,最多30个字,并且侧重在快递服务上。
- 请对三个反引号之间的评论文本进行概括,最多30个词汇,并且侧重在产品价格和质量上。
- 指定输出格式
- 优化:在描述之后,包括一个表格,提供产品的尺寸。表格应该有两列。第一列包括尺寸的名称。第二列只包括英寸的测量值。
四、任务类型
使用上面提到的优化迭代方法进行不断的优化和迭代;
4.1、文本概括
示例:单一文本概括
prompt = "您的任务是从电子商务网站上生成一个产品评论的简短摘要。" # 如果只想要提取某一个单独的信息并过滤其他所有的信息可以使用文本提取 prompt = "您的任务是从电子商务网站上的产品评论中提取相关信息。"
多条文本概括
for 循环、整合品论、分布式
4.2、推断
示例:情感分类
prompt = f""" 以下用三个反引号分隔的产品评论的情感是什么? 用一个单词回答:「正面」或「负面」。 评论文本: ```{lamp_review}``` """ prompt = f""" 识别以下评论的作者表达的情感。包含不超过五个项目。将答案格式化为以逗号分隔的单词列表。 评论文本: ```{lamp_review}``` """
示例:信息提取
prompt = f""" 从评论文本中识别以下项目: - 评论者购买的物品 - 制造该物品的公司 评论文本用三个反引号分隔。将你的响应格式化为以 “物品” 和 “品牌” 为键的 JSON 对象。 如果信息不存在,请使用 “未知” 作为值。 让你的回应尽可能简短。 评论文本: ```{lamp_review}``` """
示例:信息提取和情感分析
prompt = f""" 从评论文本中识别以下项目: - 情绪(正面或负面) - 审稿人是否表达了愤怒?(是或否) - 评论者购买的物品 - 制造该物品的公司 评论用三个反引号分隔。将你的响应格式化为 JSON 对象,以 “情感倾向”、“是否生气”、“物品类型” 和 “品牌” 作为键。 如果信息不存在,请使用 “未知” 作为值。 让你的回应尽可能简短。 将 “是否生气” 值格式化为布尔值。 评论文本: ```{lamp_review}``` """
示例:推断主题
prompt = f""" 确定以下给定文本中讨论的五个主题。 每个主题用1-2个词概括。 请输出一个可解析的Python列表,每个元素是一个字符串,展示了一个主题。 给定文本: ```{story}``` """
示例:推断主题并判断
prompt = f""" 判断主题列表中的每一项是否是给定文本中的一个话题, 以列表的形式给出答案,每个元素是一个Json对象,键为对应主题,值为对应的 0 或 1。 主题列表:美国航空航天局、当地政府、工程、员工满意度、联邦政府 给定文本: ```{story}``` """
4.3、文本转换
示例:文本翻译
prompt = f""" 将以下中文翻译成西班牙语: \ ```您好,我想订购一个搅拌机。``` """ prompt = f""" 请将以下文本分别翻译成中文、英文、法语和西班牙语: ```I want to order a basketball.``` """
示例:识别语种
prompt = f""" 请告诉我以下文本是什么语种: ```Combien coûte le lampadaire?``` """
示例:写作语气与风格调整
prompt = f""" 将以下文本翻译成商务信函的格式: ```小老弟,我小羊,上回你说咱部门要采购的显示器是多少寸来着?``` """
示例:文件格式转换
prompt = f""" 将以下Python字典从JSON转换为HTML表格,保留表格标题和列名:{data_json} """
示例:拼写及语法纠正
prompt = f"""请校对并更正以下文本,注意纠正文本保持原始语种,无需输出原始文本。 如果您没有发现任何错误,请说“未发现错误”。 例如: 输入:I are happy. 输出:I am happy. ```{text[i]}```""" prompt = f"校对并更正以下商品评论:```{text}```"
示例:综合案例
prompt = f""" 针对以下三个反引号之间的英文评论文本, 首先进行拼写及语法纠错, 然后将其转化成中文, 再将其转化成优质淘宝评论的风格,从各种角度出发,分别说明产品的优点与缺点,并进行总结。 润色一下描述,使评论更具有吸引力。 输出结果格式为: 【优点】xxx 【缺点】xxx 【总结】xxx 注意,只需填写xxx部分,并分段输出。 将结果输出成Markdown格式。 ```{text}``` """
4.4、 文本扩展
示例:定制客户邮件
根据客户的评价和其中的情感倾向,使用大语言模型针对性地生成回复邮件。
prompt = f""" 你是一位客户服务的AI助手。 你的任务是给一位重要客户发送邮件回复。 根据客户通过“```”分隔的评价,生成回复以感谢客户的评价。提醒模型使用评价中的具体细节 用简明而专业的语气写信。 作为“AI客户代理”签署电子邮件。 客户评论: ```{review}``` 评论情感:{sentiment} """
4.5、聊天对话
像 ChatGPT 这样的聊天模型实际上是组装成以一系列消息作为输入,并返回一个模型生成的消息作为输出的。
示例:设置角色
messages = [ {'role':'system', 'content':'你是个友好的聊天机器人。'}, {'role':'user', 'content':'Hi, 我是Isa。'} ]
示例:构建构建上下文
messages = [ {'role':'system', 'content':'你是个友好的聊天机器人。'}, {'role':'user', 'content':'Hi, 我是Isa'}, {'role':'assistant', 'content': "Hi Isa! 很高兴认识你。今天有什么可以帮到你的吗?"}, {'role':'user', 'content':'是的,你可以提醒我, 我的名字是什么?'} ]
示例:订餐机器人
context = [{'role':'system', 'content':""" 你是订餐机器人,为披萨餐厅自动收集订单信息。 你要首先问候顾客。然后等待用户回复收集订单信息。收集完信息需确认顾客是否还需要添加其他内容。 最后需要询问是否自取或外送,如果是外送,你要询问地址。 最后告诉顾客订单总金额,并送上祝福。 请确保明确所有选项、附加项和尺寸,以便从菜单中识别出该项唯一的内容。 你的回应应该以简短、非常随意和友好的风格呈现。 菜单包括: 菜品: 意式辣香肠披萨(大、中、小) 12.95、10.00、7.00 芝士披萨(大、中、小) 10.95、9.25、6.50 茄子披萨(大、中、小) 11.95、9.75、6.75 薯条(大、小) 4.50、3.50 希腊沙拉 7.25 配料: 奶酪 2.00 蘑菇 1.50 香肠 3.00 加拿大熏肉 3.50 AI酱 1.50 辣椒 1.00 饮料: 可乐(大、中、小) 3.00、2.00、1.00 雪碧(大、中、小) 3.00、2.00、1.00 瓶装水 5.00 """} ] messages = context.copy() messages.append( {'role':'system', 'content': '''创建上一个食品订单的 json 摘要。\ 逐项列出每件商品的价格,字段应该是 1) 披萨,包括大小 2) 配料列表 3) 饮料列表,包括大小 4) 配菜列表包括大小 5) 总价 你应该给我返回一个可解析的Json对象,包括上述字段'''}, )
五、最佳实践
5.1、使用 CO-STAR 框架构建提示
CO-STAR是结构化的Prompt模版六大要素的首字母缩写,即:
- (C) Context 上下文:为任务提供背景信息 通过为大语言模型(LLM)提供详细的背景信息,可以帮助它精确理解讨论的具体场景,确保提供的反馈具有相关性。
- (O) Objective 目标:明确你要求大语言模型完成的任务 清晰地界定任务目标,可以使大语言模型更专注地调整其回应,以实现这一具体目标。
- (S) Style 风格:明确你期望的写作风格 你可以指定一个特定的著名人物或某个行业专家的写作风格,如商业分析师或 CEO。这将指导大语言模型以一种符合你需求的方式和词汇选择进行回应。
- (T) Tone 语气:设置回应的情感调 设定适当的语气,确保大语言模型的回应能够与预期的情感或情绪背景相协调。可能的语气包括正式、幽默、富有同情心等。
- (A) Audience 受众:识别目标受众 针对特定受众定制大语言模型的回应,无论是领域内的专家、初学者还是儿童,都能确保内容在特定上下文中适当且容易理解。
- (R) Response响应:规定输出的格式 确定输出格式是为了确保大语言模型按照你的具体需求进行输出,便于执行下游任务。常见的格式包括列表、JSON 格式的数据、专业报告等。对于大部分需要程序化处理大语言模型输出的应用来说,JSON 格式是理想的选择。
场景:假设你是一名社交媒体经理,你需要帮助起草一篇 Facebook 帖子来宣传你公司的新产品。
基于CO-STAR框架的prompt:
# CONTEXT # 我想为我公司的新产品做广告。我公司的名字叫Alpha,产品叫Beta,是一种新型的超快吹风机。 # OBJECTIVE # 为我创建一个Facebook帖子,旨在让人们点击产品链接进行购买。 # STYLE # 遵循成功公司为类似产品做广告的写作风格,比如戴森。 # TONE # 有说服力的 # AUDIENCE # 我公司在Facebook上的受众群通常是老一辈。定制你的帖子,以针对这些受众在头发产品中通常想要的东西。 # RESPONSE # Facebook上的帖子简洁而有影响力。
5.2、使用分隔符给Prompt分段
善于利用分隔符,帮助大模型更好的理解提示内容。Prompt内容越复杂,分隔符的作用就越重要。分隔符可以自己设计,但不应与标点符号等相同,容易发生歧义,常见的分隔符可以是###、===、<<<>>>等。同时,也可以使用xml标签来分隔Prompt。
一个运用分隔符的示例:
Classify the sentiment of each conversation in <<<CONVERSATIONS>>> as ‘Positive’ or ‘Negative’. Give the sentiment classifications without any other preamble text. ### EXAMPLE CONVERSATIONS [Agent]: Good morning, how can I assist you today? [Customer]: This product is terrible, nothing like what was advertised! [Customer]: I’m extremely disappointed and expect a full refund. [Agent]: Good morning, how can I help you today? [Customer]: Hi, I just wanted to say that I’m really impressed with your product. It exceeded my expectations! ### EXAMPLE OUTPUTS Negative Positive ### <<< [Agent]: Hello! Welcome to our support. How can I help you today? [Customer]: Hi there! I just wanted to let you know I received my order, and it’s fantastic! [Agent]: That’s great to hear! We’re thrilled you’re happy with your purchase. Is there anything else I can assist you with? [Customer]: No, that’s it. Just wanted to give some positive feedback. Thanks for your excellent service! [Agent]: Hello, thank you for reaching out. How can I assist you today? [Customer]: I’m very disappointed with my recent purchase. It’s not what I expected at all. [Agent]: I’m sorry to hear that. Could you please provide more details so I can help? [Customer]: The product is of poor quality and it arrived late. I’m really unhappy with this experience. >>>
该例子使用 ### 进行分段,并使用大写“EXAMPLE CONVERSATIONS”对话和“EXAMPLE OUTPUTS”以进行区分。Prompt最前面描述已经指出要分类的对话会使用 <<<CONVERSATIONS>>> 分隔,这些对话随后在提示的底部提供给 LLM,虽然没有任何解释性文本,但 LLM 可以理解这是它应该分类的对话,因为存在分隔符 <<< 和 >>>。
5.3、利用 LLM 防护措施创建系统提示
由于大模型记忆能力有限,对于需要重复设置的指令,可以通过OpenAI中的System Prompt设置,这些提示会和User Prompt合并后每次都提交给大模型,从而减少记忆丢失和提示繁琐的问题。一般可以设置以下类别:
- 任务定义: 这样 LLM 在整个聊天过程中始终记得它需要做什么。
- 输出格式: 这样 LLM 将始终记得它应该如何响应。
- 护栏: 这样 LLM 将始终记得它不应该如何响应。护栏指的是 LLM 允许操作的配置边界。比如一些避免提示攻击的一些防御性说明。
5.4、仅使用 LLM 分析数据集
大模型不擅长精确数学计算或复杂、基于规则的任务处理,但大模型擅长识别模式和趋势分析,这种能力源于它们在大量不同数据上的广泛训练,使它们能够识别可能并不那么明显的复杂模式。这使得它们非常适合基于数据集内模式查找的任务,能在更短的时间内产生比使用代码更好的结果。
例如:
- 异常检测:根据一个或多个列值识别偏离规范的异常数据点。
- 聚类:将具有相似特征的数据点分组到各个列中。
- 跨列关系:识别跨列的组合趋势。
- 文本分析(针对基于文本的列):基于主题或情感进行分类。
- 趋势分析(针对具有时间方面的数据集):识别列中跨时间段的模式、季节性变化或趋势。
示例《仅使用LLM分析 Kaggle 数据集 》:
Kaggle真实数据集(https://www.kaggle.com/datasets/imakash3011/customer-personality-analysis),该数据集是为客户个性分析准备,试图细分其客户群,以便更好地了解其客户。
5.4.1、具体操作流程:
1、系统配置及任务指令输入
System Prompt:
I want you to act as a data scientist to analyze datasets. Do not make up information that is not in the dataset. For each analysis I ask for, provide me with the exact and definitive answer and do not provide me with code or instructions to do the analysis on other platforms.
User Prompt:
# CONTEXT # I sell wine. I have a dataset of information on my customers: [year of birth, marital status, income, number of children, days since last purchase, amount spent]. ############# # OBJECTIVE # I want you use the dataset to cluster my customers into groups and then give me ideas on how to target my marketing efforts towards each group. Use this step-by-step process and do not use code: 1. CLUSTERS: Use the columns of the dataset to cluster the rows of the dataset, such that customers within the same cluster have similar column values while customers in different clusters have distinctly different column values. Ensure that each row only belongs to 1 cluster. For each cluster found, 2. CLUSTER_INFORMATION: Describe the cluster in terms of the dataset columns. 3. CLUSTER_NAME: Interpret [CLUSTER_INFORMATION] to obtain a short name for the customer group in this cluster. 4. MARKETING_IDEAS: Generate ideas to market my product to this customer group. 5. RATIONALE: Explain why [MARKETING_IDEAS] is relevant and effective for this customer group. ############# # STYLE # Business analytics report ############# # TONE # Professional, technical ############# # AUDIENCE # My business partners. Convince them that your marketing strategy is well thought-out and fully backed by data. ############# # RESPONSE: MARKDOWN REPORT # <For each cluster in [CLUSTERS]> — Customer Group: [CLUSTER_NAME] — Profile: [CLUSTER_INFORMATION] — Marketing Ideas: [MARKETING_IDEAS] — Rationale: [RATIONALE] <Annex> Give a table of the list of row numbers belonging to each cluster, in order to back up your analysis. Use these table headers: [[CLUSTER_NAME], List of Rows]. ############# # START ANALYSIS # If you understand, ask me for my dataset.
2、GPT回答及用户提供待分析数据
3、GPT 生成分析报告
4、验证 LLM 的分析正确性
5.4.2、技巧总结
1、任务分解:
LLM擅长执行简单的任务,但不太擅长执行复杂的任务。因此,对于像这样的复杂任务,重要的是将任务分解成简单的、逐步的指令,供大型语言模型遵循。
与简单地将整体任务交给 LLM,例如这样的指令“将客户进行分组,然后给出针对每个组的营销策略”相比,通过逐步的指令,LLM 更有可能提供正确的结果。
Use this step-by-step process and do not use code: 1. CLUSTERS: Use the columns of the dataset to cluster the rows of the dataset, such that customers within the same cluster have similar column values while customers in different clusters have distinctly different column values. Ensure that each row only belongs to 1 cluster. For each cluster found, 2. CLUSTER_INFORMATION: Describe the cluster in terms of the dataset columns. 3. CLUSTER_NAME: Interpret [CLUSTER_INFORMATION] to obtain a short name for the customer group in this cluster. 4. MARKETING_IDEAS: Generate ideas to market my product to this customer group. 5. RATIONALE: Explain why [MARKETING_IDEAS] is relevant and effective for this customer group
2、引用LLM每个步骤生成中间输出
当向 LLM 提供每步过程时,将每个步骤的中间输出赋予一个大写的变量名,即 CLUSTERS、CLUSTER_INFORMATION、CLUSTER_NAME、MARKETING_IDEAS 和 RATIONALE。
使用大写字母是为了将这些变量名与给出的指令主体区分开来。这些中间输出稍后可以使用方括号引用为 [VARIABLE_NAME]。
3、格式化 LLM 的响应
在这里, LLM 的响应为 markdown 报告格式。同时,从中间输出中使用变量名来设定报告的结构也非常方便。
# RESPONSE: MARKDOWN REPORT # <For each cluster in [CLUSTERS]> — Customer Group: [CLUSTER_NAME] — Profile: [CLUSTER_INFORMATION] — Marketing Ideas: [MARKETING_IDEAS] — Rationale: [RATIONALE] <Annex> Give a table of the list of row numbers belonging to each cluster, in order to back up your analysis. Use these table headers: [[CLUSTER_NAME], List of Rows].
4、将任务指令与数据集分离
在第一个Prompt中从未将数据集提供给 LLM。相反,Prompt仅给出了数据集分析的任务说明,并添加了以下内容:
# START ANALYSIS # If you understand, ask me for my dataset.
这样做的好处是帮助 LLM 更清晰地理解每个指令,降低遗漏信息的可能性,尤其是在像这样指令较长的复杂任务中。通过首先接收指令,然后再接收指令所针对的数据集,LLM 可以先消化它应该做什么,然后再在提供的数据集上执行它。