多功能提示词模板
文章目录
- 多功能提示词模板
- 模型/O功能之输出解析器
- 输出解析器的功能
多功能提示词模板
LangChain提供了极其灵活的提示词模板方法和组合提示词的方式,能满足各种开发需求。在所有的方法中,基础模板和少样本提示词模板是最基础的,其他所有的方法都在此基础上进行扩展。LangChain提供了一套默认的提示词模板,可以生成适用于各种任务的提示词,但是可能会出现默认提示词模板无法满足需求的情况。例如,你可能需要创建一个带有特定动态指令的提示词模板。在这种情况下,LangChain提供了很多不同功能的提示词模板,支持创建复杂结构的提示词模板。多功能提示词模板包括Partial提示词模板、PipelinePrompt组合模板、序列化模板、组合特征库和验证模板。
- Partial提示词模板功能有时你可能会面临一个复杂的配置或构建过程,其中某些参数在早期已知,而其他参数在后续步骤中才会知道。使用Partial提示词模板可以帮助你逐步构建最终的提示词模板,Partial会先传递当前的时间戳,最后剩余的是用户的输入填充。Partial提示词模板适用于已经创建了提示词模板对象,但是还没有明确的用户输人变量的场景。LangChain以两种方式支持Partial提示词模板:实例化对象的时候指定属性值(partial_variables=-{“foo”:“foo”}));或者得到一个实例化对象后调用partial方法。
prompt =PromptTemplate(template="(foo)(bar)",input variables=["foo","bar"])
partial_prompt prompt.partial(foo="foo");
print (partial_prompt.format (bar="baz"))
这里使用Partial提前传递了变量foo的值,模拟用户输入变量bar的值,最终的提示词如下:foobaz。可以通过PipelinePrompt组合模板来组合多个不同的提示词,这在希望重用部分提示词时非常有用:
full template =""
{introduction}
{example}
{start}
"""
full_prompt =PromptTemplate.from_template(full template)
input_prompts=[("introduction",introduction prompt),
("example",example_prompt)
("start",start_prompt)
]
pipeline_prompt=PipelinePromptTemplate(final_prompt=full_prompt,pipeline_prompts=input_prompts)
- PipelinePrompt组合模板功能,PipelinePromptTemplate实例化的时候,将pipeline prompts属性设置成了一个包含3个模板对象的列表,并且设置了final prompt属性的模板字符串。将这3个模板对象与模板字符串整合为一个完整的提示词对象。
- 序列化模板功能,LangChain支持加载JSON和YAML格式的提示词模板,用于序列化和反序列化提示词信息。你可以将应用程序的提示词模板保存到JSON或YAML文件中(序列化),或从这些文件中加载提示词模板(反序列化)。序列化模板功能可以让开发者对提示词模板进行共享、存储和版本控制。
{
type":"few shot",
"input variables":["adjective"],
"prefix":"Write antonyms for the following words.",
"example_prompt":{
"_type":"prompt",
"input_variables":["input","output"],
"template":"Input:(input)\nOutput:(output)"
},
"examples":"examples.json",
"suffix":"Input:(adjective}\n
Output:}
例如你有一个JSON文件,里面定义了实例化提示词模板类的参数:
prompt= load_prompt ("few_shot_prompt.json")
print (prompt.format (adjective="funny"))
使用load prompt方法可以很便利地利用外部文件,构造自己的少样本提示词模板,如下:
Write antonyms for the following words.
Input:happy
Output:sad
Input:tall
Output:short
Input:funny
Output:
-
组合特征库功能。为了个性化大语言模型应用,你可能需要将模型应用与特定用户的最新信息进行组合。特征库可以很好地保持这些数据的新鲜度,而LangChain提供了一种方便的方式,可以将这些数据与大语言模型应用进行组合,做法是从提示词模板内部调用特征库,检索值,然后将这些值格式化为提示词。
-
验证模板功能最后,PromptTemplate类会验证模板字符串,检查input variables是否与模板中定义的变量匹配。可以通过将validate_template设为False来禁用这种方式。这意味着,如果你确信模板字符串和输人变量是正确匹配的,你可以选择关闭这个验证功能,以节省一些额外的计算时间。PromptTemplate类默认使用Python f-string作为模板格式,也支持其他模板格式,如jinja2,可以通过template format参数来指定。这意味着,除了Python的f-string格式,你还可以选择使用像jinja2这样的更强大、更灵活的模板引擎,以适应更复杂的模板格式需求。
模型/O功能之输出解析器
在使用GPT-4或类似的大语言模型时,一个常见的挑战是如何将模型生成的输出格式转化为可以在代码中直接使用的格式。对于这个问题,通常使用LangChain的输出解析器(OutputParsers)工具来解决。
虽然大语言模型输出的文本信息可能非常有用,但应用与真实的软件数据世界连接的时候,希望得到的不仅仅是文本,而是更加结构化的数据。为了在应用程序中展示这些结构化的信息,需要将输出转换为某种常见的数据格式。可以编写一个函数来提取输出,但这并不理想。比如在模型指导提示词中加上“请输出JSON格式的答案”,模型会返回字符串形式的JSON,还需要通过函数将其转化为JSON对象。但是在实践中常常会遇到异常问题,例如返回的字符串JSON无法被正确解析。处理生产环境中的数据时,可能会遇到千奇百怪的输人,导致模型的响应无法解析,因此需要增加额外的补丁来进行异常处理。这使得整个处理流程变得更为复杂。
另外,大语言模型目前确实存在一些问题,例如机器幻觉,这是指模型在理解或生成文本时会产生错误或误解。另一个问题是为了显得自己“聪明”而加人不必要的、冗长华丽的语句,这可能会导致模型输出过度详细,显得“话痨”。这时你可以在提示词的结尾加上“你的答案是:”,模型就不会“话痨”了。
在真实的开发环境中,开发者不仅希望获取模型的输出结果,还希望能够对输出结果进行后续处理,比如解析模型的输出数据。
这就是为什么在大语言模型的开发中,结构化数据,如数组或JSON对象,显得尤为重要。结构化数据在软件开发中起着至关重要的作用,它提高了数据处理的效率,简化了数据的存储和检索,支持数据分析,并且有助于提高数据质量。
结构化数据可以帮助开发者更好地理解和处理模型的输出结果,比如通过解析输出的JSON对象,可以得到模型的预测结果,而不仅仅是一个长文本字符串。也可以根据需要对这些结果进行进一步的处理,例如提取关键信息、进行数据分析等,这样不仅可以得到模型的“直接回答”,还可以根据自己的需求进行定制化的后续处理,比如传递给下一个任务函数,从而更好地利用大语言模型。
输出解析器的功能
输出解析器具有两大功能:添加提示词模板的输出指令和解析输出格式。看到这里你也许会感到很奇怪,解析输出格式很好理解,但是输出解析器跟提示词模板有什么关系呢?确实,从名字上看,输出解析器(OutputParser)似乎与提示词模板没有关系,因为它听起来更像用于处理和解析输出的工具。然而实际上,输出解析器是通过改变提示词模板,即增加输出指令,来指导模型按照特定格式输出内容的。换句话说,原本的提示词模板中不包含输出指令,如果你想得到某种特定格式的输出结果,就得使用输出解析器。这样做的目的是分离提示词模板的输人和输出,输出解析器会把增加“输出指令”这件事做好。如果不要求模型按照特定的格式输出结果,则保持原提示词模板即可。
举例来说,下面这个输出指令要求模型输出一系列用逗号分隔的值(CSV),即模型的答案中应该含有多个值,这些值之间用逗号分隔。"Your response should be a list of comma separated values,"eg:'foo,bar,baz'"
大语言模型接收到这条指令并且进行意图识别后,响应的结果是使用逗号分隔的值(CSV)。你可以直接将这个指令写人提示词模板,也可以构造好提示词模板后使用输出解析器的预设指令。两者的效果是等价的,区别在于亲自写还是使用预设指令,以及一起写还是分开写。
这些区别决定了LangChain输出解析器的意义。输出解析器的便利性体现在,你想要某种输出格式时不需要手动写人输出指令,而是导人预设的输出解析器即可。除了预设大量的输出指令,输出解析器的parse方法还支持将模型的输出解析为对应的数据格式。总的来说,输出解析器已经写好了输出指令(注入提示词模板的字符串),也写好了输出数据的格式处理函数,开发者不需要“重复造轮子”。
LangChain提供了一系列预设的输出解析器,这些输出解析器能够针对不同的数据类型给出合适的输出指令,并将输出解析为不同的数据格式。这些输出解析器包括:
- BooleanOutputParser:用于解析布尔值类型的输出。
- CommaSeparatedListOutputParser:用于解析以逗号分隔的列表类型的输出。
- DatetimeOutputParser:用于解析日期时间类型的输出。
- EnumOutputParser:用于解析枚举类型的输出。
- ListOutputParser::用于解析列表类型的输出。
- PydanticOutputParser:用于解析符合Pydantic大语言模型需求的输出。
- StructuredOutputParser:用于解析具有特定结构的输出。
还是拿刚才的以逗号分隔的列表类型的输出指令举例,我们来看看LangChain是如何编写输出指令的。CommaSeparatedListOutputParser类的源码如下:
class CommaSeparatedListOutputParser(ListOutputParser):
"""Parse out comma separated lists."""
def get_format instructions(self)->str:
return( "Your response should be a list of comma separated values",
"eg:foo,bar,baz'")
def parse(self,text:str)->List[str]:
"""Parse the output of an LLM call.""
return text.strip().split (",")
从以上代码中可以很直观地看到预设的输出指令:
"Your response should be a list of comma separated values",
"eg:'foo,bar,baz'"
实例化CommaSeparatedListOutputParser类之后,调用get format_instructions()方法返回上述字符串。其实这个字符串就是前面示例中用逗号分隔的输出指令。同CommaSeparatedListOutputParse输出解析器一样,其他几种输出解析器也按照不同的数据类型预设了相应的输出指令,pase方法内处理了不同类型的数据,这些都是LangChain造好的“轮子”。