diff --git a/backend/app/api/chapters.py b/backend/app/api/chapters.py
index edbda2c..7e52559 100644
--- a/backend/app/api/chapters.py
+++ b/backend/app/api/chapters.py
@@ -10,6 +10,7 @@ from datetime import datetime
from asyncio import Queue, Lock
from app.database import get_db
+from app.services.chapter_context_service import ChapterContextBuilder, FocusedMemoryRetriever
from app.models.chapter import Chapter
from app.models.project import Project
from app.models.outline import Outline
@@ -1314,86 +1315,23 @@ async def generate_chapter_content_stream(
else:
logger.info("未指定写作风格,使用原始提示词")
- # 🚀 使用智能上下文构建(支持海量章节)
- smart_context = await build_smart_chapter_context(
- db=db_session,
- project_id=project.id,
- current_chapter_number=current_chapter.chapter_number,
- user_id=current_user_id
+ # 🚀 使用新的优化上下文构建器
+ logger.info(f"🔧 使用优化的章节上下文构建器(V2)")
+ context_builder = ChapterContextBuilder()
+ chapter_context = await context_builder.build(
+ chapter=current_chapter,
+ project=project,
+ outline=outline,
+ user_id=current_user_id,
+ db=db_session
)
- # 组装上下文
- previous_content = ""
- if smart_context['story_skeleton']:
- previous_content += smart_context['story_skeleton'] + "\n\n"
- if smart_context['relevant_history']:
- previous_content += smart_context['relevant_history'] + "\n\n"
- if smart_context['recent_summary']:
- previous_content += smart_context['recent_summary'] + "\n\n"
- if smart_context['recent_full']:
- previous_content += smart_context['recent_full']
-
- # 🔧 修复1-n模式重复问题: 提取上一章结尾作为精确衔接点
- if current_chapter.chapter_number > 1:
- recent_chapters_parts = smart_context['recent_full'].split('===')
- if len(recent_chapters_parts) >= 2:
- # 提取最后一章(recent_full包含最近3章,最后一个是上一章)
- last_chapter_content = recent_chapters_parts[-1].strip()
- # 提取结尾500字
- last_chapter_ending = last_chapter_content[-600:] if len(last_chapter_content) > 600 else last_chapter_content
-
- previous_content += f"\n\n{'='*50}\n"
- previous_content += f"【⚠️ 上一章结尾内容(必读,用于衔接)】\n"
- previous_content += f"以下是上一章(第{current_chapter.chapter_number-1}章)的结尾部分:\n\n"
- previous_content += last_chapter_ending + "\n"
- previous_content += f"\n{'='*50}\n"
- previous_content += f"【本章({current_chapter.chapter_number}章)创作要求】\n"
- previous_content += f"1. 必须自然承接上述结尾的场景/情节/对话\n"
- previous_content += f"2. 不要重复叙述上一章已经发生的事件\n"
- previous_content += f"3. 从新的情节点、新的场景或新的时间点开始\n"
- previous_content += f"4. 角色状态要延续,不要重新介绍已出场角色\n"
- previous_content += f"{'='*50}\n"
-
# 日志输出统计信息
- stats = smart_context['stats']
- logger.info(f"📊 智能上下文统计:")
- logger.info(f" - 前置章节总数: {stats.get('total_previous', 0)}")
- logger.info(f" - 故事骨架采样: {stats.get('skeleton_samples', 0)}章")
- logger.info(f" - 相关历史检索: {stats.get('relevant_history', 0)}章")
- logger.info(f" - 近期章节概要: {stats.get('recent_summaries', 0)}章")
- logger.info(f" - 最近完整内容: {stats.get('recent_full', 0)}章")
- logger.info(f" - 上下文总长度: {stats.get('total_length', 0)}字符")
-
- # 🧠 构建记忆增强上下文
- logger.info(f"🧠 开始构建记忆增强上下文...")
- memory_context = await memory_service.build_context_for_generation(
- user_id=current_user_id,
- project_id=project.id,
- current_chapter=current_chapter.chapter_number,
- chapter_outline=outline.content if outline else current_chapter.summary or "",
- character_names=[c.name for c in characters] if characters else None
- )
-
- # 计算各部分的字符长度
- context_lengths = {
- 'recent_context': len(memory_context.get('recent_context', '')),
- 'relevant_memories': len(memory_context.get('relevant_memories', '')),
- 'foreshadows': len(memory_context.get('foreshadows', '')),
- 'character_states': len(memory_context.get('character_states', '')),
- 'plot_points': len(memory_context.get('plot_points', ''))
- }
- total_memory_length = sum(context_lengths.values())
-
- logger.info(f"✅ 记忆上下文构建完成: {memory_context['stats']}")
- logger.info(f"📏 记忆上下文长度统计:")
- logger.info(f" - 最近章节记忆: {context_lengths['recent_context']} 字符")
- logger.info(f" - 语义相关记忆: {context_lengths['relevant_memories']} 字符")
- logger.info(f" - 未完结伏笔: {context_lengths['foreshadows']} 字符")
- logger.info(f" - 角色状态记忆: {context_lengths['character_states']} 字符")
- logger.info(f" - 重要情节点: {context_lengths['plot_points']} 字符")
- logger.info(f" - 记忆总长度: {total_memory_length} 字符")
- logger.info(f" - 前置章节上下文长度: {len(previous_content)} 字符")
- logger.info(f" - 总上下文长度(估算): {total_memory_length + len(previous_content) + 2000} 字符")
+ 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)} 字符")
# 发送开始事件
yield f"data: {json.dumps({'type': 'start', 'message': '开始AI创作...'}, ensure_ascii=False)}\n\n"
@@ -1520,74 +1458,55 @@ async def generate_chapter_content_stream(
chapter_outline_content = outline.content if outline else current_chapter.summary or '暂无大纲'
logger.warning(f"⚠️ 一对多模式但无expansion_plan,使用大纲内容")
- # 根据是否有前置内容选择不同的提示词,并应用写作风格、记忆增强和MCP参考资料(支持自定义)
- if previous_content:
- template = await PromptService.get_template("CHAPTER_GENERATION_WITH_CONTEXT", current_user_id, db_session)
+ # 🚀 使用 V2 优化模板构建提示词
+ if chapter_context.continuation_point:
+ # 有前置内容,使用 WITH_CONTEXT 模板
+ template = await PromptService.get_template("CHAPTER_GENERATION_V2_WITH_CONTEXT", current_user_id, db_session)
base_prompt = PromptService.format_prompt(
template,
- title=project.title,
- theme=project.theme or '',
- genre=project.genre or '',
- narrative_perspective=chapter_perspective,
- time_period=project.world_time_period or '未设定',
- location=project.world_location or '未设定',
- atmosphere=project.world_atmosphere or '未设定',
- rules=project.world_rules or '未设定',
- characters_info=characters_info or '暂无角色信息',
- outlines_context=outlines_context,
- previous_content=previous_content,
+ # 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,
- max_word_count=target_word_count + 1000,
- memory_context=memory_context.get('recent_context', '') + "\n" + memory_context.get('relevant_memories', '') + "\n" + memory_context.get('foreshadows', '') + "\n" + memory_context.get('character_states', '') + "\n" + memory_context.get('plot_points', '') if memory_context else "暂无相关记忆"
+ continuation_point=chapter_context.continuation_point,
+ # P1 重要参数
+ genre=project.genre or '未设定',
+ narrative_perspective=chapter_perspective,
+ characters_info=characters_info or '暂无角色信息',
+ # P2 参考参数(动态裁剪后的)
+ story_skeleton=chapter_context.story_skeleton or '',
+ relevant_memories=chapter_context.relevant_memories or ''
)
- # 插入模式说明和MCP参考
- mode_instruction = "\n\n【创作模式说明】\n本章采用细纲模式:本章是大纲节点的细化展开之一。请严格遵循上述详细规划(expansion_plan)中的剧情点、角色焦点、情感基调和叙事目标,确保与整体规划保持一致,同时自然衔接前文内容。\n" if outline_mode == 'one-to-many' else "\n\n【创作模式说明】\n本章采用一对一模式:一个大纲节点对应一个章节。请在承接前文的基础上,充分展开大纲中的情节,保持叙事的完整性。\n"
- mcp_text = ""
- if mcp_reference_materials:
- mcp_text = "\n【📚 MCP工具搜索 - 参考资料】\n以下是通过MCP工具搜索到的相关参考资料,可用于丰富情节和细节:\n\n" + mcp_reference_materials + "\n"
- base_prompt = base_prompt.replace("本章信息:", mcp_text + mode_instruction + "\n本章信息:")
- # 应用写作风格
- if style_content:
- prompt = WritingStyleManager.apply_style_to_prompt(base_prompt, style_content)
- else:
- prompt = base_prompt
else:
- template = await PromptService.get_template("CHAPTER_GENERATION", current_user_id, db_session)
+ # 第一章,使用无前置内容模板
+ template = await PromptService.get_template("CHAPTER_GENERATION_V2", current_user_id, db_session)
base_prompt = PromptService.format_prompt(
template,
- title=project.title,
- theme=project.theme or '',
- genre=project.genre or '',
- narrative_perspective=chapter_perspective,
- time_period=project.world_time_period or '未设定',
- location=project.world_location or '未设定',
- atmosphere=project.world_atmosphere or '未设定',
- rules=project.world_rules or '未设定',
- characters_info=characters_info or '暂无角色信息',
- outlines_context=outlines_context,
+ # 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,
- max_word_count=target_word_count + 1000
+ # P1 重要参数
+ genre=project.genre or '未设定',
+ narrative_perspective=chapter_perspective,
+ characters_info=characters_info or '暂无角色信息'
)
- # 插入模式说明和记忆、MCP参考
- mode_instruction = "\n\n【创作模式说明】\n本章采用细纲模式:本章是大纲节点的细化展开之一。请严格遵循上述详细规划中的剧情点、角色焦点和情感基调,确保与整体规划保持一致。\n" if outline_mode == 'one-to-many' else "\n\n【创作模式说明】\n本章采用一对一模式:一个大纲节点对应一个章节。请充分展开大纲中的情节,注重叙事的完整性和丰满度。\n"
- memory_text = ""
- if memory_context:
- memory_text = "\n【🧠 智能记忆系统 - 重要参考】\n" + memory_context.get('recent_context', '') + "\n" + memory_context.get('relevant_memories', '') + "\n" + memory_context.get('foreshadows', '') + "\n" + memory_context.get('character_states', '') + "\n" + memory_context.get('plot_points', '')
- mcp_text = ""
- if mcp_reference_materials:
- mcp_text = "\n【📚 MCP工具搜索 - 参考资料】\n以下是通过MCP工具搜索到的相关参考资料,可用于丰富情节和细节:\n\n" + mcp_reference_materials + "\n"
- base_prompt = base_prompt.replace("本章信息:", memory_text + mcp_text + mode_instruction + "\n\n本章信息:")
- # 应用写作风格
- if style_content:
- prompt = WritingStyleManager.apply_style_to_prompt(base_prompt, style_content)
- else:
- prompt = base_prompt
+
+ # 添加 MCP 参考资料(如果有)
+ if mcp_reference_materials:
+ mcp_section = f"\n\n\n{mcp_reference_materials}\n"
+ base_prompt = base_prompt.replace("", f"{mcp_section}\n")
+ logger.info(f"📖 已整合MCP参考资料({len(mcp_reference_materials)}字符)")
+
+ # 应用写作风格
+ if style_content:
+ prompt = WritingStyleManager.apply_style_to_prompt(base_prompt, style_content)
+ else:
+ prompt = base_prompt
if mcp_reference_materials:
logger.info(f"📖 已整合MCP参考资料({len(mcp_reference_materials)}字符)到章节生成提示词")
@@ -2774,34 +2693,24 @@ 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 ""
- # 构建智能上下文
- smart_context = await build_smart_chapter_context(
- db=db_session,
- project_id=project.id,
- current_chapter_number=chapter.chapter_number,
- user_id=user_id
- )
-
- # 组装上下文
- previous_content = ""
- if smart_context['story_skeleton']:
- previous_content += smart_context['story_skeleton'] + "\n\n"
- if smart_context['relevant_history']:
- previous_content += smart_context['relevant_history'] + "\n\n"
- if smart_context['recent_summary']:
- previous_content += smart_context['recent_summary'] + "\n\n"
- if smart_context['recent_full']:
- previous_content += smart_context['recent_full']
-
- # 构建记忆增强上下文
- memory_context = await memory_service.build_context_for_generation(
+ # 🚀 使用新的优化上下文构建器
+ logger.info(f"🔧 批量生成 - 使用优化的章节上下文构建器(V2)")
+ context_builder = ChapterContextBuilder()
+ chapter_context = await context_builder.build(
+ chapter=chapter,
+ project=project,
+ outline=outline,
user_id=user_id,
- project_id=project.id,
- current_chapter=chapter.chapter_number,
- chapter_outline=outline.content if outline else chapter.summary or "",
- character_names=[c.name for c in characters] if characters else None
+ db=db_session
)
+ # 日志输出统计信息
+ logger.info(f"📊 批量生成 - 优化上下文统计:")
+ logger.info(f" - 章节序号: {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)} 字符")
+
# 📋 根据大纲模式构建差异化的章节大纲上下文
chapter_outline_content = ""
if outline_mode == 'one-to-one':
@@ -2844,61 +2753,49 @@ async def generate_single_chapter_for_batch(
chapter_outline_content = outline.content if outline else chapter.summary or '暂无大纲'
logger.warning(f"⚠️ 批量生成 - 一对多模式但无expansion_plan,使用大纲内容")
- # 生成提示词(支持自定义)
- if previous_content:
- # 获取自定义提示词模板
- template = await PromptService.get_template("CHAPTER_GENERATION_WITH_CONTEXT", user_id, db_session)
+ # 🚀 使用 V2 优化模板构建提示词(批量生成)
+ if chapter_context.continuation_point:
+ # 有前置内容,使用 WITH_CONTEXT 模板
+ template = await PromptService.get_template("CHAPTER_GENERATION_V2_WITH_CONTEXT", user_id, db_session)
base_prompt = PromptService.format_prompt(
template,
- title=project.title,
- theme=project.theme or '',
- genre=project.genre or '',
- narrative_perspective=project.narrative_perspective or '第三人称',
- time_period=project.world_time_period or '未设定',
- location=project.world_location or '未设定',
- atmosphere=project.world_atmosphere or '未设定',
- rules=project.world_rules or '未设定',
- characters_info=characters_info or '暂无角色信息',
- outlines_context=outlines_context,
- previous_content=previous_content,
+ # 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,
- max_word_count=target_word_count + 1000,
- memory_context=memory_context.get('recent_context', '') + "\n" + memory_context.get('relevant_memories', '') + "\n" + memory_context.get('foreshadows', '') + "\n" + memory_context.get('character_states', '') + "\n" + memory_context.get('plot_points', '') if memory_context else "暂无相关记忆"
+ continuation_point=chapter_context.continuation_point,
+ # P1 重要参数
+ genre=project.genre or '未设定',
+ narrative_perspective=project.narrative_perspective or '第三人称',
+ characters_info=characters_info or '暂无角色信息',
+ # P2 参考参数(动态裁剪后的)
+ story_skeleton=chapter_context.story_skeleton or '',
+ relevant_memories=chapter_context.relevant_memories or ''
)
- # 应用写作风格
- if style_content:
- prompt = WritingStyleManager.apply_style_to_prompt(base_prompt, style_content)
- else:
- prompt = base_prompt
else:
- # 获取自定义提示词模板
- template = await PromptService.get_template("CHAPTER_GENERATION", user_id, db_session)
+ # 第一章,使用无前置内容模板
+ template = await PromptService.get_template("CHAPTER_GENERATION_V2", user_id, db_session)
base_prompt = PromptService.format_prompt(
template,
- title=project.title,
- theme=project.theme or '',
- genre=project.genre or '',
- narrative_perspective=project.narrative_perspective or '第三人称',
- time_period=project.world_time_period or '未设定',
- location=project.world_location or '未设定',
- atmosphere=project.world_atmosphere or '未设定',
- rules=project.world_rules or '未设定',
- characters_info=characters_info or '暂无角色信息',
- outlines_context=outlines_context,
+ # 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,
- max_word_count=target_word_count + 1000
+ # P1 重要参数
+ genre=project.genre or '未设定',
+ narrative_perspective=project.narrative_perspective or '第三人称',
+ characters_info=characters_info or '暂无角色信息'
)
- # 应用写作风格
- if style_content:
- prompt = WritingStyleManager.apply_style_to_prompt(base_prompt, style_content)
- else:
- prompt = base_prompt
+
+ # 应用写作风格
+ if style_content:
+ prompt = WritingStyleManager.apply_style_to_prompt(base_prompt, style_content)
+ else:
+ prompt = base_prompt
# 🎨 方案一:将写作风格注入到系统提示词(批量生成)
system_prompt_with_style = None
diff --git a/backend/app/services/chapter_context_service.py b/backend/app/services/chapter_context_service.py
new file mode 100644
index 0000000..805bde0
--- /dev/null
+++ b/backend/app/services/chapter_context_service.py
@@ -0,0 +1,745 @@
+"""章节上下文构建服务 - 实现RTCO框架的智能上下文构建"""
+
+from dataclasses import dataclass, field
+from typing import Dict, Any, Optional, List
+from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy import select
+import json
+
+from app.models.chapter import Chapter
+from app.models.project import Project
+from app.models.outline import Outline
+from app.models.character import Character
+from app.models.career import Career, CharacterCareer
+from app.models.memory import StoryMemory
+from app.logger import get_logger
+
+logger = get_logger(__name__)
+
+
+@dataclass
+class ChapterContext:
+ """
+ 章节上下文数据结构
+
+ 采用RTCO框架的分层设计:
+ - P0-核心(必须):大纲、衔接点、字数要求
+ - P1-重要(按需):角色、情感基调、风格
+ - P2-参考(条件触发):记忆、故事骨架、MCP资料
+ """
+
+ # === P0-核心信息(必须包含)===
+ chapter_outline: str = "" # 本章大纲
+ continuation_point: Optional[str] = None # 衔接锚点(上一章结尾)
+ target_word_count: int = 3000 # 目标字数
+ min_word_count: int = 2500 # 最小字数
+ max_word_count: int = 4000 # 最大字数
+ narrative_perspective: str = "第三人称" # 叙事视角
+
+ # === 本章基本信息 ===
+ chapter_number: int = 1 # 章节序号
+ chapter_title: str = "" # 章节标题
+
+ # === 项目基本信息 ===
+ title: str = "" # 书名
+ genre: str = "" # 类型
+ theme: str = "" # 主题
+
+ # === P1-重要信息(按需包含)===
+ chapter_characters: str = "" # 本章涉及角色(精简)
+ emotional_tone: str = "" # 情感基调
+ style_instruction: str = "" # 写作风格指令(摘要化)
+
+ # === P2-参考信息(条件触发)===
+ relevant_memories: Optional[str] = None # 相关记忆(精简版)
+ story_skeleton: Optional[str] = None # 故事骨架(50章+启用)
+ mcp_references: Optional[str] = None # MCP参考资料
+
+ # === 元信息 ===
+ context_stats: Dict[str, Any] = field(default_factory=dict) # 统计信息
+
+ def get_total_context_length(self) -> int:
+ """计算总上下文长度"""
+ total = 0
+ for field_name in ['chapter_outline', 'continuation_point', 'chapter_characters',
+ 'relevant_memories', 'story_skeleton', 'style_instruction']:
+ value = getattr(self, field_name, None)
+ if value:
+ total += len(value)
+ return total
+
+
+class ChapterContextBuilder:
+ """
+ 章节上下文构建器
+
+ 实现动态裁剪逻辑,根据章节序号自动调整上下文复杂度:
+ - 第1章:无前置上下文,仅提供大纲和角色
+ - 第2-10章:上一章结尾300字 + 涉及角色
+ - 第11-50章:上一章结尾500字 + 相关记忆3条
+ - 第51章+:上一章结尾500字 + 故事骨架 + 智能记忆5条
+ """
+
+ # 配置常量
+ ENDING_LENGTH_SHORT = 300 # 1-10章:短衔接
+ ENDING_LENGTH_NORMAL = 500 # 11章+:标准衔接
+ MEMORY_COUNT_LIGHT = 3 # 11-50章:轻量记忆
+ MEMORY_COUNT_FULL = 5 # 51章+:完整记忆
+ SKELETON_THRESHOLD = 50 # 启用故事骨架的章节阈值
+ SKELETON_SAMPLE_INTERVAL = 10 # 故事骨架采样间隔
+ MEMORY_IMPORTANCE_THRESHOLD = 0.7 # 记忆重要性阈值
+ STYLE_MAX_LENGTH = 200 # 风格描述最大长度
+ MAX_CONTEXT_LENGTH = 3000 # 总上下文最大字符数
+
+ def __init__(self, memory_service=None):
+ """
+ 初始化构建器
+
+ Args:
+ memory_service: 记忆服务实例(可选,用于检索相关记忆)
+ """
+ self.memory_service = memory_service
+
+ async def build(
+ self,
+ chapter: Chapter,
+ project: Project,
+ outline: Optional[Outline],
+ user_id: str,
+ db: AsyncSession,
+ style_content: Optional[str] = None,
+ target_word_count: int = 3000,
+ temp_narrative_perspective: Optional[str] = None
+ ) -> ChapterContext:
+ """
+ 构建章节生成所需的上下文
+
+ Args:
+ chapter: 章节对象
+ project: 项目对象
+ outline: 大纲对象(可选)
+ user_id: 用户ID
+ db: 数据库会话
+ style_content: 写作风格内容(可选)
+ target_word_count: 目标字数
+ temp_narrative_perspective: 临时叙事视角(可选,覆盖项目默认)
+
+ Returns:
+ ChapterContext: 结构化的上下文对象
+ """
+ chapter_number = chapter.chapter_number
+ logger.info(f"📝 开始构建章节上下文: 第{chapter_number}章")
+
+ # 确定叙事视角
+ narrative_perspective = (
+ temp_narrative_perspective or
+ project.narrative_perspective or
+ "第三人称"
+ )
+
+ # 初始化上下文
+ context = ChapterContext(
+ chapter_number=chapter_number,
+ chapter_title=chapter.title or "",
+ title=project.title or "",
+ genre=project.genre or "",
+ theme=project.theme or "",
+ target_word_count=target_word_count,
+ min_word_count=max(500, target_word_count - 500),
+ max_word_count=target_word_count + 1000,
+ narrative_perspective=narrative_perspective
+ )
+
+ # === P0-核心信息(始终构建)===
+ context.chapter_outline = await self._build_chapter_outline(
+ chapter, outline, project.outline_mode
+ )
+
+ # === 衔接锚点(根据章节调整长度)===
+ if chapter_number == 1:
+ context.continuation_point = None
+ logger.info(" ✅ 第1章无需衔接锚点")
+ elif chapter_number <= 10:
+ context.continuation_point = await self._get_last_ending(
+ chapter, db, self.ENDING_LENGTH_SHORT
+ )
+ logger.info(f" ✅ 衔接锚点(短): {len(context.continuation_point or '')}字符")
+ else:
+ context.continuation_point = await self._get_last_ending(
+ chapter, db, self.ENDING_LENGTH_NORMAL
+ )
+ logger.info(f" ✅ 衔接锚点(标准): {len(context.continuation_point or '')}字符")
+
+ # === P1-重要信息 ===
+ context.chapter_characters = await self._build_chapter_characters(
+ chapter, project, outline, db
+ )
+ context.emotional_tone = self._extract_emotional_tone(chapter, outline)
+
+ # 写作风格(摘要化)
+ if style_content:
+ context.style_instruction = self._summarize_style(style_content)
+
+ # === P2-参考信息(条件触发)===
+ if chapter_number > 10 and self.memory_service:
+ memory_limit = (
+ self.MEMORY_COUNT_LIGHT if chapter_number <= 50
+ else self.MEMORY_COUNT_FULL
+ )
+ context.relevant_memories = await self._get_relevant_memories(
+ user_id, project.id, chapter_number,
+ context.chapter_outline,
+ limit=memory_limit
+ )
+ logger.info(f" ✅ 相关记忆: {len(context.relevant_memories or '')}字符")
+
+ # 故事骨架(50章+)
+ if chapter_number > self.SKELETON_THRESHOLD:
+ context.story_skeleton = await self._build_story_skeleton(
+ project.id, chapter_number, db
+ )
+ logger.info(f" ✅ 故事骨架: {len(context.story_skeleton or '')}字符")
+
+ # === 统计信息 ===
+ context.context_stats = {
+ "chapter_number": chapter_number,
+ "has_continuation": context.continuation_point is not None,
+ "continuation_length": len(context.continuation_point or ""),
+ "characters_length": len(context.chapter_characters),
+ "memories_length": len(context.relevant_memories or ""),
+ "skeleton_length": len(context.story_skeleton or ""),
+ "total_length": context.get_total_context_length()
+ }
+
+ logger.info(f"📊 上下文构建完成: 总长度 {context.context_stats['total_length']} 字符")
+
+ return context
+
+ async def _build_chapter_outline(
+ self,
+ chapter: Chapter,
+ outline: Optional[Outline],
+ outline_mode: str
+ ) -> str:
+ """
+ 构建本章大纲内容
+
+ Args:
+ chapter: 章节对象
+ outline: 大纲对象
+ outline_mode: 大纲模式(one-to-one/one-to-many)
+
+ Returns:
+ 本章大纲文本
+ """
+ if outline_mode == 'one-to-one':
+ # 一对一模式:使用大纲的 content
+ return outline.content if outline else chapter.summary or '暂无大纲'
+ else:
+ # 一对多模式:优先使用 expansion_plan 的详细规划
+ if chapter.expansion_plan:
+ try:
+ plan = json.loads(chapter.expansion_plan)
+ 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', '未设定')}"""
+ return outline_content
+ except json.JSONDecodeError:
+ pass
+
+ # 回退到大纲内容
+ return outline.content if outline else chapter.summary or '暂无大纲'
+
+ async def _get_last_ending(
+ self,
+ chapter: Chapter,
+ db: AsyncSession,
+ max_length: int
+ ) -> Optional[str]:
+ """
+ 获取上一章结尾内容作为衔接锚点
+
+ Args:
+ chapter: 当前章节
+ db: 数据库会话
+ max_length: 最大长度
+
+ Returns:
+ 上一章结尾内容
+ """
+ if chapter.chapter_number <= 1:
+ return None
+
+ # 查询上一章
+ result = await db.execute(
+ select(Chapter)
+ .where(Chapter.project_id == chapter.project_id)
+ .where(Chapter.chapter_number == chapter.chapter_number - 1)
+ )
+ prev_chapter = result.scalar_one_or_none()
+
+ if not prev_chapter or not prev_chapter.content:
+ return None
+
+ # 提取结尾内容
+ content = prev_chapter.content.strip()
+ if len(content) <= max_length:
+ return content
+
+ return content[-max_length:]
+
+ async def _build_chapter_characters(
+ self,
+ chapter: Chapter,
+ project: Project,
+ outline: Optional[Outline],
+ db: AsyncSession
+ ) -> str:
+ """
+ 构建本章涉及的角色信息(精简版)
+
+ 只返回本章相关的角色,而非全部角色
+
+ Args:
+ chapter: 章节对象
+ project: 项目对象
+ outline: 大纲对象
+ db: 数据库会话
+
+ Returns:
+ 本章角色信息文本
+ """
+ # 获取所有角色
+ characters_result = await db.execute(
+ select(Character).where(Character.project_id == project.id)
+ )
+ characters = characters_result.scalars().all()
+
+ if not characters:
+ return "暂无角色信息"
+
+ # 提取本章相关角色名单
+ filter_character_names = None
+
+ # 从大纲或扩展计划中提取角色
+ if project.outline_mode == 'one-to-one':
+ if outline and outline.structure:
+ try:
+ structure = json.loads(outline.structure)
+ filter_character_names = structure.get('characters', [])
+ except json.JSONDecodeError:
+ pass
+ else:
+ if chapter.expansion_plan:
+ try:
+ plan = json.loads(chapter.expansion_plan)
+ filter_character_names = plan.get('character_focus', [])
+ except json.JSONDecodeError:
+ pass
+
+ # 筛选角色
+ if filter_character_names:
+ characters = [c for c in characters if c.name in filter_character_names]
+
+ if not characters:
+ return "暂无相关角色"
+
+ # 构建精简的角色信息(每个角色最多100字符)
+ char_lines = []
+ for c in characters[:10]: # 最多10个角色
+ role_type = "主角" if c.role_type == "protagonist" else (
+ "反派" if c.role_type == "antagonist" else "配角"
+ )
+
+ # 性格摘要(最多50字符)
+ personality_brief = ""
+ if c.personality:
+ personality_brief = c.personality[:50]
+ if len(c.personality) > 50:
+ personality_brief += "..."
+
+ char_lines.append(f"- {c.name}({role_type}): {personality_brief}")
+
+ return "\n".join(char_lines)
+
+ def _extract_emotional_tone(
+ self,
+ chapter: Chapter,
+ outline: Optional[Outline]
+ ) -> str:
+ """
+ 提取本章情感基调
+
+ Args:
+ chapter: 章节对象
+ outline: 大纲对象
+
+ Returns:
+ 情感基调描述
+ """
+ # 尝试从扩展计划中提取
+ if chapter.expansion_plan:
+ try:
+ plan = json.loads(chapter.expansion_plan)
+ tone = plan.get('emotional_tone')
+ if tone:
+ return tone
+ except json.JSONDecodeError:
+ pass
+
+ # 尝试从大纲结构中提取
+ if outline and outline.structure:
+ try:
+ structure = json.loads(outline.structure)
+ tone = structure.get('emotion') or structure.get('emotional_tone')
+ if tone:
+ return tone
+ except json.JSONDecodeError:
+ pass
+
+ return "未设定"
+
+ def _summarize_style(self, style_content: str) -> str:
+ """
+ 将风格描述压缩为关键要点
+
+ Args:
+ style_content: 完整风格描述
+
+ Returns:
+ 摘要化的风格描述
+ """
+ if not style_content:
+ return ""
+
+ if len(style_content) <= self.STYLE_MAX_LENGTH:
+ return style_content
+
+ # 简单截断(后续可以用AI提取关键词)
+ return style_content[:self.STYLE_MAX_LENGTH] + "..."
+
+ async def _get_relevant_memories(
+ self,
+ user_id: str,
+ project_id: str,
+ chapter_number: int,
+ chapter_outline: str,
+ limit: int = 3
+ ) -> Optional[str]:
+ """
+ 获取与本章最相关的记忆(精简版)
+
+ 策略:
+ 1. 仅检索与大纲语义最相关的记忆
+ 2. 提高重要性阈值,过滤低质量记忆
+ 3. 优先返回未回收的伏笔
+
+ Args:
+ user_id: 用户ID
+ project_id: 项目ID
+ chapter_number: 当前章节号
+ chapter_outline: 本章大纲
+ limit: 返回数量限制
+
+ Returns:
+ 格式化的记忆文本
+ """
+ if not self.memory_service:
+ return None
+
+ try:
+ # 1. 语义检索相关记忆(提高阈值)
+ relevant = await self.memory_service.search_memories(
+ user_id=user_id,
+ project_id=project_id,
+ query=chapter_outline,
+ limit=limit,
+ min_importance=self.MEMORY_IMPORTANCE_THRESHOLD
+ )
+
+ # 2. 检查即将到期的伏笔
+ foreshadows = await self._get_due_foreshadows(
+ user_id, project_id, chapter_number,
+ lookahead=5 # 仅看5章内需要回收的
+ )
+
+ # 3. 合并并格式化
+ return self._format_memories(relevant, foreshadows, max_length=500)
+
+ except Exception as e:
+ logger.error(f"❌ 获取相关记忆失败: {str(e)}")
+ return None
+
+ async def _get_due_foreshadows(
+ self,
+ user_id: str,
+ project_id: str,
+ chapter_number: int,
+ lookahead: int = 5
+ ) -> List[Dict[str, Any]]:
+ """
+ 获取即将需要回收的伏笔
+
+ Args:
+ user_id: 用户ID
+ project_id: 项目ID
+ chapter_number: 当前章节号
+ lookahead: 往前看的章节数
+
+ Returns:
+ 待回收伏笔列表
+ """
+ if not self.memory_service:
+ return []
+
+ try:
+ foreshadows = await self.memory_service.find_unresolved_foreshadows(
+ user_id, project_id, chapter_number
+ )
+
+ # 过滤:只保留埋下时间较长(超过lookahead章)的伏笔
+ due_foreshadows = []
+ for fs in foreshadows:
+ meta = fs.get('metadata', {})
+ fs_chapter = meta.get('chapter_number', 0)
+ if chapter_number - fs_chapter >= lookahead:
+ due_foreshadows.append({
+ 'chapter': fs_chapter,
+ 'content': fs.get('content', '')[:60],
+ 'importance': meta.get('importance', 0.5)
+ })
+
+ return due_foreshadows[:2] # 最多2条
+
+ except Exception as e:
+ logger.error(f"❌ 获取待回收伏笔失败: {str(e)}")
+ return []
+
+ def _format_memories(
+ self,
+ relevant: List[Dict[str, Any]],
+ foreshadows: List[Dict[str, Any]],
+ max_length: int = 500
+ ) -> str:
+ """
+ 格式化记忆为简洁文本,严格限制长度
+
+ Args:
+ relevant: 相关记忆列表
+ foreshadows: 待回收伏笔列表
+ max_length: 最大长度
+
+ Returns:
+ 格式化的记忆文本
+ """
+ lines = []
+ current_length = 0
+
+ # 优先添加待回收伏笔
+ if foreshadows:
+ lines.append("【待回收伏笔】")
+ for fs in foreshadows[:2]:
+ text = f"- 第{fs['chapter']}章埋下:{fs['content']}"
+ if current_length + len(text) > max_length:
+ break
+ lines.append(text)
+ current_length += len(text)
+
+ # 添加相关记忆
+ if relevant and current_length < max_length:
+ lines.append("【相关记忆】")
+ for mem in relevant:
+ content = mem.get('content', '')[:80]
+ text = f"- {content}"
+ if current_length + len(text) > max_length:
+ break
+ lines.append(text)
+ current_length += len(text)
+
+ return "\n".join(lines) if lines else None
+
+ async def _build_story_skeleton(
+ self,
+ project_id: str,
+ chapter_number: int,
+ db: AsyncSession
+ ) -> Optional[str]:
+ """
+ 构建故事骨架(每N章采样)
+
+ Args:
+ project_id: 项目ID
+ chapter_number: 当前章节号
+ db: 数据库会话
+
+ Returns:
+ 故事骨架文本
+ """
+ try:
+ # 获取所有已完成章节的摘要
+ result = await db.execute(
+ select(Chapter.chapter_number, Chapter.title)
+ .where(Chapter.project_id == project_id)
+ .where(Chapter.chapter_number < chapter_number)
+ .where(Chapter.content != None)
+ .where(Chapter.content != "")
+ .order_by(Chapter.chapter_number)
+ )
+ chapters = result.all()
+
+ if not chapters:
+ return None
+
+ # 采样:每N章取一个
+ skeleton_lines = ["【故事骨架】"]
+ for i, (ch_num, ch_title) in enumerate(chapters):
+ if i % self.SKELETON_SAMPLE_INTERVAL == 0:
+ # 尝试获取章节摘要
+ summary_result = await db.execute(
+ select(StoryMemory.content)
+ .where(StoryMemory.project_id == project_id)
+ .where(StoryMemory.story_timeline == ch_num)
+ .where(StoryMemory.memory_type == 'chapter_summary')
+ .limit(1)
+ )
+ summary = summary_result.scalar_one_or_none()
+
+ if summary:
+ skeleton_lines.append(f"第{ch_num}章《{ch_title}》:{summary[:100]}")
+ else:
+ skeleton_lines.append(f"第{ch_num}章《{ch_title}》")
+
+ if len(skeleton_lines) <= 1:
+ return None
+
+ return "\n".join(skeleton_lines)
+
+ except Exception as e:
+ logger.error(f"❌ 构建故事骨架失败: {str(e)}")
+ return None
+
+
+class FocusedMemoryRetriever:
+ """
+ 精简记忆检索器
+
+ 相比原有的memory_service,提供更精准、更简洁的记忆检索
+ """
+
+ def __init__(self, memory_service):
+ """
+ 初始化检索器
+
+ Args:
+ memory_service: 基础记忆服务实例
+ """
+ self.memory_service = memory_service
+
+ async def get_relevant_memories(
+ self,
+ user_id: str,
+ project_id: str,
+ chapter_number: int,
+ chapter_outline: str,
+ limit: int = 3
+ ) -> str:
+ """
+ 获取与本章最相关的记忆
+
+ 策略:
+ 1. 仅检索与大纲语义最相关的记忆
+ 2. 提高重要性阈值,过滤低质量记忆
+ 3. 优先返回未回收的伏笔
+
+ Args:
+ user_id: 用户ID
+ project_id: 项目ID
+ chapter_number: 当前章节号
+ chapter_outline: 本章大纲
+ limit: 返回数量限制
+
+ Returns:
+ 格式化的记忆文本
+ """
+ # 1. 语义检索相关记忆(提高阈值)
+ relevant = await self.memory_service.search_memories(
+ user_id=user_id,
+ project_id=project_id,
+ query=chapter_outline,
+ limit=limit,
+ min_importance=0.7 # 从0.4提高到0.7
+ )
+
+ # 2. 检查即将到期的伏笔
+ due_foreshadows = await self._get_due_foreshadows(
+ user_id, project_id, chapter_number,
+ lookahead=5 # 仅看5章内需要回收的
+ )
+
+ # 3. 合并并格式化
+ return self._format_memories(relevant, due_foreshadows, max_length=500)
+
+ async def _get_due_foreshadows(
+ self,
+ user_id: str,
+ project_id: str,
+ chapter_number: int,
+ lookahead: int = 5
+ ) -> List[Dict[str, Any]]:
+ """获取即将需要回收的伏笔"""
+ foreshadows = await self.memory_service.find_unresolved_foreshadows(
+ user_id, project_id, chapter_number
+ )
+
+ # 过滤:只保留埋下时间较长的伏笔
+ due_foreshadows = []
+ for fs in foreshadows:
+ meta = fs.get('metadata', {})
+ fs_chapter = meta.get('chapter_number', 0)
+ if chapter_number - fs_chapter >= lookahead:
+ due_foreshadows.append({
+ 'chapter': fs_chapter,
+ 'content': fs.get('content', '')[:60],
+ 'importance': meta.get('importance', 0.5)
+ })
+
+ return due_foreshadows[:2] # 最多2条
+
+ def _format_memories(
+ self,
+ relevant: List[Dict[str, Any]],
+ foreshadows: List[Dict[str, Any]],
+ max_length: int = 500
+ ) -> str:
+ """格式化为简洁文本,严格限制长度"""
+ lines = []
+ current_length = 0
+
+ # 优先添加待回收伏笔
+ if foreshadows:
+ lines.append("【待回收伏笔】")
+ for fs in foreshadows[:2]:
+ text = f"- 第{fs['chapter']}章埋下:{fs['content']}"
+ if current_length + len(text) > max_length:
+ break
+ lines.append(text)
+ current_length += len(text)
+
+ # 添加相关记忆
+ if relevant and current_length < max_length:
+ lines.append("【相关记忆】")
+ for mem in relevant:
+ content = mem.get('content', '')[:80]
+ text = f"- {content}"
+ if current_length + len(text) > max_length:
+ break
+ lines.append(text)
+ current_length += len(text)
+
+ return "\n".join(lines) if lines else ""
\ No newline at end of file
diff --git a/backend/app/services/prompt_service.py b/backend/app/services/prompt_service.py
index 40580dc..6662716 100644
--- a/backend/app/services/prompt_service.py
+++ b/backend/app/services/prompt_service.py
@@ -25,288 +25,309 @@ class WritingStyleManager:
class PromptService:
"""提示词模板管理"""
- # 世界构建提示词
- WORLD_BUILDING = """你是一位资深的世界观设计师。基于以下输入信息,构建一个高度原创、深度自洽、充满戏剧冲突的小说世界观。
+ # ========== V2版本提示词模板(RTCO框架)==========
+
+ # 世界构建提示词 V2(RTCO框架)
+ WORLD_BUILDING = """
+你是资深的世界观设计师,擅长为{genre}类型的小说构建真实、自洽的世界观。
+
-# 输入信息
+
+【设计任务】
+为小说《{title}》构建完整的世界观设定。
+
+【核心要求】
+- 主题契合:世界观必须支撑主题"{theme}"
+- 简介匹配:为简介中的情节提供合理背景
+- 类型适配:符合{genre}类型的特征
+- 规模适当:根据题材选择合适的设定尺度
+
+
+
+【项目信息】
书名:{title}
-主题:{theme}
类型:{genre}
+主题:{theme}
简介:{description}
+
-# 核心要求
-* **简介契合性**:世界观设定必须能够支撑简介中描述的故事情节和核心矛盾
-* **类型适配性**:世界观必须符合小说类型的特征,不要生成不匹配的设定
-* **主题贴合性**:时代背景要能有效支撑和体现小说主题
-* **原创性**:在类型框架内发挥创意,创造独特但合理的世界设定
-* **具象化**:避免空洞概念,用具体可感的细节描述世界
-* **逻辑自洽**:确保所有设定相互支撑,形成完整体系
-* **戏剧张力**:设定要能为故事冲突提供支撑,尤其要为简介中的故事线索创造合适的环境
-
-# 类型指导原则
-根据小说类型选择适当的设定风格:
+
+【类型指导原则】
**现代都市/言情/青春**:
-- 时间设定:当代现实社会(2020年代)或近未来(2030-2050年)
-- 避免使用:大崩解、纪元、末日、重生等宏大概念
-- 重点描述:具体的城市环境、社会现状、文化氛围
-- 例如:一线城市的竞争压力、职场文化、代际冲突、社交媒体影响等
+- 时间:当代社会(2020年代)或近未来(2030-2050年)
+- 避免:大崩解、纪元、末日等宏大概念
+- 重点:具体城市环境、职场文化、社会现状
**历史/古代**:
-- 时间设定:明确的历史朝代或虚构但有历史感的古代社会
-- 避免使用:科技元素、未来概念
-- 重点描述:时代特征、礼教制度、阶级分化
+- 时间:明确的历史朝代或虚构古代
+- 重点:时代特征、礼教制度、阶级分化
**玄幻/仙侠/修真**:
-- 时间设定:修炼文明的特定时期,可以有门派兴衰、修炼体系变革
-- 可以使用宏大设定,但要与修炼体系紧密结合
-- 重点描述:修炼规则、灵气环境、门派势力
+- 时间:修炼文明的特定时期
+- 重点:修炼规则、灵气环境、门派势力
**科幻**:
-- 时间设定:未来某个明确时期(如2150年、星际时代初期等)
-- 可以有文明转折,但要具体说明科技水平和社会形态
-- 避免空泛的纪元名称,多用具体的科技特征描述
+- 时间:未来明确时期(如2150年、星际时代初期)
+- 重点:科技水平、社会形态、文明转折
**奇幻/魔法**:
-- 时间设定:魔法文明的特定阶段
-- 重点描述:魔法体系、种族关系、大陆格局
+- 时间:魔法文明的特定阶段
+- 重点:魔法体系、种族关系、大陆格局
-**悬疑/推理/惊悚**:
-- 时间设定:当代或历史某个时期
-- 重点描述:案件背景、社会环境、人际关系网
+**设定尺度控制**:
+- 现代都市:聚焦某个城市、行业、阶层
+- 校园青春:学校环境、学生生活、成长困境
+- 职场言情:公司文化、行业特点、职业压力
+- 史诗题材:才需要宏大的世界观架构
+
-**军事/战争**:
-- 时间设定:战争时期的具体年代
-- 重点描述:战争形势、阵营对立、军事科技水平
-
-# 设定尺度控制
-**切记:不要为所有类型都生成宏大的世界观!**
-
-- 如果是现代都市题材,就写现实社会的某个城市、某个行业、某个阶层
-- 如果是校园青春,就写学校环境、学生生活、成长困境
-- 如果是职场言情,就写公司文化、行业特点、职业压力
-- 只有史诗级题材(玄幻、科幻、奇幻等)才需要宏大的世界观架构
-
-# 输出要求
-生成包含以下四个字段的JSON对象,每个字段用300-500字的连贯段落描述:
+
- # 批量角色生成提示词
- CHARACTERS_BATCH_GENERATION = """你是一位专业的角色设定师。请根据以下世界观和要求,生成{count}个立体丰满的角色和组织:
+
+【必须遵守】
+✅ 简介契合:为简介情节提供合理背景
+✅ 类型适配:符合{genre}的特征
+✅ 主题贴合:支撑主题"{theme}"
+✅ 具象化:用具体细节而非空洞概念
+✅ 逻辑自洽:所有设定相互支撑
-世界观信息:
-- 时间背景:{time_period}
-- 地理位置:{location}
-- 氛围基调:{atmosphere}
-- 世界规则:{rules}
+【禁止事项】
+❌ 生成与类型不匹配的设定
+❌ 为小规模题材使用宏大世界观
+❌ 使用模板化、空泛的表达
+❌ 输出markdown或代码块标记
+"""
+
+ # 批量角色生成提示词 V2(RTCO框架)
+ CHARACTERS_BATCH_GENERATION = """
+你是专业的角色设定师,擅长为{genre}类型的小说创建立体丰满的角色。
+
+
+
+【生成任务】
+生成{count}个角色和组织实体。
+
+【数量要求 - 严格遵守】
+数组中必须精确包含{count}个对象,不多不少。
+
+【实体类型分配】
+- 至少1个主角(protagonist)
+- 多个配角(supporting)
+- 可包含反派(antagonist)
+- 可包含1-2个高影响力组织(power_level: 70-95)
+
+
+
+【世界观信息】
+时间背景:{time_period}
+地理位置:{location}
+氛围基调:{atmosphere}
+世界规则:{rules}
主题:{theme}
类型:{genre}
-特殊要求:{requirements}
+
-【数量要求 - 必须严格遵守】
-请精确生成{count}个实体,不多不少。数组中必须包含且仅包含{count}个对象。
+
+【特殊要求】
+{requirements}
+
-实体类型分配:
-- 至少1个主角(protagonist)
-- 多个配角(supporting)
-- 可以包含反派(antagonist)
-- 可以包含1-2个**高影响力的重要组织**(势力等级应在70-95之间)
+
-**示例说明**:
-- 如果生成了角色A、组织B、角色C,则角色A的organization_memberships只能是[组织B],不能是其他组织
-- 如果角色A在数组第一位,它的relationships_array必须为空[],因为还没有其他角色
-- 如果角色C在数组第三位,它的relationships_array可以引用角色A,但不能引用不存在的角色D
+
+【必须遵守】
+✅ 数量精确:数组必须包含{count}个对象
+✅ 符合世界观:角色设定与世界观一致
+✅ 有深度:性格和背景要立体
+✅ 关系网络:角色间形成合理关系
+✅ 组织合理:组织是推动剧情的关键力量
-再次强调:
-1. 只返回纯JSON数组,不要有```json```这样的标记
-2. 数组中必须精确包含{count}个对象
-3. 不要引用任何本批次中不存在的角色或组织名称
-4. 所有内容描述中严禁使用任何特殊符号,包括但不限于中文引号、英文引号、方括号、书名号等"""
+【关系约束】
+✅ relationships_array只能引用本批次已出现的角色
+✅ organization_memberships只能引用本批次的组织
+✅ 第一个角色的relationships_array必须为空[]
+✅ 禁止幻觉:不引用不存在的角色或组织
- # 向导大纲生成提示词
- OUTLINE_CREATE = """你是一位经验丰富的小说作家和编剧。请根据以下信息为小说生成**开篇{chapter_count}章**的大纲:
+【格式约束】
+✅ 纯JSON数组输出,无markdown标记
+✅ 内容描述中严禁使用特殊符号(引号、方括号、书名号等)
+✅ 专有名词直接书写,不使用符号包裹
+
+【禁止事项】
+❌ 生成数量不符(多于或少于{count}个)
+❌ 引用不存在的角色或组织
+❌ 生成低影响力的无关紧要组织
+❌ 使用markdown或代码块标记
+❌ 在描述中使用特殊符号
+"""
+
+ # 大纲生成提示词 V2(RTCO框架)
+ OUTLINE_CREATE = """
+你是经验丰富的小说作家和编剧,擅长为{genre}类型的小说设计精彩开篇。
+
+
+
+【创作任务】
+为小说《{title}》生成开篇{chapter_count}章的大纲。
【重要说明】
-本次任务是为项目初始化生成开头部分的大纲,而不是整本书的完整大纲。这些章节应该:
-- 完成故事的**开局设定**和**世界观展示**
+这是项目初始化的开头部分,不是完整大纲:
+- 完成开局设定和世界观展示
- 引入主要角色,建立初始关系
- 埋下核心矛盾和悬念钩子
- 为后续剧情发展打下基础
-- **不需要完整的故事闭环**,结尾应该为续写留出空间
+- 不需要完整闭环,为续写留空间
+
-基本信息:
-- 书名:{title}
-- 主题:{theme}
-- 类型:{genre}
-- 开篇章节数:{chapter_count}
-- 叙事视角:{narrative_perspective}
-- 全书目标字数:{target_words}
+
+【项目信息】
+书名:{title}
+主题:{theme}
+类型:{genre}
+开篇章节数:{chapter_count}
+叙事视角:{narrative_perspective}
+全书目标字数:{target_words}
+
-世界观:
-- 时间背景:{time_period}
-- 地理位置:{location}
-- 氛围基调:{atmosphere}
-- 世界规则:{rules}
+
+【世界观】
+时间背景:{time_period}
+地理位置:{location}
+氛围基调:{atmosphere}
+世界规则:{rules}
+
-角色信息:
+
+【角色信息】
{characters_info}
+
+
{mcp_references}
+
-其他要求:{requirements}
+
+【其他要求】
+{requirements}
+
-开篇大纲要求:
-- **开局设定**:前几章完成世界观呈现、主角登场、初始状态建立
-- **矛盾引入**:引出核心冲突或故事主线,但不急于展开
-- **角色亮相**:主要角色依次登场,展示性格特点和相互关系
-- **节奏控制**:开篇不宜过快,给读者适应和代入的时间
-- **悬念设置**:埋下伏笔和钩子,为后续续写大纲预留发展空间
-- **视角统一**:采用{narrative_perspective}视角叙事
-- **留白艺术**:结尾不要收束过紧,要为后续剧情留出足够的发展空间
+
+
+
+【开篇大纲要求】
+✅ 开局设定:前几章完成世界观呈现、主角登场、初始状态
+✅ 矛盾引入:引出核心冲突,但不急于展开
+✅ 角色亮相:主要角色依次登场,展示性格和关系
+✅ 节奏控制:开篇不宜过快,给读者适应时间
+✅ 悬念设置:埋下伏笔和钩子,为续写预留空间
+✅ 视角统一:采用{narrative_perspective}视角
+✅ 留白艺术:结尾不收束过紧,留发展空间
+
+【必须遵守】
+✅ 数量精确:数组包含{chapter_count}个章节对象
+✅ 符合类型:情节符合{genre}类型特征
+✅ 主题贴合:体现主题"{theme}"
+✅ 开篇定位:是开局而非完整故事
+
+【禁止事项】
+❌ 输出markdown或代码块标记
+❌ 在描述中使用特殊符号
+❌ 试图在开篇完结故事
+❌ 节奏过快,信息过载
+"""
- # 大纲续写提示词(记忆增强版)
- OUTLINE_CONTINUE = """你是一位经验丰富的小说作家和编剧。请基于以下信息续写小说大纲:
+ # 大纲续写提示词 V2(RTCO框架 + 记忆增强)
+ OUTLINE_CONTINUE = """
+你是经验丰富的小说作家和编剧,擅长续写{genre}类型的小说大纲。
+
+
+【续写任务】
+基于已有{current_chapter_count}章内容,续写第{start_chapter}章到第{end_chapter}章的大纲(共{chapter_count}章)。
+
+【当前情节阶段】
+{plot_stage_instruction}
+
+【故事发展方向】
+{story_direction}
+
+
+
【项目信息】
-- 书名:{title}
-- 主题:{theme}
-- 类型:{genre}
-- 叙事视角:{narrative_perspective}
-- 续写章节数:{chapter_count}章
+书名:{title}
+主题:{theme}
+类型:{genre}
+叙事视角:{narrative_perspective}
+
+
【世界观】
-- 时间背景:{time_period}
-- 地理位置:{location}
-- 氛围基调:{atmosphere}
-- 世界规则:{rules}
+时间背景:{time_period}
+地理位置:{location}
+氛围基调:{atmosphere}
+世界规则:{rules}
+
+
【角色信息】
{characters_info}
+
+
【已有章节概览】(共{current_chapter_count}章)
{all_chapters_brief}
【最近剧情】
{recent_plot}
+
+
【🧠 智能记忆系统 - 续写参考】
-以下是从故事记忆库中检索到的相关信息,请在续写大纲时参考:
+以下是从故事记忆库中检索到的相关信息:
{memory_context}
+
+
{mcp_references}
+
-【续写指导】
-- 当前情节阶段:{plot_stage_instruction}
-- 起始章节编号:第{start_chapter}章
-- 故事发展方向:{story_direction}
-- 其他要求:{requirements}
+
+【其他要求】
+{requirements}
+
-请生成第{start_chapter}章到第{end_chapter}章的大纲。
-要求:
-- **剧情连贯性**:与前文自然衔接,保持故事连贯性
-- **记忆参考**:适当参考记忆系统中的伏笔、钩子和情节点
-- **伏笔回收**:可以考虑回收未完结的伏笔,制造呼应
-- **角色发展**:遵循角色在前文中的成长轨迹
-- **情节阶段**:遵循情节阶段的发展要求
-- **风格一致**:保持与已有章节相同的风格和详细程度
+
+
+
+【续写要求】
+✅ 剧情连贯:与前文自然衔接,保持连贯性
+✅ 记忆参考:适当参考记忆中的伏笔、钩子、情节点
+✅ 伏笔回收:考虑回收未完结伏笔,制造呼应
+✅ 角色发展:遵循角色成长轨迹
+✅ 情节阶段:遵循{plot_stage_instruction}的要求
+✅ 风格一致:保持与已有章节相同风格和详细程度
+
+【必须遵守】
+✅ 数量精确:数组包含{chapter_count}个章节
+✅ 编号正确:从第{start_chapter}章开始
+✅ 描述详细:每个summary 100-200字
+✅ 承上启下:自然衔接前文
+
+【禁止事项】
+❌ 输出markdown或代码块标记
+❌ 在描述中使用特殊符号
+❌ 与前文矛盾或脱节
+❌ 忽略已有角色发展
+"""
- # AI去味提示词(核心特色功能)
- AI_DENOISING = """你是一位追求自然写作风格的编辑。你的任务是将AI生成的文本改写得更像人类作家的手笔。
+ # 章节生成V2 - 无前置章节版本(用于第1章)
+ CHAPTER_GENERATION_V2 = """
+你是《{project_title}》的作者,一位专注于{genre}类型的网络小说家。
+
-原文:
-{original_text}
+
+【创作任务】
+撰写第{chapter_number}章《{chapter_title}》的完整正文。
-修改要求:
-1. 去除AI痕迹:
- - 删除过于工整的排比句
- - 减少重复的修辞手法
- - 去掉刻意的对称结构
- - 避免机械式的总结陈词
-
-2. 增加人性化:
- - 使用更口语化的表达
- - 添加不完美的细节
- - 保留适度的随意性
- - 增加真实的情感波动
-
-3. 优化叙事:
- - 让节奏更自然不做作
- - 用简单词汇替换华丽辞藻
- - 保持叙述的松弛感
- - 让对话更生活化
-
-4. 保持原意:
- - 不改变核心情节
- - 保留关键信息点
- - 维持角色性格
- - 确保逻辑连贯
-
-修改风格:
-- 像是一个喜欢讲故事的普通人写的
-- 有点粗糙但很真诚
-- 自然流畅不刻意
-- 让人读起来很舒服
-
-请直接输出修改后的文本,无需解释。"""
-
- # 章节完整创作提示词
- CHAPTER_GENERATION = """你是一位专业的小说作家。请根据以下信息创作本章内容:
-
-项目信息:
-- 书名:{title}
-- 主题:{theme}
-- 类型:{genre}
+【基本要求】
+- 目标字数:{target_word_count}字(允许±200字浮动)
- 叙事视角:{narrative_perspective}
+
-世界观:
-- 时间背景:{time_period}
-- 地理位置:{location}
-- 氛围基调:{atmosphere}
-- 世界规则:{rules}
+
+【本章大纲 - 必须遵循】
+{chapter_outline}
+
-角色信息:
+
+【本章角色】
{characters_info}
+
-全书大纲:
-{outlines_context}
+
+【必须遵守】
+✅ 严格按照大纲推进情节
+✅ 保持角色性格、说话方式一致
+✅ 字数控制在目标范围内
-本章信息:
-- 章节序号:第{chapter_number}章
-- 章节标题:{chapter_title}
-- 章节大纲:{chapter_outline}
+【禁止事项】
+❌ 输出章节标题、序号等元信息
+❌ 使用"总之"、"综上所述"等AI常见总结语
+❌ 在结尾处使用开放式反问
+❌ 添加作者注释或创作说明
+
-创作要求:
-1. 严格按照大纲内容展开情节
-2. 保持与前后章节的连贯性
-3. 符合角色性格设定
-4. 体现世界观特色
-5. 使用{narrative_perspective}视角
-6. 字数要求:目标{target_word_count}字,不得低于{target_word_count}字,必须严格控制在{target_word_count}至{max_word_count}字之间
-7. 语言自然流畅,避免AI痕迹
+"""
- # 章节完整创作提示词(带前置章节上下文和记忆增强)
- CHAPTER_GENERATION_WITH_CONTEXT = """你是一位专业的小说作家。请根据以下信息创作本章内容:
+ # 章节生成V2 - 带前置章节版本(用于第2章及以后)
+ CHAPTER_GENERATION_V2_WITH_CONTEXT = """
+你是《{project_title}》的作者,一位专注于{genre}类型的网络小说家。
+
-项目信息:
-- 书名:{title}
-- 主题:{theme}
-- 类型:{genre}
+
+【创作任务】
+撰写第{chapter_number}章《{chapter_title}》的完整正文。
+
+【基本要求】
+- 目标字数:{target_word_count}字(允许±500字浮动)
- 叙事视角:{narrative_perspective}
+
-世界观:
-- 时间背景:{time_period}
-- 地理位置:{location}
-- 氛围基调:{atmosphere}
-- 世界规则:{rules}
+
+【本章大纲 - 必须遵循】
+{chapter_outline}
+
-角色信息:
+
+【衔接锚点 - 必须承接】
+上一章结尾:
+「{continuation_point}」
+
+⚠️ 要求:从此处自然续写,不得重复上述内容
+
+
+
+【本章角色】
{characters_info}
+
-全书大纲:
-{outlines_context}
+
+【相关记忆 - 参考】
+{relevant_memories}
+
-【已完成的前置章节内容】
-{previous_content}
+
+【故事骨架 - 背景】
+{story_skeleton}
+
-【🧠 智能记忆系统 - 重要参考】
-以下是从故事记忆库中检索到的相关信息,请在创作时适当参考和呼应:
+
+【必须遵守】
+✅ 严格按照大纲推进情节
+✅ 自然承接上一章结尾,不重复已发生事件
+✅ 保持角色性格、说话方式一致
+✅ 字数控制在目标范围内
-{memory_context}
+【禁止事项】
+❌ 输出章节标题、序号等元信息
+❌ 使用"总之"、"综上所述"等AI常见总结语
+❌ 在结尾处使用开放式反问
+❌ 添加作者注释或创作说明
+❌ 重复叙述上一章已发生的事件
+
-本章信息:
-- 章节序号:第{chapter_number}章
-- 章节标题:{chapter_title}
-- 章节大纲:{chapter_outline}
+"""
-2. **🔴 防止内容重复(关键)**:
-- ⚠️ 仔细阅读【上一章结尾内容】,绝对不要重复叙述已经发生的事件
-- ⚠️ 本章必须从新的情节点开始,不要重新描述上一章的场景或对话
-- ⚠️ 如果上一章以某个动作或对话结束,本章应该从紧接着的下一个动作或反应开始
-- ⚠️ 角色状态应该延续而非重置,不要让角色重新经历上一章已经经历的心理过程
-- ⚠️ 场景转换要明确,如果是同一场景的延续,要从不同的视角或新的细节切入
+ # 单个角色生成提示词 V2(RTCO框架)
+ SINGLE_CHARACTER_GENERATION = """
+你是专业的角色设定师,擅长创建立体饱满的小说角色。
+
-3. **情节推进**:
-- 严格按照本章大纲(expansion_plan)展开情节
-- 推动故事向前发展,不要原地踏步
-- 保持与全书大纲的一致性
-- 确保本章有独特的叙事价值,而非前章内容的重复
-
-4. **角色一致性**:
-- 符合角色性格设定
-- 延续角色在前文中的成长和变化
-- 保持角色关系的连贯性
-
-5. **写作风格**:
-- 使用{narrative_perspective}视角
-- 字数要求:目标{target_word_count}字,不得低于{target_word_count}字,必须严格控制在{target_word_count}至{max_word_count}字之间
-- 语言自然流畅,避免AI痕迹
-- 体现世界观特色
-
-6. **承上启下**:
- - 开头自然衔接上一章结尾(但不重复上一章内容)
-
-7. **记忆系统使用指南**:
- - **最近章节记忆**:保持情节连贯,注意角色状态和剧情发展
- - **语义相关记忆**:参考相似情节的处理方式
- - **未完结伏笔**:适当时机可以回收伏笔,制造呼应效果
- - **角色状态记忆**:确保角色行为符合其发展轨迹
- - **重要情节点**:与关键剧情保持一致
-
-请直接输出章节正文内容,不要包含章节标题和其他说明文字。"""
-
-
- # 单个角色生成提示词
- SINGLE_CHARACTER_GENERATION = """你是一位专业的角色设定师。请根据以下信息创建一个立体饱满的小说角色。
+
+【设计任务】
+根据用户需求和项目上下文,创建一个完整的角色设定。
+
+
+【项目上下文】
{project_context}
+【用户需求】
{user_input}
+
-请生成一个完整的角色卡片,包含以下所有信息:
+
-再次强调:
-1. 只返回纯JSON对象,不要有```json```这样的标记
-2. 所有内容描述中严禁使用任何特殊符号
-3. 不要有任何额外的文字说明"""
+
+【必须遵守】
+✅ 符合世界观:角色设定与项目世界观一致
+✅ 主题关联:背景故事与项目主题关联
+✅ 立体饱满:性格复杂有矛盾性,不脸谱化
+✅ 为故事服务:设定要推动剧情发展
+✅ 职业匹配:职业选择与角色高度契合
- # 单个组织生成提示词
- SINGLE_ORGANIZATION_GENERATION = """你是一位专业的组织设定师。请根据以下信息创建一个完整的组织/势力设定。
+【角色定位要求】
+✅ 主角:有成长空间和目标动机
+✅ 反派:有合理动机,不脸谱化
+✅ 配角:有独特性,不是工具人
+【关系约束】
+✅ relationships只引用已存在的角色
+✅ organization_memberships只引用已存在的组织
+✅ 无关系或组织时对应数组为空[]
+
+【格式约束】
+✅ 纯JSON对象输出,无markdown标记
+✅ 内容描述中严禁使用特殊符号
+✅ 专有名词直接书写
+
+【禁止事项】
+❌ 输出markdown或代码块标记
+❌ 在描述中使用特殊符号(引号、方括号等)
+❌ 引用不存在的角色或组织
+❌ 脸谱化的角色设定
+"""
+
+ # 单个组织生成提示词 V2(RTCO框架)
+ SINGLE_ORGANIZATION_GENERATION = """
+你是专业的组织设定师,擅长创建完整的组织/势力设定。
+
+
+
+【设计任务】
+根据用户需求和项目上下文,创建一个完整的组织/势力设定。
+
+
+
+【项目上下文】
{project_context}
+【用户需求】
{user_input}
+
-请生成一个完整的组织设定,包含以下所有信息:
+
-**说明**:
-1. power_level是0-100的整数,表示组织在世界中的影响力
-2. organization_members是组织内重要成员的名字列表(如果已有角色,可以关联)
-3. 所有文本描述要详细具体,避免空泛
+
+【必须遵守】
+✅ 符合世界观:组织设定与项目世界观一致
+✅ 主题关联:背景与项目主题关联
+✅ 推动剧情:组织能推动故事发展
+✅ 有层级结构:内部有明确的层级和结构
+✅ 势力互动:与其他势力有互动关系
-再次强调:
-1. 只返回纯JSON对象,不要有```json```这样的标记
-2. 所有内容描述中严禁使用任何特殊符号
-3. 不要有任何额外的文字说明"""
+【组织定位要求】
+✅ 有存在必要性:不是可有可无的背景板
+✅ 目标合理:不过于理想化或脸谱化
+✅ 具体细节:描述详细具体,避免空泛
- # 情节分析提示词
- PLOT_ANALYSIS = """你是一位专业的小说编辑和剧情分析师。请深度分析以下章节内容:
+【格式约束】
+✅ 纯JSON对象输出,无markdown标记
+✅ 内容描述中严禁使用特殊符号
+✅ 专有名词直接书写
-**章节信息:**
-- 章节: 第{chapter_number}章
-- 标题: {title}
-- 字数: {word_count}字
+【禁止事项】
+❌ 输出markdown或代码块标记
+❌ 在描述中使用特殊符号(引号、方括号等)
+❌ 过于理想化或脸谱化的设定
+❌ 空泛的描述
+"""
-**章节内容:**
+ # 情节分析提示词 V2(RTCO框架)
+ PLOT_ANALYSIS = """
+你是专业的小说编辑和剧情分析师,擅长深度剖析章节内容。
+
+
+
+【分析任务】
+全面分析第{chapter_number}章《{title}》的剧情要素、钩子、伏笔、冲突和角色发展。
+
+
+
+【章节信息】
+章节:第{chapter_number}章
+标题:{title}
+字数:{word_count}字
+
+【章节内容】
{content}
+
----
+
+【分析维度】
-**分析任务:**
-请从专业编辑的角度,全面分析这一章节:
+**1. 剧情钩子 (Hooks)**
+识别吸引读者的关键元素:
+- 悬念钩子:未解之谜、疑问、谜团
+- 情感钩子:引发共鸣的情感点
+- 冲突钩子:矛盾对抗、紧张局势
+- 认知钩子:颠覆认知的信息
-### 1. 剧情钩子 (Hooks) - 吸引读者的元素
-识别能够吸引读者继续阅读的关键元素:
-- **悬念钩子**: 未解之谜、疑问、谜团
-- **情感钩子**: 引发共鸣的情感点、触动心弦的时刻
-- **冲突钩子**: 矛盾对抗、紧张局势
-- **认知钩子**: 颠覆认知的信息、惊人真相
-
-每个钩子需要:
+每个钩子需要:
- 类型分类
- 具体内容描述
- 强度评分(1-10)
- 出现位置(开头/中段/结尾)
-- **关键词**: 【必填】从章节原文中逐字复制一段关键文本(8-25字),必须是原文中真实存在的连续文字,用于在文本中精确定位。不要概括或改写,必须原样复制!
+- **关键词**:【必填】从原文逐字复制8-25字的文本片段,用于精确定位
-### 2. 伏笔分析 (Foreshadowing)
-- **埋下的新伏笔**: 描述内容、预期作用、隐藏程度(1-10)
-- **回收的旧伏笔**: 呼应哪一章、回收效果评分
-- **伏笔质量**: 巧妙性和合理性评估
-- **关键词**: 【必填】从章节原文中逐字复制一段关键文本(8-25字),必须是原文中真实存在的连续文字,用于在文本中精确定位。不要概括或改写,必须原样复制!
+**2. 伏笔分析 (Foreshadowing)**
+- 埋下的新伏笔:内容、预期作用、隐藏程度(1-10)
+- 回收的旧伏笔:呼应哪一章、回收效果
+- 伏笔质量:巧妙性和合理性
+- **关键词**:【必填】从原文逐字复制8-25字
-### 3. 冲突分析 (Conflict)
-- 冲突类型: 人与人/人与己/人与环境/人与社会
-- 冲突各方及其立场
-- 冲突强度评分(1-10)
-- 冲突解决进度(0-100%)
+**3. 冲突分析 (Conflict)**
+- 冲突类型:人与人/人与己/人与环境/人与社会
+- 冲突各方及立场
+- 冲突强度(1-10)
+- 解决进度(0-100%)
-### 4. 情感曲线 (Emotional Arc)
-- 主导情绪(最多10个字): 紧张/温馨/悲伤/激昂/平静/压抑/欢快/恐惧/期待/失落等
+**4. 情感曲线 (Emotional Arc)**
+- 主导情绪(最多10字)
- 情感强度(1-10)
-- 情绪变化轨迹描述
+- 情绪变化轨迹
-### 5. 角色状态追踪 (Character Development)
-对每个出场角色分析:
+**5. 角色状态追踪 (Character Development)**
+对每个出场角色分析:
- 心理状态变化(前→后)
- 关系变化
- 关键行动和决策
- 成长或退步
-- **💼 职业变化(重要 - 新增)**:
- - 如果角色在本章有职业相关的进展或突破,请详细分析
- - 主职业阶段变化: 是否晋级、突破或降级(用整数表示变化量,如: +1表示晋升一阶段,-1表示退步一阶段,0表示无变化)
- - 副职业变化: 是否学习新的副职业或副职业有所精进
- - 职业突破描述: 具体的突破过程、原因和标志性事件
- - 注意:只有当章节中明确描述了职业相关的成长、突破或变化时才填写此项
+- **💼 职业变化(可选)**:
+ - 仅当章节明确描述职业进展时填写
+ - main_career_stage_change: 整数(+1晋升/-1退步/0无变化)
+ - sub_career_changes: 副职业变化数组
+ - new_careers: 新获得职业
+ - career_breakthrough: 突破过程描述
-### 6. 关键情节点 (Plot Points)
-列出3-5个核心情节点:
+**6. 关键情节点 (Plot Points)**
+列出3-5个核心情节点:
- 情节内容
- 类型(revelation/conflict/resolution/transition)
- 重要性(0.0-1.0)
- 对故事的影响
-- **关键词**: 【必填】从章节原文中逐字复制一段关键文本(8-25字),必须是原文中真实存在的连续文字,用于在文本中精确定位。不要概括或改写,必须原样复制!
+- **关键词**:【必填】从原文逐字复制8-25字
-### 7. 场景与节奏
+**7. 场景与节奏**
- 主要场景
- 叙事节奏(快/中/慢)
-- 对话与描写的比例
+- 对话与描写比例
-### 8. 质量评分
-- 节奏把控: 1-10分
-- 吸引力: 1-10分
-- 连贯性: 1-10分
-- 整体质量: 1-10分
+**8. 质量评分(支持小数,严格区分度)**
+评分范围:1.0-10.0,支持一位小数(如 6.5、7.8)
+每个维度必须根据以下标准严格评分,避免所有内容都打中等分数:
-### 9. 改进建议
-提供3-5条具体的改进建议
+**节奏把控 (pacing)**:
+- 1.0-3.9(差):节奏混乱,该快不快该慢不慢;场景切换生硬;大段无意义描写拖沓
+- 4.0-5.9(中下):节奏基本可读但有明显问题;部分场景过于冗长或仓促
+- 6.0-7.9(中上):节奏整体流畅,偶有小问题;张弛有度但不够精妙
+- 8.0-9.4(优秀):节奏把控精准,高潮迭起;场景切换自然,详略得当
+- 9.5-10.0(完美):节奏大师级,每个段落都恰到好处
----
+**吸引力 (engagement)**:
+- 1.0-3.9(差):内容乏味,缺乏钩子;读者难以继续阅读
+- 4.0-5.9(中下):有基本情节但缺乏亮点;钩子设置生硬或缺失
+- 6.0-7.9(中上):有一定吸引力,钩子有效但不够巧妙
+- 8.0-9.4(优秀):引人入胜,钩子设置精妙;让人欲罢不能
+- 9.5-10.0(完美):极具吸引力,每个段落都有阅读动力
-**输出格式(纯JSON,不要markdown标记):**
+**连贯性 (coherence)**:
+- 1.0-3.9(差):逻辑混乱,前后矛盾;角色行为不合理
+- 4.0-5.9(中下):基本连贯但有明显漏洞;部分情节衔接生硬
+- 6.0-7.9(中上):整体连贯,偶有小瑕疵;角色行为基本合理
+- 8.0-9.4(优秀):逻辑严密,衔接自然;角色行为高度一致
+- 9.5-10.0(完美):无懈可击的连贯性
+
+**整体质量 (overall)**:
+- 计算公式:(pacing + engagement + coherence) / 3,保留一位小数
+- 可根据综合印象±0.5调整,必须与各项分数保持一致性
+
+**9. 改进建议(与分数关联)**
+建议数量必须与整体质量分数关联:
+- overall < 4.0:必须提供4-5条具体改进建议,指出严重问题
+- overall 4.0-5.9:必须提供3-4条改进建议,指出主要问题
+- overall 6.0-7.9:提供1-2条优化建议,指出可提升之处
+- overall ≥ 8.0:提供0-1条锦上添花的建议
+
+每条建议必须:
+- 指出具体问题位置或类型
+- 说明为什么是问题
+- 给出明确的改进方向
+
+
+
-**重要提示:**
-1. 每个钩子、伏笔、情节点的keyword字段是必填的,不能为空
-2. keyword必须是从章节原文中逐字复制的文本,长度8-25字
-3. keyword用于在前端标注文本位置,所以必须能在原文中精确找到
-4. 不要使用概括性语句或改写后的文字作为keyword
-5. **职业变化字段说明**:
- - career_changes是可选字段,只有当章节中明确描述了职业相关变化时才填写
- - main_career_stage_change: 整数,表示主职业阶段变化量(+1=晋升一阶,-1=退步一阶,0=无变化)
- - sub_career_changes: 数组,包含副职业的变化,每项包含career_name(职业名称)和stage_change(阶段变化量)
- - new_careers: 数组,包含新获得的职业名称(如果有)
- - career_breakthrough: 字符串,描述职业突破的具体过程和标志性事件
- - 如果角色没有职业变化,可以不填写career_changes字段或设为空对象
+
+【必须遵守】
+✅ keyword字段必填:钩子、伏笔、情节点的keyword不能为空
+✅ 逐字复制:keyword必须从原文复制,长度8-25字
+✅ 精确定位:keyword能在原文中精确找到
+✅ 职业变化可选:仅当章节明确描述时填写
-只返回JSON,不要其他说明。"""
+【评分约束 - 严格执行】
+✅ 严格按评分标准打分,支持小数(如6.5、7.2、8.3)
+✅ 不要默认给7.0-8.0分,差的内容必须给低分(1.0-5.0),好的内容才给高分(8.0-10.0)
+✅ score_justification必填:简要说明各项评分的依据
+✅ 建议数量必须与overall分数关联:
+ - overall≤4.0 → 4-5条建议
+ - overall 4.0-6.0 → 3-4条建议
+ - overall 6.0-8.0 → 1-2条建议
+ - overall≥8.0 → 0-1条建议
+✅ 每条建议必须标注问题类型(如【节奏问题】【描写不足】等)
- # 大纲单批次展开提示词
- OUTLINE_EXPAND_SINGLE = """你是专业的小说情节架构师。请分析以下大纲节点,将其展开为 {target_chapter_count} 个章节的详细规划。
+【禁止事项】
+❌ keyword使用概括或改写的文字
+❌ 输出markdown标记
+❌ 遗漏必填的keyword字段
+❌ 无根据地添加职业变化
+❌ 所有章节都打7-8分的"安全分"
+❌ 高分章节给大量建议,或低分章节不给建议
+"""
+ # 大纲单批次展开提示词 V2(RTCO框架)
+ OUTLINE_EXPAND_SINGLE = """
+你是专业的小说情节架构师,擅长将大纲节点展开为详细章节规划。
+
+
+
+【展开任务】
+将第{outline_order_index}节大纲《{outline_title}》展开为{target_chapter_count}个章节的详细规划。
+
+【展开策略】
+{strategy_instruction}
+
+
+
【项目信息】
小说名称:{project_title}
类型:{project_genre}
@@ -966,90 +1043,105 @@ class PromptService:
时间背景:{project_world_time_period}
地理位置:{project_world_location}
氛围基调:{project_world_atmosphere}
+
+
【角色信息】
{characters_info}
+
+
【当前大纲节点 - 展开对象】
序号:第 {outline_order_index} 节
标题:{outline_title}
内容:{outline_content}
+
+
【上下文参考】
{context_info}
+
-【展开策略】
-{strategy_instruction}
-
-【⚠️ 重要约束 - 必须严格遵守】
-1. **内容边界约束**:
- - ✅ 只能展开【当前大纲节点】中明确描述的内容
- - ❌ 绝对不能推进到后续大纲的内容(如果有【后一节】信息)
- - ❌ 不要让剧情快速推进,要深化而非跨越
-
-2. **展开原则**:
- - 将当前大纲的单一事件拆解为多个细节丰富的章节
- - 深入挖掘情感、心理、环境、对话等细节
- - 放慢叙事节奏,让读者充分体验当前阶段的剧情
- - 每个章节都应该是当前大纲内容的不同侧面或阶段
-
-3. **如何避免剧情越界**:
- - 如果当前大纲描述"主角遇到困境",展开时应详写困境的发现、分析、情感冲击等
- - 不要直接写到"解决困境",除非原大纲明确包含解决过程
- - 如果看到【后一节】的内容,那些是禁区,绝不提前展开
-
-4. **🔴 相邻章节差异化约束(重要 - 防止内容重复)**:
- - 每个章节必须有独特的开场方式(不同的场景、时间点、角色状态)
- - 每个章节必须有独特的结束方式(不同的悬念、转折、情感收尾)
- - key_events在相邻章节间绝不允许重叠,每章的关键事件必须完全不同
- - plot_summary必须描述该章的独特内容,不能与其他章节雷同
- - 即使是同一事件的不同阶段,也要明确区分"前、中、后"的具体内容
- - 例如:第1章可以是"发现线索",第2章必须是"追踪调查"而非再次"发现线索"
-
-【任务要求】
-1. 深度分析该大纲的剧情容量和叙事节奏
-2. 识别关键剧情点、冲突点和情感转折点(仅限当前大纲范围内)
-3. 将大纲拆解为 {target_chapter_count} 个章节,每章需包含:
- - sub_index: 子章节序号(1, 2, 3...)
- - title: 章节标题(体现该章核心冲突或情感)
- - plot_summary: 剧情摘要(200-300字,详细描述该章发生的事件,仅限当前大纲内容)
- - key_events: 关键事件列表(3-5个关键剧情点,必须在当前大纲范围内)
- - character_focus: 角色焦点(主要涉及的角色名称)
- - emotional_tone: 情感基调(如:紧张、温馨、悲伤、激动等)
- - narrative_goal: 叙事目标(该章要达成的叙事效果)
- - conflict_type: 冲突类型(如:内心挣扎、人际冲突、环境挑战等)
- - estimated_words: 预计字数(建议2000-5000字)
-{scene_instruction}
-5. 确保章节间:
- - 衔接自然流畅(每章从不同的起点开始)
- - 剧情递进合理(但不超出当前大纲边界)
- - 节奏张弛有度
- - 每章都有明确且独特的叙事价值(不重复前一章的内容)
- - 最后一章结束时,剧情发展程度应恰好完成当前大纲描述的内容,不多不少
- - **关键事件无重叠**:仔细检查相邻章节的key_events,确保没有任何重复或雷同
-
+
- # 大纲分批展开提示词
- OUTLINE_EXPAND_MULTI = """你是专业的小说情节架构师。请继续分析以下大纲节点,将其展开为第{start_index}-{end_index}节(共{target_chapter_count}个章节)的详细规划。
+
+【⚠️ 内容边界约束 - 必须严格遵守】
+✅ 只能展开当前大纲节点的内容
+✅ 深化当前大纲,而非跨越到后续
+✅ 放慢叙事节奏,充分体验当前阶段
+❌ 绝对不能推进到后续大纲内容
+❌ 不要让剧情快速推进
+❌ 不要提前展开【后一节】的内容
+
+【展开原则】
+✅ 将单一事件拆解为多个细节丰富的章节
+✅ 深入挖掘情感、心理、环境、对话
+✅ 每章是当前大纲内容的不同侧面或阶段
+
+【🔴 相邻章节差异化约束(防止重复)】
+✅ 每章有独特的开场方式(不同场景、时间点、角色状态)
+✅ 每章有独特的结束方式(不同悬念、转折、情感收尾)
+✅ key_events在相邻章节间绝不重叠
+✅ plot_summary描述该章独特内容,不与其他章雷同
+✅ 同一事件的不同阶段要明确区分"前、中、后"
+
+【章节间要求】
+✅ 衔接自然流畅(每章从不同起点开始)
+✅ 剧情递进合理(但不超出当前大纲边界)
+✅ 节奏张弛有度
+✅ 每章有明确且独特的叙事价值
+✅ 最后一章结束时恰好完成当前大纲内容
+✅ 关键事件无重叠:检查相邻章节key_events
+
+【禁止事项】
+❌ 输出非JSON格式
+❌ 剧情越界到后续大纲
+❌ 相邻章节内容重复
+❌ 关键事件雷同
+"""
+
+ # 大纲分批展开提示词 V2(RTCO框架)
+ OUTLINE_EXPAND_MULTI = """
+你是专业的小说情节架构师,擅长分批展开大纲节点。
+
+
+
+【展开任务】
+继续展开第{outline_order_index}节大纲《{outline_title}》,生成第{start_index}-{end_index}节(共{target_chapter_count}个章节)的详细规划。
+
+【分批说明】
+- 这是整个展开的一部分
+- 必须与前面已生成的章节自然衔接
+- 从第{start_index}节开始编号
+- 继续深化当前大纲内容
+
+【展开策略】
+{strategy_instruction}
+
+
+
【项目信息】
小说名称:{project_title}
类型:{project_genre}
@@ -1060,76 +1152,37 @@ class PromptService:
时间背景:{project_world_time_period}
地理位置:{project_world_location}
氛围基调:{project_world_atmosphere}
+
+
【角色信息】
{characters_info}
+
+
【当前大纲节点 - 展开对象】
序号:第 {outline_order_index} 节
标题:{outline_title}
内容:{outline_content}
+
+
【上下文参考】
{context_info}
+
+【已生成的前序章节】
{previous_context}
+
-【展开策略】
-{strategy_instruction}
-
-【⚠️ 重要约束 - 必须严格遵守】
-1. **内容边界约束**:
- - ✅ 只能展开【当前大纲节点】中明确描述的内容
- - ❌ 绝对不能推进到后续大纲的内容(如果有【后一节】信息)
- - ❌ 不要让剧情快速推进,要深化而非跨越
-
-2. **分批连续性约束**:
- - 这是第{start_index}-{end_index}节,是整个展开的一部分
- - 必须与前面已生成的章节自然衔接
- - 从第{start_index}节开始编号(sub_index从{start_index}开始)
- - 继续深化当前大纲的内容,保持叙事连贯性
-
-3. **展开原则**:
- - 将当前大纲的单一事件拆解为多个细节丰富的章节
- - 深入挖掘情感、心理、环境、对话等细节
- - 放慢叙事节奏,让读者充分体验当前阶段的剧情
- - 每个章节都应该是当前大纲内容的不同侧面或阶段
-
-4. **🔴 相邻章节差异化约束(重要 - 防止内容重复)**:
- - 每个章节必须有独特的开场方式(不同的场景、时间点、角色状态)
- - 每个章节必须有独特的结束方式(不同的悬念、转折、情感收尾)
- - key_events在相邻章节间绝不允许重叠,每章的关键事件必须完全不同
- - plot_summary必须描述该章的独特内容,不能与其他章节雷同
- - 特别注意与【已生成的前序章节】的差异化,避免重复已有内容
- - 即使是同一事件的不同阶段,也要明确区分"前、中、后"的具体内容
-
-【任务要求】
-1. 深度分析该大纲的剧情容量和叙事节奏
-2. 识别关键剧情点、冲突点和情感转折点(仅限当前大纲范围内)
-3. 生成第{start_index}-{end_index}节的章节规划,每章需包含:
- - sub_index: 子章节序号(从{start_index}开始)
- - title: 章节标题(体现该章核心冲突或情感)
- - plot_summary: 剧情摘要(200-300字,详细描述该章发生的事件)
- - key_events: 关键事件列表(3-5个关键剧情点)
- - character_focus: 角色焦点(主要涉及的角色名称)
- - emotional_tone: 情感基调(如:紧张、温馨、悲伤、激动等)
- - narrative_goal: 叙事目标(该章要达成的叙事效果)
- - conflict_type: 冲突类型(如:内心挣扎、人际冲突、环境挑战等)
- - estimated_words: 预计字数(建议2000-5000字)
-{scene_instruction}
-5. 确保章节间:
- - 与前面章节衔接自然流畅(每章从不同的起点开始)
- - 剧情递进合理(但不超出当前大纲边界)
- - 节奏张弛有度
- - 每章都有明确且独特的叙事价值(不重复前面章节的内容)
- - **关键事件无重叠**:仔细检查本批次章节的key_events,以及与前序章节的key_events,确保没有任何重复或雷同
-
+
- # 章节重写系统提示词
- CHAPTER_REGENERATION_SYSTEM = """你是一位经验丰富的专业小说编辑和作家。现在需要根据反馈意见重新创作一个章节。
+
+【⚠️ 内容边界约束】
+✅ 只能展开当前大纲节点的内容
+✅ 深化当前大纲,而非跨越到后续
+✅ 放慢叙事节奏
-你的任务是:
-1. 仔细理解原始章节的内容和意图
-2. 认真分析所有的修改要求
-3. 在保持故事连贯性的前提下,创作一个改进后的新版本
-4. 确保新版本在艺术性和可读性上都有明显提升
+❌ 绝对不能推进到后续大纲内容
+❌ 不要让剧情快速推进
----
+【分批连续性约束】
+✅ 与前面已生成章节自然衔接
+✅ 从第{start_index}节开始编号
+✅ 保持叙事连贯性
+
+【🔴 相邻章节差异化约束(防止重复)】
+✅ 每章有独特的开场和结束方式
+✅ key_events在相邻章节间绝不重叠
+✅ plot_summary描述该章独特内容
+✅ 特别注意与前序章节的差异化
+✅ 避免重复已有内容
+
+【章节间要求】
+✅ 与前面章节衔接自然流畅
+✅ 剧情递进合理(但不超出当前大纲边界)
+✅ 节奏张弛有度
+✅ 每章有明确且独特的叙事价值
+✅ 关键事件无重叠:检查本批次和前序章节的key_events
+
+【禁止事项】
+❌ 输出非JSON格式
+❌ 剧情越界到后续大纲
+❌ 相邻章节内容重复
+❌ 与前序章节key_events雷同
+"""
+
+ # 章节重写系统提示词 V2(RTCO框架)
+ CHAPTER_REGENERATION_SYSTEM = """
+你是经验丰富的专业小说编辑和作家,擅长根据反馈意见重新创作章节。
+你的任务是根据修改指令,对原始章节进行深度改写和优化。
+
+
+
+【重写任务】
+1. 仔细理解原始章节的内容、情节走向和叙事意图
+2. 认真分析所有的修改要求,包括AI分析建议和用户自定义指令
+3. 针对每一条修改建议,在新版本中进行具体改进
+4. 在保持故事连贯性和角色一致性的前提下,创作改进后的新版本
+5. 确保新版本在艺术性、可读性和叙事质量上都有明显提升
+
+
+
+【改写原则】
+- **问题导向**:针对修改指令中指出的每个问题进行改进
+- **保持精华**:保留原章节中优秀的描写、对话和情节设计
+- **深化细节**:增强场景描写、情感渲染和人物刻画
+- **节奏优化**:调整叙事节奏,避免拖沓或过快
+- **风格一致**:如果提供了写作风格要求,必须严格遵循
+
+【重点关注】
+- 如果修改指令提到"节奏"问题,重点调整叙事速度和场景切换
+- 如果修改指令提到"情感"问题,重点深化人物内心戏和情感表达
+- 如果修改指令提到"描写"问题,重点丰富环境和动作细节
+- 如果修改指令提到"对话"问题,重点让对话更自然、更有个性
+- 如果修改指令提到"冲突"问题,重点强化矛盾和戏剧张力
+
+
+
"""
# MCP工具测试提示词
MCP_TOOL_TEST = """你是MCP插件测试助手,需要测试插件 '{plugin_name}' 的功能。
@@ -1317,19 +1436,32 @@ class PromptService:
请查询最关键的1个问题(不要超过1个)。"""
- # 自动角色引入 - 预测性分析提示词(方案A)
- AUTO_CHARACTER_ANALYSIS = """你是专业的小说角色设计顾问。请根据即将续写的剧情方向,预测是否需要引入新角色。
+ # 自动角色引入 - 预测性分析提示词 V2(RTCO框架)
+ AUTO_CHARACTER_ANALYSIS = """
+你是专业的小说角色设计顾问,擅长预测剧情发展对角色的需求。
+
+
+【分析任务】
+预测在接下来的{chapter_count}章续写中,根据剧情发展方向和阶段,是否需要引入新角色。
+
+【重要说明】
+这是预测性分析,而非基于已生成内容的事后分析。
+
+
+
【项目信息】
-- 书名:{title}
-- 类型:{genre}
-- 主题:{theme}
+书名:{title}
+类型:{genre}
+主题:{theme}
【世界观】
-- 时间背景:{time_period}
-- 地理位置:{location}
-- 氛围基调:{atmosphere}
+时间背景:{time_period}
+地理位置:{location}
+氛围基调:{atmosphere}
+
+
【已有角色】
{existing_characters}
@@ -1341,42 +1473,48 @@ class PromptService:
- 续写数量:{chapter_count}章
- 剧情阶段:{plot_stage}
- 发展方向:{story_direction}
+
-【预测性分析任务】
-请预测在接下来的{chapter_count}章中,根据剧情发展方向和阶段,是否需要引入新角色。
+
+【预测分析维度】
-**分析要点:**
-1. **剧情需求预测**:根据发展方向,哪些场景、冲突需要新角色参与
-2. **角色充分性**:现有角色是否足以支撑即将发生的剧情
-3. **引入时机**:新角色应该在哪个章节登场最合适
-4. **重要性判断**:新角色对后续剧情的影响程度
+**1. 剧情需求预测**
+根据发展方向,哪些场景、冲突需要新角色参与?
-**预测依据:**
+**2. 角色充分性**
+现有角色是否足以支撑即将发生的剧情?
+
+**3. 引入时机**
+新角色应该在哪个章节登场最合适?
+
+**4. 重要性判断**
+新角色对后续剧情的影响程度如何?
+
+【预测依据】
- 剧情阶段的典型角色需求(如:高潮阶段可能需要强力对手)
- 故事发展方向的逻辑需要(如:进入新地点需要当地角色)
- 冲突升级的角色需求(如:更强的反派、意外的盟友)
- 世界观扩展的需要(如:新组织、新势力的代表)
+
-**如果需要新角色,请详细说明:**
-- 角色定位和作用
-- 建议的角色类型和重要性
-- 预计登场时机
-- 与现有角色的潜在关系
+
-**重要提示:**
-- 这是预测性分析,不是基于已生成内容的事后分析
-- 要考虑剧情的自然发展和节奏
-- 不要为了引入角色而引入,确保必要性
-- 优先考虑角色的长期作用,而非一次性功能
+
+【必须遵守】
+✅ 这是预测性分析,面向未来剧情
+✅ 考虑剧情的自然发展和节奏
+✅ 确保引入必要性,不为引入而引入
+✅ 优先考虑角色的长期作用
-只返回纯JSON,不要有markdown标记或其他文字。"""
+【禁止事项】
+❌ 输出markdown标记
+❌ 基于已生成内容做事后分析
+❌ 为了引入角色而强行引入
+❌ 设计一次性功能角色
+"""
- # 自动角色引入 - 生成提示词
- AUTO_CHARACTER_GENERATION = """你是专业的角色设定师。请根据以下信息,为小说生成新角色的完整设定。
+ # 自动角色引入 - 生成提示词 V2(RTCO框架)
+ AUTO_CHARACTER_GENERATION = """
+你是专业的角色设定师,擅长根据剧情需求创建完整的角色设定。
+
+
+【生成任务】
+为小说生成新角色的完整设定,包括基本信息、性格背景、关系网络和职业信息。
+
+
+
【项目信息】
-- 书名:{title}
-- 类型:{genre}
-- 主题:{theme}
+书名:{title}
+类型:{genre}
+主题:{theme}
【世界观】
-- 时间背景:{time_period}
-- 地理位置:{location}
-- 氛围基调:{atmosphere}
-- 世界规则:{rules}
+时间背景:{time_period}
+地理位置:{location}
+氛围基调:{atmosphere}
+世界规则:{rules}
+
+
【已有角色】
{existing_characters}
@@ -1424,40 +1579,43 @@ class PromptService:
【角色规格要求】
{character_specification}
+
+
【MCP工具参考】
{mcp_references}
+
-【生成要求】
+
+【核心要求】
1. 角色必须符合剧情需求和世界观设定
2. **必须分析新角色与已有角色的关系**,至少建立1-3个有意义的关系
3. 性格、背景要有深度和独特性
4. 外貌描写要具体生动
5. 特长和能力要符合角色定位
-6. **如果【已有角色】中包含职业列表,必须为角色设定职业**(参考下方职业信息要求)
+6. **如果【已有角色】中包含职业列表,必须为角色设定职业**
-**关系建立指导(非常重要):**
+【关系建立指导】
- 仔细审视【已有角色】列表,思考新角色与哪些现有角色有联系
-- 根据剧情需求,建立合理的角色关系(如:主角的新朋友、反派的手下、某角色的亲属等)
+- 根据剧情需求,建立合理的角色关系
- 每个关系都要有明确的类型、亲密度和描述
-- 关系应该服务于剧情发展,推动故事前进
+- 关系应该服务于剧情发展
- 如果新角色是组织成员,记得填写organization_memberships
-**重要格式要求:**
-1. 只返回纯JSON格式,不要包含任何markdown标记或其他说明文字
-2. JSON字符串值中严禁使用特殊符号(引号、方括号、书名号等)
-3. 所有专有名词直接书写,不使用任何符号包裹
-
-【职业信息要求(重要)】
-如果【已有角色】部分包含"可用主职业列表"或"可用副职业列表",则必须:
+【职业信息要求】
+如果【已有角色】部分包含"可用主职业列表"或"可用副职业列表":
- 仔细查看可用的主职业和副职业列表
- 根据角色的背景、能力、故事定位,选择最合适的职业
-- 主职业:从"可用主职业列表"中选择一个,填写职业名称(name字段)
+- 主职业:从"可用主职业列表"中选择一个,填写职业名称
- 主职业阶段:根据职业的阶段信息和角色实力,设定合理的当前阶段
-- 副职业:可选择0-2个副职业,每个包含职业名称和阶段
-- ⚠️ 重要:必须填写职业的名称而非ID,系统会自动匹配
+- 副职业:可选择0-2个副职业
+- ⚠️ 重要:必须填写职业的名称而非ID
+
+
+
-**关系建立示例:**
-- 如果新角色是主角的新队友,应该与主角建立"队友"或"朋友"关系
-- 如果新角色是反派的手下,应该与反派建立"上司-下属"关系
-- 如果新角色与某角色有血缘,应该建立家族关系
+
+【必须遵守】
+✅ 符合剧情需求和世界观设定
+✅ relationships数组必填:至少1-3个关系
+✅ target_character_name必须精确匹配【已有角色】
+✅ organization_memberships只能引用已存在的组织
+✅ 职业选择必须从可用列表中选择
-只返回纯JSON对象,不要有```json```这样的标记。"""
+【禁止事项】
+❌ 输出markdown标记
+❌ 在描述中使用特殊符号
+❌ 引用不存在的角色或组织
+❌ 使用职业ID而非职业名称
+"""
- # 职业体系生成提示词
- CAREER_SYSTEM_GENERATION = """你是专业的游戏/小说职业体系设计师。请根据以下世界观信息,设计一个完整且合理的职业体系。
+ # 职业体系生成提示词 V2(RTCO框架)
+ CAREER_SYSTEM_GENERATION = """
+你是专业的职业体系设计师,擅长为不同世界观设计完整的职业体系。
+
+
+【设计任务】
+根据世界观信息,设计一个完整且合理的职业体系,包括主职业和副职业。
+
+
+
【项目信息】
-- 书名:{title}
-- 类型:{genre}
-- 主题:{theme}
-- 时间背景:{time_period}
-- 地理位置:{location}
-- 氛围基调:{atmosphere}
-- 世界规则:{rules}
+书名:{title}
+类型:{genre}
+主题:{theme}
+时间背景:{time_period}
+地理位置:{location}
+氛围基调:{atmosphere}
+世界规则:{rules}
+
+
【设计要求】
-1. **主职业(main_careers)**:
- - 根据世界观特点,决定需要多少个主职业
- - 主职业是角色的核心发展方向,直接影响战斗力或核心能力
- - 必须严格符合世界观规则,体现核心能力体系
- - 每个主职业的阶段数量可以不同:根据职业的复杂度、重要性、修炼难度等因素,为不同职业设定不同的max_stage
-2. **副职业(sub_careers)**:
- - 根据世界需要,决定需要多少个副职业
- - 副职业包含生产、辅助、特殊技能类,丰富角色的能力维度
- - 每个副职业的阶段数量可以不同:简单的副职业可能只有3-5个阶段,复杂的可能有6-10个阶段
- - 不要让所有副职业都是相同的阶段数
+**1. 主职业(main_careers)**
+- 根据世界观特点,决定需要多少个主职业
+- 主职业是角色的核心发展方向
+- 必须严格符合世界观规则
+- 每个主职业的阶段数量可以不同(体现职业复杂度差异)
-3. **阶段设计(stages)**:
- - 每个职业的stages数组长度必须等于max_stage
- - 阶段名称要符合世界观文化背景和时代特征
- - 阶段描述要体现明确的能力提升和成长路径
- - 重要:确保职业间的阶段数量有差异,体现职业的多样性
+**2. 副职业(sub_careers)**
+- 根据世界需要,决定需要多少个副职业
+- 副职业包含生产、辅助、特殊技能类
+- 每个副职业的阶段数量可以不同
+- 不要让所有副职业都是相同的阶段数
-【JSON格式】
+**3. 阶段设计(stages)**
+- 每个职业的stages数组长度必须等于max_stage
+- 阶段名称要符合世界观文化背景
+- 阶段描述要体现明确的能力提升路径
+- 确保职业间的阶段数量有差异
+
+
+
-【重要提示】
-- 职业的数量、类型完全由你根据世界观自行决定,不要受任何数字限制
-- **阶段数量多样性(关键)**:
- - 不同职业的max_stage必须不同,不要所有职业都是相同的阶段数
- - 主职业的阶段数建议范围:5-15个阶段(根据职业重要性和复杂度灵活设定)
- - 副职业的阶段数建议范围:3-10个阶段(根据职业特性灵活设定)
- - 例如:剑修可能有12个阶段,炼丹师可能有8个阶段,体修可能有10个阶段
-- 确保职业体系与世界观高度契合,符合该世界的逻辑和文化
-- 只返回纯JSON,不要添加markdown标记或其他解释文字
+
+【必须遵守】
+✅ 职业数量和类型根据世界观自行决定
+✅ 不同职业的max_stage必须不同
+✅ 主职业阶段数建议:5-15个
+✅ 副职业阶段数建议:3-10个
+✅ stages数组长度必须等于max_stage
+✅ 确保职业体系与世界观高度契合
-请让每个职业的阶段数有所不同,体现职业的独特性和多样性!"""
+【禁止事项】
+❌ 所有职业使用相同的阶段数
+❌ 输出markdown标记
+❌ 职业设计与世界观脱节
+"""
@staticmethod
def format_prompt(template: str, **kwargs) -> str:
@@ -1752,38 +1922,6 @@ class PromptService:
return "\n".join(prompt_parts)
- @classmethod
- def get_inspiration_prompt(cls, step: str) -> Optional[Dict[str, str]]:
- """获取灵感模式指定步骤的提示词"""
- # 根据步骤名称返回对应的system和user提示词
- step_map = {
- "title": {
- "system": cls.INSPIRATION_TITLE_SYSTEM,
- "user": cls.INSPIRATION_TITLE_USER
- },
- "description": {
- "system": cls.INSPIRATION_DESCRIPTION_SYSTEM,
- "user": cls.INSPIRATION_DESCRIPTION_USER
- },
- "theme": {
- "system": cls.INSPIRATION_THEME_SYSTEM,
- "user": cls.INSPIRATION_THEME_USER
- },
- "genre": {
- "system": cls.INSPIRATION_GENRE_SYSTEM,
- "user": cls.INSPIRATION_GENRE_USER
- }
- }
- return step_map.get(step)
-
- @classmethod
- def get_inspiration_quick_complete_prompt(cls, existing: str) -> Dict[str, str]:
- """获取灵感模式智能补全的提示词"""
- return {
- "system": cls.format_prompt(cls.INSPIRATION_QUICK_COMPLETE, existing=existing),
- "user": "请补全小说信息"
- }
-
@classmethod
async def get_mcp_tool_test_prompts(
cls,
@@ -1818,7 +1956,6 @@ class PromptService:
"user": cls.format_prompt(user_template, plugin_name=plugin_name),
"system": system_template
}
-# 创建全局提示词服务实例
# ========== 自定义提示词支持 ==========
@@ -1884,14 +2021,7 @@ class PromptService:
# 2. 降级到系统默认模板
logger.info(f"⚪ 使用系统默认提示词: user_id={user_id}, template_key={template_key} (未找到自定义模板)")
- # 特殊处理灵感模式的提示词(直接从类属性获取)
- if template_key.startswith("INSPIRATION_"):
- # 直接从类属性获取
- template_content = getattr(cls, template_key, None)
- if template_content:
- return template_content
-
- # 其他模板直接从类属性获取
+ # 直接从类属性获取系统默认模板
template_content = getattr(cls, template_key, None)
if template_content is None:
@@ -1951,37 +2081,20 @@ class PromptService:
"all_chapters_brief", "recent_plot", "memory_context", "mcp_references",
"plot_stage_instruction", "start_chapter", "end_chapter", "story_direction", "requirements"]
},
- "OUTLINE_GENERATION": {
- "name": "基础大纲生成",
- "category": "大纲生成",
- "description": "生成基础章节大纲框架",
- "parameters": ["genre", "theme", "target_words", "requirements"]
- },
- "OUTLINE_EXPANSION": {
- "name": "大纲展开",
- "category": "大纲生成",
- "description": "将单个大纲节点展开为多个章节",
- "parameters": ["title", "genre", "theme", "narrative_perspective", "time_period", "location",
- "atmosphere", "rules", "characters_info", "outline_order", "outline_title",
- "outline_content", "context_info", "strategy_instruction", "target_chapters",
- "scene_instruction", "scene_field"]
- },
- "CHAPTER_GENERATION": {
- "name": "章节创作",
+ "CHAPTER_GENERATION_V2": {
+ "name": "章节创作V2(首章)",
"category": "章节创作",
- "description": "根据大纲创作章节内容",
- "parameters": ["title", "theme", "genre", "narrative_perspective", "time_period", "location",
- "atmosphere", "rules", "characters_info", "outlines_context", "chapter_number",
- "chapter_title", "chapter_outline", "target_word_count", "max_word_count"]
+ "description": "根据大纲创作章节内容(用于第1章,无前置章节)",
+ "parameters": ["project_title", "genre", "chapter_number", "chapter_title", "chapter_outline",
+ "target_word_count", "narrative_perspective", "characters_info"]
},
- "CHAPTER_GENERATION_WITH_CONTEXT": {
- "name": "章节创作(带上下文)",
+ "CHAPTER_GENERATION_V2_WITH_CONTEXT": {
+ "name": "章节创作V2(续章)",
"category": "章节创作",
- "description": "基于前置章节内容创作新章节",
- "parameters": ["title", "theme", "genre", "narrative_perspective", "time_period", "location",
- "atmosphere", "rules", "characters_info", "outlines_context", "previous_content",
- "memory_context", "chapter_number", "chapter_title", "chapter_outline",
- "target_word_count", "max_word_count"]
+ "description": "基于前置章节内容创作新章节(用于第2章及以后)",
+ "parameters": ["project_title", "genre", "chapter_number", "chapter_title", "chapter_outline",
+ "target_word_count", "narrative_perspective", "characters_info", "continuation_point",
+ "relevant_memories", "story_skeleton"]
},
"CHAPTER_REGENERATION_SYSTEM": {
"name": "章节重写系统提示",
@@ -1990,12 +2103,6 @@ class PromptService:
"parameters": ["chapter_number", "title", "word_count", "content", "modification_instructions",
"project_context", "style_content", "target_word_count"]
},
- "AI_DENOISING": {
- "name": "AI去味",
- "category": "辅助功能",
- "description": "将AI生成的文本改写得更自然",
- "parameters": ["original_text"]
- },
"PLOT_ANALYSIS": {
"name": "情节分析",
"category": "情节分析",
@@ -2151,4 +2258,6 @@ class PromptService:
if template["template_key"] == template_key:
return template
return None
+
+# ========== 全局实例 ==========
prompt_service = PromptService()
\ No newline at end of file