提示#
提示指的是输入到LLM模型的文本。 当发送到LLM时,模型使用提示来自回归生成下一个标记,继续这个过程,直到达到指定的停止标准。 提示本身在所需任务的性能中起着至关重要的作用。 研究人员经常使用特殊标记 [1]来分隔提示的不同部分,例如系统消息、用户消息和助手消息。 理想情况下,开发人员应使用训练时特定于模型的特殊标记来格式化此提示。 然而,许多专有API并未披露其特殊标记,并要求用户以不同角色的消息形式发送它们。
设计#
AdalFlow 旨在最大化开发者对提示的控制。
因此,在大多数情况下,我们帮助开发者收集不同的部分并将它们组合成一个提示。
这个提示将作为一个单一的消息发送给LLM。
我们使用的消息的默认角色是system。
虽然它不是一个特殊的标记,但我们使用来表示提示中的系统消息,效果非常好。
simple_prompt = r"""<SYS> You are a helpful assistant. </SYS> User: What can you help me with?"""
如果是Llama3模型,最终发送给模型进行分词的文本将是:
final_prompt = r"""<|begin_of_text|><|start_header_id|>system<|end_header_id|>
{{simple_prompt}} <|eot_id|>"""
而LLM将返回以下文本:
prediction = r"""<|start_header_id|>assistant<|end_header_id|> You can ask me anything you want. <|eot_id|><|end_of_text|>"""
LLM应用程序中的数据流#
LLM应用程序中的数据流#
看看最复杂的情况:我们将有用户查询、检索到的上下文、任务描述、工具定义、少量示例、过去的对话历史、代理的步骤历史以及输出格式规范。 所有这些不同的部分都需要格式化为一个单一的提示。 我们必须灵活地完成所有这些工作,同时也要让开发者易于阅读。
为什么选择Jinja2?#
要格式化提示,您可以使用任何Python的原生字符串格式化方法。
1 # percent(%) formatting
2 print("<SYS>%s</SYS> User: %s" % (task_desc_str, input_str))
3
4 # format() method with kwargs
5 print(
6 "<SYS>{task_desc_str}</SYS> User: {input_str}".format(
7 task_desc_str=task_desc_str, input_str=input_str
8 )
9 )
10
11 # f-string
12 print(f"<SYS>{task_desc_str}</SYS> User: {input_str}")
13
14 # Templates
15 from string import Template
16
17 t = Template("<SYS>$task_desc_str</SYS> User: $input_str")
18 print(t.substitute(task_desc_str=task_desc_str, input_str=input_str))
我们选择了Jinja2 [1]作为提示的模板引擎。
除了使用{{}}作为关键字参数的占位符外,Jinja2还允许用户编写类似于Python语法的代码。
这包括条件语句、循环、过滤器,甚至是注释,这些都是Python原生字符串格式化所缺乏的。
以下是使用Jinja2格式化提示的一个示例:
def jinja2_template_example(**kwargs):
from jinja2 import Template
template = r"""<SYS>{{ task_desc_str }}</SYS>
{# tools #}
{% if tools %}
<TOOLS>
{% for tool in tools %}
{{loop.index}}. {{ tool }}
{% endfor %}
</TOOLS>
{% endif %}
User: {{ input_str }}"""
t = Template(template, trim_blocks=True, lstrip_blocks=True)
print(t.render(**kwargs))
让我们在有工具和没有工具的情况下调用它:
jinja2_template_example(task_desc_str=task_desc_str, input_str=input_str)
jinja2_template_example(
task_desc_str=task_desc_str, input_str=input_str, tools=tools
)
打印输出将是:
<SYS>You are a helpful assitant</SYS>
User: What is the capital of France?
以及使用工具:
<SYS>You are a helpful assitant</SYS>
<TOOLS>
1. google
2. wikipedia
3. wikidata
</TOOLS>
User: What is the capital of France?
我们可以看到使用Jinja2以编程方式格式化提示是多么简单和灵活。
提示类#
我们创建了我们的Prompt Component来使用字符串template和prompt_kwargs渲染提示。
这是一个简单的组件,但它非常方便。
让我们使用与上面相同的模板:
from adalflow.core.prompt_builder import Prompt
prompt = Prompt(
template=template,
prompt_kwargs={
"task_desc_str": task_desc_str,
"tools": tools,
},
)
print(prompt)
print(prompt(input_str=input_str)) # takes the rest arguments in keyword arguments
Prompt 类允许我们在初始化时预设一些提示参数,然后我们可以使用其余的参数调用提示。
此外,通过继承 Component,我们可以轻松地使用 print 可视化这个组件。
以下是输出:
Prompt(
template: <SYS>{{ task_desc_str }}</SYS>
{# tools #}
{% if tools %}
<TOOLS>
{% for tool in tools %}
{{loop.index}}. {{ tool }}
{% endfor %}
</TOOLS>
{% endif %}
User: {{ input_str }}, prompt_kwargs: {'task_desc_str': 'You are a helpful assitant', 'tools': ['google', 'wikipedia', 'wikidata']}, prompt_variables: ['input_str', 'tools', 'task_desc_str']
)
与所有组件一样,您可以使用to_dict和from_dict来序列化和反序列化组件。
默认提示模板#
默认情况下,如果未提供模板,Prompt 类将使用 DEFAULT_ADALFLOW_SYSTEM_PROMPT 作为其字符串模板。
此默认模板允许您有条件地传递从上述数据流图设计的七个重要变量。
这些变量是:
ADALFLOW_DEFAULT_PROMPT_ARGS = [
"task_desc_str", # task description
"output_format_str", # output format of the task
"tools_str", # tools used in the task
"examples_str", # examples of the task
"chat_history_str", # chat history of the user
"context_str", # context of the user query
"steps_str", # used in agent steps
"input_str", # user query or input
]
现在,让我们看看只有用户查询的最小情况:
prompt = Prompt()
output = prompt(input_str=input_str)
print(output)
输出将是最基本的,仅包含用户查询和助手回应的前缀:
<User>
What is the capital of France?
</User>
You:
注意
实际上,我们几乎不需要直接使用原始的Prompt类,因为它是由Generator组件和我们接下来要介绍的ModelClient一起协调的。
参考文献