LlamaIndex 基础 03-prompt

这段代码展示了如何使用 LlamaIndex 库构建并更新 Prompt 模板。具体来说:

  1. 定义 Prompt 模板:通过 PromptTemplateChatPromptTemplate 创建用于文本生成的提示词。
  2. 使用 Prompt 模板
    • 通过 qa_template.format()chat_template.format(topic=topic) 插入上下文信息并生成相应的提示词字符串。
    • 使用 qa_template.format_messages()chat_template.format_messages(topic=topic) 生成 ChatMessage 对象,模拟聊天场景。
  3. 构建索引与查询引擎:通过 VectorStoreIndex 从文档中构建向量存储索引,并设置 LLM 模型和嵌入模型。
  4. 更新查询引擎的提示词:将之前定义的文本问答模板应用到查询引擎上。
    整个过程展示了如何在 LlamaIndex 中灵活地使用不同类型的 Prompt 模板来满足特定的应用场景需求。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from llama_index.core import PromptTemplate
template = (
"我提供以下上下文信息\n"
"-------------------\n"
"{context_str}\n"
"-------------------\n"
"给定这个信息,请回答问题:{query_str}\n"
)
qa_template = PromptTemplate(template=template)
# 文本生成场景
context_str = "在阳光明媚的春日午后,小明和小华相约在公园的樱花树下野餐。铺开格子布,摆上三明治和果汁,两人边吃边聊,笑声不断。樱花随风飘落,像粉色的雪花,落在他们的头上和食物上,增添了几分浪漫。小明突然提议玩一个猜谜游戏,小华兴奋地接受了挑战,两人的友谊在这场智力角逐中更加深厚。"
query_str = "谁提出玩游戏的?"
prompt = qa_template.format(context_str=context_str, query_str=query_str)
print(prompt)
1
2
3
4
5
我提供以下上下文信息
-------------------
在阳光明媚的春日午后,小明和小华相约在公园的樱花树下野餐。铺开格子布,摆上三明治和果汁,两人边吃边聊,笑声不断。樱花随风飘落,像粉色的雪花,落在他们的头上和食物上,增添了几分浪漫。小明突然提议玩一个猜谜游戏,小华兴奋地接受了挑战,两人的友谊在这场智力角逐中更加深厚。
-------------------
给定这个信息,请回答问题:谁提出玩游戏的?
1
2
message=qa_template.format_messages(context_str=context_str, query_str=query_str)
print(message)
1
[ChatMessage (role=<MessageRole.USER: 'user'>, additional_kwargs={}, blocks=[TextBlock (block_type='text', text='我提供以下上下文信息\n-------------------\n 在阳光明媚的春日午后,小明和小华相约在公园的樱花树下野餐。铺开格子布,摆上三明治和果汁,两人边吃边聊,笑声不断。樱花随风飘落,像粉色的雪花,落在他们的头上和食物上,增添了几分浪漫。小明突然提议玩一个猜谜游戏,小华兴奋地接受了挑战,两人的友谊在这场智力角逐中更加深厚。\n-------------------\n 给定这个信息,请回答问题:谁提出玩游戏的?\n')])]

除了使用 PromptTemplate 外,类似的方法还可以创建 ChatPromptTemplate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from llama_index.core import ChatPromptTemplate
from llama_index.core.llms import ChatMessage,MessageRole
message_template=[
ChatMessage(content="你是一个说书人",role=MessageRole.SYSTEM),
ChatMessage(content="给我讲讲{topic}?",role=MessageRole.USER)
]
chat_template=ChatPromptTemplate(message_templates=message_template)
topic="三国演义的空城计"
# 用于文本生成
prompt=chat_template.format(topic=topic)
print(prompt)
print('----------')
# 用于聊天
message=chat_template.format_messages(topic=topic)
print(message)
1
2
3
4
5
system: 你是一个说书人
User: 给我讲讲三国演义的空城计?
Assistant:
----------
[ChatMessage (role=<MessageRole.SYSTEM: 'system'>, additional_kwargs={}, blocks=[TextBlock (block_type='text', text='你是一个说书人')]), ChatMessage (role=<MessageRole.USER: 'user'>, additional_kwargs={}, blocks=[TextBlock (block_type='text', text='给我讲讲三国演义的空城计?')])]

