/** * 导入小说工作流定义 * * 逆向推演全流程: * 步骤1: 写入正文 + 构建知识库 * 步骤2: 向量采样 + AI 推演全局配置/架构/角色 * 步骤3: AI 按章推演精准蓝图 + 蓝图入向量库 + 拼装轻量全局摘要 * 步骤4: 完成后处理(刷新 UI 状态) */ import type { WorkflowDefinition } from '../../stores/workflow-store' import type { ImportedChapter } from './commands/import-novel.command' export interface ImportWorkflowParams { /** 拆分后的章节数据 */ chapters: ImportedChapter[] } /** * 创建导入小说工作流 */ export function createImportWorkflow(params: ImportWorkflowParams): WorkflowDefinition { return { type: 'novel_import', title: `📥 导入小说(${params.chapters.length} 章)`, steps: [ // ===== 步骤 1: 写入正文 + 构建知识库 ===== { name: '写入正文与构建知识库', description: `将 ${params.chapters.length} 章正文写入 manuscript/ 并灌入向量知识库`, executor: async (step, context, callbacks) => { const { ImportInitializeCommand } = await import('./commands/import-novel.command') const cmd = new ImportInitializeCommand(params.chapters) return cmd.execute({ step, context, callbacks }) }, }, // ===== 步骤 2: 向量采样 + AI 推演全局设定 ===== { name: 'AI 推演全局配置与架构', description: '通过向量检索关键片段,AI 推演小说配置、故事架构、角色卡', executor: async (step, context, callbacks) => { const { InferGlobalSettingsCommand } = await import('./commands/import-novel.command') const cmd = new InferGlobalSettingsCommand() return cmd.execute({ step, context, callbacks }) }, }, // ===== 步骤 3: AI 按章推演蓝图 + 蓝图入向量库 + 拼装摘要 ===== { name: 'AI 逐章推演蓝图', description: `逐章推演蓝图 + 蓝图要点入向量库 + 拼装全局摘要(共 ${params.chapters.length} 章)`, executor: async (step, context, callbacks) => { const { InferBlueprintsPerChapterCommand } = await import('./commands/import-novel.command') const cmd = new InferBlueprintsPerChapterCommand() return cmd.execute({ step, context, callbacks }) }, }, // ===== 步骤 4: 完成后处理 ===== { name: '完成后处理', description: '刷新项目状态,加载角色卡与蓝图数据', executor: async (_step, _context, callbacks) => { callbacks.log('🔄 正在刷新项目数据...') callbacks.setProgress(30) // 刷新文件树 const { useProjectStore } = await import('../../stores/project-store') await useProjectStore.getState().refreshFileTree() // 加载角色卡 try { const { useCharacterStore } = await import('../../stores/character-store') const project = useProjectStore.getState().currentProject if (project) { await useCharacterStore.getState().loadCharacters(project.path) } } catch { /* 忽略 */ } // 加载草稿索引 try { const { useDraftStore } = await import('../../stores/draft-store') await useDraftStore.getState().loadAllDrafts() } catch { /* 忽略 */ } callbacks.log('🎉 小说导入全流程完成!所有数据已就位。') callbacks.setProgress(100) }, }, ], onComplete: { mode: 'silent', message: '🎉 小说导入完成!全部结构化数据已生成,可以开始续写了。', }, } } /** * 预估导入的 Token 消耗 * @param totalWords 总字数 * @param chapterCount 章节数 * @returns 预估信息 */ export function estimateImportCost(_totalWords: number, chapterCount: number): { estimatedTokens: number estimatedMinutes: number breakdown: string } { // 粗略预估(偏保守): // 1. 全局推演:首章+末章(约6000字) × 2(输入+输出) ≈ 12000 tokens // 2. 按章蓝图:每章正文(平址3000字) + 蓝图输出(约500字) ≈ 3500 tokens/章 // 3. 全局摘要:从蓝图拼装,零 LLM 调用 // 4. 知识库向量化不计入 LLM Token const globalInferTokens = 15000 const blueprintTokensPerChapter = 4000 const totalBlueprintTokens = blueprintTokensPerChapter * chapterCount const estimatedTokens = globalInferTokens + totalBlueprintTokens // 预估时间(假设每次 LLM 调用约 8-15 秒,并发 3) const llmCallCount = 1 + Math.ceil(chapterCount / 3) // 全局推演 + 蓝图批次 const estimatedMinutes = Math.ceil(llmCallCount * 12 / 60) // 按每次 12 秒计算 const breakdown = [ `· 全局推演:~${(globalInferTokens / 1000).toFixed(0)}K tokens`, `· 蓝图推演:~${(totalBlueprintTokens / 1000).toFixed(0)}K tokens(${chapterCount} 章 × ${(blueprintTokensPerChapter / 1000).toFixed(1)}K)`, `· 全局摘要:零消耗(从蓝图拼装)`, `· 总计:~${(estimatedTokens / 1000).toFixed(0)}K tokens`, ].join('\n') return { estimatedTokens, estimatedMinutes, breakdown } }