3636
3737# ============ 日志配置 ============
3838logging .basicConfig (level = logging .INFO , format = "%(asctime)s [%(levelname)s] %(message)s" )
39+
3940def debug_log (prompt : str , response_content : str ):
40- """在控制台打印或记录下每次Prompt与Response,[调试]"""
41- logging .info (f"\n [Prompt >>>] { prompt } \n " )
42- logging .info (f"[Response >>>] { response_content } \n " )
41+ """在控制台打印或记录下每次Prompt与Response,[调试]"""
42+ logging .info (f"\n [Prompt >>>] { prompt } \n " )
43+ logging .info (f"[Response >>>] { response_content } \n " )
44+
4345# ============ 向量检索相关 ============
4446
4547VECTOR_STORE_DIR = os .path .join (os .getcwd (), "vectorstore" )
@@ -156,8 +158,6 @@ def Novel_novel_directory_generate(
156158 temperature = temperature
157159 )
158160
159-
160-
161161 def generate_base_setting (state : OverallState ) -> Dict [str , str ]:
162162 prompt = set_prompt .format (
163163 topic = state ["topic" ],
@@ -271,7 +271,7 @@ def clean_text(txt: str) -> str:
271271
272272 logging .info ("Novel settings and directory generated successfully." )
273273
274- # ============ 新增: 获取最近N章内容,生成短期摘要 ============
274+ # ============ 获取最近N章内容,生成短期摘要 ============
275275
276276def get_last_n_chapters_text (chapters_dir : str , current_chapter_num : int , n : int = 3 ) -> List [str ]:
277277 """
@@ -295,9 +295,7 @@ def summarize_recent_chapters(model: ChatOpenAI, chapters_text_list: List[str])
295295 if not chapters_text_list :
296296 return ""
297297
298- # 拼接这几章的内容
299298 combined_text = "\n " .join (chapters_text_list )
300- # 在这里可以写一个更详细的提示
301299 prompt = f"""\
302300 这是最近几章的故事内容,请生成一份详细的短期内容摘要(不少于一章篇幅的细节),用于帮助后续创作时回顾细节。
303301请着重强调发生的事件、角色的心理和关系变化、冲突或悬念等。
@@ -310,6 +308,49 @@ def summarize_recent_chapters(model: ChatOpenAI, chapters_text_list: List[str])
310308 debug_log (prompt , response .content )
311309 return response .content .strip ()
312310
311+ # ============ 新增1:记录剧情要点/未解决冲突 ============
312+
313+ PLOT_ARCS_PROMPT = """\
314+ 下面是新生成的章节内容:
315+ {chapter_text}
316+
317+ 这里是已记录的剧情要点/未解决冲突(可能为空):
318+ {old_plot_arcs}
319+
320+ 请基于新的章节内容,提炼出本章引入或延续的悬念、冲突、角色暗线等,将其合并到旧的剧情要点中。
321+ 若有新的冲突则添加,若有已解决/不再重要的冲突可标注或移除。
322+ 最终输出一份更新后的剧情要点列表,以帮助后续保持故事的整体一致性和悬念延续。
323+ """
324+
325+ def update_plot_arcs (
326+ chapter_text : str ,
327+ old_plot_arcs : str ,
328+ api_key : str ,
329+ base_url : str ,
330+ model_name : str ,
331+ temperature : float
332+ ) -> str :
333+ """
334+ 利用模型分析最新章节文本,提炼或更新“未解决冲突或剧情要点”。
335+ 并返回更新后的字符串。
336+ """
337+ model = ChatOpenAI (
338+ model = model_name ,
339+ api_key = api_key ,
340+ base_url = base_url ,
341+ temperature = temperature
342+ )
343+ prompt = PLOT_ARCS_PROMPT .format (
344+ chapter_text = chapter_text ,
345+ old_plot_arcs = old_plot_arcs
346+ )
347+ response = model .invoke (prompt )
348+ if not response :
349+ logging .warning ("update_plot_arcs: No response." )
350+ return old_plot_arcs
351+ debug_log (prompt , response .content )
352+ return response .content .strip ()
353+
313354# ============ 生成章节草稿 & 定稿 ============
314355
315356def generate_chapter_draft (
@@ -331,9 +372,7 @@ def generate_chapter_draft(
331372 仅生成当前章节的草稿,不更新全局摘要/角色状态/向量库。
332373 并将生成的内容写到 "chapter_{novel_number}.txt" 覆盖写入。
333374 同时生成 "outline_{novel_number}.txt" 存储大纲内容。
334- recent_chapters_summary: 最近 3 章的“短期内容摘要”
335375 """
336-
337376 # 0) 根据 novel_number 从 novel_novel_directory 中获取本章标题及简述
338377 chapter_info = get_chapter_info_from_directory (novel_novel_directory , novel_number )
339378 chapter_title = chapter_info ["chapter_title" ]
@@ -352,7 +391,6 @@ def generate_chapter_draft(
352391 temperature = temperature
353392 )
354393
355- # Prompt 拼接
356394 outline_prompt_text = chapter_outline_prompt .format (
357395 novel_setting = novel_settings ,
358396 character_state = character_state + "\n \n 【历史上下文】\n " + relevant_context ,
@@ -362,7 +400,6 @@ def generate_chapter_draft(
362400 chapter_brief = chapter_brief
363401 )
364402
365- # 在后面加上用户指导与最近章节摘要(可根据需要灵活组织)
366403 outline_prompt_text += f"\n \n 【本章目录标题与简述】\n 标题:{ chapter_title } \n 简述:{ chapter_brief } \n "
367404 outline_prompt_text += f"\n 【最近几章摘要】\n { recent_chapters_summary } "
368405 outline_prompt_text += f"\n \n 【用户指导】\n { user_guidance if user_guidance else '(无)' } "
@@ -375,7 +412,6 @@ def generate_chapter_draft(
375412 debug_log (outline_prompt_text , response_outline .content )
376413 chapter_outline = response_outline .content .strip ()
377414
378- # 将大纲写到 outline_{novel_number}.txt
379415 outlines_dir = os .path .join (filepath , "outlines" )
380416 os .makedirs (outlines_dir , exist_ok = True )
381417 outline_file = os .path .join (outlines_dir , f"outline_{ novel_number } .txt" )
@@ -393,7 +429,6 @@ def generate_chapter_draft(
393429 chapter_brief = chapter_brief
394430 )
395431
396- # 同样插入用户指导和最近摘要
397432 writing_prompt_text += f"\n \n 【本章目录标题与简述】\n 标题:{ chapter_title } \n 简述:{ chapter_brief } \n "
398433 writing_prompt_text += f"\n 【最近几章摘要】\n { recent_chapters_summary } "
399434 writing_prompt_text += f"\n \n 【用户指导】\n { user_guidance if user_guidance else '(无)' } "
@@ -406,7 +441,6 @@ def generate_chapter_draft(
406441 debug_log (writing_prompt_text , response_chapter .content )
407442 chapter_content = response_chapter .content .strip ()
408443
409- # 4) 覆盖写到 chapter_{novel_number}.txt
410444 chapters_dir = os .path .join (filepath , "chapters" )
411445 os .makedirs (chapters_dir , exist_ok = True )
412446 chapter_file = os .path .join (chapters_dir , f"chapter_{ novel_number } .txt" )
@@ -430,7 +464,8 @@ def finalize_chapter(
430464 1. 读取 chapter_{novel_number}.txt 的最终内容;
431465 2. 更新全局摘要、角色状态文件;
432466 3. 如果字数明显少于 word_number 的 80%,则自动调用 enrich_chapter_text 再次扩写;
433- 4. 更新向量库。
467+ 4. 更新向量库;
468+ 5. 新增:更新剧情要点/未解决冲突 -> plot_arcs.txt
434469 """
435470 # 读取当前章节内容
436471 chapters_dir = os .path .join (filepath , "chapters" )
@@ -440,12 +475,14 @@ def finalize_chapter(
440475 logging .warning (f"Chapter { novel_number } is empty, cannot finalize." )
441476 return
442477
443- # 读取角色状态 & 全局摘要
478+ # 读取角色状态 & 全局摘要 & 剧情要点
444479 character_state_file = os .path .join (filepath , "character_state.txt" )
445480 global_summary_file = os .path .join (filepath , "global_summary.txt" )
481+ plot_arcs_file = os .path .join (filepath , "plot_arcs.txt" ) # 新增文件
446482
447483 old_char_state = read_file (character_state_file )
448484 old_global_summary = read_file (global_summary_file )
485+ old_plot_arcs = read_file (plot_arcs_file )
449486
450487 # 1) 先检查字数是否过少,若少于 80% 则调用 enrich 逻辑
451488 if len (chapter_text ) < 0.8 * word_number :
@@ -500,17 +537,30 @@ def update_character_state(chapter_text: str, old_state: str) -> str:
500537
501538 new_char_state = update_character_state (chapter_text , old_char_state )
502539
503- # 4) 覆盖写入角色状态文件与全局摘要文件
540+ # ============ 新增2: 更新剧情要点 =============
541+ new_plot_arcs = update_plot_arcs (
542+ chapter_text = chapter_text ,
543+ old_plot_arcs = old_plot_arcs ,
544+ api_key = api_key ,
545+ base_url = base_url ,
546+ model_name = model_name ,
547+ temperature = temperature
548+ )
549+
550+ # 4) 覆盖写入角色状态文件、全局摘要文件、剧情要点文件
504551 clear_file_content (character_state_file )
505552 save_string_to_txt (new_char_state , character_state_file )
506553
507554 clear_file_content (global_summary_file )
508555 save_string_to_txt (new_global_summary , global_summary_file )
509556
557+ clear_file_content (plot_arcs_file )
558+ save_string_to_txt (new_plot_arcs , plot_arcs_file )
559+
510560 # 5) 更新向量检索库
511561 update_vector_store (api_key , base_url , chapter_text )
512562
513- logging .info (f"Chapter { novel_number } has been finalized (summary & state updated, vector store updated)." )
563+ logging .info (f"Chapter { novel_number } has been finalized (summary & state updated, plot arcs updated, vector store updated)." )
514564
515565def enrich_chapter_text (
516566 chapter_text : str ,
@@ -549,29 +599,23 @@ def import_knowledge_file(api_key: str, base_url: str, file_path: str) -> None:
549599 """
550600 将用户选定的文本文件导入到向量库,以便在写作时检索。
551601 """
552-
553- # 1. 检查文件路径是否有效
554602 if not os .path .exists (file_path ):
555603 logging .warning (f"知识库文件不存在: { file_path } " )
556604 return
557605
558- # 2. 读取文件内容
559606 content = read_file (file_path )
560607 if not content .strip ():
561608 logging .warning ("知识库文件内容为空。" )
562609 return
563610
564- # 3. 对内容进行高级切分处理
565611 paragraphs = advanced_split_content (content )
566612
567- # 4. 加载或初始化向量存储
568613 store = load_vector_store (api_key , base_url )
569614 if not store :
570615 logging .info ("Vector store does not exist. Initializing a new one for knowledge import..." )
571616 init_vector_store (api_key , base_url , paragraphs )
572617 return
573618
574- # 5. 创建Document对象并更新到向量库
575619 docs = [Document (page_content = p ) for p in paragraphs ]
576620 store .add_documents (docs )
577621 store .persist ()
@@ -609,7 +653,6 @@ def advanced_split_content(content: str,
609653 if current_sentences :
610654 merged_paragraphs .append (" " .join (current_sentences ))
611655
612- # 按最大长度二次拆分
613656 final_segments = []
614657 for para in merged_paragraphs :
615658 if len (para ) > max_length :
0 commit comments