如何做好 Prompt
下面这个例子是一个很典型的提示工程:
from langchain.output_parsers import StructuredOutputParser, ResponseSchema from langchain_core.prompts import PromptTemplate from langchain_openai import ChatOpenAI llm = ChatOpenAI( model_name="gpt-3.5-turbo", temperature=0.5, max_tokens=200 ) response_schemas = [ ResponseSchema(name="content", description="The original content"), ResponseSchema(name="summary", description="The summary of the content"), ] output_parser = StructuredOutputParser.from_response_schemas(response_schemas) format_instructions = output_parser.get_format_instructions() summarizing_prompt_template = """ {format_instructions} 总结以下文本为一个 20 字以内的句子: --- {content} """ prompt = PromptTemplate.from_template( summarizing_prompt_template, partial_variables={'format_instructions': format_instructions} ) summarizing_chain = prompt | llm | output_parser print(summarizing_chain.invoke({"content": "这是一个测试。"}))
format_instructions
会生成关于如何输出的指令,这样 LLM 就知道如何输出了。
吴恩达给出的两大原则
- 写出清晰而具体的指示(也就是类似上面的
format_instructions
) - 给模型思考的时间
OpenAI 官方给出的 6 大策略
- 写清晰的指示
- 给模型提供参考(也就是示例)
- 将复杂任务拆分成子任务
- 给 GPT 时间思考
- 使用外部工具
- 反复迭代问题
提示的结构
在这个提示框架中:
- 指令(Instruction)告诉模型这个任务大概要做什么、怎么做,比如如何使用提供的外部信息、如何处理查询以及如何构造输出。这通常是一个提示模板中比较固定的部分。一个常见用例是告诉模型 “你是一个有用的 xx 助手”,这会让他更认真地对待自己的角色。
- 上下文(Context)则充当模型的额外知识来源。这些信息可以手动插入到提示中,通过向量数据库检索得来,或通过其他方式(如调用 API、计算器等工具)拉入。一个常见的用例是把从向量数据库查询到的知识作为上下文传递给模型。
- 提示输入(Prompt Input)通常就是具体的问题或者需要大模型做的具体事情,这个部分和 “指令” 部分其实也可以合二为一。但是拆分出来成为一个独立的组件,就更加结构化,便于复用模板。这通常是作为变量,在调用模型之前传递给提示模板,以形成具体的提示。
- 输出指示器(Output Indicator)标记要生成的文本的开始。这就像我们小时候的数学考卷,先写一个 “解”,就代表你要开始答题了。如果生成 Python 代码,可以使用 “import” 向模型表明它必须开始编写 Python 代码(因为大多数 Python 脚本以 import 开头)。这部分在我们和 ChatGPT 对话时往往是可有可无的,当然 langchain 中的代理在构建提示模板时,经常性的会用一个 “Thought:” (思考)作为提示词,指示模型开始输出自己的推理(Reasoning)。
langchain 提示模板类型
- PromptTemplate 这是最常用的 String 提示模板
- ChatPromptTemplate 常用的 Chat 提示模板,用于组合各种角色的消息模板,传入聊天模型
- ChatMessagePromptTemplate
- HumanMessagePromptTemplate
- AIMessagePromptTemplate
- SystemMessagePromptTemplate
- FewShotPromptTemplate 少样本提示模板,通过示例的展示来 “教” 模型如何回答
- PipelinePrompt 用于把几个提示组合在一起使用
- 自定义模板:langchain 还允许你基于其他模板来定制自己的提示模板
PromptTemplate
from langchain import PromptTemplate template = """你是业务咨询顾问。你给一个销售{product}的电商公司,起一个好的名字。""" prompt = PromptTemplate.from_template(template) print(prompt.format(product="手机"))
这个程序的主要功能是生成适用于不同场景的提示,对用户的一种产品或服务提供公司命名建议。
另外一种创建方式:
from langchain import PromptTemplate prompt = PromptTemplate( template="""你是业务咨询顾问。你给一个销售{product}的电商公司,起一个好的名字。""", input_variables=['product'] ) print(prompt.format(product="电脑"))
ChatPromptTemplate
下面代码展示了 OpenAI 的 Chat Model 中的各种消息角色:
from openai import OpenAI client = OpenAI() client.chat.completions.create( model='gpt-3.5-turbo', messages=[ {'role': 'system', 'content': 'You are a helpful assistant.'}, {'role': 'user', 'content': 'What is the capital of France?'}, {'role': 'assistant', 'content': 'The capital of France is Paris.'} ] )
OpenAI 对传输到 gpt-3.5-turbo 和 GPT-4 的 message 格式说明如下:
消息必须是消息对象的数组,其中每个对象都有一个角色(系统、用户、助理)和内容。对话可以短至一条消息,也可以来回多次。
通常,对话首先由系统消息格式化,然后是交替的用户消息和助理消息。
系统消息有助于设置助手的行为。例如,你可以修改助手的个性或提供有关其在整个对话过程中应如何表现的具体说明。
但请注意,系统消息是可选的,并且没有系统消息的模型的行为可能类似于使用通用消息,例如 “你是一个有用的助手”。
用户消息提供助理响应的请求或评论。
助理消息存储以前的助理响应,但也可以由你编写以给出所需行为的示例。
下面是使用 ChatPromptTemplate 的示例:
from langchain.prompts import ( ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate ) template = "你是一位专业顾问,负责为专注于{product}的公司起名" system_message_prompt = SystemMessagePromptTemplate.from_template(template) human_template = "公司主打产品是{product_detail}" human_message_prompt = HumanMessagePromptTemplate.from_template(human_template) prompt_template = ChatPromptTemplate.from_messages([ system_message_prompt, human_message_prompt ]) prompt = prompt_template.format_prompt(product="鲜花装饰", product_detail="创新的鲜花设计").to_messages() from langchain.chat_models import ChatOpenAI chat = ChatOpenAI() result = chat(prompt) print(result)
FewShot
Few-Shot(少样本)、One-Shot(单样本) 和与之对应的 Zero-Shot(零样本)的概念都起源于机器学习。如何让机器学习模型在极少量甚至没有示例的情况下学习到新的概念或类别,
对于许多现实世界的问题都是非常有价值的,因为我们往往无法获取到大量的标签化数据。
提示工程中的 FewShot
在提示工程中:
- 在 Few-Shot 学习设置中,模型会被给予几个示例,以帮助模型理解任务,并生成正确的响应。
- 在 Zero-Shot 学习设置中,模型只根据任务的描述生成响应,不需要任何示例。
使用 FewShotPromptTemplate
下面这个例子中,我们在样本末尾加上了 flower_type
和 occasion
,这样模型就可以根据这两个变量以及前面的样本生成广告文案。
samples = [ { "flower_type": "玫瑰", "occasion": "爱情", "ad_copy": "玫瑰,浪漫的象征,是你向心爱的人表达爱意的最佳选择。" }, { "flower_type": "康乃馨", "occasion": "母亲节", "ad_copy": "康乃馨代表着母爱的纯洁与伟大,是母亲节赠送给母亲的完美礼物。" }, { "flower_type": "百合", "occasion": "庆祝", "ad_copy": "百合象征着纯洁与高雅,是你庆祝特殊时刻的理想选择。" }, { "flower_type": "向日葵", "occasion": "鼓励", "ad_copy": "向日葵代表着阳光与希望,是你鼓励朋友或家人的最佳礼物。" } ] from langchain.prompts.prompt import PromptTemplate template = "鲜花类型:{flower_type}\n适用场合:{occasion}\n广告文案:{ad_copy}" prompt_sample = PromptTemplate( input_variables=["flower_type", "occasion", "ad_copy"], template=template ) print(prompt_sample.format_prompt(**samples[0])) from langchain.prompts.few_shot import FewShotPromptTemplate prompt = FewShotPromptTemplate( examples=samples, example_prompt=prompt_sample, suffix="鲜花类型:{flower_type}\n适用场合:{occasion}", input_variables=["flower_type", "occasion"] ) print(prompt.format(flower_type="野玫瑰", occasion="爱情")) from langchain.chat_models import ChatOpenAI llm = ChatOpenAI( model='gpt-3.5-turbo', temperature=0.5, max_tokens=200 ) res = llm.invoke(prompt.format(flower_type="野玫瑰", occasion="爱情")) print(res)
选择最相似的样本
可以使用向量搜索来选择最相似的样本,这样模型就可以根据这个样本生成广告文案。
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector from langchain.vectorstores import Chroma from langchain.embeddings import OpenAIEmbeddings example_selector = SemanticSimilarityExampleSelector.from_examples( samples, OpenAIEmbeddings(), Chroma, k=1 ) prompt = FewShotPromptTemplate( example_selector=example_selector, example_prompt=prompt_sample, suffix="鲜花类型:{flower_type}\n适用场合:{occasion}", input_variables=["flower_type", "occasion"] ) print(prompt.format(flower_type="野玫瑰", occasion="爱情"))
总结
FewShot 其实就是给模型一些示例做参考,模型才能明白你要什么。
提供示例对于解决某些任务至关重要,通常情况下,FewShot 的方式能够显著提高模型回答的质量。
不过,当少样本提示的效果不佳时,这可能表示模型在任务上的学习不足。
在这种情况下,我们建议对模型进行微调或尝试更高级的提示技术,或者换一个模型。