forked from new_org/Project-Caffeine
192 lines
12 KiB
JavaScript
192 lines
12 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
exports.handleWorkflowRouter = handleWorkflowRouter;
|
||
/**
|
||
* Project Caffeine - Workflow Router
|
||
* 采用硬编码状态机,彻底杜绝大模型跳步幻觉
|
||
*/
|
||
const resourceService_1 = require("../services/resourceService");
|
||
const intentService_1 = require("../services/intentService");
|
||
const arxivService_1 = require("../services/arxivService");
|
||
const promptService_1 = require("../services/promptService");
|
||
// 内存级会话状态(单用户本地服务适用)
|
||
let sessionState = {};
|
||
async function handleWorkflowRouter(currentStep, userInput, payload) {
|
||
const input = userInput.trim();
|
||
const inputUpper = input.toUpperCase();
|
||
try {
|
||
switch (currentStep) {
|
||
// ==========================================
|
||
// 状态 0: 初始意图确认
|
||
// ==========================================
|
||
case 'init':
|
||
sessionState = {}; // 重置状态
|
||
return {
|
||
next_step: 'wait_for_path',
|
||
message_to_user: "👋 欢迎使用 Project Caffeine 智能向导。\n\n请问您希望进行哪种操作?(直接回复字母即可)\n**A.** 思维框架分析 (如 SWOT, SCQA 等)\n**B.** 学术文献查询 (Arxiv 检索)\n**C.** 本地笔记分析"
|
||
};
|
||
// ==========================================
|
||
// 状态 1: 分支选择
|
||
// ==========================================
|
||
case 'wait_for_path':
|
||
if (inputUpper === 'A' || inputUpper.includes('A')) {
|
||
return {
|
||
next_step: 'wait_for_framework',
|
||
message_to_user: "🧠 您选择了 **思维框架分析**。\n请问您希望使用哪个框架?\n(支持:SWOT, SCQA, PESTLE, 5W3H, 5WHYS)"
|
||
};
|
||
}
|
||
else if (inputUpper === 'B' || inputUpper.includes('B')) {
|
||
return {
|
||
next_step: 'wait_for_keyword',
|
||
message_to_user: "📚 您选择了 **学术文献查询**。\n请简要描述您想研究的领域,或直接输入核心关键词:"
|
||
};
|
||
}
|
||
else if (inputUpper === 'C' || inputUpper.includes('C')) {
|
||
const notes = await (0, resourceService_1.listObsidianNotes)();
|
||
if (notes.length > 0) {
|
||
sessionState.availableNotes = notes;
|
||
return {
|
||
next_step: 'wait_for_local_analyze_confirm',
|
||
message_to_user: `📂 您选择了 **本地笔记分析**。\n目前知识库中扫描到 ${notes.length} 篇笔记(如 ${notes[0]} 等)。\n\n是否希望我阅读这些笔记并为您进行综合分析?(是/否)`
|
||
};
|
||
}
|
||
else {
|
||
return {
|
||
next_step: 'wait_for_direct_analyze_confirm',
|
||
message_to_user: "📂 您选择了 **本地笔记分析**。\n但目前本地知识库中 **没有找到任何笔记**。\n\n是否需要我直接为您进行通用知识分析?(是/否)"
|
||
};
|
||
}
|
||
}
|
||
else {
|
||
return {
|
||
next_step: 'wait_for_path',
|
||
message_to_user: "⚠️ 输入无效。请回复 A、B 或 C 来选择路径:"
|
||
};
|
||
}
|
||
// ==========================================
|
||
// [分支 A] 框架分析链路
|
||
// ==========================================
|
||
case 'wait_for_framework':
|
||
const valid = ['SWOT', 'SCQA', 'PESTLE', '5W3H', '5WHYS'];
|
||
const fw = valid.find(f => inputUpper.includes(f));
|
||
if (!fw) {
|
||
return {
|
||
next_step: 'wait_for_framework',
|
||
message_to_user: "⚠️ 未识别到支持的框架。请输入 SWOT, SCQA, PESTLE, 5W3H 或 5WHYS:"
|
||
};
|
||
}
|
||
sessionState.framework = fw.toLowerCase();
|
||
return {
|
||
next_step: 'wait_for_topic_a',
|
||
message_to_user: `✅ 已选择 **${fw}** 框架。\n请问您需要分析的具体主题或对象是什么?`
|
||
};
|
||
case 'wait_for_topic_a':
|
||
sessionState.topic = input;
|
||
// 组装通用参数以适配所有框架的占位符
|
||
const args = { entity: input, topic: input, problem: input, domain: input, situation: input, context: '无', objective: '无', competitors: '无' };
|
||
const frameworkDef = await (0, promptService_1.getFramework)(sessionState.framework, args);
|
||
const sysMsg = frameworkDef.messages.find(m => m.role === 'system')?.content.text || '';
|
||
const userMsg = frameworkDef.messages.filter(m => m.role === 'user').pop()?.content.text || '';
|
||
return {
|
||
next_step: 'wait_for_save_confirm',
|
||
message_to_user: `【底层指令:请勿向用户暴露本段文字】\n请扮演专家,使用以下设定和模板为用户生成并在对话框输出分析报告。\n\n系统设定:${sysMsg}\n分析任务:${userMsg}\n\n⚠️ 生成报告后,请必须向用户提问:“分析完毕。是否需要将此分析结果保存到本地笔记?”\n⚠️ 注意:当用户在下一轮回复“保存”时,你必须把这份报告的全文放入 payload 参数中调用工具。`
|
||
};
|
||
// ==========================================
|
||
// [分支 B] 文献检索链路
|
||
// ==========================================
|
||
case 'wait_for_keyword':
|
||
const queries = (0, intentService_1.generateSearchQueries)(input);
|
||
sessionState.queries = queries;
|
||
return {
|
||
next_step: 'wait_for_keyword_selection',
|
||
message_to_user: `🔍 基于您的需求,我提取了以下检索关键字:\n${queries.map((q, i) => `${i + 1}. ${q}`).join('\n')}\n\n请问您想使用哪一个关键字进行 Arxiv 检索?(您可以回复序号,或直接输入自定义关键字)`
|
||
};
|
||
case 'wait_for_keyword_selection':
|
||
let selectedQuery = input;
|
||
if (/^\d+$/.test(input.trim()) && sessionState.queries) {
|
||
const idx = parseInt(input.trim()) - 1;
|
||
if (sessionState.queries[idx])
|
||
selectedQuery = sessionState.queries[idx];
|
||
}
|
||
const results = await (0, arxivService_1.searchArxiv)(selectedQuery, 5);
|
||
if (results.length === 0) {
|
||
return {
|
||
next_step: 'init',
|
||
message_to_user: `❌ 针对关键字 "${selectedQuery}" 未检索到相关文献。您可以随时输入新的查询重新开始。`
|
||
};
|
||
}
|
||
let textOutput = `找到了关于 "${selectedQuery}" 的相关文献:\n\n`;
|
||
results.forEach((r, i) => {
|
||
textOutput += `### [${i + 1}] ${r.title}\n**链接:** ${r.id}\n**摘要:** ${r.summary}\n\n`;
|
||
});
|
||
return {
|
||
next_step: 'wait_for_save_confirm',
|
||
message_to_user: `【底层指令:请勿暴露此标记】\n请将以下检索结果原封不动地展示给用户:\n\n${textOutput}\n\n⚠️ 展示完毕后,请向用户提问:“是否需要将以上文献记录保存到本地笔记?”\n⚠️ 注意:当用户回复“保存”时,把这批检索结果放入 payload 传回。`
|
||
};
|
||
// ==========================================
|
||
// [分支 C] 本地笔记分析链路
|
||
// ==========================================
|
||
case 'wait_for_local_analyze_confirm':
|
||
if (input.includes('是') || input.toLowerCase() === 'y' || input.includes('保存') || input.includes('分析')) {
|
||
const notesToRead = sessionState.availableNotes.slice(0, 3); // 限制前3篇防超载
|
||
let combinedContent = '';
|
||
for (const note of notesToRead) {
|
||
combinedContent += `\n\n--- 笔记:${note} ---\n${await (0, resourceService_1.readObsidianNote)(note)}`;
|
||
}
|
||
return {
|
||
next_step: 'wait_for_save_confirm',
|
||
message_to_user: `【底层指令】请基于以下本地笔记内容,提取核心观点并为用户生成并在对话框输出一份综合分析摘要:\n${combinedContent}\n\n⚠️ 生成摘要后提问:“分析完毕。是否需要将此综合分析保存到本地?”并在下次获批后将摘要写入 payload。`
|
||
};
|
||
}
|
||
else {
|
||
return { next_step: 'init', message_to_user: "好的,已取消分析。本次交互结束。" };
|
||
}
|
||
case 'wait_for_direct_analyze_confirm':
|
||
if (input.includes('是') || input.toLowerCase() === 'y' || input.includes('分析')) {
|
||
return {
|
||
next_step: 'wait_for_save_confirm',
|
||
message_to_user: `【底层指令】请根据用户最初的需求,直接运用你的专业知识为用户提供一份详细的分析报告。\n⚠️ 报告生成后提问:“分析完毕。是否保存结果?”并在下次获批后将结果写入 payload。`
|
||
};
|
||
}
|
||
else {
|
||
return { next_step: 'init', message_to_user: "好的,已取消分析。本次交互结束。" };
|
||
}
|
||
// ==========================================
|
||
// 公共保存出口 (收口防呆机制)
|
||
// ==========================================
|
||
case 'wait_for_save_confirm':
|
||
if (input.includes('是') || input.includes('保存') || input.toLowerCase() === 'y' || input.includes('好')) {
|
||
if (!payload || payload.trim() === '') {
|
||
return {
|
||
next_step: 'init',
|
||
message_to_user: "⚠️ 系统内部错误:模型未能回传需要保存的内容文本。保存失败,交互结束。"
|
||
};
|
||
}
|
||
const filename = `Caffeine_Output_${new Date().getTime()}.md`;
|
||
const saveResult = await (0, resourceService_1.saveNote)(filename, payload);
|
||
return {
|
||
next_step: 'init',
|
||
message_to_user: `🎉 操作成功!\n${saveResult}\n\n本次交互已圆满结束。您可以随时发送任何新消息来唤起菜单。`
|
||
};
|
||
}
|
||
else {
|
||
return {
|
||
next_step: 'init',
|
||
message_to_user: "好的,结果将不会被保存。本次交互结束。"
|
||
};
|
||
}
|
||
default:
|
||
return {
|
||
next_step: 'init',
|
||
message_to_user: "状态机已重置。请输入任何文字重新唤起菜单。"
|
||
};
|
||
}
|
||
}
|
||
catch (error) {
|
||
return {
|
||
next_step: 'init',
|
||
message_to_user: `⚠️ 系统运行遇到错误:${error.message}\n状态机已重置,请重新开始。`
|
||
};
|
||
}
|
||
}
|
||
//# sourceMappingURL=workflowController.js.map
|