client = new ApiClient($backend, get_option('pk_active_pool', 'default'));\r } else {\r $this->client = ApiClient::make();\r }\r $this->is_licensed = PKStoryCacheHelper::check_cache_integrity();\r }\r \r /* ─────────────────────────────────────────────────────────────────────────\r * PUBLIC API\r * ───────────────────────────────────────────────────────────────────────── */\r \r /**\r * Generate a full outline for a novel.\r */\r public function generate_outline(array $params): array {\r $title = sanitize_text_field($params['title'] ?? '');\r $genre = sanitize_text_field($params['genre'] ?? 'Tiên hiệp');\r $sub_genres= (array) ($params['sub_genres'] ?? []);\r $chapters = max(1, intval($params['total_chapters'] ?? 20));\r $char = sanitize_textarea_field($params['character_setting'] ?? '');\r $world = sanitize_textarea_field($params['world_setting'] ?? '');\r if (empty($world)) {\r $world = Settings::get('default_world_setting', '');\r }\r $plot = sanitize_textarea_field($params['plot_idea'] ?? '');\r \r if (!$this->is_licensed) return $this->error('âš ï¸ Vui lòng kích hoạt bản quyá» n để sá»­ dụng tính năng này!');\r \r $project_id = isset($params['project_id']) ? intval($params['project_id']) : 0;\r if ($project_id) {\r $project = Database::get_project($project_id);\r if ($project && ($project['engine'] ?? 'legacy') === 'ainovel') {\r return $this->error('⚠️ Dự án này đang sử dụng động cơ Go (ainovel-cli). Dàn ý sẽ được tự động thiết lập và quy hoạch bởi tác nhân Architect.');\r }\r }\r \r if (!$title) return $this->error('Vui lòng nhập tiêu đỠtruyện.');\r if (!$char) return $this->error('Vui lòng nhập thiết lập nhân vật.');\r if (!$world) return $this->error('Vui lòng nhập thiết lập thế giá»›i.');\r if (!$plot) return $this->error('Vui lòng nhập ý tưởng cốt truyện.');\r \r $sub_str = implode(', ', $sub_genres);\r \r $system = Settings::get('outline_system_prompt', "Bạn là một nhà văn chuyên nghiệp viết tiểu thuyết mạng. Nhiệm vụ của bạn là tạo ra một bản dàn ý tiểu thuyết chi tiết, hấp dẫn và có cấu trúc rõ ràng. Mỗi chương phải có tiêu đề sáng tạo và mô tả tình huống cụ thể. Định dạng: 'Chương N: Tiêu đề - Tóm tắt nội dung'.");\r \r $user = "Hãy tạo bản dàn ý gồm {$chapters} chương cho tiêu thuyết sau:\r Tiêu đỠ: {$title}\r Thể loại: {$genre}" . ($sub_str ? "\ Chá»§ đỠphụ: {$sub_str}" : '') . "\r Nhân vật: {$char}\r Thế giá»›i quan: {$world}\r Cốt truyện: {$plot}\r \r Yêu cầu: Má»—i dòng là má»™t chương theo định dạng 'Chương N: Tiêu đề - Mô tả tóm tắt'. Phải có đúng {$chapters} chương.";\r \r $max_tokens = intval(get_option('pk_max_tokens', 16384));\r return $this->client->generate(\r [['role' => 'system', 'content' => $system], ['role' => 'user', 'content' => $user]],\r 0.8, $max_tokens\r );\r }\r \r /**\r * Build the structured system and user prompts/messages for chapter generation.\r * Shared between NovelGenerator (cron/background) and StreamHandler (real-time SSE streaming).\r */\r public function build_chapter_messages(array $params): array {\r $project_id = intval($params['project_id'] ?? 0);\r $chapter_num = intval($params['chapter_num'] ?? 1);\r $chapter_title= sanitize_text_field($params['chapter_title'] ?? '');\r $chapter_title= trim(str_replace('*', '', $chapter_title));\r $chapter_desc = sanitize_textarea_field($params['chapter_desc'] ?? '');\r \r // Load project context\r $project = $project_id ? Database::get_project($project_id) : null;\r \r // v6.0: Multi-agent Brainstorming AI\r $use_brainstorm = !empty($params['use_brainstorm']);\r $brainstorm_text = '';\r if ($use_brainstorm && $project_id) {\r $brainstorm_res = $this->brainstorm_chapter_scenes([\r 'project_id' => $project_id,\r 'chapter_num' => $chapter_num,\r 'chapter_title' => $chapter_title,\r 'chapter_desc' => $chapter_desc,\r ]);\r if ($brainstorm_res['success'] && !empty($brainstorm_res['content'])) {\r $brainstorm_text = $brainstorm_res['content'];\r }\r }\r // v5.3: Multi-chapter context — lấy 2-3 chương trước thay vì chỉ 1\r $prev_content = '';\r $prev_prev_content = '';\r if ($project_id && $chapter_num > 1) {\r $prev = Database::get_chapter($project_id, $chapter_num - 1);\r if ($prev) $prev_content = mb_substr($prev['content'], -4000);\r // Lấy thêm chương N-2 (đầu + cuối)\r if ($chapter_num > 2) {\r $prev2 = Database::get_chapter($project_id, $chapter_num - 2);\r if ($prev2) {\r $p2_content = $prev2['content'];\r $prev_prev_content = mb_substr($p2_content, 0, 500) . "\ ...\ " . mb_substr($p2_content, -500);\r }\r }\r }\r \r // AI long-term memory integration using Mnemosyne (Siêu thông minh!)\r $memory_context = '';\r if ($project_id && get_option('pk_enable_memory', 1)) {\r // Tối ưu hóa truy xuất ký ức (Memory Recall Expansion):\r // Quét các nhân vật và lore quan trọng xuất hiện trong mô tả chương hiện tại hoặc chương liền trước\r $search_entities = [$chapter_title, $chapter_desc];\r \r // Quét nhân vật\r $characters = CharacterBible::get_all($project_id);\r $scan_text = $chapter_title . ' ' . $chapter_desc . ' ' . $prev_content;\r if (!empty($characters)) {\r foreach ($characters as $char) {\r if (preg_match('/' . preg_quote($char['name'], '/') . '/iu', $scan_text)) {\r $search_entities[] = $char['name'];\r }\r if (!empty($char['alias'])) {\r $aliases = array_filter(array_map('trim', explode(',', $char['alias'])));\r foreach ($aliases as $al) {\r if (!empty($al) && preg_match('/' . preg_quote($al, '/') . '/iu', $scan_text)) {\r $search_entities[] = $al;\r break;\r }\r }\r }\r }\r }\r // Quét lorebook\r $lore_entries = Database::get_lore_entries($project_id);\r if (!empty($lore_entries)) {\r foreach ($lore_entries as $entry) {\r if (preg_match('/' . preg_quote($entry['keyword'], '/') . '/iu', $scan_text) || preg_match('/' . preg_quote($entry['title'], '/') . '/iu', $scan_text)) {\r $search_entities[] = $entry['keyword'];\r $search_entities[] = $entry['title'];\r }\r }\r }\r // Gộp làm chuỗi truy vấn để Mnemosyne trả về các ký ức dài hạn có liên quan trực tiếp\r $query_text = implode(' ', array_filter(array_unique($search_entities)));\r \r $mem_res = MemoryManager::recall($project_id, $query_text, 20); // Tăng top_k lên 20 để phủ rộng toàn bộ lịch sử\r if (!empty($mem_res['success']) && !empty($mem_res['memories'])) {\r // Sắp xếp ký ức theo dòng thời gian chương tăng dần\r $sorted_memories = MemoryManager::sort_memories_chronologically($mem_res['memories']);\r \r $global_memories = [];\r $story_memories = [];\r \r foreach ($sorted_memories as $mem) {\r if (empty($mem['text'])) continue;\r \r // Nhận diện ký ức chương hay thiết lập cố định\r if (preg_match('/(?:Tại\s+)?Chương\s+(\d+)/iu', $mem['text'])) {\r $story_memories[] = $mem['text'];\r } else {\r $global_memories[] = $mem['text'];\r }\r }\r \r $memory_context = "[KÝ ỨC DÀI HẠN & BỐI CẢNH LIÊN QUAN TRƯỚC ĐÓ]\ ";\r if (!empty($global_memories)) {\r $memory_context .= "• Thiết lập nhân vật & bối cảnh cố định:\ ";\r foreach ($global_memories as $g_mem) {\r $memory_context .= " - " . trim($g_mem) . "\ ";\r }\r }\r if (!empty($story_memories)) {\r $memory_context .= "• Diễn biến dòng thời gian cốt truyện trước đó:\ ";\r foreach ($story_memories as $s_mem) {\r $memory_context .= " - " . trim($s_mem) . "\ ";\r }\r }\r }\r }\r \r $novel_title = $project['title'] ?? sanitize_text_field($params['novel_title'] ?? '');\r $char = $project['character_setting'] ?? sanitize_textarea_field($params['character_setting'] ?? '');\r $world = $project['world_setting'] ?? sanitize_textarea_field($params['world_setting'] ?? '');\r $plot = $project['plot_idea'] ?? sanitize_textarea_field($params['plot_idea'] ?? '');\r $genre = $project['genre'] ?? sanitize_text_field($params['genre'] ?? '');\r $outline = $project['outline_text'] ?? sanitize_textarea_field($params['outline_text'] ?? '');\r $custom = sanitize_textarea_field($params['custom_prompt'] ?? '');\r \r $target_words= intval(get_option('pk_target_words', 4000));\r \r // v5.1: Smart selective context retrieval for Characters and Lore\r $smart_character_context = '';\r $smart_lore_context = '';\r if ($project_id) {\r $scan_text = $chapter_title . ' ' . $chapter_desc;\r if ($prev_content) {\r $scan_text .= ' ' . mb_substr($prev_content, -1000);\r }\r $smart_character_context = $this->build_smart_character_context($project_id, $scan_text);\r $smart_lore_context = $this->build_smart_lore_context($project_id, $scan_text);\r }\r \r // v5.2: Timeline Recap tích lũy\r $timeline_recap_text = '';\r if ($project_id) {\r $raw_recap = Database::get_timeline_recap($project_id);\r if (!empty($raw_recap)) {\r $timeline_recap_text = "[DÒNG THỜI GIAN CỐT TRUYỆN - TÓM TẮT CÁC CHƯƠNG Đà QUA]\ "\r . "⚠️ QUY TẮC BẮT BUỘC: Bạn phải viết tiếp nối đúng dòng thời gian này. Tuyệt đối không phủ nhận hoặc làm mâu thuẫn bất kỳ sự thật lịch sử nào đã được ghi nhận ở dưới.\ "\r . $raw_recap;\r }\r }\r \r // v5.3: Relationship Map — Bản đồ quan hệ nhân vật tự động\r $relationship_map_text = '';\r if ($project_id) {\r $relationship_map_text = Database::get_relationship_map_text($project_id);\r }\r \r // v5.3: Anti-Repetition Guard — Phát hiện và cấm lặp cụm từ/mẫu câu\r $anti_repetition_text = '';\r if ($project_id && $chapter_num > 1) {\r $lookback = intval(Settings::get('anti_repetition_lookback', 5));\r $repeated = MemoryManager::extract_repeated_phrases($project_id, $chapter_num, $lookback);\r if (!empty($repeated)) {\r $anti_repetition_text = "\ \ [CẢNH BÁO CHỐNG LẶP VĂN — ANTI-REPETITION]\ ";\r $anti_repetition_text .= "⚠️ Các cụm từ/câu mở đầu sau đã xuất hiện trong các chương trước. TUYỆT ĐỐI KHÔNG sử dụng lại:\ ";\r foreach ($repeated as $phrase) {\r $anti_repetition_text .= " ❌ \"{$phrase}\"\ ";\r }\r $anti_repetition_text .= "Hãy sáng tạo cách diễn đạt MỚI HOÀN TOÀN, dùng từ ngữ và cấu trúc câu khác biệt.\ ";\r }\r }\r \r $style = $project ? ($project['writing_style'] ?? 'default') : 'default';\r \r $style_instructions = [\r 'default' => Settings::get('style_instruction_default', "Bạn là một nhà văn tiểu thuyết mạng chuyên nghiệp. Hãy viết nội dung chương truyện theo yêu cầu, văn phong mượt mà, hấp dẫn, giàu chi tiết, xúc cảm."),\r 'tien_hiep' => Settings::get('style_instruction_tien_hiep', "Bạn là đại sư văn học kỳ ảo cổ phong Trung Hoa. Hãy viết truyện bằng ngôn từ đậm chất tiên hiệp/kiếm hiệp cổ kính, sử dụng từ Hán-Việt tinh tế, tả cảnh kỳ vĩ thơ mộng, hành văn trang nghiêm thanh tao."),\r 'ngon_tinh' => Settings::get('style_instruction_ngon_tinh', "Bạn là nhà văn ngôn tình đô thị xuất sắc. Hãy hành văn nhẹ nhàng sâu lắng, chú trọng miêu tả diễn biến tâm lý nhân vật tinh tế, cử chỉ, biểu cảm và những đoạn hội thoại đời thường giàu xúc cảm."),\r 'kinh_di' => Settings::get('style_instruction_kinh_di', "Bạn là tiểu thuyết gia kinh dị giật gân hồi hộp. Hãy sử dụng nhịp điệu dồn dập xen kẽ những đoạn tả chậm, tập trung miêu tả cảm giác lạnh lẽo, bầu không khí u ám, âm thanh ma mị và chi tiết kịch tính."),\r 'khoa_huyen' => Settings::get('style_instruction_khoa_huyen', "Bạn là nhà văn khoa học viễn tưởng / cyberpunk. Hãy viết bằng nhịp điệu nhanh, dứt khoát, lồng ghép các thuật ngữ khoa học công nghệ, mô tả bối cảnh máy móc, neon và góc nhìn lạnh lùng khách quan."),\r 'hai_huoc' => Settings::get('style_instruction_hai_huoc', "Bạn là tác giả truyện trào phúng, hài hước, thể loại hệ thống. Hãy viết với giọng điệu vui vẻ, tự giễu, sử dụng ngôn ngữ mạng hóm hỉnh hiện đại, tạo ra các tình huống dở khóc dở cười sinh động."),\r 'anime' => Settings::get('style_instruction_anime', "Bạn là dịch giả/nhà văn light novel, đồng nhân phong cách Nhật Bản/phương Tây. Hành văn sinh động, nhiều câu thoại biểu cảm sống động, nhịp điệu nhanh, từ ngữ phóng khoáng giàu tính hành động.")\r ];\r \r $system_instruction = $style_instructions[$style] ?? $style_instructions['default'];\r \r $system = $system_instruction . "\ \ " .\r "=== NGUYÊN TẮC NHẤT QUÁN CỐT TRUYỆN (BẮT BUỘC THỰC HIỆN): ===\ " .\r "⚠️ BẮT BUỘC ĐỌC LẠI TOÀN BỘ HỒ SƠ VÀ DỮ LIỆU TRƯỚC KHI VIẾT: Trước khi bắt đầu viết bất kỳ từ nào của chương mới, bạn PHẢI đọc và nghiên cứu kỹ lưỡng Dàn ý truyện, Danh sách tên chương truyện cùng phần 'DỮ LIỆU BỐI CẢNH & HỒ SƠ CHI TIẾT' phía dưới (bao gồm: Dòng thời gian cốt truyện và các tình tiết đã qua, Bản đồ quan hệ nhân vật, Hồ sơ nhân vật, Lorebook công pháp/tu vi, Ký ức dài hạn bổ sung). Bạn phải nắm rõ cốt truyện, dàn ý, các tên chương và vị trí của chương hiện tại so với các chương khác để viết đảm bảo tính logic văn phong và kết cấu mạch lạc cao nhất.\ " .\r "1. KHÔNG MÂU THUẪN VỚI QUÁ KHỨ: Đọc kỹ phần 'Dòng thời gian cốt truyện' và 'Ký ức bổ sung'. Mọi sự kiện đã diễn ra là sự thật lịch sử của truyện, tuyệt đối không được viết trái ngược lại. Đặc biệt chú ý các tình tiết nền móng ở các chương đầu tiên (Chương 1, 2, 3...) như ân nhân, kẻ thù, sư môn, năng lực ban đầu - đây là gốc rễ của nhân vật và tuyệt đối không được viết mâu thuẫn hay bỏ quên động cơ cốt lõi này.\ " .\r "2. QUẢN LÝ TRẠNG THÁI NHÂN VẬT CHẶT CHẼ:\ " .\r " - Nhân vật Đà CHẾT hoặc BIẾN MẤT (trong danh sách tương ứng) TUYỆT ĐỐI không được xuất hiện trực tiếp, không được đối thoại hay chiến đấu. Chỉ được nhắc đến qua suy nghĩ, ký ức hoặc lời kể của người khác.\ " .\r " - Nhân vật Đà QUEN BIẾT từ trước tuyệt đối không viết lại cảnh 'lần đầu gặp gỡ', 'bỡ ngỡ' hay giới thiệu lại tên tuổi một cách xa lạ.\ " .\r "3. TUÂN THỦ HỆ THỐNG THẾ GIỚI QUAN: Giữ đúng quy luật thế giới quan, cấp độ tu luyện/sức mạnh, bang hội đã thiết lập. Không tự sáng chế ra các cấp bậc sức mạnh phi logic hoặc xung đột với Lorebook.\ " .\r "4. LIÊN TỤC VÀ KHÔNG LẶP LẠI:\ " .\r " - Hãy viết tiếp nối trực tiếp, mượt mà từ đoạn văn cuối cùng của chương trước ([Cuối chương trước]). Tránh lặp lại câu từ hoặc ý tự của đoạn kết đó.\ " .\r " - Không lặp lại các tình huống hội thoại cũ, không để nhân vật hỏi đi hỏi lại một vấn đề đã được giải quyết.\ " .\r "5. LỐI VIẾT NGHỆ THUẬT VÀ CHI TIẾT: Tập trung miêu tả sâu sắc hành động, biểu cảm, nội tâm nhân vật và bối cảnh môi trường. Tránh lối kể tóm tắt hời hợt kiểu AI báo cáo. Hãy làm cho câu chuyện sống động và có nhịp điệu tự nhiên.\ " .\r "6. GIỮ ĐÚNG MỐI QUAN HỆ NHÂN VẬT Đà THIẾT LẬP:\ " .\r " - Nếu một nhân vật đã bái sư/kết nghĩa/gia nhập nhóm ở chương trước, thì ở chương này họ VẪN LÀ đệ tử/đồng minh/thành viên đó. TUYỆT ĐỐI KHÔNG viết ngược lại (ví dụ: đệ tử trung thành bỗng thành kẻ thù lần đầu gặp mặt).\ " .\r " - Thái độ cảm xúc giữa các nhân vật (ngưỡng mộ, căm thù, yêu thương...) phải nhất quán với những gì đã diễn ra trong dòng thời gian cốt truyện phía trên, trừ khi cốt truyện của CHƯƠNG NÀY có lý do rõ ràng cho sự thay đổi.\ " .\r "7. NHẤT QUÁN CÁCH XƯNG HÔ (BẮT BUỘC):\ " .\r " - A bái B làm sư phụ → A gọi B là 'sư phụ', B gọi A là 'đồ đệ/đồ nhi'. Đồng môn gọi nhau bằng 'sư huynh/sư đệ/sư tỷ/sư muội'. Tuyệt đối không xưng xô bừa bãi hoặc thay đổi làm hỏng quan hệ.\ " .\r "8. ĐẢM BẢO LOGIC LIÊN CHƯƠNG: Khi viết chương hiện tại (ví dụ Chương {$chapter_num}), bạn phải đối chiếu logic với toàn bộ diễn biến lịch sử từ các chương xa trước đó (từ Chương 1, 2, 3 trở đi). Tuyệt đối không để nhân vật quên các giao ước, các vật phẩm đã sở hữu, hoặc các kỹ năng đã học được từ thuở ban đầu.\ " .\r "9. ĐỊNH DẠNG ĐẦU RA: Chỉ trả về nội dung câu chữ của chương mới. KHÔNG viết lời mở đầu, lời cảm ơn, không ghi tiêu đề chương hay bất kỳ ghi chú nào khác của AI.\ " .\r "10. TUYỆT ĐỐI TUÂN THỦ THIẾT LẬP NHÂN VẬT VÀ THẾ GIỚI QUAN: Bạn phải đọc kỹ thiết lập nhân vật chính để viết đúng tính cách, ngoại hình, địa vị, hành vi, thói quen và vai trò đã đề ra. TUYỆT ĐỐI KHÔNG viết sai lệch thiết lập nhân vật (Ví dụ: Nếu nhân vật chính là một tồn tại cổ đại từ trước thời Hồng Hoang giả làm phàm nhân, thì phải viết đúng phong thái của một phàm nhân giả tạo và một thực thể siêu việt ẩn mình, tuyệt đối KHÔNG được viết theo kiểu có 'hệ thống bàn tay vàng' hoặc 'xuyên không/chuyển sinh' từ thế giới khác đến). Không tự ý bịa thêm các tình tiết hoặc yếu tố lai tạp như 'hệ thống' (bảng thuộc tính, nhiệm vụ hệ thống, điểm kinh nghiệm, cửa hàng hệ thống, giao diện hệ thống...) hoặc 'xuyên không/chuyển sinh' (từ hiện đại, kiếp trước, linh hồn nhập xác...) trừ khi thiết lập nhân vật hoặc yêu cầu cụ thể có nêu rõ.\ \ " .\r "=== HƯỚNG DẪN KHÁC: ===\ " .\r "- Hãy diễn tả đầy đủ độ dài yêu cầu, tránh kết chương quá vội vã.";\r \r // v5.3: Dynamic Narrative Directives — Chỉ thị sáng tạo xoay vòng\r $narrative_directives_option = Settings::get('narrative_directives');\r if (empty($narrative_directives_option)) {\r $narrative_directives = [\r 'Mở đầu chương bằng một cảnh hành động hoặc xung đột ngay lập tức, KHÔNG mở bằng miêu tả cảnh vật.',\r 'Bắt đầu chương từ góc nhìn hoặc suy nghĩ nội tâm của một nhân vật PHỤ (không phải nhân vật chính), sau đó mới chuyển sang nhân vật chính.',\r 'Mở đầu bằng một đoạn hội thoại kịch tính giữa hai nhân vật, KHÔNG có lời dẫn trước.',\r 'Tập trung chương này vào CHIỀU SÂU TÂM LÝ — miêu tả nội tâm, hoài nghi, nỗi sợ, tham vọng của nhân vật nhiều hơn hành động bên ngoài.',\r 'Sử dụng kỹ thuật flashback ngắn (hồi tưởng) để mở đầu chương — nhân vật nhớ lại một sự kiện quá khứ trước khi tiếp tục hiện tại.',\r 'Viết chương này với nhịp điệu NHANH DỒN DẬP — nhiều hành động, ít miêu tả, câu ngắn gọn, dứt khoát.',\r 'Viết chương này với nhịp điệu CHẬM SÂU LẮNG — tập trung vào cảnh sinh hoạt đời thường, tương tác nhẹ nhàng giữa các nhân vật.',\r 'Mở đầu bằng một bí ẩn hoặc manh mối mới — một phát hiện bất ngờ, một lời cảnh báo, hoặc một dấu hiệu kỳ lạ.',\r 'Chương này hãy sử dụng nhiều GIÁC QUAN (thị giác, thính giác, khứu giác, xúc giác) để miêu tả bối cảnh sống động.',\r 'Kết thúc chương bằng một cliffhanger mạnh — tạo sự hồi hộp, tò mò cho chương tiếp.',\r 'Chương này tập trung vào một nhân vật phụ — cho họ phát triển, bộc lộ tính cách, và tạo ấn tượng sâu sắc.',\r 'Sử dụng kỹ thuật song tuyến — xen kẽ hai tuyến truyện diễn ra đồng thời ở hai địa điểm khác nhau.',\r 'Mở đầu bằng cảnh thiên nhiên hoặc thời tiết đặc biệt (bão, tuyết, hoàng hôn...) phản ánh tâm trạng nhân vật.',\r 'Chương này hãy bất ngờ — tạo một plot twist nhỏ mà người đọc không ngờ tới.',\r 'Viết chương với nhiều đoạn thoại hóm hỉnh, đời thường để giảm căng thẳng sau các chương hành động.',\r ];\r } else {\r $narrative_directives = array_filter(array_map('trim', explode("\ ", str_replace("\r", "", $narrative_directives_option))));\r }\r \r // Chọn 2 directives dựa trên chapter_num để không trùng giữa các chương liên tiếp\r $d_count = count($narrative_directives);\r $d1_idx = ($chapter_num * 7) % $d_count;\r $d2_idx = ($chapter_num * 7 + 5) % $d_count;\r if ($d2_idx === $d1_idx) $d2_idx = ($d2_idx + 1) % $d_count;\r $selected_directives = "\ \ === CHỈ THỊ SÁNG TẠO CHO CHƯƠNG NÀY (BẮT BUỘC TUÂN THỦ) ===\ ";\r $selected_directives .= "1. {$narrative_directives[$d1_idx]}\ ";\r $selected_directives .= "2. {$narrative_directives[$d2_idx]}\ ";\r $system .= $selected_directives;\r \r $continuity = '';\r if ($prev_prev_content) {\r $continuity .= "\ [Tóm lược chương " . ($chapter_num - 2) . "]\ " . $prev_prev_content . "\ ";\r }\r if ($prev_content) {\r $continuity .= "\ [Cuối chương trước (chương " . ($chapter_num - 1) . ")]\ " . $prev_content . "\ \ [Hãy viết liền mạch tiếp nối từ đây]";\r }\r \r // ĐẶT TIMELINE RECAP LÊN ĐẦU TIÊN\r $context_blocks = [];\r if (!empty($timeline_recap_text)) {\r $context_blocks[] = trim($timeline_recap_text);\r }\r if (!empty($relationship_map_text)) {\r $context_blocks[] = trim($relationship_map_text);\r }\r if (!empty($smart_character_context)) {\r $context_blocks[] = trim($smart_character_context);\r }\r if (!empty($smart_lore_context)) {\r $context_blocks[] = trim($smart_lore_context);\r }\r if (!empty($memory_context)) {\r $context_blocks[] = trim($memory_context);\r }\r if (!empty($anti_repetition_text)) {\r $context_blocks[] = trim($anti_repetition_text);\r }\r $context_section = !empty($context_blocks) ? "=== DỮ LIỆU BỐI CẢNH & HỒ SƠ CHI TIẾT ===\ " . implode("\ \ ", $context_blocks) : '';\r \r $user = "=== THÔNG TIN TIỂU THUYẾT ===\ " .\r "• Tên tác phẩm: {$novel_title}\ " .\r "• Thể loại chính: {$genre}\ " .\r "• Thiết lập nhân vật cốt lõi: {$char}\ " .\r "• Thiết lập thế giới cốt lõi: {$world}\ " .\r (!empty($outline) ? "• Dàn ý truyện & Danh sách tên chương truyện: \ {$outline}\ " : "") . "\ " .\r $context_section . "\ \ " .\r (!empty($brainstorm_text) ? "=== PHÂN CẢNH CHI TIẾT Đà THẢO LUẬN (BRAINSTORMING) ===\ ⚠️ QUY TẮC BẮT BUỘC: Bạn phải bám sát cấu trúc phân cảnh chi tiết đã thảo luận dưới đây để phát triển nội dung chương truyện. Hãy viết chi tiết từng cảnh, đối thoại linh hoạt đúng xưng hô:\ {$brainstorm_text}\ \ " : "") .\r "=== DIỄN BIẾN LIÊN TỤC (CONTINUITY) ===\ " .\r $continuity . "\ \ " .\r "=== YÊU CẦU CHO CHƯƠNG MỚI ===\ " .\r "• Hãy viết nội dung chương {$chapter_num}: {$chapter_title}\ " .\r "• Tóm tắt cốt truyện cần xảy ra trong chương này: {$chapter_desc}\ " .\r "• Độ dài mục tiêu: khoảng {$target_words} từ (hãy viết chi tiết, mô tả sâu sắc, tránh viết nhanh lướt qua).\ " .\r ($custom ? "• Yêu cầu đặc biệt bổ sung từ tác giả: {$custom}\ " : "");\r \r return [\r ['role' => 'system', 'content' => $system],\r ['role' => 'user', 'content' => $user]\r ];\r }\r \r /**\r * Generate a single chapter from outline entry.\r */\r public function generate_chapter(array $params): array {\r if (!$this->is_licensed) return $this->error('⚠️ Vui lòng kích hoạt bản quyền để sử dụng tính năng này!');\r \r $project_id = isset($params['project_id']) ? intval($params['project_id']) : 0;\r if ($project_id) {\r $project = Database::get_project($project_id);\r if ($project && ($project['engine'] ?? 'legacy') === 'ainovel') {\r return $this->error('⚠️ Dự án này đang sử dụng động cơ Go (ainovel-cli). Vui lòng sử dụng Bảng điều khiển Động cơ để viết truyện chạy ngầm.');\r }\r }\r \r @set_time_limit(0);\r \r $reflect = !empty($params['use_reflection']);\r $target_words= intval(get_option('pk_target_words', 4000));\r \r $messages = $this->build_chapter_messages($params);\r \r $result = $this->client->generate(\r $messages,\r floatval(get_option('pk_temperature', 0.7)),\r intval(get_option('pk_max_tokens', 16384))\r );\r \r // Self-reflection pass\r if ($result['success'] && $reflect) {\r $user_prompt = $messages[1]['content'];\r $reflect_system = Settings::get('reflection_system_prompt', "Bạn là một biên tập viên văn học cấp cao. Hãy đọc kỹ YÊU CẦU GỐC và BẢN NHÁP, sau đó viết lại bản HOÀN CHỈNH với các cải thiện sau:\ \ 1. LOẠI BỎ VĂN PHONG AI ROBOT: Xóa các mẫu câu máy móc, lặp đi lặp lại, thiếu cảm xúc. Thay bằng ngôn ngữ tự nhiên, giàu hình ảnh.\ 2. TĂNG CHIỀU SÂU CẢM XÚC: Bổ sung nội tâm nhân vật, phản ứng cảm xúc chân thực, mâu thuẫn nội tại.\ 3. CẢI THIỆN NHỊP ĐIỆU: Xen kẽ câu ngắn/dài, đoạn nhanh/chậm. Tránh các đoạn miêu tả dài đơn điệu.\ 4. TĂNG TÍNH LIÊN KẾT: Đảm bảo mạch truyện mượt mà, không có lỗ hổng logic giữa các đoạn.\ 5. GIỮ NGUYÊN CỐT TRUYỆN: Không thêm/bớt sự kiện chính, chỉ nâng cấp cách diễn đạt.\ \ Chỉ trả về bản đã chỉnh sửa hoàn chỉnh. KHÔNG giải thích hay bình luận.");\r $reflect_user = "Yêu cầu gốc:\ {$user_prompt}\ \ Bản nháp:\ " . $result['content'] . "\ \ Hãy viết lại bản hoàn chỉnh, ít nhất {$target_words} chữ:";\r $r2 = $this->client->generate(\r [['role' => 'system', 'content' => $reflect_system], ['role' => 'user', 'content' => $reflect_user]],\r 0.65,\r intval(get_option('pk_max_tokens', 16384))\r );\r if ($r2['success']) $result = $r2;\r }\r \r // v7.0: Multi-Agent Editorial Board — Luồng biên tập 3 Agent chuyên biệt\r if ($result['success'] && Settings::get('enable_multi_agent', 0)) {\r \r $chapter_context = '';\r if (!empty($params['project_id'])) {\r $pid_ma = intval($params['project_id']);\r $proj_ma = Database::get_project($pid_ma);\r $chapter_context = "Thể loại: " . ($proj_ma['genre'] ?? '') . "\ ";\r // Lấy tóm tắt timeline để Agent Continuity có đủ bối cảnh\r $chapters_ma = Database::get_chapters($pid_ma, 30);\r if (!empty($chapters_ma)) {\r $timeline_lines = [];\r foreach (array_slice($chapters_ma, -10) as $c_ma) {\r $timeline_lines[] = "Chương {$c_ma['chapter_num']}: {$c_ma['title']}"; \r }\r $chapter_context .= "Dòng thời gian gần đây:\ " . implode("\ ", $timeline_lines);\r }\r }\r \r // AGENT 2: Terminology & Style — Chuẩn hóa xưng hô và từ Hán-Việt\r $term_system = Settings::get('terminology_agent_prompt', '');\r if (!empty($term_system)) {\r $term_user = "Thể loại: " . ($proj_ma['genre'] ?? 'Tiên hiệp') . "\ \ Bản văn cần biên tập:\ " . $result['content'];\r $term_result = $this->client->generate(\r [['role' => 'system', 'content' => $term_system], ['role' => 'user', 'content' => $term_user]],\r 0.3,\r intval(get_option('pk_max_tokens', 16384))\r );\r if ($term_result['success'] && !empty($term_result['content'])) {\r $result['content'] = $term_result['content'];\r }\r }\r \r // AGENT 3: Continuity Guard — Kiểm duyệt và sửa mâu thuẫn cốt truyện\r $cont_system = Settings::get('continuity_agent_prompt', '');\r if (!empty($cont_system)) {\r $cont_user = $chapter_context . "\ \ Bản văn chương mới:\ " . $result['content'];\r $cont_result = $this->client->generate(\r [['role' => 'system', 'content' => $cont_system], ['role' => 'user', 'content' => $cont_user]],\r 0.2,\r intval(get_option('pk_max_tokens', 16384))\r );\r if ($cont_result['success'] && !empty($cont_result['content'])) {\r $result['content'] = $cont_result['content'];\r }\r }\r }\r \r // v5.3: Fact Verification Layer — Kiểm tra mâu thuẫn với timeline\r if ($result['success'] && !empty($params['project_id'])) {\r $project_id = intval($params['project_id']);\r $verified = $this->verify_chapter_facts($project_id, $result['content'], $params);\r if ($verified !== null) {\r $result['fact_check'] = $verified;\r }\r }\r \r // Tự động sinh và chèn ảnh minh họa vào nội dung chương truyện\r $num_images = intval($params['num_images'] ?? 0);\r if ($result['success'] && $num_images > 0 && !empty($params['project_id'])) {\r $project_id = intval($params['project_id']);\r $chapter_num = intval($params['chapter_num'] ?? 1);\r $result['content'] = $this->generate_and_insert_chapter_images($result['content'], $num_images, $project_id, $chapter_num, $params);\r }\r \r return $result;\r }\r \r /**\r * brainstorm_chapter_scenes\r * Multi-agent brainstorming discussion before writing the chapter.\r */\r public function brainstorm_chapter_scenes(array $params): array {\r if (!$this->is_licensed) return $this->error('⚠️ Vui lòng kích hoạt bản quyền để sử dụng tính năng này!');\r \r $project_id = intval($params['project_id'] ?? 0);\r $chapter_num = intval($params['chapter_num'] ?? 1);\r $chapter_title = sanitize_text_field($params['chapter_title'] ?? '');\r $chapter_desc = sanitize_textarea_field($params['chapter_desc'] ?? '');\r \r $project = $project_id ? Database::get_project($project_id) : null;\r $novel_title = $project['title'] ?? '';\r $genre = $project['genre'] ?? '';\r $char = $project['character_setting'] ?? '';\r $world = $project['world_setting'] ?? '';\r $plot = $project['plot_idea'] ?? '';\r $outline = $project['outline_text'] ?? '';\r \r // Tóm tắt các chương đã qua nếu có\r $timeline_recap = '';\r if ($project_id) {\r $timeline_recap = Database::get_timeline_recap($project_id);\r }\r \r // Lấy 1 chương trước để liền mạch\r $prev_desc = '';\r if ($project_id && $chapter_num > 1) {\r $prev = Database::get_chapter($project_id, $chapter_num - 1);\r if ($prev) {\r $prev_desc = "Chương trước đó viết về: " . mb_substr($prev['content'], -1000);\r }\r }\r \r $system_prompt = "Bạn là một nhóm chuyên gia văn học gồm 3 người: Biên kịch (Screenwriter), Độc giả phản biện (Critic), và Tác giả chính (Author). Nhiệm vụ của bạn là giả lập một cuộc thảo luận để phân tích và lên kịch bản phân cảnh chi tiết cho Chương {$chapter_num} của bộ truyện \"{$novel_title}\" (thể loại {$genre}).\ \ "\r . "Hãy viết cuộc thảo luận theo định dạng sau (sử dụng tiếng Việt):\ "\r . "1. [Biên kịch]: Đề xuất hướng đi cho chương, các tình tiết chính cần có để giải quyết tóm tắt chương, đề xuất nhịp điệu (nhanh/chậm).\ "\r . "2. [Phản biện]: Nhận xét đề xuất của Biên kịch, chỉ ra các sạn logic tiềm ẩn, chi tiết sáo rỗng cần tránh, sự mâu thuẫn có thể xảy ra với thiết lập nhân vật/thế giới hoặc các sự kiện đã qua.\ "\r . "3. [Tác giả chính]: Tổng hợp ý kiến của cả hai, đưa ra quyết định cuối cùng và lập DÀN Ý PHÂN CẢNH CHI TIẾT (Scene 1, Scene 2, Scene 3...) kèm theo hướng dẫn cụ thể về cách xưng hô của các nhân vật xuất hiện trong chương.\ \ "\r . "Chỉ trả về nội dung cuộc thảo luận và kịch bản phân cảnh chi tiết cuối cùng. Không thêm lời mở đầu hay kết thúc.";\r \r $user_prompt = "=== BỐI CẢNH BỘ TRUYỆN ===\ "\r . "• Tên truyện: {$novel_title}\ "\r . "• Thiết lập thế giới: {$world}\ "\r . "• Nhân vật: {$char}\ "\r . "• Cốt truyện chính: {$plot}\ "\r . (!empty($timeline_recap) ? "• Dòng thời gian đã qua:\ {$timeline_recap}\ " : "")\r . (!empty($prev_desc) ? "• {$prev_desc}\ " : "")\r . "\ === CHƯƠNG ĐANG CẦN THẢO LUẬN ===\ "\r . "• Chương số: {$chapter_num}\ "\r . "• Tiêu đề chương: {$chapter_title}\ "\r . "• Mô tả sự kiện chương này: {$chapter_desc}\ \ "\r . "Hãy thực hiện cuộc thảo luận giả lập và đưa ra dàn ý phân cảnh chi tiết cho chương này.";\r \r $max_tokens = intval(get_option('pk_max_tokens', 16384));\r return $this->client->generate(\r [['role' => 'system', 'content' => $system_prompt], ['role' => 'user', 'content' => $user_prompt]],\r 0.75, $max_tokens\r );\r }\r \r /**\r * verify_chapter_facts_public\r * Public wrapper for verify_chapter_facts.\r */\r public function verify_chapter_facts_public(string $content, int $project_id, int $chapter_num): array {\r $params = ['chapter_title' => 'Chương ' . $chapter_num, 'chapter_num' => $chapter_num];\r $res = $this->verify_chapter_facts($project_id, $content, $params);\r if ($res && isset($res['contradictions'])) {\r return [\r 'success' => true,\r 'has_contradiction' => true,\r 'warnings' => $res['contradictions'],\r 'message' => 'Phát hiện mâu thuẫn!'\r ];\r }\r return [\r 'success' => true,\r 'has_contradiction' => false,\r 'warnings' => '',\r 'message' => 'Không phát hiện mâu thuẫn.'\r ];\r }\r \r /**\r * copilot_action\r * Run selected text through specialized prompts.\r */\r public function copilot_action(string $text, string $action, string $custom_instruction, int $project_id): array {\r if (!$this->is_licensed) return $this->error('⚠️ Vui lòng kích hoạt bản quyền để sử dụng tính năng này!');\r \r $text = sanitize_textarea_field($text);\r if (empty($text)) return $this->error('Văn bản trống.');\r \r $project = $project_id ? Database::get_project($project_id) : null;\r $novel_title = $project['title'] ?? '';\r $genre = $project['genre'] ?? '';\r $char = $project['character_setting'] ?? '';\r $world = $project['world_setting'] ?? '';\r \r $action_prompts = [\r 'expand' => "Nhiệm vụ của bạn là VIẾT DÀI RA đoạn văn bản dưới đây. Hãy bổ sung thêm chi tiết miêu tả hành động, biểu cảm khuôn mặt, suy nghĩ nội tâm của nhân vật, hoặc bối cảnh môi trường xung quanh để làm đoạn văn sống động, chân thực hơn. Tuyệt đối giữ nguyên cốt truyện chính và phong cách hành văn gốc. Chỉ trả về đoạn văn bản mới đã được mở rộng hoàn chỉnh.",\r 'polish' => "Nhiệm vụ của bạn là ĐÁNH BÓNG và CHỈNH SỬA văn phong của đoạn văn bản dưới đây. Hãy thay thế các câu văn vụng về, từ ngữ lặp lại bằng các cấu trúc câu mượt mà, diễn đạt trôi chảy, giàu xúc cảm và nghệ thuật hơn. Chỉ trả về đoạn văn bản mới đã được tinh chỉnh.",\r 'vietnamese' => "Nhiệm vụ của bạn là VIỆT HÓA đoạn văn bản chứa nhiều từ Hán-Việt hoặc văn phong convert/dịch thô dưới đây. Hãy chuyển đổi các cụm từ Hán-Việt tối nghĩa hoặc cứng nhắc sang tiếng Việt thuần túy, tự nhiên, dễ hiểu nhưng vẫn giữ được sự trang trọng hoặc cổ kính cần thiết của bối cảnh truyện. Chỉ trả về đoạn văn bản mới đã được thuần Việt hóa.",\r 'continue' => "Nhiệm vụ của bạn là VIẾT TIẾP một đoạn văn mạch lạc tiếp nối trực tiếp từ đoạn văn bản gốc dưới đây. Phát triển tiếp diễn biến hành động hoặc đối thoại tiếp theo một cách tự nhiên. Chỉ trả về đoạn văn viết tiếp thêm (khoảng 200-400 từ), không lặp lại đoạn gốc.",\r ];\r \r $system_instruction = $action_prompts[$action] ?? $action_prompts['polish'];\r \r if (!empty($custom_instruction)) {\r $system_instruction .= "\ Yêu cầu bổ sung từ tác giả: " . $custom_instruction;\r }\r \r $system_instruction .= "\ \ ⚠️ QUY TẮC QUAN TRỌNG: Chỉ trả về nội dung văn bản kết quả. Tuyệt đối không thêm bất kỳ bình luận, lời mở đầu, lời chào, hay ghi chú nào của AI.";\r \r $user_prompt = "=== BỐI CẢNH TRUYỆN ===\ ";\r if (!empty($novel_title)) {\r $user_prompt .= "• Tên tác phẩm: {$novel_title}\ • Thể loại: {$genre}\ • Thiết lập nhân vật: {$char}\ • Thế giới quan: {$world}\ \ ";\r }\r $user_prompt .= "=== VĂN BẢN GỐC CẦN XỬ LÝ ===\ " . $text . "\ \ Hãy thực hiện nhiệm vụ và trả về kết quả:";\r \r $max_tokens = intval(get_option('pk_max_tokens', 16384));\r return $this->client->generate(\r [['role' => 'system', 'content' => $system_instruction], ['role' => 'user', 'content' => $user_prompt]],\r 0.75, $max_tokens\r );\r }\r \r /**\r * v5.3: Fact Verification — Kiểm tra nhanh mâu thuẫn giữa chương mới và timeline.\r * Trả về null nếu không có vấn đề, hoặc array mâu thuẫn nếu phát hiện.\r */\r private function verify_chapter_facts(int $project_id, string $chapter_content, array $params): ?array {\r $recap = Database::get_timeline_recap($project_id, 8000);\r if (empty($recap)) return null;\r \r $chapter_excerpt = mb_substr(wp_strip_all_tags($chapter_content), 0, 3000);\r $chapter_title = sanitize_text_field($params['chapter_title'] ?? '');\r \r $system = Settings::get('fact_check_system_prompt', "Bạn là } Top Tuần - Đọc Truyện Online

Top Tuần

Top 50 truyện xem nhiều trong tuần