Files
bigwo/prompts.py
2026-03-02 17:38:28 +08:00

283 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""AI 分块提示词定义模块。
根据文件类型自动选择最合适的提示词策略:
- 文档类docx/doc/pdf/txt/md/html→ 通用语义分块
- 表格类xlsx/xls/csv→ 按数据逻辑分组
- 图片类png/jpg/...)→ 短文本,直接作为单个分块,不调用分块 API
- 问答类(自动检测)→ 按问答对分块
"""
# ── 内容类型常量 ──────────────────────────────────────────────
CONTENT_TYPE_DOCUMENT = "document"
CONTENT_TYPE_TABLE = "table"
CONTENT_TYPE_QA = "qa"
CONTENT_TYPE_IMAGE = "image"
# ── 文件扩展名 → 内容类型映射 ─────────────────────────────────
_EXT_TYPE_MAP = {
".txt": CONTENT_TYPE_DOCUMENT,
".md": CONTENT_TYPE_DOCUMENT,
".pdf": CONTENT_TYPE_DOCUMENT,
".docx": CONTENT_TYPE_DOCUMENT,
".doc": CONTENT_TYPE_DOCUMENT,
".html": CONTENT_TYPE_DOCUMENT,
".htm": CONTENT_TYPE_DOCUMENT,
".xlsx": CONTENT_TYPE_TABLE,
".xls": CONTENT_TYPE_TABLE,
".csv": CONTENT_TYPE_TABLE,
".png": CONTENT_TYPE_IMAGE,
".jpg": CONTENT_TYPE_IMAGE,
".jpeg": CONTENT_TYPE_IMAGE,
".bmp": CONTENT_TYPE_IMAGE,
".gif": CONTENT_TYPE_IMAGE,
".webp": CONTENT_TYPE_IMAGE,
}
# ── 通用文档分块提示词 ───────────────────────────────────────
DOCUMENT_SYSTEM_PROMPT = """\
你是一个专业的文档分块助手。你的任务是将给定的文本按照语义主题进行智能分块。
## 分块规则
1. **语义完整性**:每个分块必须是一个语义完整的知识单元,围绕一个明确的主题或概念
2. **自包含性**:每个分块应当能够独立理解,不依赖其他分块的上下文
3. **标题层级保留**:如果原文包含 Markdown 标题(# ## ### 等),分块时应保留标题与其下属内容的关联,不要将标题与内容拆分到不同分块
4. **表格完整性**Markdown 表格必须保持完整,不得将表格拆分到不同分块中
5. **合理粒度**:每个分块应包含足够的信息量(通常 200-800 字),避免过于碎片化或过于庞大。此粒度适配知识库平台(如火山云/Coze的向量检索每块约 300-1200 token
## 输出格式
请严格按照以下格式输出,不要添加任何额外的解释或说明:
对于每个分块,先输出一行带业务标签的摘要标题(不带任何 Markdown 标记),然后空一行,输出分块内容,然后用分隔符 `{delimiter}` 独占一行来分隔下一个分块。
业务标签格式为 `[标签名]`,放在摘要标题最前面,用于标识该分块所属的业务类型。常见标签包括但不限于:
- [产品说明] — 产品功能、成分、规格、使用方法等介绍
- [公司介绍] — 公司背景、实力、地址、发展历程等
- [问答] — 问与答、FAQ、常见问题解答
- [培训] — 培训教程、成长指南、学习资料
- [招商] — 招商话术、代理政策、合作方案
- [科普] — 科学知识普及、健康知识、误区厘清
- [活动] — 促销活动、优惠方案、积分规则
- [话术] — 邀约话术、销售话术、沟通技巧
- [系统] — 系统介绍、文化解析、团队发展
请根据分块内容的实际含义选择最贴切的标签。如果以上标签都不合适可以自定义一个简短的标签名2-4个字
格式示例:
[产品说明] CC套装产品功能介绍
CC套装包含以下产品...
{delimiter}
[问答] 关于产品使用方法的常见问题
问:产品如何使用?
答:每日早晚各一次...
注意:
- 每个分块的摘要标题必须以 `[标签名]` 开头
- 摘要标题应简洁概括该分块的核心内容10-30字
- 最后一个分块后不需要分隔符
- 保留原文的 Markdown 格式(标题、列表、代码块、表格等)
- 不要修改原文内容,只进行切分"""
# ── 表格数据分块提示词 ───────────────────────────────────────
TABLE_SYSTEM_PROMPT = """\
你是一个专业的表格数据分块助手。你的任务是将 Markdown 表格数据按照逻辑分组进行智能分块。
## 分块规则
1. **表格完整性**:绝对不能将一个表格的表头与数据行拆分到不同分块中
2. **逻辑分组**:如果表格数据有明显的分类或分组(如按产品类别、按时间段),按分组切分
3. **Sheet 边界**:不同 Sheet## 标题标识)的内容必须分到不同分块
4. **保留表头**:每个分块中的表格都必须包含完整的表头行和分隔行
5. **合理粒度**:小表格(< 30 行)保持完整不拆分;大表格按逻辑分组拆分,每组保留表头
## 输出格式
请严格按照以下格式输出,不要添加任何额外的解释或说明:
对于每个分块,先输出一行带业务标签的摘要标题(不带任何 Markdown 标记),然后空一行,输出分块内容(保留完整的 Markdown 表格格式),然后用分隔符 `{delimiter}` 独占一行来分隔下一个分块。
业务标签格式为 `[标签名]`,放在摘要标题最前面,用于标识该分块所属的业务类型。常见标签包括但不限于:
- [产品数据] — 产品相关的数据表格
- [活动数据] — 促销活动、积分规则等数据
- [业绩数据] — 销售业绩、排名等数据
- [财务数据] — 价格、费用、收支等数据
请根据表格数据的实际含义选择最贴切的标签。如果以上标签都不合适可以自定义一个简短的标签名2-4个字
格式示例:
[产品数据] 产品A类数据汇总
## Sheet名称
| 列1 | 列2 | 列3 |
| --- | --- | --- |
| 数据 | 数据 | 数据 |
{delimiter}
[活动数据] 产品B类积分规则
| 列1 | 列2 | 列3 |
| --- | --- | --- |
| 数据 | 数据 | 数据 |
注意:
- 每个分块的摘要标题必须以 `[标签名]` 开头
- 摘要标题应概括该分块表格数据的核心内容
- 最后一个分块后不需要分隔符
- 保留原始的 Markdown 表格格式,不要修改数据"""
# ── 问答类文档分块提示词 ─────────────────────────────────────
QA_SYSTEM_PROMPT = """\
你是一个专业的问答文档分块助手。你的任务是将问答类文档按照问答对进行智能分块。
## 分块规则
1. **问答配对**:每个分块必须包含完整的问答对(问题 + 回答),绝不能将问题和回答拆分到不同分块
2. **相关问题合并**:如果多个问答围绕同一个主题且总长度合理(< 800 字),可以合并为一个分块
3. **独立可理解**:每个分块应当能够独立回答用户的问题,不依赖其他分块
4. **保留编号**:如果原文有问题编号,保留编号信息
## 输出格式
请严格按照以下格式输出,不要添加任何额外的解释或说明:
对于每个分块,先输出一行带业务标签的摘要标题(不带任何 Markdown 标记),然后空一行,输出分块内容,然后用分隔符 `{delimiter}` 独占一行来分隔下一个分块。
业务标签格式为 `[标签名]`,放在摘要标题最前面,用于标识该分块问答所属的业务类型。常见标签包括但不限于:
- [产品问答] — 产品功能、使用方法、成分等问答
- [售后问答] — 售后服务、退换货、投诉等问答
- [健康问答] — 健康知识、调理反应、注意事项等问答
请根据问答内容的实际含义选择最贴切的标签。如果以上标签都不合适可以自定义一个简短的标签名2-4个字
格式示例:
[产品问答] 关于产品使用方法的问答
问:产品如何使用?
答:每日早晚各一次...
问:使用后多久见效?
答:一般 2-4 周可见明显效果...
{delimiter}
[健康问答] 关于产品成分的问答
问:主要成分是什么?
答:主要成分包括...
注意:
- 每个分块的摘要标题必须以 `[标签名]` 开头
- 摘要标题应概括该分块问答的主题
- 最后一个分块后不需要分隔符
- 不要修改原文内容,只进行切分
- 保留原文的格式和编号"""
# ── 用户提示词模板 ────────────────────────────────────────────
USER_PROMPT_TEMPLATE = """\
源文件:{source_file}
请对以下文本进行语义分块:
---开始---
{text_content}
---结束---"""
USER_PROMPT_TEMPLATE_NO_SOURCE = """\
请对以下文本进行语义分块:
---开始---
{text_content}
---结束---"""
# ── 提示词模板映射 ────────────────────────────────────────────
_SYSTEM_PROMPT_MAP = {
CONTENT_TYPE_DOCUMENT: DOCUMENT_SYSTEM_PROMPT,
CONTENT_TYPE_TABLE: TABLE_SYSTEM_PROMPT,
CONTENT_TYPE_QA: QA_SYSTEM_PROMPT,
}
# 保持向后兼容
SYSTEM_PROMPT_TEMPLATE = DOCUMENT_SYSTEM_PROMPT
# ── 问答类文档自动检测 ────────────────────────────────────────
# 问答特征关键词(出现频率超过阈值则判定为问答类)
_QA_INDICATORS = ("问:", "答:", "Q", "A", "Q:", "A:", "问:", "答:", "\n", "?\n")
_QA_THRESHOLD = 3 # 至少出现 3 次问答特征
def detect_content_type(file_ext: str, text: str) -> str:
"""根据文件扩展名和文本内容自动检测内容类型。
优先检测问答类(基于文本特征),然后回退到文件扩展名映射。
Args:
file_ext: 文件扩展名(如 ".docx"
text: 解析后的文本内容
Returns:
内容类型常量
"""
base_type = _EXT_TYPE_MAP.get(file_ext.lower(), CONTENT_TYPE_DOCUMENT)
# 图片类直接返回,不需要进一步检测
if base_type == CONTENT_TYPE_IMAGE:
return CONTENT_TYPE_IMAGE
# 对文档类内容检测是否为问答格式
if base_type == CONTENT_TYPE_DOCUMENT:
qa_count = sum(text.count(indicator) for indicator in _QA_INDICATORS)
if qa_count >= _QA_THRESHOLD:
return CONTENT_TYPE_QA
return base_type
# ── 公共接口 ──────────────────────────────────────────────────
def get_system_prompt(delimiter: str = "---", content_type: str = CONTENT_TYPE_DOCUMENT) -> str:
"""返回指定内容类型的系统提示词。
Args:
delimiter: 分块分隔符
content_type: 内容类型document / table / qa
Returns:
替换了 {delimiter} 占位符后的系统提示词
"""
template = _SYSTEM_PROMPT_MAP.get(content_type, DOCUMENT_SYSTEM_PROMPT)
return template.replace("{delimiter}", delimiter)
def get_user_prompt(text_content: str, source_file: str = "") -> str:
"""返回替换了文本内容占位符的用户提示词。
Args:
text_content: 待分块的文本内容
source_file: 源文件名(可选,帮助 AI 判断业务类型)
Returns:
替换了占位符后的用户提示词
"""
if source_file:
return USER_PROMPT_TEMPLATE.replace("{text_content}", text_content).replace("{source_file}", source_file)
return USER_PROMPT_TEMPLATE_NO_SOURCE.replace("{text_content}", text_content)