From f5691f268f6b5fe3849568ac81f1f8a2748404c9 Mon Sep 17 00:00:00 2001 From: YILING0013 Date: Sun, 16 Feb 2025 23:10:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dembedding=E5=A4=B1=E8=AF=AF?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E6=88=90llm=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- novel_generator/chapter.py | 85 +++++++++++++++++++++++++++------ novel_generator/finalization.py | 14 +++--- novel_generator/knowledge.py | 2 +- ui/generation_handlers.py | 68 +++++++++++++++++++++++++- 4 files changed, 143 insertions(+), 26 deletions(-) diff --git a/novel_generator/chapter.py b/novel_generator/chapter.py index b3eddae..9d1a6a4 100644 --- a/novel_generator/chapter.py +++ b/novel_generator/chapter.py @@ -1,4 +1,4 @@ -#novel_generator/chapter.py +# novel_generator/chapter.py # -*- coding: utf-8 -*- """ 章节草稿生成及获取历史章节文本、短期摘要等 @@ -68,7 +68,7 @@ def summarize_recent_chapters( short_summary = response_text return (short_summary, next_chapter_keywords) -def generate_chapter_draft( +def build_chapter_prompt( api_key: str, base_url: str, model_name: str, @@ -91,10 +91,7 @@ def generate_chapter_draft( timeout: int = 600 ) -> str: """ - 根据 novel_number 判断是否为第一章。 - - 若是第一章,则使用 first_chapter_draft_prompt - - 否则使用 next_chapter_draft_prompt - 最终将生成文本存入 chapters/chapter_{novel_number}.txt。 + 构造当前章节的请求提示词,不调用 LLM,仅返回构造好的提示词字符串。 """ arch_file = os.path.join(filepath, "Novel_architecture.txt") novel_architecture_text = read_file(arch_file) @@ -155,15 +152,12 @@ def generate_chapter_draft( else: previous_chapter_excerpt = text_block break - from llm_adapters import create_llm_adapter # 避免循环依赖 - embedding_adapter = create_llm_adapter( - interface_format=embedding_interface_format, - base_url=embedding_url, - model_name=embedding_model_name, - api_key=embedding_api_key, - temperature=temperature, - max_tokens=max_tokens, - timeout=timeout + from embedding_adapters import create_embedding_adapter # 避免循环依赖 + embedding_adapter = create_embedding_adapter( + embedding_interface_format, + embedding_api_key, + embedding_url, + embedding_model_name ) retrieval_query = short_summary + " " + next_chapter_keywords relevant_context = get_relevant_context_from_vector_store( @@ -195,6 +189,67 @@ def generate_chapter_draft( context_excerpt=relevant_context, previous_chapter_excerpt=previous_chapter_excerpt ) + return prompt_text + +def generate_chapter_draft( + api_key: str, + base_url: str, + model_name: str, + filepath: str, + novel_number: int, + word_number: int, + temperature: float, + user_guidance: str, + characters_involved: str, + key_items: str, + scene_location: str, + time_constraint: str, + embedding_api_key: str, + embedding_url: str, + embedding_interface_format: str, + embedding_model_name: str, + embedding_retrieval_k: int = 2, + interface_format: str = "openai", + max_tokens: int = 2048, + timeout: int = 600, + custom_prompt_text: str = None # 新增参数,若不为 None,则使用用户编辑后的提示词 +) -> str: + """ + 根据 novel_number 判断是否为第一章。 + - 若是第一章,则使用 first_chapter_draft_prompt + - 否则使用 next_chapter_draft_prompt + 若 custom_prompt_text 提供,则以此作为提示词进行生成。 + 最终将生成文本存入 chapters/chapter_{novel_number}.txt。 + """ + # 构造提示词:若用户提供了编辑后的提示词,则使用之;否则构造默认提示词 + if custom_prompt_text is None: + prompt_text = build_chapter_prompt( + api_key=api_key, + base_url=base_url, + model_name=model_name, + filepath=filepath, + novel_number=novel_number, + word_number=word_number, + temperature=temperature, + user_guidance=user_guidance, + characters_involved=characters_involved, + key_items=key_items, + scene_location=scene_location, + time_constraint=time_constraint, + embedding_api_key=embedding_api_key, + embedding_url=embedding_url, + embedding_interface_format=embedding_interface_format, + embedding_model_name=embedding_model_name, + embedding_retrieval_k=embedding_retrieval_k, + interface_format=interface_format, + max_tokens=max_tokens, + timeout=timeout + ) + else: + prompt_text = custom_prompt_text + + chapters_dir = os.path.join(filepath, "chapters") + os.makedirs(chapters_dir, exist_ok=True) llm_adapter = create_llm_adapter( interface_format=interface_format, diff --git a/novel_generator/finalization.py b/novel_generator/finalization.py index 0e6a362..a86c5ed 100644 --- a/novel_generator/finalization.py +++ b/novel_generator/finalization.py @@ -6,6 +6,7 @@ import os import logging from llm_adapters import create_llm_adapter +from embedding_adapters import create_embedding_adapter from prompt_definitions import summary_prompt, update_character_state_prompt from novel_generator.common import invoke_with_cleaning from utils import read_file, clear_file_content, save_string_to_txt @@ -75,14 +76,11 @@ def finalize_chapter( save_string_to_txt(new_char_state, character_state_file) update_vector_store( - embedding_adapter=create_llm_adapter( - interface_format=embedding_interface_format, - base_url=embedding_url, - model_name=embedding_model_name, - api_key=embedding_api_key, - temperature=temperature, - max_tokens=max_tokens, - timeout=timeout + embedding_adapter=create_embedding_adapter( + embedding_interface_format, + embedding_api_key, + embedding_url, + embedding_model_name ), new_chapter=chapter_text, filepath=filepath diff --git a/novel_generator/knowledge.py b/novel_generator/knowledge.py index b883455..8226c7a 100644 --- a/novel_generator/knowledge.py +++ b/novel_generator/knowledge.py @@ -68,7 +68,7 @@ def import_knowledge_file( logging.warning("知识库文件内容为空。") return paragraphs = advanced_split_content(content) - from llm_adapters import create_embedding_adapter + from embedding_adapters import create_embedding_adapter embedding_adapter = create_embedding_adapter( embedding_interface_format, embedding_api_key, diff --git a/ui/generation_handlers.py b/ui/generation_handlers.py index c798aae..681e2e9 100644 --- a/ui/generation_handlers.py +++ b/ui/generation_handlers.py @@ -131,8 +131,11 @@ def generate_chapter_draft_ui(self): embedding_model_name = self.embedding_model_name_var.get().strip() embedding_k = self.safe_get_int(self.embedding_retrieval_k_var, 4) - self.safe_log(f"开始生成第{chap_num}章草稿...") - draft_text = generate_chapter_draft( + self.safe_log(f"生成第{chap_num}章草稿:准备生成请求提示词...") + + # 调用新添加的 build_chapter_prompt 函数构造初始提示词 + from novel_generator.chapter import build_chapter_prompt + prompt_text = build_chapter_prompt( api_key=api_key, base_url=base_url, model_name=model_name, @@ -154,6 +157,67 @@ def generate_chapter_draft_ui(self): max_tokens=max_tokens, timeout=timeout_val ) + + # 弹出可编辑提示词对话框,等待用户确认或取消 + result = {"prompt": None} + event = threading.Event() + + def create_dialog(): + dialog = ctk.CTkToplevel(self.master) + dialog.title("当前章节请求提示词(可编辑)") + dialog.geometry("600x400") + text_box = ctk.CTkTextbox(dialog, wrap="word", font=("Microsoft YaHei", 12)) + text_box.pack(fill="both", expand=True, padx=10, pady=10) + text_box.insert("0.0", prompt_text) + button_frame = ctk.CTkFrame(dialog) + button_frame.pack(pady=10) + def on_confirm(): + result["prompt"] = text_box.get("1.0", "end").strip() + dialog.destroy() + event.set() + def on_cancel(): + result["prompt"] = None + dialog.destroy() + event.set() + btn_confirm = ctk.CTkButton(button_frame, text="确认使用", font=("Microsoft YaHei", 12), command=on_confirm) + btn_confirm.pack(side="left", padx=10) + btn_cancel = ctk.CTkButton(button_frame, text="取消请求", font=("Microsoft YaHei", 12), command=on_cancel) + btn_cancel.pack(side="left", padx=10) + # 若用户直接关闭弹窗,则调用 on_cancel 处理 + dialog.protocol("WM_DELETE_WINDOW", on_cancel) + dialog.grab_set() + self.master.after(0, create_dialog) + event.wait() # 等待用户操作完成 + edited_prompt = result["prompt"] + if edited_prompt is None: + self.safe_log("❌ 用户取消了草稿生成请求。") + return + + self.safe_log("开始生成章节草稿...") + from novel_generator.chapter import generate_chapter_draft + draft_text = generate_chapter_draft( + api_key=api_key, + base_url=base_url, + model_name=model_name, + filepath=filepath, + novel_number=chap_num, + word_number=word_number, + temperature=temperature, + user_guidance=user_guidance, + characters_involved=char_inv, + key_items=key_items, + scene_location=scene_loc, + time_constraint=time_constr, + embedding_api_key=embedding_api_key, + embedding_url=embedding_url, + embedding_interface_format=embedding_interface_format, + embedding_model_name=embedding_model_name, + embedding_retrieval_k=embedding_k, + interface_format=interface_format, + max_tokens=max_tokens, + timeout=timeout_val, + custom_prompt_text=edited_prompt # 使用用户编辑后的提示词 + ) if draft_text: self.safe_log(f"✅ 第{chap_num}章草稿生成完成。请在左侧查看或编辑。") self.master.after(0, lambda: self.show_chapter_in_textbox(draft_text))