342 lines
16 KiB
Python
342 lines
16 KiB
Python
"""
|
||
增强的 Prompt 模板
|
||
|
||
提供针对不同场景和文件类型优化的 Prompt 模板。
|
||
参考 server/aaa/jenius_attachment_knowledge_base/jenius_rag_util.py 和
|
||
server/aaa/jenius_personal_knowledge_base/personal_kb_prompt.py 的实现。
|
||
"""
|
||
from typing import List, Dict, Set
|
||
|
||
|
||
# ==================== 基础 RAG Prompt ====================
|
||
|
||
RAG_CONTENT_PROMPT = """
|
||
## 上传文件内容的分析说明
|
||
- 如果用户问题相关的文件内容可以直接回答用户的问题,则直接基于文件内容回答用户的提问;回答问题不要添加编造成分,不要使用使用大模型的自身知识回答。
|
||
- 输出格式要求:
|
||
* 回答开头应为"根据您上传的文件`related_file_name`", `related_file_name`替换为问题相关的文件名,多个文件用英文逗号,分隔,并用markdown反引号`包裹。如果上下文没有提及,`related_file_name`设为空字符串。
|
||
* 如果聊天历史中,系统给出的文件内容并非是用户想要的,回答开头请加入委婉的回应,例如:"抱歉,我刚才可能理解错了,现在改为分析您需要的文件`related_file_name`。
|
||
- 如果用户问题相关的文件内容不能回答用户的全部问题,则将文件内容作为上下文,进入工具调用的流程。
|
||
- {rag_content}
|
||
## 用户的问题
|
||
- {query}
|
||
"""
|
||
|
||
|
||
RAG_FILE_IMAGE_CONTENT_PROMPT = """
|
||
## 指令: 根据文件和图片的内容回答用户的问题。
|
||
1. 文本文件的问答(docx,pdf,xlsx,xls):
|
||
- 如果用户问题相关的文件内容可以直接回答用户的问题,则直接基于文件内容回答用户的问题;回答问题不要添加编造成分,不要使用使用大模型的自身知识回答。
|
||
- 如果用户问题相关的文件内容不能回答用户的全部问题,则将文件内容作为上下文,进入工具调用的流程。
|
||
|
||
2. 图片内容的问答(png,jpeg,jpg,bmp):
|
||
- 如果图片的文字内容为空,或者无法回答用户的问题,进入工具调用流程,使用合适的工具回答用户问题。
|
||
- 如果用户问题为询问图片的主要内容和描述等,进入工具调用流程,使用合适的工具回答用户问题。
|
||
- 如果用户问题涉及图片操作、图片处理、图片加工、进入工具调用流程,使用合适的工具回答用户问题。
|
||
- 如果用户的问题涉及图片的视觉信息,必须进入工具调用流程,使用合适的工具回答用户问题。
|
||
* 视觉信息包括但不限于:物体、场景、颜色、布局、风格、人物、动物、植物、动作。
|
||
|
||
## 输出格式要求:
|
||
- 回答开头应为"根据您上传的文件`related_file_name`", `related_file_name`替换为问题相关的文件名,多个文件用英文逗号,分隔,并用markdown反引号`包裹。如果上下文没有提及,`related_file_name`设为空字符串。
|
||
- 如果聊天历史中,系统给出的文件内容并非是用户想要的,回答开头请加入委婉的回应,例如:"抱歉,我刚才可能理解错了,现在改为分析您需要的文件`related_file_name`。
|
||
- 注意:图片问答中,禁止输出"文本内容无法完整回答您的问题"或任何等价说明。
|
||
|
||
## 输入信息
|
||
- 文件/图片内容:{rag_content}
|
||
- 用户的问题: {query}
|
||
"""
|
||
|
||
|
||
RAG_EXCEL_CONTENT_PROMPT = """
|
||
## 上传文件内容的分析说明
|
||
- 如果用户问题相关的文件内容、pandas代码、pandas执行结果,可以直接回答用户的问题,则直接回答用户的提问;回答问题不要添加编造成分,不要使用使用大模型的自身知识回答。
|
||
- 如果用户问题相关的文件内容、pandas代码、pandas执行结果不能回答用户的全部问题,则将他们作为上下文,进入工具调用的流程。
|
||
- 输出格式要求:
|
||
* 回答开头应为"根据您上传的文件`related_file_name`", `related_file_name`替换为问题相关的文件名,多个文件用英文逗号,分隔,并用markdown反引号`包裹。如果上下文没有提及,`related_file_name`设为空字符串。
|
||
* 如果聊天历史中,系统给出的文件内容并非是用户想要的,回答开头请加入委婉的回应,例如:"抱歉,我刚才可能理解错了,现在改为分析您需要的文件`related_file_name`。
|
||
- {rag_content}
|
||
## 用户的问题
|
||
- {query}
|
||
"""
|
||
|
||
|
||
# ==================== 知识库 Prompt ====================
|
||
|
||
KB_CHAT_RAG_PROMPT = """
|
||
## 指令:根据文件内容回答用户的问题。请严格按照以下规则处理用户问题:
|
||
|
||
1. 如果文件内容可以直接回答用户的问题:
|
||
- 基于文件内容回答用户的问题。
|
||
- 不要添加编造成分,不要使用大模型的自身知识回答。
|
||
- 如果文件来源是用户上传的文件,回答开头应为:"根据您上传的文件`related_file_name`"
|
||
- 如果文件来源是知识库中的文件,回答开头应为:"根据您知识库中的文件`related_file_name`"
|
||
- 其中`related_file_name`替换为问题相关的文件名,多个文件用英文逗号,分隔,并用markdown反引号`包裹,如果无法确定具体文件名,则不要添加回答开头的这句话。
|
||
- 如果聊天历史中,系统给出的文件内容并非是用户想要的,回答开头请加入委婉的回应,例如:"抱歉,我刚才可能理解错了,现在改为分析您需要的文件`related_file_name`。
|
||
|
||
2. 如果文件内容不能回答用户的问题:
|
||
- 不要输出前缀"根据您上传的文件"
|
||
- 将文件内容作为上下文,进入工具调用的流程。
|
||
|
||
## 知识库的文件内容
|
||
- {kb_rag_content}
|
||
|
||
## 用户的问题
|
||
- {query}
|
||
"""
|
||
|
||
|
||
# ==================== 文件类型特定 Prompt ====================
|
||
|
||
TEXT_FILE_INSTRUCTION = """
|
||
文本文件的问答(docx,pdf,txt):
|
||
- 如果用户问题相关的文件内容可以直接回答用户的问题,则直接基于文件内容回答用户的问题;回答问题不要添加编造成分,不要使用大模型的自身知识回答。
|
||
- 如果用户问题相关的文件内容不能回答用户的全部问题,则将文件内容作为上下文,进入工具调用的流程。
|
||
"""
|
||
|
||
|
||
IMAGE_FILE_INSTRUCTION = """
|
||
图片文件的问答(png,jpeg,jpg,bmp):
|
||
- 如果图片的文字内容为空,或者无法回答用户的问题,进入工具调用流程,使用合适的工具回答用户问题。
|
||
- 如果用户问题为询问图片的主要内容和描述等,调用图像理解工具获取更详细的内容来回答用户问题。
|
||
- 如果用户问题涉及图片操作、图片处理、图片加工,进入工具调用流程,使用合适的工具回答用户问题。
|
||
- 如果用户的问题涉及图片的视觉信息,必须进入工具调用流程,使用合适的工具回答用户问题。
|
||
* 视觉信息包括但不限于:物体、场景、颜色、布局、风格、人物、动物、植物、动作。
|
||
- 如果用户要显示图片,则使用file_url进行展示。
|
||
- 如果ocr的结果为空或显然无意义,则在回答中不要提及ocr的结果。
|
||
"""
|
||
|
||
|
||
EXCEL_FILE_INSTRUCTION = """
|
||
表格文件的问答(xlsx,xls,csv):
|
||
- 如果用户问题相关的表格内容可以直接回答用户的问题,则直接基于文件内容回答用户的问题;回答问题不要添加编造成分,不要使用大模型的自身知识回答。
|
||
- 如果用户问题相关的表格内容不能回答用户的全部问题,则将文件内容作为上下文,进入工具调用的流程。
|
||
- 如果用户问题涉及修改、创建excel表格等操作,进入工具调用流程,使用合适的工具回答用户问题。
|
||
"""
|
||
|
||
|
||
AUDIO_FILE_INSTRUCTION = """
|
||
音频文件的问答(wav,mp3,flac,m4a,ogg,aac,pcm):
|
||
- 如果用户问题涉及音频文件,进入工具调用流程,使用合适的音频工具回答用户问题。
|
||
"""
|
||
|
||
|
||
# ==================== 输出格式 Prompt ====================
|
||
|
||
CHAT_OUTPUT_FORMAT = """
|
||
## 输出格式要求:
|
||
- 如果回答的根据来源是用户上传的文件,回答开头应为:"根据您上传的文件`related_file_name`"
|
||
- 如果需要综合所有文件内容回答,则回答开头根据用户的问题灵活调整
|
||
- `related_file_name`替换为问题相关的文件名,多个文件用英文逗号,分隔,并用markdown反引号`包裹。如果无法确定具体文件名,则不要添加回答开头的这句话。
|
||
- 如果聊天历史中,系统给出的文件内容用户明确表示不是想要的,回答开头请加入委婉的回应,例如:"抱歉,我刚才可能理解错了,现在改为分析您需要的文件`related_file_name`。
|
||
- 注意:图片问答中,禁止输出"文本内容无法完整回答您的问题"或任何等价说明。
|
||
|
||
## 输入信息
|
||
## 用户上传的文件内容
|
||
- {rag_content}
|
||
## 用户的问题
|
||
- {query}
|
||
"""
|
||
|
||
|
||
KB_OUTPUT_FORMAT = """
|
||
## 输出格式要求:
|
||
- 如果回答的根据来源是知识库中的文件,且`related_file_name`是文件名,回答开头应为:"根据您知识库中的文件`related_file_name`"
|
||
- 如果回答的根据来源是知识库中的网页,且`related_file_name`是网页URL,回答开头应为:"根据您知识库中的网页`related_file_name`"
|
||
- 如果需要综合所有文件内容回答,则回答开头根据用户的问题灵活调整
|
||
- `related_file_name`替换为问题相关的文件名,多个文件用英文逗号,分隔,并用markdown反引号`包裹。如果无法确定具体文件名,则不要添加回答开头的这句话。
|
||
- 如果聊天历史中,系统给出的文件内容用户明确表示不是想要的,回答开头请加入委婉的回应,例如:"抱歉,我刚才可能理解错了,现在改为分析您需要的文件`related_file_name`。
|
||
- 注意:图片问答中,禁止输出"文本内容无法完整回答您的问题"或任何等价说明。
|
||
|
||
## 输入信息
|
||
## 知识库的文件内容
|
||
- {kb_rag_content}
|
||
## 用户的问题
|
||
- {query}
|
||
"""
|
||
|
||
|
||
# ==================== Prompt 生成函数 ====================
|
||
|
||
def get_file_extensions(file_list: List[Dict]) -> Set[str]:
|
||
"""
|
||
获取文件扩展名集合
|
||
|
||
Args:
|
||
file_list: 文件列表,每个元素包含 file_name 字段
|
||
|
||
Returns:
|
||
Set[str]: 文件扩展名集合(小写,带点号)
|
||
"""
|
||
extensions = set()
|
||
for file_info in file_list:
|
||
file_name = file_info.get('file_name', '')
|
||
if '.' in file_name:
|
||
ext = '.' + file_name.split('.')[-1].lower()
|
||
extensions.add(ext)
|
||
return extensions
|
||
|
||
|
||
def build_rag_prompt(
|
||
query: str,
|
||
rag_content: str,
|
||
file_list: List[Dict],
|
||
intent_type: str = "summary"
|
||
) -> str:
|
||
"""
|
||
构建 RAG Prompt
|
||
|
||
Args:
|
||
query: 用户查询
|
||
rag_content: RAG 内容
|
||
file_list: 文件列表
|
||
intent_type: 意图类型 (summary, excel_analysis, search)
|
||
|
||
Returns:
|
||
str: 完整的 Prompt
|
||
"""
|
||
extensions = get_file_extensions(file_list)
|
||
|
||
# 根据文件类型选择指令
|
||
instructions = []
|
||
|
||
if extensions & {'.docx', '.pdf', '.txt'}:
|
||
instructions.append(TEXT_FILE_INSTRUCTION)
|
||
|
||
if extensions & {'.png', '.jpeg', '.jpg', '.bmp'}:
|
||
instructions.append(IMAGE_FILE_INSTRUCTION)
|
||
|
||
if extensions & {'.xlsx', '.xls', '.csv'}:
|
||
instructions.append(EXCEL_FILE_INSTRUCTION)
|
||
|
||
if extensions & {'.wav', '.mp3', '.flac', '.m4a', '.ogg', '.aac', '.pcm'}:
|
||
instructions.append(AUDIO_FILE_INSTRUCTION)
|
||
|
||
# 根据意图类型选择基础 Prompt
|
||
if intent_type == "excel_analysis":
|
||
base_prompt = RAG_EXCEL_CONTENT_PROMPT
|
||
elif extensions & {'.png', '.jpeg', '.jpg', '.bmp'}:
|
||
base_prompt = RAG_FILE_IMAGE_CONTENT_PROMPT
|
||
else:
|
||
base_prompt = RAG_CONTENT_PROMPT
|
||
|
||
# 组装完整 Prompt
|
||
full_instructions = "\n".join(instructions) if instructions else ""
|
||
|
||
if full_instructions:
|
||
# 如果有文件类型特定指令,插入到基础 Prompt 之前
|
||
final_prompt = "## 指令: 根据文件内容回答用户的问题。\n\n" + full_instructions + "\n" + CHAT_OUTPUT_FORMAT
|
||
else:
|
||
final_prompt = base_prompt
|
||
|
||
return final_prompt.format(query=query, rag_content=rag_content)
|
||
|
||
|
||
def build_kb_rag_prompt(
|
||
query: str,
|
||
kb_rag_content: str,
|
||
file_list: List[Dict]
|
||
) -> str:
|
||
"""
|
||
构建知识库 RAG Prompt
|
||
|
||
Args:
|
||
query: 用户查询
|
||
kb_rag_content: 知识库 RAG 内容
|
||
file_list: 文件列表
|
||
|
||
Returns:
|
||
str: 完整的 Prompt
|
||
"""
|
||
extensions = get_file_extensions(file_list)
|
||
|
||
# 根据文件类型选择指令
|
||
instructions = []
|
||
|
||
if extensions & {'.docx', '.pdf', '.txt'}:
|
||
instructions.append(TEXT_FILE_INSTRUCTION)
|
||
|
||
if extensions & {'.png', '.jpeg', '.jpg', '.bmp'}:
|
||
instructions.append(IMAGE_FILE_INSTRUCTION)
|
||
|
||
if extensions & {'.xlsx', '.xls', '.csv'}:
|
||
instructions.append(EXCEL_FILE_INSTRUCTION)
|
||
|
||
# 组装完整 Prompt
|
||
full_instructions = "\n".join(instructions) if instructions else ""
|
||
|
||
if full_instructions:
|
||
final_prompt = "## 指令: 根据知识库文件内容回答用户的问题。\n\n" + full_instructions + "\n" + KB_OUTPUT_FORMAT
|
||
else:
|
||
final_prompt = KB_CHAT_RAG_PROMPT
|
||
|
||
return final_prompt.format(query=query, kb_rag_content=kb_rag_content)
|
||
|
||
|
||
def build_mixed_rag_prompt(
|
||
query: str,
|
||
chat_rag_content: str,
|
||
kb_rag_content: str,
|
||
chat_file_list: List[Dict],
|
||
kb_file_list: List[Dict]
|
||
) -> str:
|
||
"""
|
||
构建混合 RAG Prompt(同时包含聊天文件和知识库文件)
|
||
|
||
Args:
|
||
query: 用户查询
|
||
chat_rag_content: 聊天文件 RAG 内容
|
||
kb_rag_content: 知识库 RAG 内容
|
||
chat_file_list: 聊天文件列表
|
||
kb_file_list: 知识库文件列表
|
||
|
||
Returns:
|
||
str: 完整的 Prompt
|
||
"""
|
||
chat_extensions = get_file_extensions(chat_file_list)
|
||
kb_extensions = get_file_extensions(kb_file_list)
|
||
all_extensions = chat_extensions | kb_extensions
|
||
|
||
# 根据文件类型选择指令
|
||
instructions = []
|
||
|
||
if all_extensions & {'.docx', '.pdf', '.txt'}:
|
||
instructions.append(TEXT_FILE_INSTRUCTION)
|
||
|
||
if all_extensions & {'.png', '.jpeg', '.jpg', '.bmp'}:
|
||
instructions.append(IMAGE_FILE_INSTRUCTION)
|
||
|
||
if all_extensions & {'.xlsx', '.xls', '.csv'}:
|
||
instructions.append(EXCEL_FILE_INSTRUCTION)
|
||
|
||
if all_extensions & {'.wav', '.mp3', '.flac', '.m4a', '.ogg', '.aac', '.pcm'}:
|
||
instructions.append(AUDIO_FILE_INSTRUCTION)
|
||
|
||
# 混合输出格式
|
||
MIXED_OUTPUT_FORMAT = """
|
||
## 输出格式要求:
|
||
- 如果文件来源是用户上传的文件,回答开头应为:"根据您上传的文件`related_file_name`"
|
||
- 如果文件来源是知识库中的文件,回答开头应为:"根据您知识库中的文件`related_file_name`"
|
||
- 如果需要综合所有文件内容回答,则回答开头根据用户的问题灵活调整
|
||
- `related_file_name`替换为问题相关的文件名,多个文件用英文逗号,分隔,并用markdown反引号`包裹。如果无法确定具体文件名,则不要添加回答开头的这句话。
|
||
- 注意:图片问答中,禁止输出"文本内容无法完整回答您的问题"或任何等价说明。
|
||
|
||
## 输入信息
|
||
## 知识库的文件内容
|
||
- {kb_rag_content}
|
||
## 用户上传的文件内容
|
||
- {chat_rag_content}
|
||
## 用户的问题
|
||
- {query}
|
||
"""
|
||
|
||
# 组装完整 Prompt
|
||
full_instructions = "\n".join(instructions) if instructions else ""
|
||
|
||
final_prompt = "## 指令: 根据文件内容回答用户的问题。\n\n" + full_instructions + "\n" + MIXED_OUTPUT_FORMAT
|
||
|
||
return final_prompt.format(
|
||
query=query,
|
||
chat_rag_content=chat_rag_content,
|
||
kb_rag_content=kb_rag_content
|
||
)
|