refactor: 重构章节上下文构建系统,实现1-1和1-N模式独立构建器
This commit is contained in:
+176
-156
@@ -11,7 +11,10 @@ from asyncio import Queue, Lock
|
||||
|
||||
from app.database import get_db
|
||||
from app.api.common import verify_project_access
|
||||
from app.services.chapter_context_service import ChapterContextBuilder, FocusedMemoryRetriever
|
||||
from app.services.chapter_context_service import (
|
||||
OneToManyContextBuilder,
|
||||
OneToOneContextBuilder
|
||||
)
|
||||
from app.models.chapter import Chapter
|
||||
from app.models.project import Project
|
||||
from app.models.outline import Outline
|
||||
@@ -1325,55 +1328,6 @@ async def generate_chapter_content_stream(
|
||||
)
|
||||
outline = outline_result.scalar_one_or_none()
|
||||
|
||||
# 获取所有大纲用于上下文
|
||||
all_outlines_result = await db_session.execute(
|
||||
select(Outline)
|
||||
.where(Outline.project_id == current_chapter.project_id)
|
||||
.order_by(Outline.order_index)
|
||||
.execution_options(populate_existing=True)
|
||||
)
|
||||
all_outlines = all_outlines_result.scalars().all()
|
||||
outlines_context = "\n".join([
|
||||
f"第{o.order_index}章 {o.title}: {o.content[:100]}..."
|
||||
for o in all_outlines
|
||||
])
|
||||
|
||||
# 获取角色信息(包含职业信息)
|
||||
characters_result = await db_session.execute(
|
||||
select(Character).where(Character.project_id == current_chapter.project_id)
|
||||
)
|
||||
characters = characters_result.scalars().all()
|
||||
|
||||
# 📝 根据大纲模式智能筛选相关角色
|
||||
filter_character_names = None
|
||||
if outline_mode == 'one-to-one':
|
||||
# 1-1模式:从outline.structure中提取characters字段
|
||||
if outline and outline.structure:
|
||||
try:
|
||||
structure = json.loads(outline.structure)
|
||||
filter_character_names = structure.get('characters', [])
|
||||
if filter_character_names:
|
||||
logger.info(f"📋 1-1模式:从structure提取角色列表 {filter_character_names}")
|
||||
except json.JSONDecodeError:
|
||||
logger.warning(f"⚠️ outline.structure解析失败,使用全部角色")
|
||||
else:
|
||||
# 1-n模式:从chapter.expansion_plan中提取character_focus字段
|
||||
if current_chapter.expansion_plan:
|
||||
try:
|
||||
plan = json.loads(current_chapter.expansion_plan)
|
||||
filter_character_names = plan.get('character_focus', [])
|
||||
if filter_character_names:
|
||||
logger.info(f"📋 1-n模式:从expansion_plan提取角色焦点 {filter_character_names}")
|
||||
except json.JSONDecodeError:
|
||||
logger.warning(f"⚠️ expansion_plan解析失败,使用全部角色")
|
||||
|
||||
characters_info = await build_characters_info_with_careers(
|
||||
db=db_session,
|
||||
project_id=current_chapter.project_id,
|
||||
characters=characters,
|
||||
filter_character_names=filter_character_names
|
||||
)
|
||||
|
||||
# 获取写作风格
|
||||
style_content = ""
|
||||
if style_id:
|
||||
@@ -1395,23 +1349,59 @@ async def generate_chapter_content_stream(
|
||||
else:
|
||||
logger.info("未指定写作风格,使用原始提示词")
|
||||
|
||||
# 🚀 使用新的优化上下文构建器(含伏笔服务)
|
||||
logger.info(f"🔧 使用优化的章节上下文构建器(V2 + 伏笔提醒)")
|
||||
context_builder = ChapterContextBuilder(foreshadow_service=foreshadow_service)
|
||||
# 🚀 根据大纲模式选择独立的上下文构建器
|
||||
if outline_mode == 'one-to-one':
|
||||
# ========== 1-1模式:使用独立的简化构建器 ==========
|
||||
logger.info(f"🔧 [1-1模式] 使用 OneToOneContextBuilder")
|
||||
context_builder = OneToOneContextBuilder(
|
||||
memory_service=memory_service,
|
||||
foreshadow_service=foreshadow_service
|
||||
)
|
||||
chapter_context = await context_builder.build(
|
||||
chapter=current_chapter,
|
||||
project=project,
|
||||
outline=outline,
|
||||
user_id=current_user_id,
|
||||
db=db_session
|
||||
db=db_session,
|
||||
target_word_count=target_word_count
|
||||
)
|
||||
|
||||
# 日志输出统计信息
|
||||
logger.info(f"📊 优化上下文统计:")
|
||||
logger.info(f"📊 [1-1模式] 上下文统计:")
|
||||
logger.info(f" - 章节序号: {current_chapter.chapter_number}")
|
||||
logger.info(f" - 衔接锚点长度: {len(chapter_context.continuation_point or '')} 字符")
|
||||
logger.info(f" - 相关记忆: {chapter_context.context_stats.get('memory_count', 0)} 条")
|
||||
logger.info(f" - 总上下文长度: {chapter_context.context_stats.get('total_length', 0)} 字符")
|
||||
logger.info(f" - 大纲长度: {chapter_context.context_stats.get('outline_length', 0)} 字符")
|
||||
logger.info(f" - 上一章内容: {chapter_context.context_stats.get('previous_content_length', 0)} 字符")
|
||||
logger.info(f" - 角色信息: {chapter_context.context_stats.get('characters_length', 0)} 字符")
|
||||
logger.info(f" - 伏笔提醒: {chapter_context.context_stats.get('foreshadow_length', 0)} 字符")
|
||||
logger.info(f" - 相关记忆: {chapter_context.context_stats.get('memories_length', 0)} 字符")
|
||||
logger.info(f" - 总长度: {chapter_context.context_stats.get('total_length', 0)} 字符")
|
||||
else:
|
||||
# ========== 1-N模式:使用独立的完整构建器 ==========
|
||||
logger.info(f"🔧 [1-N模式] 使用 OneToManyContextBuilder")
|
||||
context_builder = OneToManyContextBuilder(
|
||||
memory_service=memory_service,
|
||||
foreshadow_service=foreshadow_service
|
||||
)
|
||||
chapter_context = await context_builder.build(
|
||||
chapter=current_chapter,
|
||||
project=project,
|
||||
outline=outline,
|
||||
user_id=current_user_id,
|
||||
db=db_session,
|
||||
style_content=style_content,
|
||||
target_word_count=target_word_count,
|
||||
temp_narrative_perspective=temp_narrative_perspective
|
||||
)
|
||||
|
||||
# 日志输出统计信息
|
||||
logger.info(f"📊 [1-N模式] 上下文统计:")
|
||||
logger.info(f" - 章节序号: {current_chapter.chapter_number}")
|
||||
logger.info(f" - 衔接锚点: {chapter_context.context_stats.get('continuation_length', 0)} 字符")
|
||||
logger.info(f" - 角色信息: {chapter_context.context_stats.get('characters_length', 0)} 字符")
|
||||
logger.info(f" - 相关记忆: {chapter_context.context_stats.get('memories_length', 0)} 字符")
|
||||
logger.info(f" - 故事骨架: {chapter_context.context_stats.get('skeleton_length', 0)} 字符")
|
||||
logger.info(f" - 伏笔提醒: {chapter_context.context_stats.get('foreshadow_length', 0)} 字符")
|
||||
logger.info(f" - 总长度: {chapter_context.context_stats.get('total_length', 0)} 字符")
|
||||
|
||||
yield await tracker.loading("上下文构建完成", 0.8)
|
||||
|
||||
@@ -1423,102 +1413,91 @@ async def generate_chapter_content_stream(
|
||||
)
|
||||
logger.info(f"📝 使用叙事人称: {chapter_perspective}")
|
||||
|
||||
# 📋 根据大纲模式构建差异化的章节大纲上下文
|
||||
chapter_outline_content = ""
|
||||
# 🚀 根据大纲模式选择提示词模板和参数
|
||||
if outline_mode == 'one-to-one':
|
||||
# 一对一模式:使用大纲的 content
|
||||
chapter_outline_content = outline.content if outline else current_chapter.summary or '暂无大纲'
|
||||
logger.info(f"✏️ 一对一模式:使用大纲内容作为章节指导")
|
||||
else:
|
||||
# 一对多模式:优先使用 expansion_plan 的详细规划
|
||||
if current_chapter.expansion_plan:
|
||||
try:
|
||||
plan = json.loads(current_chapter.expansion_plan)
|
||||
chapter_outline_content = f"""【本章详细规划】
|
||||
剧情摘要:{plan.get('plot_summary', '无')}
|
||||
|
||||
关键事件:
|
||||
{chr(10).join(f'- {event}' for event in plan.get('key_events', []))}
|
||||
|
||||
角色焦点:{', '.join(plan.get('character_focus', []))}
|
||||
|
||||
情感基调:{plan.get('emotional_tone', '未设定')}
|
||||
|
||||
叙事目标:{plan.get('narrative_goal', '未设定')}
|
||||
|
||||
冲突类型:{plan.get('conflict_type', '未设定')}"""
|
||||
|
||||
# 可选:附加章节 summary
|
||||
if current_chapter.summary and current_chapter.summary.strip():
|
||||
chapter_outline_content += f"\n\n【章节补充说明】\n{current_chapter.summary}"
|
||||
|
||||
# 可选:附加大纲的背景信息(限制长度,避免喧宾夺主)
|
||||
if outline:
|
||||
outline_bg = outline.content
|
||||
if len(outline_bg) > 200:
|
||||
outline_bg = outline_bg[:200] + "..."
|
||||
chapter_outline_content += f"\n\n【大纲节点背景】\n{outline_bg}"
|
||||
|
||||
logger.info(f"✏️ 一对多模式:使用expansion_plan详细规划({len(chapter_outline_content)}字符)")
|
||||
except json.JSONDecodeError as e:
|
||||
logger.warning(f"⚠️ expansion_plan解析失败: {e},回退到大纲内容")
|
||||
chapter_outline_content = outline.content if outline else current_chapter.summary or '暂无大纲'
|
||||
else:
|
||||
# 没有expansion_plan,使用大纲内容
|
||||
chapter_outline_content = outline.content if outline else current_chapter.summary or '暂无大纲'
|
||||
logger.warning(f"⚠️ 一对多模式但无expansion_plan,使用大纲内容")
|
||||
|
||||
# 🚀 使用 V2 优化模板构建提示词
|
||||
# 1-1模式
|
||||
if chapter_context.continuation_point:
|
||||
# 有前置内容,使用 WITH_CONTEXT 模板
|
||||
|
||||
# 尝试从context中提取上一章摘要
|
||||
previous_summary = "(无上一章摘要,请根据锚点续写)"
|
||||
if chapter_context.context_stats.get('recent_summaries', 0) > 0:
|
||||
# 简单的提取逻辑,实际可能需要更精确的解析
|
||||
# 但在这里,context_stats并没有直接存储内容。
|
||||
# 我们利用ChapterContext对象中可能存在的summary信息,或者直接从recent_summary文本中截取最后一段
|
||||
if hasattr(chapter_context, 'recent_summary') and chapter_context.recent_summary:
|
||||
lines = chapter_context.recent_summary.strip().split('\n')
|
||||
if lines:
|
||||
previous_summary = lines[-1]
|
||||
|
||||
template = await PromptService.get_template("CHAPTER_GENERATION_V2_WITH_CONTEXT", current_user_id, db_session)
|
||||
# 有上一章内容
|
||||
template = await PromptService.get_template("CHAPTER_GENERATION_ONE_TO_ONE_NEXT", current_user_id, db_session)
|
||||
base_prompt = PromptService.format_prompt(
|
||||
template,
|
||||
# P0 核心参数
|
||||
project_title=project.title,
|
||||
chapter_number=current_chapter.chapter_number,
|
||||
chapter_title=current_chapter.title,
|
||||
chapter_outline=chapter_outline_content,
|
||||
chapter_outline=chapter_context.chapter_outline,
|
||||
target_word_count=target_word_count,
|
||||
continuation_point=chapter_context.continuation_point,
|
||||
# P1 重要参数
|
||||
genre=project.genre or '未设定',
|
||||
narrative_perspective=chapter_perspective,
|
||||
characters_info=characters_info or '暂无角色信息',
|
||||
previous_chapter_content=chapter_context.continuation_point,
|
||||
characters_info=chapter_context.chapter_characters or '暂无角色信息',
|
||||
chapter_careers=chapter_context.chapter_careers or '暂无职业信息',
|
||||
foreshadow_reminders=chapter_context.foreshadow_reminders or '暂无需要关注的伏笔',
|
||||
relevant_memories=chapter_context.relevant_memories or '暂无相关记忆'
|
||||
)
|
||||
logger.debug(f"创建第{current_chapter.chapter_number}章提示词: {base_prompt}")
|
||||
else:
|
||||
# 第一章
|
||||
template = await PromptService.get_template("CHAPTER_GENERATION_ONE_TO_ONE", current_user_id, db_session)
|
||||
base_prompt = PromptService.format_prompt(
|
||||
template,
|
||||
project_title=project.title,
|
||||
chapter_number=current_chapter.chapter_number,
|
||||
chapter_title=current_chapter.title,
|
||||
chapter_outline=chapter_context.chapter_outline,
|
||||
target_word_count=target_word_count,
|
||||
genre=project.genre or '未设定',
|
||||
narrative_perspective=chapter_perspective,
|
||||
characters_info=chapter_context.chapter_characters or '暂无角色信息',
|
||||
chapter_careers=chapter_context.chapter_careers or '暂无职业信息',
|
||||
foreshadow_reminders=chapter_context.foreshadow_reminders or '暂无需要关注的伏笔',
|
||||
relevant_memories=chapter_context.relevant_memories or '暂无相关记忆'
|
||||
)
|
||||
logger.debug(f"创建第一章提示词: {base_prompt}")
|
||||
else:
|
||||
# ========== 1-n模式:使用完整模板 ==========
|
||||
if chapter_context.continuation_point:
|
||||
# 有前置内容,使用 WITH_CONTEXT 模板
|
||||
logger.info(f"📝 [1-n模式] 使用带上下文的模板(第{current_chapter.chapter_number}章)")
|
||||
|
||||
# 提取上一章摘要
|
||||
previous_summary = "(无上一章摘要,请根据锚点续写)"
|
||||
if chapter_context.previous_chapter_summary:
|
||||
previous_summary = chapter_context.previous_chapter_summary
|
||||
|
||||
template = await PromptService.get_template("CHAPTER_GENERATION_ONE_TO_MANY_NEXT", current_user_id, db_session)
|
||||
base_prompt = PromptService.format_prompt(
|
||||
template,
|
||||
project_title=project.title,
|
||||
chapter_number=current_chapter.chapter_number,
|
||||
chapter_title=current_chapter.title,
|
||||
chapter_outline=chapter_context.chapter_outline,
|
||||
target_word_count=target_word_count,
|
||||
continuation_point=chapter_context.continuation_point,
|
||||
genre=project.genre or '未设定',
|
||||
narrative_perspective=chapter_perspective,
|
||||
characters_info=chapter_context.chapter_characters or '暂无角色信息',
|
||||
foreshadow_reminders=chapter_context.foreshadow_reminders or '暂无需要关注的伏笔',
|
||||
previous_chapter_summary=previous_summary,
|
||||
# P2 参考参数(动态裁剪后的)
|
||||
story_skeleton=chapter_context.story_skeleton or '',
|
||||
relevant_memories=chapter_context.relevant_memories or ''
|
||||
)
|
||||
logger.debug(f"创建第{current_chapter.chapter_number}章提示词: {base_prompt}")
|
||||
else:
|
||||
# 第一章,使用无前置内容模板
|
||||
template = await PromptService.get_template("CHAPTER_GENERATION_V2", current_user_id, db_session)
|
||||
# 第1章,使用无前置内容模板
|
||||
logger.info(f"📝 [1-n模式] 使用第一章模板")
|
||||
template = await PromptService.get_template("CHAPTER_GENERATION_ONE_TO_MANY", current_user_id, db_session)
|
||||
base_prompt = PromptService.format_prompt(
|
||||
template,
|
||||
# P0 核心参数
|
||||
project_title=project.title,
|
||||
chapter_number=current_chapter.chapter_number,
|
||||
chapter_title=current_chapter.title,
|
||||
chapter_outline=chapter_outline_content,
|
||||
chapter_outline=chapter_context.chapter_outline,
|
||||
target_word_count=target_word_count,
|
||||
# P1 重要参数
|
||||
genre=project.genre or '未设定',
|
||||
narrative_perspective=chapter_perspective,
|
||||
characters_info=characters_info or '暂无角色信息'
|
||||
characters_info=chapter_context.chapter_characters or '暂无角色信息'
|
||||
)
|
||||
logger.debug(f"创建第一章提示词: {base_prompt}")
|
||||
|
||||
# 应用写作风格
|
||||
if style_content:
|
||||
@@ -2687,18 +2666,6 @@ async def generate_single_chapter_for_batch(
|
||||
)
|
||||
outline = outline_result.scalar_one_or_none()
|
||||
|
||||
# 获取所有大纲用于上下文
|
||||
all_outlines_result = await db_session.execute(
|
||||
select(Outline)
|
||||
.where(Outline.project_id == chapter.project_id)
|
||||
.order_by(Outline.order_index)
|
||||
)
|
||||
all_outlines = all_outlines_result.scalars().all()
|
||||
outlines_context = "\n".join([
|
||||
f"第{o.order_index}章 {o.title}: {o.content[:100]}..."
|
||||
for o in all_outlines
|
||||
])
|
||||
|
||||
# 获取角色信息(包含职业信息)
|
||||
characters_result = await db_session.execute(
|
||||
select(Character).where(Character.project_id == chapter.project_id)
|
||||
@@ -2746,15 +2713,37 @@ async def generate_single_chapter_for_batch(
|
||||
if style.user_id is None or style.user_id == user_id:
|
||||
style_content = style.prompt_content or ""
|
||||
|
||||
# 🚀 使用新的优化上下文构建器(含伏笔服务)
|
||||
logger.info(f"🔧 批量生成 - 使用优化的章节上下文构建器(V2 + 伏笔提醒)")
|
||||
context_builder = ChapterContextBuilder(foreshadow_service=foreshadow_service)
|
||||
# 🚀 根据大纲模式选择独立的上下文构建器(批量生成)
|
||||
if outline_mode == 'one-to-one':
|
||||
# 1-1模式
|
||||
logger.info(f"🔧 批量生成 - [1-1模式] 使用 OneToOneContextBuilder")
|
||||
context_builder = OneToOneContextBuilder(
|
||||
memory_service=memory_service,
|
||||
foreshadow_service=foreshadow_service
|
||||
)
|
||||
chapter_context = await context_builder.build(
|
||||
chapter=chapter,
|
||||
project=project,
|
||||
outline=outline,
|
||||
user_id=user_id,
|
||||
db=db_session
|
||||
db=db_session,
|
||||
target_word_count=target_word_count
|
||||
)
|
||||
else:
|
||||
# 1-N模式:使用独立的完整构建器
|
||||
logger.info(f"🔧 批量生成 - [1-N模式] 使用 OneToManyContextBuilder")
|
||||
context_builder = OneToManyContextBuilder(
|
||||
memory_service=memory_service,
|
||||
foreshadow_service=foreshadow_service
|
||||
)
|
||||
chapter_context = await context_builder.build(
|
||||
chapter=chapter,
|
||||
project=project,
|
||||
outline=outline,
|
||||
user_id=user_id,
|
||||
db=db_session,
|
||||
style_content=style_content,
|
||||
target_word_count=target_word_count
|
||||
)
|
||||
|
||||
# 日志输出统计信息
|
||||
@@ -2809,12 +2798,48 @@ async def generate_single_chapter_for_batch(
|
||||
chapter_outline_content = outline.content if outline else chapter.summary or '暂无大纲'
|
||||
logger.warning(f"⚠️ 批量生成 - 一对多模式但无expansion_plan,使用大纲内容")
|
||||
|
||||
# 🚀 使用 V2 优化模板构建提示词(批量生成)
|
||||
# 🚀 根据大纲模式选择提示词模板(批量生成)
|
||||
if outline_mode == 'one-to-one':
|
||||
# 1-1模式
|
||||
if chapter_context.continuation_point:
|
||||
# 有上一章内容
|
||||
template = await PromptService.get_template("CHAPTER_GENERATION_ONE_TO_ONE_NEXT", user_id, db_session)
|
||||
base_prompt = PromptService.format_prompt(
|
||||
template,
|
||||
project_title=project.title,
|
||||
chapter_number=chapter.chapter_number,
|
||||
chapter_title=chapter.title,
|
||||
chapter_outline=chapter_context.chapter_outline,
|
||||
target_word_count=target_word_count,
|
||||
genre=project.genre or '未设定',
|
||||
narrative_perspective=project.narrative_perspective or '第三人称',
|
||||
previous_chapter_content=chapter_context.continuation_point,
|
||||
characters_info=chapter_context.chapter_characters or '暂无角色信息',
|
||||
chapter_careers=chapter_context.chapter_careers or '暂无职业信息',
|
||||
foreshadow_reminders=chapter_context.foreshadow_reminders or '暂无需要关注的伏笔',
|
||||
relevant_memories=chapter_context.relevant_memories or '暂无相关记忆'
|
||||
)
|
||||
else:
|
||||
# 第一章
|
||||
template = await PromptService.get_template("CHAPTER_GENERATION_ONE_TO_ONE", user_id, db_session)
|
||||
base_prompt = PromptService.format_prompt(
|
||||
template,
|
||||
project_title=project.title,
|
||||
chapter_number=chapter.chapter_number,
|
||||
chapter_title=chapter.title,
|
||||
chapter_outline=chapter_context.chapter_outline,
|
||||
target_word_count=target_word_count,
|
||||
genre=project.genre or '未设定',
|
||||
narrative_perspective=project.narrative_perspective or '第三人称',
|
||||
characters_info=chapter_context.chapter_characters or '暂无角色信息',
|
||||
chapter_careers=chapter_context.chapter_careers or '暂无职业信息',
|
||||
foreshadow_reminders=chapter_context.foreshadow_reminders or '暂无需要关注的伏笔',
|
||||
relevant_memories=chapter_context.relevant_memories or '暂无相关记忆'
|
||||
)
|
||||
else:
|
||||
# 1-n模式:使用原有的完整模板
|
||||
if chapter_context.continuation_point:
|
||||
# 有前置内容,使用 WITH_CONTEXT 模板
|
||||
|
||||
# 确定上一章摘要:优先使用传入的 previous_summary_context(批量生成的上一章),
|
||||
# 否则尝试从 chapter_context 中获取
|
||||
final_prev_summary = "(无上一章摘要,请根据锚点续写)"
|
||||
|
||||
if previous_summary_context:
|
||||
@@ -2824,38 +2849,33 @@ async def generate_single_chapter_for_batch(
|
||||
if lines:
|
||||
final_prev_summary = lines[-1]
|
||||
|
||||
template = await PromptService.get_template("CHAPTER_GENERATION_V2_WITH_CONTEXT", user_id, db_session)
|
||||
template = await PromptService.get_template("CHAPTER_GENERATION_ONE_TO_MANY_NEXT", user_id, db_session)
|
||||
base_prompt = PromptService.format_prompt(
|
||||
template,
|
||||
# P0 核心参数
|
||||
project_title=project.title,
|
||||
chapter_number=chapter.chapter_number,
|
||||
chapter_title=chapter.title,
|
||||
chapter_outline=chapter_outline_content,
|
||||
target_word_count=target_word_count,
|
||||
continuation_point=chapter_context.continuation_point,
|
||||
# P1 重要参数
|
||||
genre=project.genre or '未设定',
|
||||
narrative_perspective=project.narrative_perspective or '第三人称',
|
||||
characters_info=characters_info or '暂无角色信息',
|
||||
foreshadow_reminders=chapter_context.foreshadow_reminders or '暂无需要关注的伏笔',
|
||||
previous_chapter_summary=final_prev_summary,
|
||||
# P2 参考参数(动态裁剪后的)
|
||||
story_skeleton=chapter_context.story_skeleton or '',
|
||||
relevant_memories=chapter_context.relevant_memories or ''
|
||||
)
|
||||
else:
|
||||
# 第一章,使用无前置内容模板
|
||||
template = await PromptService.get_template("CHAPTER_GENERATION_V2", user_id, db_session)
|
||||
template = await PromptService.get_template("CHAPTER_GENERATION_ONE_TO_MANY", user_id, db_session)
|
||||
base_prompt = PromptService.format_prompt(
|
||||
template,
|
||||
# P0 核心参数
|
||||
project_title=project.title,
|
||||
chapter_number=chapter.chapter_number,
|
||||
chapter_title=chapter.title,
|
||||
chapter_outline=chapter_outline_content,
|
||||
target_word_count=target_word_count,
|
||||
# P1 重要参数
|
||||
genre=project.genre or '未设定',
|
||||
narrative_perspective=project.narrative_perspective or '第三人称',
|
||||
characters_info=characters_info or '暂无角色信息'
|
||||
|
||||
+262
-786
File diff suppressed because it is too large
Load Diff
@@ -355,6 +355,7 @@ async def career_system_generator(
|
||||
title=project.title,
|
||||
genre=project.genre or '未设定',
|
||||
theme=project.theme or '未设定',
|
||||
description=project.description or '暂无简介',
|
||||
time_period=world_data.get('time_period', '未设定'),
|
||||
location=world_data.get('location', '未设定'),
|
||||
atmosphere=world_data.get('atmosphere', '未设定'),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -294,7 +294,6 @@ class PromptService:
|
||||
类型:{genre}
|
||||
开篇章节数:{chapter_count}
|
||||
叙事视角:{narrative_perspective}
|
||||
全书目标字数:{target_words}
|
||||
</project>
|
||||
|
||||
<worldview priority="P1">
|
||||
@@ -327,9 +326,9 @@ class PromptService:
|
||||
{{
|
||||
"chapter_number": 1,
|
||||
"title": "章节标题",
|
||||
"summary": "章节概要(300-500字):主要情节、冲突、转折",
|
||||
"summary": "章节概要(500-1000字):主要情节、角色互动、关键事件、冲突与转折",
|
||||
"scenes": ["场景1描述", "场景2描述", "场景3描述"],
|
||||
"characters": ["角色1", "角色2"],
|
||||
"characters": ["涉及角色1", "涉及角色2"],
|
||||
"key_points": ["情节要点1", "情节要点2"],
|
||||
"emotion": "本章情感基调",
|
||||
"goal": "本章叙事目标"
|
||||
@@ -348,8 +347,9 @@ class PromptService:
|
||||
|
||||
【格式规范】
|
||||
- 纯JSON数组输出,无markdown标记
|
||||
- 内容描述中严禁使用特殊符号(引号、方括号、书名号等)
|
||||
- 专有名词、事件名直接书写
|
||||
- 内容描述中严禁使用特殊符号
|
||||
- 专有名词直接书写
|
||||
- 字段结构与已有章节完全一致
|
||||
</output>
|
||||
|
||||
<constraints>
|
||||
@@ -367,6 +367,7 @@ class PromptService:
|
||||
✅ 符合类型:情节符合{genre}类型特征
|
||||
✅ 主题贴合:体现主题"{theme}"
|
||||
✅ 开篇定位:是开局而非完整故事
|
||||
✅ 描述详细:每个summary 500-1000字
|
||||
|
||||
【禁止事项】
|
||||
❌ 输出markdown或代码块标记
|
||||
@@ -375,7 +376,7 @@ class PromptService:
|
||||
❌ 节奏过快,信息过载
|
||||
</constraints>"""
|
||||
|
||||
# 大纲续写提示词 V2(RTCO框架 + 记忆增强)
|
||||
# 大纲续写提示词 V2(RTCO框架 - 简化版)
|
||||
OUTLINE_CONTINUE = """<system>
|
||||
你是经验丰富的小说作家和编剧,擅长续写{genre}类型的小说大纲。
|
||||
</system>
|
||||
@@ -399,7 +400,7 @@ class PromptService:
|
||||
叙事视角:{narrative_perspective}
|
||||
</project>
|
||||
|
||||
<worldview priority="P2">
|
||||
<worldview priority="P1">
|
||||
【世界观】
|
||||
时间背景:{time_period}
|
||||
地理位置:{location}
|
||||
@@ -407,35 +408,27 @@ class PromptService:
|
||||
世界规则:{rules}
|
||||
</worldview>
|
||||
|
||||
<characters priority="P1">
|
||||
【角色信息】
|
||||
<previous_context priority="P0">
|
||||
{recent_outlines}
|
||||
</previous_context>
|
||||
|
||||
<characters priority="P0">
|
||||
【所有角色信息】
|
||||
{characters_info}
|
||||
</characters>
|
||||
|
||||
<previous_context priority="P0">
|
||||
【已有章节概览】(共{current_chapter_count}章)
|
||||
{all_chapters_brief}
|
||||
|
||||
【最近剧情】
|
||||
{recent_plot}
|
||||
</previous_context>
|
||||
|
||||
<memory priority="P1">
|
||||
【🧠 智能记忆系统 - 续写参考】
|
||||
以下是从故事记忆库中检索到的相关信息:
|
||||
|
||||
{memory_context}
|
||||
</memory>
|
||||
<user_input priority="P0">
|
||||
【用户输入】
|
||||
续写章节数:{chapter_count}章
|
||||
情节阶段:{plot_stage_instruction}
|
||||
故事方向:{story_direction}
|
||||
其他要求:{requirements}
|
||||
</user_input>
|
||||
|
||||
<mcp_context priority="P2">
|
||||
{mcp_references}
|
||||
</mcp_context>
|
||||
|
||||
<requirements priority="P1">
|
||||
【其他要求】
|
||||
{requirements}
|
||||
</requirements>
|
||||
|
||||
<output priority="P0">
|
||||
【输出格式】
|
||||
返回第{start_chapter}到第{end_chapter}章的JSON数组(共{chapter_count}个对象):
|
||||
@@ -444,7 +437,7 @@ class PromptService:
|
||||
{{
|
||||
"chapter_number": {start_chapter},
|
||||
"title": "章节标题",
|
||||
"summary": "章节概要(300-500字):主要情节、角色互动、关键事件、冲突与转折",
|
||||
"summary": "章节概要(500-1000字):主要情节、角色互动、关键事件、冲突与转折",
|
||||
"scenes": ["场景1描述", "场景2描述", "场景3描述"],
|
||||
"characters": ["涉及角色1", "涉及角色2"],
|
||||
"key_points": ["情节要点1", "情节要点2"],
|
||||
@@ -473,16 +466,15 @@ class PromptService:
|
||||
<constraints>
|
||||
【续写要求】
|
||||
✅ 剧情连贯:与前文自然衔接,保持连贯性
|
||||
✅ 记忆参考:适当参考记忆中的伏笔、钩子、情节点
|
||||
✅ 伏笔回收:考虑回收未完结伏笔,制造呼应
|
||||
✅ 角色发展:遵循角色成长轨迹
|
||||
✅ 角色发展:遵循角色成长轨迹,充分利用角色信息
|
||||
✅ 情节阶段:遵循{plot_stage_instruction}的要求
|
||||
✅ 风格一致:保持与已有章节相同风格和详细程度
|
||||
✅ 大纲详细:充分解析最近10章大纲的structure字段信息
|
||||
|
||||
【必须遵守】
|
||||
✅ 数量精确:数组包含{chapter_count}个章节
|
||||
✅ 编号正确:从第{start_chapter}章开始
|
||||
✅ 描述详细:每个summary 100-200字
|
||||
✅ 描述详细:每个summary 500-1000字
|
||||
✅ 承上启下:自然衔接前文
|
||||
|
||||
【禁止事项】
|
||||
@@ -490,10 +482,11 @@ class PromptService:
|
||||
❌ 在描述中使用特殊符号
|
||||
❌ 与前文矛盾或脱节
|
||||
❌ 忽略已有角色发展
|
||||
❌ 忽略最近大纲中的情节线索
|
||||
</constraints>"""
|
||||
|
||||
# 章节生成V2 - 无前置章节版本(用于第1章)
|
||||
CHAPTER_GENERATION_V2 = """<system>
|
||||
# 章节生成 - 1-N模式(第1章)
|
||||
CHAPTER_GENERATION_ONE_TO_MANY = """<system>
|
||||
你是《{project_title}》的作者,一位专注于{genre}类型的网络小说家。
|
||||
</system>
|
||||
|
||||
@@ -537,8 +530,127 @@ class PromptService:
|
||||
现在开始创作:
|
||||
</output>"""
|
||||
|
||||
# 章节生成V2 - 带前置章节版本(用于第2章及以后)
|
||||
CHAPTER_GENERATION_V2_WITH_CONTEXT = """<system>
|
||||
# 章节生成 - 1-1模式(第1章)
|
||||
CHAPTER_GENERATION_ONE_TO_ONE = """<system>
|
||||
你是《{project_title}》的作者,一位专注于{genre}类型的网络小说家。
|
||||
</system>
|
||||
|
||||
<task priority="P0">
|
||||
【创作任务】
|
||||
撰写第{chapter_number}章《{chapter_title}》的完整正文。
|
||||
|
||||
【基本要求】
|
||||
- 目标字数:{target_word_count}字(允许±200字浮动)
|
||||
- 叙事视角:{narrative_perspective}
|
||||
</task>
|
||||
|
||||
<outline priority="P0">
|
||||
【本章大纲】
|
||||
{chapter_outline}
|
||||
</outline>
|
||||
|
||||
<characters priority="P1">
|
||||
【本章角色】
|
||||
{characters_info}
|
||||
</characters>
|
||||
|
||||
<careers priority="P2">
|
||||
【本章职业】
|
||||
{chapter_careers}
|
||||
</careers>
|
||||
|
||||
<constraints>
|
||||
【必须遵守】
|
||||
✅ 严格按照大纲推进情节
|
||||
✅ 保持角色性格、说话方式一致
|
||||
✅ 字数需要严格控制在目标字数内
|
||||
|
||||
【禁止事项】
|
||||
❌ 输出章节标题、序号等元信息
|
||||
❌ 使用"总之"、"综上所述"等AI常见总结语
|
||||
❌ 添加作者注释或创作说明
|
||||
❌ 生成字数禁止超过目标字数
|
||||
</constraints>
|
||||
|
||||
<output>
|
||||
【输出规范】
|
||||
直接输出小说正文内容,从故事场景或动作开始。
|
||||
无需任何前言、后记或解释性文字。
|
||||
|
||||
现在开始创作:
|
||||
</output>"""
|
||||
|
||||
# 章节生成 - 1-1模式(第2章及以后)
|
||||
CHAPTER_GENERATION_ONE_TO_ONE_NEXT = """<system>
|
||||
你是《{project_title}》的作者,一位专注于{genre}类型的网络小说家。
|
||||
</system>
|
||||
|
||||
<task priority="P0">
|
||||
【创作任务】
|
||||
撰写第{chapter_number}章《{chapter_title}》的完整正文。
|
||||
|
||||
【基本要求】
|
||||
- 目标字数:{target_word_count}字(允许±200字浮动)
|
||||
- 叙事视角:{narrative_perspective}
|
||||
</task>
|
||||
|
||||
<outline priority="P0">
|
||||
【本章大纲】
|
||||
{chapter_outline}
|
||||
</outline>
|
||||
|
||||
<previous_chapter priority="P1">
|
||||
【上一章末尾500字内容】
|
||||
{previous_chapter_content}
|
||||
</previous_chapter>
|
||||
|
||||
<characters priority="P1">
|
||||
【本章角色】
|
||||
{characters_info}
|
||||
</characters>
|
||||
|
||||
<careers priority="P2">
|
||||
【本章职业】
|
||||
{chapter_careers}
|
||||
</careers>
|
||||
|
||||
<foreshadow_reminders priority="P2">
|
||||
【🎯 伏笔提醒】
|
||||
{foreshadow_reminders}
|
||||
</foreshadow_reminders>
|
||||
|
||||
<memory priority="P2">
|
||||
【相关记忆】
|
||||
{relevant_memories}
|
||||
</memory>
|
||||
|
||||
<constraints>
|
||||
【必须遵守】
|
||||
✅ 严格按照大纲推进情节
|
||||
✅ 自然承接上一章末尾内容,保持连贯性
|
||||
✅ 保持角色性格、说话方式一致
|
||||
✅ 字数需要严格控制在目标字数内
|
||||
✅ 如有伏笔提醒,请在本章中适当埋入或回收相应伏笔
|
||||
|
||||
【禁止事项】
|
||||
❌ 输出章节标题、序号等元信息
|
||||
❌ 使用"总之"、"综上所述"等AI常见总结语
|
||||
❌ 在结尾处使用开放式反问
|
||||
❌ 添加作者注释或创作说明
|
||||
❌ 重复上一章已发生的事件
|
||||
❌ 生成字数禁止超过目标字数
|
||||
</constraints>
|
||||
|
||||
<output>
|
||||
【输出规范】
|
||||
直接输出小说正文内容,从故事场景或动作开始。
|
||||
无需任何前言、后记或解释性文字。
|
||||
|
||||
现在开始创作:
|
||||
</output>"""
|
||||
|
||||
# 章节生成 - 1-N模式(第2章及以后)
|
||||
CHAPTER_GENERATION_ONE_TO_MANY_NEXT = """<system>
|
||||
你是《{project_title}》的作者,一位专注于{genre}类型的网络小说家。
|
||||
</system>
|
||||
|
||||
@@ -870,7 +982,7 @@ class PromptService:
|
||||
- **category**:分类(identity=身世/mystery=悬念/item=物品/relationship=关系/event=事件/ability=能力/prophecy=预言)
|
||||
- **is_long_term**:是否长线伏笔(跨10章以上回收为true)
|
||||
- **related_characters**:涉及的角色名列表
|
||||
- **estimated_resolve_chapter**:预估回收章节号(埋下时预估,回收时为当前章节)
|
||||
- **estimated_resolve_chapter**:【必填】预估回收章节号(埋下时必须预估,回收时为当前章节)
|
||||
|
||||
**3. 冲突分析 (Conflict)**
|
||||
- 冲突类型:人与人/人与己/人与环境/人与社会
|
||||
@@ -1997,7 +2109,12 @@ class PromptService:
|
||||
|
||||
<task>
|
||||
【设计任务】
|
||||
根据世界观信息,设计一个完整且合理的职业体系,包括主职业和副职业。
|
||||
根据世界观信息和项目简介,设计一个完整且合理的职业体系。
|
||||
职业体系必须与项目简介中的故事背景和角色设定高度契合。
|
||||
|
||||
【数量要求】
|
||||
- 主职业:精确生成3个
|
||||
- 副职业:精确生成2个
|
||||
</task>
|
||||
|
||||
<worldview priority="P0">
|
||||
@@ -2005,6 +2122,9 @@ class PromptService:
|
||||
书名:{title}
|
||||
类型:{genre}
|
||||
主题:{theme}
|
||||
简介:{description}
|
||||
|
||||
【世界观设定】
|
||||
时间背景:{time_period}
|
||||
地理位置:{location}
|
||||
氛围基调:{atmosphere}
|
||||
@@ -2014,23 +2134,32 @@ class PromptService:
|
||||
<design_requirements priority="P0">
|
||||
【设计要求】
|
||||
|
||||
**1. 主职业(main_careers)**
|
||||
- 根据世界观特点,决定需要多少个主职业
|
||||
**1. 主职业(main_careers)- 必须精确生成3个**
|
||||
- 主职业是角色的核心发展方向
|
||||
- 必须严格符合世界观规则
|
||||
- 必须严格符合世界观规则和简介中的故事背景
|
||||
- 3个主职业应该覆盖不同的发展路线(如:战斗型、智慧型、特殊型)
|
||||
- 每个主职业的阶段数量可以不同(体现职业复杂度差异)
|
||||
- 职业设计要能支撑简介中描述的故事情节
|
||||
|
||||
**2. 副职业(sub_careers)**
|
||||
- 根据世界需要,决定需要多少个副职业
|
||||
**2. 副职业(sub_careers)- 必须精确生成2个**
|
||||
- 副职业包含生产、辅助、特殊技能类
|
||||
- 2个副职业应该具有互补性,丰富角色的多样性
|
||||
- 每个副职业的阶段数量可以不同
|
||||
- 不要让所有副职业都是相同的阶段数
|
||||
- 副职业要能为主职业提供辅助或增益
|
||||
|
||||
**3. 阶段设计(stages)**
|
||||
- 每个职业的stages数组长度必须等于max_stage
|
||||
- 阶段名称要符合世界观文化背景
|
||||
- 阶段描述要体现明确的能力提升路径
|
||||
- 确保职业间的阶段数量有差异
|
||||
- 主职业阶段数建议:8-12个
|
||||
- 副职业阶段数建议:5-8个
|
||||
|
||||
**4. 简介契合度**
|
||||
- 职业体系必须与项目简介中的故事设定相匹配
|
||||
- 如果简介中提到特定职业或能力,优先设计相关职业
|
||||
- 职业的能力和特点要能支撑简介中的情节发展
|
||||
</design_requirements>
|
||||
|
||||
<output priority="P0">
|
||||
@@ -2072,17 +2201,22 @@ class PromptService:
|
||||
|
||||
<constraints>
|
||||
【必须遵守】
|
||||
✅ 职业数量和类型根据世界观自行决定
|
||||
✅ 主职业数量:必须精确生成3个,不多不少
|
||||
✅ 副职业数量:必须精确生成2个,不多不少
|
||||
✅ 不同职业的max_stage必须不同
|
||||
✅ 主职业阶段数建议:5-15个
|
||||
✅ 副职业阶段数建议:3-10个
|
||||
✅ 主职业阶段数建议:8-12个
|
||||
✅ 副职业阶段数建议:5-8个
|
||||
✅ stages数组长度必须等于max_stage
|
||||
✅ 确保职业体系与世界观高度契合
|
||||
✅ 职业设计必须支撑项目简介中的故事情节
|
||||
|
||||
【禁止事项】
|
||||
❌ 生成超过3个主职业或少于3个主职业
|
||||
❌ 生成超过2个副职业或少于2个副职业
|
||||
❌ 所有职业使用相同的阶段数
|
||||
❌ 输出markdown标记
|
||||
❌ 职业设计与世界观脱节
|
||||
❌ 职业设计与世界观或简介脱节
|
||||
❌ 忽略简介中提到的职业或能力设定
|
||||
</constraints>"""
|
||||
|
||||
# 局部重写提示词(RTCO框架)
|
||||
@@ -2441,7 +2575,7 @@ class PromptService:
|
||||
"parameters": ["project_context", "user_input"]
|
||||
},
|
||||
"OUTLINE_CREATE": {
|
||||
"name": "初始大纲生成",
|
||||
"name": "大纲生成",
|
||||
"category": "大纲生成",
|
||||
"description": "根据项目信息生成完整的章节大纲",
|
||||
"parameters": ["title", "theme", "genre", "chapter_count", "narrative_perspective", "target_words",
|
||||
@@ -2456,21 +2590,36 @@ class PromptService:
|
||||
"all_chapters_brief", "recent_plot", "memory_context", "mcp_references",
|
||||
"plot_stage_instruction", "start_chapter", "end_chapter", "story_direction", "requirements"]
|
||||
},
|
||||
"CHAPTER_GENERATION_V2": {
|
||||
"name": "章节创作V2(首章)",
|
||||
"CHAPTER_GENERATION_ONE_TO_MANY": {
|
||||
"name": "章节创作-1-N模式(第1章)",
|
||||
"category": "章节创作",
|
||||
"description": "根据大纲创作章节内容(用于第1章,无前置章节)",
|
||||
"description": "1-N模式:根据大纲创作章节内容(用于第1章,无前置章节)",
|
||||
"parameters": ["project_title", "genre", "chapter_number", "chapter_title", "chapter_outline",
|
||||
"target_word_count", "narrative_perspective", "characters_info"]
|
||||
},
|
||||
"CHAPTER_GENERATION_V2_WITH_CONTEXT": {
|
||||
"name": "章节创作V2(续章)",
|
||||
"CHAPTER_GENERATION_ONE_TO_MANY_NEXT": {
|
||||
"name": "章节创作-1-N模式(第2章及以后)",
|
||||
"category": "章节创作",
|
||||
"description": "基于前置章节内容创作新章节(用于第2章及以后)",
|
||||
"description": "1-N模式:基于前置章节内容创作新章节(用于第2章及以后)",
|
||||
"parameters": ["project_title", "genre", "chapter_number", "chapter_title", "chapter_outline",
|
||||
"target_word_count", "narrative_perspective", "characters_info", "continuation_point",
|
||||
"foreshadow_reminders", "relevant_memories", "story_skeleton", "previous_chapter_summary"]
|
||||
},
|
||||
"CHAPTER_GENERATION_ONE_TO_ONE": {
|
||||
"name": "章节创作-1-1模式(第1章)",
|
||||
"category": "章节创作",
|
||||
"description": "1-1模式:章节创作(用于第1章,无前置章节)",
|
||||
"parameters": ["project_title", "genre", "chapter_number", "chapter_title", "chapter_outline",
|
||||
"target_word_count", "narrative_perspective", "characters_info", "chapter_careers"]
|
||||
},
|
||||
"CHAPTER_GENERATION_ONE_TO_ONE_NEXT": {
|
||||
"name": "章节创作-1-1模式(第2章及以后)",
|
||||
"category": "章节创作",
|
||||
"description": "1-1模式:基于上一章内容创作新章节(用于第2章及以后)",
|
||||
"parameters": ["project_title", "genre", "chapter_number", "chapter_title", "chapter_outline",
|
||||
"target_word_count", "narrative_perspective", "previous_chapter_content",
|
||||
"characters_info", "chapter_careers", "foreshadow_reminders", "relevant_memories"]
|
||||
},
|
||||
"CHAPTER_REGENERATION_SYSTEM": {
|
||||
"name": "章节重写系统提示",
|
||||
"category": "章节重写",
|
||||
@@ -2565,8 +2714,8 @@ class PromptService:
|
||||
"CAREER_SYSTEM_GENERATION": {
|
||||
"name": "职业体系生成",
|
||||
"category": "世界构建",
|
||||
"description": "根据世界观自动生成完整的职业体系,包括主职业和副职业",
|
||||
"parameters": ["title", "genre", "theme", "time_period", "location", "atmosphere", "rules"]
|
||||
"description": "根据世界观和项目简介自动生成完整的职业体系,包括主职业和副职业",
|
||||
"parameters": ["title", "genre", "theme", "description", "time_period", "location", "atmosphere", "rules"]
|
||||
},
|
||||
"INSPIRATION_TITLE_SYSTEM": {
|
||||
"name": "灵感模式-书名生成(系统提示词)",
|
||||
|
||||
Reference in New Issue
Block a user