/** * @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语法输出流程图,节点用[]包裹,箭头用-->连接。 - 每一条流程图语句必须单独一行,不能多条语句写在一行。 - 节点内容必须全部在一行内,不能有任何换行、不能有
、不能有 \\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 }; })();