更新目录分块

This commit is contained in:
YILING0013
2025-02-06 21:54:06 +08:00
parent ca0d6e5047
commit fd50a8130b
4 changed files with 227 additions and 889 deletions
+90 -24
View File
@@ -29,6 +29,7 @@ from prompt_definitions import (
world_building_prompt,
plot_architecture_prompt,
chapter_blueprint_prompt,
chunked_chapter_blueprint_prompt,
summary_prompt,
update_character_state_prompt,
chapter_draft_prompt,
@@ -386,6 +387,8 @@ def Novel_architecture_generate(
plot_arch_result = invoke_with_cleaning(llm_adapter, prompt_plot)
final_content = (
"#=== 0) 小说设定 ===\n"
f"主题:{topic},类型:{genre},篇幅:约{number_of_chapters}章(每章{word_number}字)\n\n"
"#=== 1) 核心种子 ===\n"
f"{core_seed_result}\n\n"
"#=== 2) 角色动力学 ===\n"
@@ -402,7 +405,29 @@ def Novel_architecture_generate(
logging.info("Novel_architecture.txt has been generated successfully.")
# ============ 2) 生成章节蓝图 ============
# ============ 计算分块大小的工具函数 ============
def compute_chunk_size(number_of_chapters: int, max_tokens: int) -> int:
"""
基于“每章约100 tokens”的粗略估算,
再结合当前max_tokens,计算分块大小:
chunk_size = (floor(max_tokens/100/10)*10) - 10
并确保 chunk_size 不会小于1或大于实际章节数。
"""
tokens_per_chapter = 100.0
ratio = max_tokens / tokens_per_chapter # 8192 / 100 = 81.92
# 先取到最接近的10倍
ratio_rounded_to_10 = int(ratio // 10) * 10 # => 80
# 再减10
chunk_size = ratio_rounded_to_10 - 10 # => 70
if chunk_size < 1:
chunk_size = 1
if chunk_size > number_of_chapters:
chunk_size = number_of_chapters
return chunk_size
# ============ 2) 生成章节蓝图(新增分块逻辑) ============
def Chapter_blueprint_generate(
interface_format: str,
@@ -410,9 +435,18 @@ def Chapter_blueprint_generate(
base_url: str,
llm_model: str,
filepath: str,
number_of_chapters: int,
temperature: float = 0.7,
max_tokens: int = 2048
) -> None:
"""
如果章节数小于等于 chunk_size,则直接使用 chapter_blueprint_prompt 一次性生成。
如果章节数较多,则进行分块生成:
1) 首先说明要生成的总章节数
2) 先生成 [1..chunk_size] 的章节
3) 将生成的文本作为已有目录传入,继续生成 [chunk_size+1..] 的章节
4) 最后汇总全部章节目录写入 Novel_directory.txt
"""
arch_file = os.path.join(filepath, "Novel_architecture.txt")
if not os.path.exists(arch_file):
logging.warning("Novel_architecture.txt not found. Please generate architecture first.")
@@ -423,19 +457,6 @@ def Chapter_blueprint_generate(
logging.warning("Novel_architecture.txt is empty.")
return
match_chaps = re.search(r'约(\d+)章', architecture_text)
if match_chaps:
number_of_chapters = int(match_chaps.group(1))
else:
number_of_chapters = 10
# 提取三幕式文本
plot_arch_text = ""
pat_plot = r'#=== 4\) 三幕式情节架构 ===\n([\s\S]+)$'
m = re.search(pat_plot, architecture_text)
if m:
plot_arch_text = m.group(1).strip()
llm_adapter = create_llm_adapter(
interface_format=interface_format,
base_url=base_url,
@@ -445,20 +466,65 @@ def Chapter_blueprint_generate(
max_tokens=max_tokens
)
prompt = chapter_blueprint_prompt.format(
plot_architecture=plot_arch_text,
number_of_chapters=number_of_chapters
)
blueprint_text = invoke_with_cleaning(llm_adapter, prompt)
if not blueprint_text.strip():
logging.warning("Chapter blueprint generation result is empty.")
# 计算分块大小
chunk_size = compute_chunk_size(number_of_chapters, max_tokens)
logging.info(f"Number of chapters = {number_of_chapters}, computed chunk_size = {chunk_size}.")
# 如果一次就可以生成全部
if chunk_size >= number_of_chapters:
prompt = chapter_blueprint_prompt.format(
novel_architecture=architecture_text,
number_of_chapters=number_of_chapters
)
blueprint_text = invoke_with_cleaning(llm_adapter, prompt)
if not blueprint_text.strip():
logging.warning("Chapter blueprint generation result is empty.")
return
filename_dir = os.path.join(filepath, "Novel_directory.txt")
clear_file_content(filename_dir)
save_string_to_txt(blueprint_text, filename_dir)
logging.info("Novel_directory.txt (chapter blueprint) has been generated successfully (single-shot).")
return
# 否则,分块生成
final_blueprint = ""
current_start = 1
while current_start <= number_of_chapters:
current_end = min(current_start + chunk_size - 1, number_of_chapters)
# 分块提示
chunk_prompt = chunked_chapter_blueprint_prompt.format(
novel_architecture=architecture_text,
chapter_list=final_blueprint, # 已有的章节列表文本
number_of_chapters=number_of_chapters,
n=current_start,
m=current_end
)
logging.info(f"Generating chapters [{current_start}..{current_end}] in a chunk...")
chunk_result = invoke_with_cleaning(llm_adapter, chunk_prompt)
if not chunk_result.strip():
logging.warning(f"Chunk generation for chapters [{current_start}..{current_end}] is empty.")
chunk_result = ""
# 将本次生成的文本拼接到最终结果中
if final_blueprint.strip():
final_blueprint += "\n\n" + chunk_result
else:
final_blueprint = chunk_result
current_start = current_end + 1
if not final_blueprint.strip():
logging.warning("All chunked generation results are empty, cannot create blueprint.")
return
filename_dir = os.path.join(filepath, "Novel_directory.txt")
clear_file_content(filename_dir)
save_string_to_txt(blueprint_text, filename_dir)
save_string_to_txt(final_blueprint.strip(), filename_dir)
logging.info("Novel_directory.txt (chapter blueprint) has been generated successfully.")
logging.info("Novel_directory.txt (chapter blueprint) has been generated successfully (chunked).")
# ============ 3) 生成章节草稿 ============
@@ -610,7 +676,7 @@ def finalize_chapter(
logging.warning(f"Chapter {novel_number} is empty, cannot finalize.")
return
if len(chapter_text) < 0.6 * word_number:
if len(chapter_text) < 0.7 * word_number:
chapter_text = enrich_chapter_text(chapter_text, word_number, api_key, base_url, model_name, temperature, interface_format, max_tokens)
clear_file_content(chapter_file)
save_string_to_txt(chapter_text, chapter_file)