/** * 书籍生成工作流引擎 * 参考 LangGraph 思路:状态机 + 条件边 + 节点执行 */ import { Book, BookOutline, Chapter, GenerateTask } from './book-generator.types'; import { bookGeneratorService } from './book-generator.service'; import { bookStore } from './book-generator.store'; // ============ 工作流状态 ============ /** 工作流节点类型 */ export type WorkflowNode = 'idle' | 'planning' | 'writing_chapter' | 'writing_foreword' | 'writing_afterword' | 'finished' | 'failed'; /** 工作流边类型 */ export type WorkflowEdge = | 'plan_next' // 规划下一步 | 'write_next_chapter' // 写下一章 | 'finish_chapter' // 本章写完 | 'all_chapters_done' // 全部章节写完 | 'finish_foreword' // 前言写完 | 'finish_afterword' // 后记写完 | 'error'; // 发生错误 /** 工作流状态 */ export interface WorkflowState { bookId: string; currentNode: WorkflowNode; currentChapter: number; // 当前在写第几章 totalChapters: number; completedChapters: number; pendingChapters: number[]; // 待生成的章节列表 failedChapters: number[]; // 失败的章节列表 phase: 'planning' | 'writing' | 'supplement' | 'done'; error?: string; startedAt: Date; updatedAt: Date; } // ============ 工作流引擎 ============ class BookWorkflowEngine { private workflows: Map = new Map(); /** * 启动工作流 */ startWorkflow(bookId: string, totalChapters: number): WorkflowState { const state: WorkflowState = { bookId, currentNode: 'idle', currentChapter: 0, totalChapters, completedChapters: 0, pendingChapters: Array.from({ length: totalChapters }, (_, i) => i + 1), failedChapters: [], phase: 'planning', startedAt: new Date(), updatedAt: new Date(), }; this.workflows.set(bookId, state); return state; } /** * 获取工作流状态 */ getState(bookId: string): WorkflowState | undefined { return this.workflows.get(bookId); } /** * 更新工作流状态 */ private updateState(bookId: string, updates: Partial): WorkflowState | undefined { const state = this.workflows.get(bookId); if (!state) return undefined; Object.assign(state, updates, { updatedAt: new Date() }); return state; } /** * 执行规划阶段 */ async executePlanningPhase(bookId: string): Promise<{ success: boolean; outline?: BookOutline; error?: string }> { const state = this.workflows.get(bookId); if (!state) return { success: false, error: '工作流不存在' }; this.updateState(bookId, { currentNode: 'planning', phase: 'planning' }); try { const outline = await bookGeneratorService.generateOutline(bookId); // 更新工作流状态 this.updateState(bookId, { currentNode: 'idle', phase: 'writing', pendingChapters: outline.chapters.map(c => c.number), }); return { success: true, outline }; } catch (error) { const errorMessage = error instanceof Error ? error.message : '规划失败'; this.updateState(bookId, { currentNode: 'failed', error: errorMessage }); return { success: false, error: errorMessage }; } } /** * 执行章节生成 - 单步 * 返回下一步应该执行什么 */ async executeNextChapter(bookId: string): Promise<{ done: boolean; chapter?: Chapter; nextChapter?: number; error?: string; }> { const state = this.workflows.get(bookId); if (!state) return { done: false, error: '工作流不存在' }; if (state.pendingChapters.length === 0) { return { done: true }; } // 取下一个待生成的章节 const nextChapterNum = state.pendingChapters[0]; this.updateState(bookId, { currentNode: 'writing_chapter', currentChapter: nextChapterNum, }); try { const chapter = await bookGeneratorService.generateChapter(bookId, nextChapterNum); // 更新工作流状态 const newPending = state.pendingChapters.filter(n => n !== nextChapterNum); this.updateState(bookId, { currentNode: 'idle', completedChapters: state.completedChapters + 1, pendingChapters: newPending, }); if (newPending.length === 0) { // 全部章节完成 this.updateState(bookId, { phase: 'supplement' }); return { done: true, chapter }; } return { done: false, chapter, nextChapter: newPending[0] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : '生成失败'; // 标记本章失败,继续下一章 const newPending = state.pendingChapters.filter(n => n !== nextChapterNum); const newFailed = [...state.failedChapters, nextChapterNum]; this.updateState(bookId, { currentNode: 'idle', pendingChapters: newPending, failedChapters: newFailed, }); if (newPending.length === 0) { return { done: true, error: `第${nextChapterNum}章失败` }; } return { done: false, error: `第${nextChapterNum}章失败: ${errorMessage}` }; } } /** * 执行全部章节生成(自动循环) */ async executeAllChapters(bookId: string, onProgress?: (progress: number, chapterNum: number) => void): Promise<{ success: boolean; completedCount: number; failedCount: number; errors: string[]; }> { const state = this.workflows.get(bookId); if (!state) return { success: false, completedCount: 0, failedCount: 0, errors: ['工作流不存在'] }; const errors: string[] = []; while (true) { const currentState = this.workflows.get(bookId); if (!currentState || currentState.pendingChapters.length === 0) { break; } const nextChapter = currentState.pendingChapters[0]; if (onProgress) { const progress = Math.round((currentState.completedChapters / currentState.totalChapters) * 100); onProgress(progress, nextChapter); } try { await bookGeneratorService.generateChapter(bookId, nextChapter); const updated = this.workflows.get(bookId); if (updated) { this.updateState(bookId, { completedChapters: updated.completedChapters + 1, pendingChapters: updated.pendingChapters.filter(n => n !== nextChapter), }); } } catch (error) { const errorMessage = error instanceof Error ? error.message : '生成失败'; errors.push(`第${nextChapter}章: ${errorMessage}`); const updated = this.workflows.get(bookId); if (updated) { this.updateState(bookId, { pendingChapters: updated.pendingChapters.filter(n => n !== nextChapter), failedChapters: [...updated.failedChapters, nextChapter], }); } } } this.updateState(bookId, { phase: 'supplement' }); const finalState = this.workflows.get(bookId); return { success: errors.length === 0, completedCount: finalState?.completedChapters || 0, failedCount: finalState?.failedChapters?.length || 0, errors, }; } /** * 生成前言 */ async executeForeword(bookId: string): Promise<{ success: boolean; foreword?: string; error?: string }> { this.updateState(bookId, { currentNode: 'writing_foreword' }); try { const foreword = await bookGeneratorService.generateForeword(bookId); this.updateState(bookId, { currentNode: 'idle' }); return { success: true, foreword }; } catch (error) { const errorMessage = error instanceof Error ? error.message : '生成前言失败'; return { success: false, error: errorMessage }; } } /** * 生成后记 */ async executeAfterword(bookId: string): Promise<{ success: boolean; afterword?: string; error?: string }> { this.updateState(bookId, { currentNode: 'writing_afterword' }); try { const afterword = await bookGeneratorService.generateAfterword(bookId); this.updateState(bookId, { currentNode: 'idle' }); return { success: true, afterword }; } catch (error) { const errorMessage = error instanceof Error ? error.message : '生成后记失败'; return { success: false, error: errorMessage }; } } /** * 完成工作流 */ finishWorkflow(bookId: string): WorkflowState | undefined { const state = this.workflows.get(bookId); if (!state) return undefined; this.updateState(bookId, { currentNode: 'finished', phase: 'done' }); return this.workflows.get(bookId); } /** * 获取工作流进度 */ getProgress(bookId: string): { phase: string; currentNode: string; currentChapter: number; completedChapters: number; totalChapters: number; pendingChapters: number[]; failedChapters: number[]; progress: number; } | null { const state = this.workflows.get(bookId); if (!state) return null; return { phase: state.phase, currentNode: state.currentNode, currentChapter: state.currentChapter, completedChapters: state.completedChapters, totalChapters: state.totalChapters, pendingChapters: state.pendingChapters, failedChapters: state.failedChapters, progress: Math.round((state.completedChapters / state.totalChapters) * 100), }; } /** * 取消工作流 */ cancelWorkflow(bookId: string): boolean { return this.workflows.delete(bookId); } } // 导出单例 export const workflowEngine = new BookWorkflowEngine();