Files
Project-Caffeine/projects/arabica/src/sprint3/dist/controllers/workflowController.js
2026-03-11 12:54:12 +08:00

192 lines
12 KiB
JavaScript
Raw 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.
"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