from:tech.bytedance.net/articles/72…
引言
本文是关于提示工程(Prompt Engineering)的一篇综述,帮助初学者掌握对大语言模型(LLM)提示(Prompt)的关键技巧,并且总结了学术界和工业界当前比较关注的一些方向,帮助初学者快速入门。适合阅读人群:
-
如果你刚开始接触提示词,使用经验较浅,本文适合你阅读。
-
如果你已经掌握了一套提示词框架,并且已经知道了 few-shot、CoT、Self-Consistency、LangChain、prompt injection 等概念,那可能本文对你的帮助不大。
本文分为三部分:
-
介绍提示工程的概念,以及所需要知道的基本知识。
-
分享 Prompt 核心技巧和框架,辅以一些使用和实战实例来解释。
-
总结当前学术界和工业界关注的方向,对提示词的优化和工程师关注的架构有一定启示。
什么是提示工程
提示工程(Prompt Engineering)是一项 AI 技术,可以在不更新模型参数的情况下,通过 prompt 引导大语言模型的行为,让大语言模型给出期望的结果,或是让模型完成一项目标任务。以 ChatGPT 举例来说,我们可以向他询问一个知识,解决信息搜索的需求,也可以给它输入一篇文章,通过 prompt 让它总结一份摘要。
提示工程是一门经验科学,它需要通过不断地实验迭代和纠错,来获得特定领域和任务上更好的结果,各个不同的场景可能有自己更合适的提示词,因此本文聚焦在给初学者提供 prompt 范式,而不是让初学者去记住一些特定领域的 prompt。
在继续介绍 prompt 核心技巧前,首先解释一个基本原理,如上图(来源于吴恩达教学视频),LLM 本质在做概率预测,上图左侧的 Base LLM ,即为基座模型,比如 GPT-3,该模型在做的是预测下一个单词是什么,可以理解为“文字接龙”,比如图上示例,给模型一个开头 "Once upon a time, there was a unicore",模型会根据这个开头预测下一个最可能的单词,并续写完整段话,因此当你提问 "What is the capital of France",模型不会给你答案,而是预测后面可能会接的文字。而上图右侧的 Instruction Tuned LLM,是经过人类调教后的模型,比如 GPT-3.5,它能更好地识别人的意图,根据人的指令,预测对应的回答。本文后续提到的 prompt 技巧,都是围绕着如何写好这个指令来开展。
撰写框架
关于提示工程,已经有大量的学习资料和书籍,大多都总结了一些框架,有些材料会给出各个场景的应用示例,建议大家在阅读时更多去吸收框架,而应用示例可以当成视野的扩展来阅读。在工程场景下,通常需要写一个 prompt 来完成某个特定任务,这里总结了一套适用于工程场景的框架:背景、角色、指令、输出。
背景(context)
如同和人沟通工作任务一样,prompt 需要给 LLM 交代清楚上下文背景,信息要做到精准(precise)和简洁(concise)。
精准
上下文要精准,避免引起歧义。比如,让 LLM 分析数据,写一个 SQL 语句,但是在提供上下文背景时,表结构定义给的不精确,字段的口径定义不明,导致无法按预期得到 SQL。再比如特定业务背景或是专业术语没有给 LLM 解释,会得到错误的结果,以下是一个示例:
让 GPT 扮演了一个数据分析的角色,精通 SQL 语句,prompt 给出了表结构定义和数据表数据,让 GPT 找出张三最薄弱的学科。
GPT 给出了分析过程,一切看起来都很美好,但是这个案例对于“最薄弱的学科”没有精准定义,在业务的术语中,最薄弱的学科应该是掌握度最差的学科,而非正确率最低的学科,因为题目非常难,那么题目的正确率就会低,但不代表学生对学科的掌握度就差。在这个示例中,需要针对业务常用的术语和关键字段口径做定义。
简洁
请直白地描述要求,一方面方便 LLM 清晰地理解你的意思,另一方面减少不必要的 token 消耗。比如我是一名老师,希望 LLM 根据题目和解析,给我生成一份讲稿。我可以这么说:“丰富一下解析,把它变成一份老师上课讲题的稿子。”,但不要这么说:“你能不能帮我丰富一下这个解析,把它变成一份老师上课讲题的稿子?因为明天会讲评这道题,但我还没有做好备课。”
角色(role)
设定一个角色,是最常用的 prompt 技巧,可以帮助 LLM 更精确地理解到它在这个任务中所需要扮演的角色,以提升回答质量。以下是刚才学情的示例:
实际在刚才提供的学情示例中,前置给到了 GPT 这样一个 prompt:“你是一名数据分析师,精通 SQL 语句,请根据用户要求来分析数据。”
但如果把这段 prompt 去掉,GPT 的回答质量会下降,图中虽然也给出了 SQL 和分析,但没有给到最终结果。
指令(instruction)
指令即为你希望 LLM 给你完成的任务,指令需要具体和完备。
具体
指令要足够具体,这就好像你指导一名实习生写技术方案,如果你仅仅告诉他根据需求完成一份技术方案,他可能考虑的不够全面,最终结果不尽如人意,但如果你给他一份技术方案模板,要求他参照模板来思考技术方案,他可能会给到你一个高质量的方案。以下是一个拆解题目解析的示例:
示例希望 GPT 拆解和细化题目的解析,上图是优化前的结果,输出结果虽然也正确,但是讲解偏简单,推理步骤的呈现不是很清晰,难以让学生理解。
上图对指令做了优化,对解析提出了更具体的要求,可以看到 GPT 的返回比较理想,原理和公式的推导都非常清晰。
除此以外,给出指令时要善用符号,将信息尽可能结构化,来看以下示例:
该示例将 GPT 调教成了一个翻译工具,用户可以输入想翻译的中文,产品会返回对应的英文。在第一个问答中,需求被很好地满足了,但是第二个问答结果超出了我们的预期,应该把“张三希望把下面这句话翻译成日文,需求要延期了。”这句话翻译成英文,但实际上 GPT 错误地理解了语义,以为要把“需求要延期了。”翻译成日文。
解决方法是,给出的指令要善用符号,让 GPT 清晰地理解到哪一个句子是用户输入。
上图调整了对 GPT 的 prompt,用户输入文字会用三个倒引号来分隔,解决了这个问题。实际在工程实现过程中,可以直接对用户输入做转义,在代码中加入倒引号,而不是示例中让用户来填写倒引号。
完备
指令要保证逻辑完备,就好比写代码,要考虑每一个逻辑分支和异常处理,写了 if 要考虑 else,写了 switch case 要考虑 default。
上图示例让 GPT 分析了学生最薄弱的学科,希望根据结果返回给一句鼓励,但是回复的要求里没有给出数学的要求。
需要加上对应的逻辑分支,保证输出是可控的。
输出(output)
对输出提出要求,可以让 LLM 的返回更加可控,尤其是可以让 LLM 返回结构化的结果,从而使得上层应用程序可以解析和处理。比如,我们可以让 LLM 用 Json 作为输出格式:
上图示例,让 GPT 返回了张三最薄弱的学科和掌握度,用 json 格式,方便程序后续处理。
除此以外,在日常工作中,我们写一篇文档或是画一个图,也可以借助 GPT 来协助完成部分能力,输出成 markdown 格式或 mermaid 格式,粘贴到飞书文档中。
重要技巧和优化方向
In-Context Learning(ICL)
In-context learning 或 few-shot learning 是一种 prompt 技巧,通过在 prompt 中提供样例来提升模型返回结果。和 finetune 不同,它不需要对模型做微调,实际上模型没有因此更新任何参数,相比于训练,它只需要更小的样例数据,并且非专业人员也可以在 prompt 中使用。
解释一下 few-shot learning 的概念,如上图分类任务,训练集中没有兔子,但如果想输入兔子的图片,让模型返回 告诉我这是一只兔子,该怎么办?在这里,可以给模型提供一些样例,样例是兔子的图片以及分类结果,模型看完这些样例后,再问模型这张图片是什么动物,模型就能告诉你是兔子,实际上模型学习到的是物品与物品之间的相似度,所以只要给样例,它就有能力识别出来。shot 指代的就是样例,zero-shot 就是零样本,few-shot 就是少样本。
同样的,我们也可以给 LLM 提供样例来提升效果,比如刚才学情分析的例子:
Chain of Thought(CoT)
Chain of Thought Prompting for Large Language Model Reasoning
在 NLP 任务中,我们把 chain of thought 定义为一系列中间的自然语言推理的步骤来获取最终的结果,通过这种方式来提高 LLM 的复杂推理能力,它将原有的NLP任务从“输入 -> 的输出”变为“输入 -> 中间结果 -> 输出”的形式。以下是两个示例:
Few-Shot-CoT
上图给出的 few-shot 样例,不仅只给出答案,同时给出了求解的推导过程,LLM 也会按照这个逻辑推理的方式,去给出分步骤的推理过程,最终得出了正确的解。
Zero-Shot-CoT
上图是一个经典的咒语 ,在 prompt 里增加 "Let's think step by step",可以有非常大的效果提升。类似的经典咒语还有 "Let's think about this"。
在吴恩达的课程视频中,也提到了 “Give the model time to think",本质上就是 CoT,视频中要求 GPT 列出过程中的推理步骤,给出过程中的思考,来提升解题的结果。
近期大热的 Auto-GPT 也是利用了 CoT 的方式,让 GPT 思考并完成自主推理,从而可以执行更加复杂的任务。理论类似于 ReAct 这篇论文,它让 GPT 进一步使用外部工具获得了“手和脚”,比如搜索外部网络、文件读写操作、代码执行等等,从而可以结合更多外部信息来完成进行推理。
由此延伸的相关研究还有很多,比如 Self-Consistency,用 CoT 写几个推理过程的样例并进行推理,它要求 LLM 输出多个不同的推理过程和答案,然后采用投票的方式选出最佳答案。再比如 PoT,也是一个借助外部工具来提升效果的思路,它通过让 GPT 执行代码来达到更准确的数学解题。
Prompt 迭代
上图来自吴恩达课程视频,Prompt 本质是一个经验科学,它需要大量的迭代试错和实验,最终得到适合各领域各任务的 prompt,每个人的提示词可能都不一样,所以本文分享的是撰写提示词的框架和重要技巧,而非优秀的提示词让读者死记硬背。提示词的迭代类似于算法和策略的迭代,通常有以下几个步骤:
-
需求分析:首先有 Idea,根据 Idea 做好充分的调研和数据分析。
-
Prompt 实现:然后根据领域内知识,并结合 prompt 撰写框架和技巧,写出合适的提示词。
-
实验评估:再根据现有的数据,做实验评估,可以离线在领域内权威的数据集上做评测,也可以在线做试错。
-
复盘总结:得到实验结果,反复调试,迭代试错。
因此,你需要不断地优化 prompt ,通过实验来获取更优效果。围绕着 prompt 迭代,业内也有一些提示词的优化工具,比如 PromptHub 提供了各种任务场景的 Prompt 模板,可以通过填写表单参数来自动生成优秀的 Prompt,比如 PromptPerfect 提供了提示词的自动优化工具,输入提示词后可以给出优化建议和原因。
限制和风险
幻觉(hallucination)
目前模型还有不少缺陷,其中之一就是幻觉,这个在 GPT-4 以外的模型中特别明显,比方说:
这些都是模型胡编乱造的,每次你问它,它还可以给你不同的答案。目前减少幻觉的一种方式是,让模型先找相关资料,并基于这些资料给出答案,附上引用:
注入(injection)
- 注入(日常实例,输入结构化,对抗性提示www.promptingguide.ai/zh/risks/ad…
研发同学都知道 SQL 注入,跟这个类似,prompt 也能注入,先前演示的翻译例子,就属于一种注入:
如果 prompt 写的不好,很容易出现这样异常的 case。除此以外,系统 prompt 还可能产生泄漏,在 ChatGPT 和 Bing 这类应用问世时,就有人套过 prompt,这是一个 ChatGPT 的注入:
这是一个 Bing 的注入:
针对这类问题,业界有一个重要研究主题叫 Adversarial prompting,用于识别风险并设计技术来解决这些问题,感兴趣的读者可以自行研究相关论文。
示例讲解
下面以 Auto-GPT 为示例,来讲解本文框架和技巧是如何运用的。Auto-GPT 是一个能完成自主推理的工具,它利用外部工具来获取外部信息,比如搜索外部网络、文件读写操作、代码执行等等,并通过 CoT 的思想,不断询问自己下一步操作,再结合外部信息,来完成一个复杂任务的执行。
其工作流可以拆解为两个重要步骤,第一个步骤是分解任务,通过询问大模型来获得任务的拆解,第二个步骤是循环自问自答,通过不断询问自己下一步,来做任务的执行。如果不了解 Auto-GPT,可以参考官方 github,本文不讲解 Auto-GPT 原理,仅针对核心的 Prompt,来讲解技巧。
分解任务
来源:Auto-GPT介绍
{
"role": "system",
"content": """
Your task is to devise up to 5 highly effective goals and an appropriate role-based name (_GPT) for an autonomous agent, ensuring that the goals are optimally aligned with the successful completion of its assigned task.
The user will provide the task, you will provide only the output in the exact format specified below with no explanation or conversation.
Example input:
Help me with marketing my business
Example output:
Name: CMOGPT
Description: a professional digital marketer AI that assists Solopreneurs in growing their businesses by providing world-class expertise in solving marketing problems for SaaS, content products, agencies, and more.
Goals:
- Engage in effective problem-solving, prioritization, planning, and supporting execution to address your marketing needs as your virtual Chief Marketing Officer.
- Provide specific, actionable, and concise advice to help you make informed decisions without the use of platitudes or overly wordy explanations.
- Identify and prioritize quick wins and cost-effective campaigns that maximize results with minimal time and budget investment.
- Proactively take the lead in guiding you and offering suggestions when faced with unclear information or uncertainty to ensure your marketing strategy remains on track.
"""
},
{
"role": "user",
"content": f"Task: '{user_prompt}'\nRespond only with the output in the exact format specified in the system prompt, with no explanation or conversation.\n",
},
这段 Prompt 让 LLM 来分解用户的任务:
-
第 4 行是指令部分,第 6 行是输出部分,没有背景和角色部分,因为该指令的背景主要由用户提供,无需撰写,而任务分解不涉及到角色设定,所以也不需要角色部分。
-
Prompt 在指令的具体性和输出要求上做了一些工作,举例来说:
-
8~21 行给了样例,更明确了任务拆解的要求,以及输出的格式。
-
任务明确地要求拆解为 5 个目标,而不是要求拆解为多个目标,确切的数字可以得到更稳定的输出。
-
询问下一步
"system": """You are WeatherGPT_CN, 一个智能气象助手,为您提供准确、实时的天气信息,帮助您更好地规划活动和出行。
Your decisions must always be made independently without seeking user assistance. Play to your strengths as an LLM and pursue simple strategies with no legal complications.
GOALS:
1. 以中文为主要语言,为您提供清晰、简洁的天气预报。
2. 获取并分析来自权威数据源的实时天气信息,确保准确性和可靠性。
3. 针对您的具体需求,提供详细的天气信息,包括温度、湿度、风向、风速等。
4. 根据您的地理位置,为您提供最相关的天气预报。
5. 及时更新天气信息,确保您随时了解最新的天气状况。
Constraints:
1. ~4000 word limit for short term memory. Your short term memory is short, so immediately save important information to files.
2. If you are unsure how you previously did something or want to recall past events, thinking about similar events will help you remember.
3. No user assistance
4. Exclusively use the commands listed in double quotes e.g. "command name"
Commands:
1. analyze_code: Analyze Code, args: "code": "<full_code_string>"
2. execute_python_file: Execute Python File, args: "filename": "<filename>"
3. append_to_file: Append to file, args: "filename": "<filename>", "text": "<text>"
4. delete_file: Delete file, args: "filename": "<filename>"
5. list_files: List Files in Directory, args: "directory": "<directory>"
6. read_file: Read file, args: "filename": "<filename>"
7. write_to_file: Write to file, args: "filename": "<filename>", "text": "<text>"
8. google: Google Search, args: "query": "<query>"
9. improve_code: Get Improved Code, args: "suggestions": "<list_of_suggestions>", "code": "<full_code_string>"
10. send_tweet: Send Tweet, args: "tweet_text": "<tweet_text>"
11. browse_website: Browse Website, args: "url": "<url>", "question": "<what_you_want_to_find_on_website>"
12. write_tests: Write Tests, args: "code": "<full_code_string>", "focus": "<list_of_focus_areas>"
13. delete_agent: Delete GPT Agent, args: "key": "<key>"
14. get_hyperlinks: Get text summary, args: "url": "<url>"
15. get_text_summary: Get text summary, args: "url": "<url>", "question": "<question>"
16. list_agents: List GPT Agents, args: () -> str
17. message_agent: Message GPT Agent, args: "key": "<key>", "message": "<message>"
18. start_agent: Start GPT Agent, args: "name": "<name>", "task": "<short_task_desc>", "prompt": "<prompt>"
19. Task Complete (Shutdown): "task_complete", args: "reason": "<reason>"
Resources:
1. Internet access for searches and information gathering.
2. Long Term memory management.
3. GPT-3.5 powered Agents for delegation of simple tasks.
4. File output.
Performance Evaluation:
1. Continuously review and analyze your actions to ensure you are performing to the best of your abilities.
2. Constructively self-criticize your big-picture behavior constantly.
3. Reflect on past decisions and strategies to refine your approach.
4. Every command has a cost, so be smart and efficient. Aim to complete tasks in the least number of steps.
5. Write all code to a file.
You should only respond in JSON format as described below
Response Format:
{
"thoughts": {
"text": "thought",
"reasoning": "reasoning",
"plan": "- short bulleted\n- list that conveys\n- long-term plan",
"criticism": "constructive self-criticism",
"speak": "thoughts summary to say to user"
},
"command": {
"name": "command name",
"args": {
"arg name": "value"
}
}
}
Ensure the response can be parsed by Python json.loads
"""
这段 Prompt 填充了上下文和外部输入,并让 LLM 思考下一步动作:
-
第 1
2 行是角色设定,410 行、1316 行和 4651 行是指令,1744 是背景,5470 行是输出。 -
角色:角色设定来源于第一个步骤的任务拆解,根据用户的 prompt,询问 LLM 分解得到了 Name 和 Description,即为角色设定。
-
指令:4
6 行的指令也来源于第一个步骤的任务拆解,根据用户的 prompt,询问 LLM 分解得到了 Goals 任务目标。1315 行对记忆的使用和记录做了要求,16 行再次强调了不能借助用户的帮助。4651 给出了一些系统更具体的指令,4749 行是对 CoT 的运用,50~51 行是两项额外的具体指令,分别是成本考量和日志记录。 -
背景:背景给出了命令的详细定义,Auto-GPT 借助这些命令,来完成与外部世界的沟通。这些命令给出了函数名、入参和描述,描述用于让 LLM 理解命令的含义,而函数名和入参用于和框架通信从而执行命令。如果命令定义不精准,就无法得到预期执行结果。
-
输出:55~70 行定义了输出格式,方便 Auto-GPT 的程序解析。