refactor: 重构章节上下文构建系统,实现1-1和1-N模式独立构建器

This commit is contained in:
xiamuceer-j
2026-02-06 16:44:09 +08:00
parent 29fbb7cc0b
commit 450b191b8e
5 changed files with 1471 additions and 1488 deletions
+252 -232
View File
@@ -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)
chapter_context = await context_builder.build(
chapter=current_chapter,
project=project,
outline=outline,
user_id=current_user_id,
db=db_session
)
# 日志输出统计信息
logger.info(f"📊 优化上下文统计:")
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)} 字符")
# 🚀 根据大纲模式选择独立的上下文构建器
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,
target_word_count=target_word_count
)
# 日志输出统计信息
logger.info(f"📊 [1-1模式] 上下文统计:")
logger.info(f" - 章节序号: {current_chapter.chapter_number}")
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 '暂无大纲'
# 1-1模式
if chapter_context.continuation_point:
# 有上一章内容
template = await PromptService.get_template("CHAPTER_GENERATION_ONE_TO_ONE_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,
genre=project.genre or '未设定',
narrative_perspective=chapter_perspective,
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:
# 没有expansion_plan,使用大纲内容
chapter_outline_content = outline.content if outline else current_chapter.summary or '暂无大纲'
logger.warning(f"⚠️ 一对多模式但无expansion_plan,使用大纲内容")
# 🚀 使用 V2 优化模板构建提示词
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)
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,
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 '暂无角色信息',
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 ''
)
# 第一章
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:
# 第一章,使用无前置内容模板
template = await PromptService.get_template("CHAPTER_GENERATION_V2", 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,
target_word_count=target_word_count,
# P1 重要参数
genre=project.genre or '未设定',
narrative_perspective=chapter_perspective,
characters_info=characters_info or '暂无角色信息'
)
# ========== 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,
story_skeleton=chapter_context.story_skeleton or '',
relevant_memories=chapter_context.relevant_memories or ''
)
logger.debug(f"创建第{current_chapter.chapter_number}章提示词: {base_prompt}")
else:
# 第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,
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 '暂无角色信息'
)
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,16 +2713,38 @@ 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)
chapter_context = await context_builder.build(
chapter=chapter,
project=project,
outline=outline,
user_id=user_id,
db=db_session
)
# 🚀 根据大纲模式选择独立的上下文构建器(批量生成
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,
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
)
# 日志输出统计信息
logger.info(f"📊 批量生成 - 优化上下文统计:")
@@ -2809,57 +2798,88 @@ 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 chapter_context.continuation_point:
# 有前置内容,使用 WITH_CONTEXT 模板
# 确定上一章摘要:优先使用传入的 previous_summary_context(批量生成的上一章),
# 否则尝试从 chapter_context 中获取
final_prev_summary = "(无上一章摘要,请根据锚点续写)"
if previous_summary_context:
final_prev_summary = previous_summary_context
elif hasattr(chapter_context, 'recent_summary') and chapter_context.recent_summary:
lines = chapter_context.recent_summary.strip().split('\n')
if lines:
final_prev_summary = lines[-1]
template = await PromptService.get_template("CHAPTER_GENERATION_V2_WITH_CONTEXT", 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 ''
)
# 🚀 根据大纲模式选择提示词模板(批量生成)
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:
# 第一章,使用无前置内容模板
template = await PromptService.get_template("CHAPTER_GENERATION_V2", 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 '暂无角色信息'
)
# 1-n模式:使用原有的完整模板
if chapter_context.continuation_point:
# 有前置内容,使用 WITH_CONTEXT 模板
final_prev_summary = "(无上一章摘要,请根据锚点续写)"
if previous_summary_context:
final_prev_summary = previous_summary_context
elif hasattr(chapter_context, 'recent_summary') and chapter_context.recent_summary:
lines = chapter_context.recent_summary.strip().split('\n')
if lines:
final_prev_summary = lines[-1]
template = await PromptService.get_template("CHAPTER_GENERATION_ONE_TO_MANY_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_outline_content,
target_word_count=target_word_count,
continuation_point=chapter_context.continuation_point,
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,
story_skeleton=chapter_context.story_skeleton or '',
relevant_memories=chapter_context.relevant_memories or ''
)
else:
# 第一章,使用无前置内容模板
template = await PromptService.get_template("CHAPTER_GENERATION_ONE_TO_MANY", 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_outline_content,
target_word_count=target_word_count,
genre=project.genre or '未设定',
narrative_perspective=project.narrative_perspective or '第三人称',
characters_info=characters_info or '暂无角色信息'
)
# 应用写作风格
if style_content:
File diff suppressed because it is too large Load Diff
+1
View File
@@ -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
+206 -57
View File
@@ -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": "灵感模式-书名生成(系统提示词)",