主题
使用 RichPromptTemplate 与 Jinja 语法
借助 Jinja 语法,你可以构建带变量、逻辑、对象解析等能力的提示词模板。
来看几个例子:
from llama_index.core.prompts import RichPromptTemplate
template = RichPromptTemplate(
"""We have provided context information below.
---------------------
{{ context_str }}
---------------------
Given this information, please answer the question: {{ query_str }}
"""
)
# format as a string
prompt_str = template.format(context_str=..., query_str=...)
# format as a list of chat messages
messages = template.format_messages(context_str=..., query_str=...)Jinja 提示词与 f-string 的主要区别在于:变量现在用双括号 ,而不是单括号 { }。
再看一个更复杂的例子,它用循环生成一个多模态提示词。
from llama_index.core.prompts import RichPromptTemplate
template = RichPromptTemplate(
"""
{% chat role="system" %}
Given a list if images and text from each image, please answer the question to the best of your ability.
{% endchat %}
{% chat role="user" %}
{% for image_path, text in images_and_texts %}
Here is some text: {{ text }}
And here is an image:
{{ image_path | image }}
{% endfor %}
{% endchat %}
"""
)
messages = template.format_messages(
images_and_texts=[
("page_1.png", "This is the first page of the document"),
("page_2.png", "This is the second page of the document"),
]
)这个例子里你能看到好几个特性:
{% chat %}块用于把消息格式化成对话消息并设置角色{% for %}循环用于遍历传入的images_and_texts列表0语法用于把图片路径格式化成图片内容块。这里用|给变量施加一个“过滤器”,帮助它被识别为图片。
再看一个例子,这次是用检索器返回的节点来创建模板:
from llama_index.core.prompts import RichPromptTemplate
template = RichPromptTemplate(
"""
{% chat role="system" %}
You are a helpful assistant that can answer questions about the context provided.
{% endchat %}
{% chat role="user" %}
{% for node in nodes %}
{{ node.text }}
{% endfor %}
{% endchat %}
"""
)
nodes = retriever.retrieve("What is the capital of the moon?")
messages = template.format_messages(nodes=nodes)使用 f-string 提示词模板
截至本文撰写时,许多较老的组件与示例仍在使用 f-string 提示词。
定义自定义提示词,只需写一个格式化字符串即可
from llama_index.core import PromptTemplate
template = (
"We have provided context information below. \n"
"---------------------\n"
"{context_str}"
"\n---------------------\n"
"Given this information, please answer the question: {query_str}\n"
)
qa_template = PromptTemplate(template)
# you can create text prompt (for completion API)
prompt = qa_template.format(context_str=..., query_str=...)
# or easily convert to message prompts (for chat API)
messages = qa_template.format_messages(context_str=..., query_str=...)你也可以从对话消息出发定义模板
from llama_index.core import ChatPromptTemplate
from llama_index.core.llms import ChatMessage, MessageRole
message_templates = [
ChatMessage(content="You are an expert system.", role=MessageRole.SYSTEM),
ChatMessage(
content="Generate a short story about {topic}",
role=MessageRole.USER,
),
]
chat_template = ChatPromptTemplate(message_templates=message_templates)
# you can create message prompts (for chat API)
messages = chat_template.format_messages(topic=...)
# or easily convert to text prompt (for completion API)
prompt = chat_template.format(topic=...)获取与设置自定义提示词
由于 LlamaIndex 是多步流水线,重要的是先确定你想改的是哪个环节,并把自定义提示词传到正确的位置。
比如提示词会用在响应合成器、检索器、索引构建等处;其中一些模块嵌在其他模块之内(synthesizer 就嵌在查询引擎里)。
访问/定制提示词的完整细节见这份指南。
常用提示词
最常用的提示词是 text_qa_template 与 refine_template。
text_qa_template—— 基于检索到的节点,为查询生成初始回答refine_template—— 当检索到的文本在response_mode="compact"(默认)下装不进一次 LLM 调用时使用;或者在response_mode="refine"下检索到多个节点时使用。第一次查询得到的回答会作为existing_answer填入,LLM 需要基于新上下文更新或复述这个已有回答。
访问提示词
你可以对 LlamaIndex 中许多模块调用 get_prompts,拿到该模块及其嵌套子模块所用提示词的扁平列表。
比如看看下面这段代码。
query_engine = index.as_query_engine(response_mode="compact")
prompts_dict = query_engine.get_prompts()
print(list(prompts_dict.keys()))你可能会拿到下面这些 key:
['response_synthesizer:text_qa_template', 'response_synthesizer:refine_template']注意提示词会以其子模块作为“命名空间”前缀。
更新提示词
凡是实现了 get_prompts 的模块,都可以用 update_prompts 函数来自定义提示词。只需传入参数值,其 key 与通过 get_prompts 得到的提示词字典中的 key 一致即可。
例如针对上面的例子,我们可以这样做
# shakespeare!
qa_prompt_tmpl_str = (
"Context information is below.\n"
"---------------------\n"
"{{ context_str }}\n"
"---------------------\n"
"Given the context information and not prior knowledge, "
"answer the query in the style of a Shakespeare play.\n"
"Query: {{ query_str }}\n"
"Answer: "
)
qa_prompt_tmpl = RichPromptTemplate(qa_prompt_tmpl_str)
query_engine.update_prompts(
{"response_synthesizer:text_qa_template": qa_prompt_tmpl}
)修改查询引擎所用的提示词
对于查询引擎,你还可以在查询时(也就是对索引执行查询并合成最终回答时)直接传入自定义提示词。
覆盖提示词也有两种等价方式:
- 通过高层 API
query_engine = index.as_query_engine(
text_qa_template=custom_qa_prompt, refine_template=custom_refine_prompt
)- 通过低层组合 API
retriever = index.as_retriever()
synth = get_response_synthesizer(
text_qa_template=custom_qa_prompt, refine_template=custom_refine_prompt
)
query_engine = RetrieverQueryEngine(retriever, response_synthesizer)以上两种做法完全等价,其中第 1 种本质上是第 2 种的语法糖,把底层复杂性藏了起来。想快速改几个常用参数时用第 1 种,需要更细粒度控制时用第 2 种。
关于哪些类使用哪些提示词的更多细节,请查看查询类参考。
完整提示词清单及其方法/参数见参考文档。
[Advanced] 进阶提示词能力
本节展示 LlamaIndex 中一些进阶的提示词能力。
相关指南:
函数映射
把函数作为模板变量传入,而不是固定值。
这一点相当进阶也很强大,可以用来做动态 few-shot 提示等。
这里举个例子,重新格式化 context_str。
from llama_index.core.prompts import RichPromptTemplate
def format_context_fn(**kwargs):
# format context with bullet points
context_list = kwargs["context_str"].split("\n\n")
fmtted_context = "\n\n".join([f"- {c}" for c in context_list])
return fmtted_context
prompt_tmpl = RichPromptTemplate(
"{{ context_str }}", function_mappings={"context_str": format_context_fn}
)
prompt_str = prompt_tmpl.format(context_str="context", query_str="query")部分格式化
对提示词做部分格式化:先填上一些变量,另一些留待之后填入。
from llama_index.core.prompts import RichPromptTemplate
template = RichPromptTemplate(
"""
{{ foo }} {{ bar }}
"""
)
partial_prompt_tmpl = template.partial_format(foo="abc")
fmt_str = partial_prompt_tmpl.format(bar="def")模板变量映射
LlamaIndex 的提示词抽象通常期望某些特定的 key。比如我们的 text_qa_prompt 期望用 context_str 承载上下文、用 query_str 承载用户查询。
但如果你想把某个现成的字符串模板接到 LlamaIndex 上,逐个改掉模板变量就很烦人。
这时只需定义 template_var_mappings:
from llama_index.core.prompts import RichPromptTemplate
template_var_mappings = {"context_str": "my_context", "query_str": "my_query"}
prompt_tmpl = RichPromptTemplate(
"Here is some context: {{ context_str }} and here is a query: {{ query_str }}",
template_var_mappings=template_var_mappings,
)
prompt_str = prompt_tmpl.format(my_context="context", my_query="query")