paper-burner/js/chatbot/prompt/prompt-constructor.js

226 lines
12 KiB
JavaScript
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.

/**
* @file Manages the construction of prompts and message payloads for the chatbot.
*/
window.PromptConstructor = (function() {
/**
* Builds the system prompt string based on document information and request type.
* @param {object} docContentInfo - Object containing document details (name, ocr, translation).
* @param {boolean} isMindMapRequest - Flag indicating if the request is for a mind map.
* @param {string} plainTextInput - The user's plain text input (used to check for mind map keywords if isMindMapRequest is not directly passed).
* @returns {string} The constructed system prompt.
*/
function buildSystemPrompt(docContentInfo, isMindMapRequest, plainTextInput) {
// 使用 ChatbotPreset 中的基础系统提示词
let basePrompt = window.ChatbotPreset && window.ChatbotPreset.BASE_SYSTEM_PROMPT
? window.ChatbotPreset.BASE_SYSTEM_PROMPT
: `你现在是 PDF 文档智能助手,用户正在查看文档"{docName}"。\n你的回答应该:\n1. 基于PDF文档内容\n2. 简洁清晰\n3. 学术准确`;
// 替换文档名称占位符
let systemPrompt = basePrompt.replace('{docName}', docContentInfo.name || '当前文档');
// 检查是否是思维导图请求
let actuallyIsMindMapRequest = isMindMapRequest;
// 如果未明确指定是思维导图请求,则检查输入文本中是否包含相关关键词
if (!actuallyIsMindMapRequest && window.ChatbotPreset && typeof window.ChatbotPreset.isMindMapRequest === 'function') {
actuallyIsMindMapRequest = window.ChatbotPreset.isMindMapRequest(plainTextInput);
} else if (!actuallyIsMindMapRequest && plainTextInput) {
// 后备逻辑:如果 ChatbotPreset 不可用,使用内置检测
actuallyIsMindMapRequest = plainTextInput.includes('思维导图') || plainTextInput.includes('脑图');
}
// 添加思维导图提示
if (actuallyIsMindMapRequest) {
const mindMapPrompt = window.ChatbotPreset && window.ChatbotPreset.MINDMAP_PROMPT
? window.ChatbotPreset.MINDMAP_PROMPT
: `\n\n请注意用户请求生成思维导图。请按照以下Markdown格式返回思维导图结构
# 文档主题(根节点)
## 一级主题1
### 二级主题1.1
### 二级主题1.2
## 一级主题2
### 二级主题2.1
#### 三级主题2.1.1
只需提供思维导图的结构,不要添加额外的解释。结构应该清晰反映文档的层次关系和主要内容。`;
systemPrompt += mindMapPrompt;
}
// 检查是否是配图draw.io请求基于前缀 [加入配图]
let isDrawioPicturesRequest = false;
if (window.ChatbotPreset && typeof window.ChatbotPreset.isDrawioPicturesRequest === 'function') {
isDrawioPicturesRequest = window.ChatbotPreset.isDrawioPicturesRequest(plainTextInput);
} else if (plainTextInput) {
isDrawioPicturesRequest = plainTextInput.trim().startsWith('[加入配图]');
}
if (isDrawioPicturesRequest) {
const drawioPrompt = window.ChatbotPreset && window.ChatbotPreset.DRAWIO_PICTURES_PROMPT
? window.ChatbotPreset.DRAWIO_PICTURES_PROMPT
: '\n\n请根据当前文章内容和用户要求为读者补充所需的配图并以 diagrams.net / draw.io 兼容的 XML 格式输出,仅输出 XML。';
systemPrompt += drawioPrompt;
}
// 检查是否是 Mermaid 流程图请求:基于关键词"流程图"
let isMermaidFlowchartRequest = false;
if (plainTextInput) {
isMermaidFlowchartRequest = plainTextInput.includes('流程图') && !plainTextInput.includes('Mermaid语法');
}
if (isMermaidFlowchartRequest) {
const mermaidPrompt = window.ChatbotPreset && window.ChatbotPreset.MERMAID_FLOWCHART_PROMPT
? window.ChatbotPreset.MERMAID_FLOWCHART_PROMPT
: `
请用Mermaid语法输出流程图节点用[]包裹,箭头用-->连接。
- 每一条流程图语句必须单独一行,不能多条语句写在一行。
- 节点内容必须全部在一行内,不能有任何换行、不能有 <br>、不能有 \\n。
- **节点标签内禁止使用特殊符号**:不能包含 [ ] ( ) | { } < > 等符号。`;
systemPrompt += mermaidPrompt;
}
// 获取文档内容优先翻译没有就用OCR
let content = '';
// Read the active summary source from global options, default to 'translation'
const activeSummarySource = (window.chatbotActiveOptions && window.chatbotActiveOptions.summarySource) || 'translation';
if (activeSummarySource === 'translation') {
content = docContentInfo.translation || docContentInfo.ocr || '';
// console.log('[PromptConstructor] Using translation or fallback to OCR for content.');
} else if (activeSummarySource === 'ocr') {
content = docContentInfo.ocr || docContentInfo.translation || '';
// console.log('[PromptConstructor] Using OCR or fallback to translation for content.');
} else if (activeSummarySource === 'none') {
content = ''; // Explicitly no content from document
// console.log('[PromptConstructor] Content source is NONE. No document content will be used.');
}
// ===== 新增:智能分段策略 =====
// 检查是否启用智能分段模式contentLengthStrategy === 'segmented'
const contentStrategy = (window.chatbotActiveOptions && window.chatbotActiveOptions.contentLengthStrategy) || 'default';
if (contentStrategy === 'segmented' && content.length >= 50000) {
// 智能分段模式:使用意群摘要
console.log('[PromptConstructor] 文档较长且启用智能分段,使用意群模式');
if (docContentInfo.semanticGroups && docContentInfo.semanticGroups.length > 0) {
// 已有意群,构建意群摘要
const docGist = (docContentInfo.semanticDocGist && typeof docContentInfo.semanticDocGist === 'string')
? `文档总览:${docContentInfo.semanticDocGist}\n\n` : '';
if (docContentInfo.selectedGroupContext) {
// 多轮取材:已选择意群上下文,优先提供所选上下文
content = `${docGist}已根据用户问题聚焦以下意群上下文:\n\n${docContentInfo.selectedGroupContext}\n\n如需更多细节,可继续请求深入具体意群。`;
console.log('[PromptConstructor] 使用已选择意群上下文模式');
} else {
const groupSummaries = docContentInfo.semanticGroups.map((group, idx) => {
return `【意群${idx + 1}${group.summary}\n关键词: ${(group.keywords || []).join(', ')}`;
}).join('\n\n');
content = `${docGist}文档已分为 ${docContentInfo.semanticGroups.length} 个意群,以下是各意群概要:\n\n${groupSummaries}\n\n如需深入了解某个意群,请明确指出。`;
console.log('[PromptConstructor] 使用意群摘要模式,共', docContentInfo.semanticGroups.length, '个意群');
}
} else {
// 未生成意群,降级到截断
console.warn('[PromptConstructor] 启用了分段模式但未找到意群数据,降级到截断模式');
if (content.length > 50000) {
content = content.slice(0, 50000) + '\n\n[文档过长,已截断]';
}
}
} else {
// 全文模式:但如果有多轮搜索的结果,优先使用搜索结果
if (docContentInfo.selectedGroupContext) {
const docGist = (docContentInfo.semanticDocGist && typeof docContentInfo.semanticDocGist === 'string')
? `文档总览:${docContentInfo.semanticDocGist}\n\n` : '';
content = `${docGist}已根据用户问题检索到以下相关内容:\n\n${docContentInfo.selectedGroupContext}`;
console.log('[PromptConstructor] 全文模式下使用多轮搜索结果');
} else if (content.length > 50000) {
// 没有搜索结果,使用传统截断
content = content.slice(0, 50000);
}
}
if (content) { // Only add "文档内容" section if content is not empty
systemPrompt += `\n\n文档内容:\n${content}`;
}
// 追加回答行为规范,避免模型输出"没有外部工具"等无关免责声明
systemPrompt += `\n\n回答规则补充:
- 不要声明你是否具备外部工具/联网等能力,也不要输出与回答无关的免责声明。
- 优先依据文档内容回答;若文档信息不足,请基于常识给出概览性解答并明确不确定之处。
- **遇到公式、数据、图表等关键信息时,必须直接引用原文展示完整内容,不要仅概括描述**。
* 例如用户问"有什么公式"时,应直接展示公式的完整表达式,而不是说"文档提到了公式7但未给出"。
* 如果检索到的内容包含公式、数据表、关键数值,务必完整引用,保留原文格式。
* 对于数学公式优先使用LaTeX格式展示$$公式$$$公式$)。`;
// 如果使用了多轮检索结果,添加简要说明
if (docContentInfo.selectedGroupContext) {
systemPrompt += `\n\n注:上述内容通过智能检索系统(语义搜索、关键词匹配、文档地图等)多轮获取。请充分利用所有材料,进行深入分析和综合,提供全面准确的回答。`;
}
// console.log('[PromptConstructor.buildSystemPrompt] Final systemPrompt:', systemPrompt);
return systemPrompt;
}
/**
* 增强用户输入的提示词
* 整合各种提示词增强逻辑,目前主要处理流程图提示词
*
* @param {string|Array} userInput - 用户输入,可能是字符串或多模态消息数组
* @returns {string|Array} - 增强后的用户输入
*/
function enhanceUserPrompt(userInput) {
// 如果ChatbotPreset已加载并提供了enhanceUserPrompt函数则使用它
if (window.ChatbotPreset && typeof window.ChatbotPreset.enhanceUserPrompt === 'function') {
return window.ChatbotPreset.enhanceUserPrompt(userInput);
}
// 如果没有 ChatbotPreset 或其函数不可用,则返回原始输入
return userInput;
}
/**
* 构建完整的消息负载
* 处理系统提示词和用户输入增强
*
* @param {object} docContentInfo - 文档内容信息对象
* @param {string|Array} userInput - 用户输入
* @param {object} options - 额外选项
* @returns {object} 包含系统提示词和增强用户输入的消息负载
*/
function buildMessagePayload(docContentInfo, userInput, options = {}) {
const { isMindMapRequest } = options;
// 获取原始的文本输入(用于系统提示词中检测思维导图关键词)
let plainTextInput = '';
if (typeof userInput === 'string') {
plainTextInput = userInput;
} else if (Array.isArray(userInput)) {
const textPart = userInput.find(p => p.type === 'text');
plainTextInput = textPart ? textPart.text : '';
}
// 构建系统提示词
const systemPrompt = buildSystemPrompt(docContentInfo, isMindMapRequest, plainTextInput);
// 增强用户输入
const enhancedUserInput = enhanceUserPrompt(userInput);
return {
systemPrompt,
userInput: enhancedUserInput
};
}
// Future functions for more complex context management can be added here.
// For example:
// function buildContextualizedUserInput(userInput, interestPoints, memory) { ... }
// function manageConversationHistory(history, contextPolicy) { ... }
return {
buildSystemPrompt,
enhanceUserPrompt,
buildMessagePayload
// expose other functions as needed
};
})();