283 lines
12 KiB
Python
283 lines
12 KiB
Python
"""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)
|