通过以上两种方式定义 prompt,以下将说明如何将这些 prompt 用起来

1
2
3
4
5
6
7
8
9
10
11
12
# 构建索引及引擎 
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
from llama_index.llms.ollama import Ollama
from llama_index.embeddings.ollama import OllamaEmbedding
documents = SimpleDirectoryReader("../../data/三国演义白话文/",recursive=True).load_data(show_progress=True)
# bge-base embedding model
base_url='http://localhost:11434'
Settings.embed_model = OllamaEmbedding(model_name="quentinz/bge-large-zh-v1.5:latest",base_url=base_url)
# ollama
Settings.llm = Ollama(model="qwen2.5:latest", request_timeout=360.0,base_url=base_url)
index = VectorStoreIndex.from_documents(documents,show_progress=True)
query_engine = index.as_query_engine(response_mode='compact')
1
2
3
Loading files: 100%|██████████| 41/41 [00:00<00:00, 394.94 file/s]
Parsing nodes: 100%|██████████| 41/41 [00:00<00:00, 459.78 it/s]
Generating embeddings: 100%|██████████| 127/127 [00:06<00:00, 18.39 it/s]
1
2
3
# 获取引擎的当前prompt
cur_prompt=query_engine.get_prompts()
print(cur_prompt)
1
{'response_synthesizer: text_qa_template': SelectorPromptTemplate (metadata={'prompt_type': <PromptType.QUESTION_ANSWER: 'text_qa'>}, template_vars=['context_str', 'query_str'], kwargs={}, output_parser=None, template_var_mappings={}, function_mappings={}, default_template=PromptTemplate (metadata={'prompt_type': <PromptType.QUESTION_ANSWER: 'text_qa'>}, template_vars=['context_str', 'query_str'], kwargs={}, output_parser=None, template_var_mappings=None, function_mappings=None, template='Context information is below.\n---------------------\n{context_str}\n---------------------\nGiven the context information and not prior knowledge, answer the query.\nQuery: {query_str}\nAnswer: '), conditionals=[(<function is_chat_model at 0x000002C450CB8160>, ChatPromptTemplate (metadata={'prompt_type': <PromptType.CUSTOM: 'custom'>}, template_vars=['context_str', 'query_str'], kwargs={}, output_parser=None, template_var_mappings=None, function_mappings=None, message_templates=[ChatMessage (role=<MessageRole.SYSTEM: 'system'>, additional_kwargs={}, blocks=[TextBlock (block_type='text', text="You are an expert Q&A system that is trusted around the world.\nAlways answer the query using the provided context information, and not prior knowledge.\nSome rules to follow:\n 1. Never directly reference the given context in your answer.\n 2. Avoid statements like 'Based on the context, ...' or 'The context information ...' or anything along those lines.")]), ChatMessage (role=<MessageRole.USER: 'user'>, additional_kwargs={}, blocks=[TextBlock (block_type='text', text='Context information is below.\n---------------------\n{context_str}\n---------------------\nGiven the context information and not prior knowledge, answer the query.\nQuery: {query_str}\nAnswer: ')])]))]), 'response_synthesizer: refine_template': SelectorPromptTemplate (metadata={'prompt_type': <PromptType.REFINE: 'refine'>}, template_vars=['query_str', 'existing_answer', 'context_msg'], kwargs={}, output_parser=None, template_var_mappings={}, function_mappings={}, default_template=PromptTemplate (metadata={'prompt_type': <PromptType.REFINE: 'refine'>}, template_vars=['query_str', 'existing_answer', 'context_msg'], kwargs={}, output_parser=None, template_var_mappings=None, function_mappings=None, template="The original query is as follows: {query_str}\nWe have provided an existing answer: {existing_answer}\nWe have the opportunity to refine the existing answer (only if needed) with some more context below.\n------------\n{context_msg}\n------------\nGiven the new context, refine the original answer to better answer the query. If the context isn't useful, return the original answer.\nRefined Answer: "), conditionals=[(<function is_chat_model at 0x000002C450CB8160>, ChatPromptTemplate (metadata={'prompt_type': <PromptType.CUSTOM: 'custom'>}, template_vars=['context_msg', 'query_str', 'existing_answer'], kwargs={}, output_parser=None, template_var_mappings=None, function_mappings=None, message_templates=[ChatMessage (role=<MessageRole.USER: 'user'>, additional_kwargs={}, blocks=[TextBlock (block_type='text', text="You are an expert Q&A system that strictly operates in two modes when refining existing answers:\n 1. **Rewrite** an original answer using the new context.\n 2. **Repeat** the original answer if the new context isn't useful.\nNever reference the original answer or context directly in your answer.\nWhen in doubt, just repeat the original answer.\nNew Context: {context_msg}\nQuery: {query_str}\nOriginal Answer: {existing_answer}\nNew Answer: ")])]))])}

可以自定义修改 query_engine 的 prompt

1
2
3
4
5
6
# 更新prompt 
query_engine.update_prompts(
{'response_synthesizer:text_qa_template':qa_template}
)
cur_prompt=query_engine.get_prompts()
print(cur_prompt)
1
{'response_synthesizer: text_qa_template': PromptTemplate (metadata={'prompt_type': <PromptType.CUSTOM: 'custom'>}, template_vars=['context_str', 'query_str'], kwargs={}, output_parser=None, template_var_mappings=None, function_mappings=None, template='我提供以下上下文信息\n-------------------\n{context_str}\n-------------------\n 给定这个信息,请回答问题:{query_str}\n'), 'response_synthesizer: refine_template': SelectorPromptTemplate (metadata={'prompt_type': <PromptType.REFINE: 'refine'>}, template_vars=['query_str', 'existing_answer', 'context_msg'], kwargs={}, output_parser=None, template_var_mappings={}, function_mappings={}, default_template=PromptTemplate (metadata={'prompt_type': <PromptType.REFINE: 'refine'>}, template_vars=['query_str', 'existing_answer', 'context_msg'], kwargs={}, output_parser=None, template_var_mappings=None, function_mappings=None, template="The original query is as follows: {query_str}\nWe have provided an existing answer: {existing_answer}\nWe have the opportunity to refine the existing answer (only if needed) with some more context below.\n------------\n{context_msg}\n------------\nGiven the new context, refine the original answer to better answer the query. If the context isn't useful, return the original answer.\nRefined Answer: "), conditionals=[(<function is_chat_model at 0x000002C450CB8160>, ChatPromptTemplate (metadata={'prompt_type': <PromptType.CUSTOM: 'custom'>}, template_vars=['context_msg', 'query_str', 'existing_answer'], kwargs={}, output_parser=None, template_var_mappings=None, function_mappings=None, message_templates=[ChatMessage (role=<MessageRole.USER: 'user'>, additional_kwargs={}, blocks=[TextBlock (block_type='text', text="You are an expert Q&A system that strictly operates in two modes when refining existing answers:\n 1. **Rewrite** an original answer using the new context.\n 2. **Repeat** the original answer if the new context isn't useful.\nNever reference the original answer or context directly in your answer.\nWhen in doubt, just repeat the original answer.\nNew Context: {context_msg}\nQuery: {query_str}\nOriginal Answer: {existing_answer}\nNew Answer: ")])]))])}

可以看到,prompt 被更新,和 langchian 相比,其提示词没有那么抽象