update:1.更新大纲细化功能
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, Query, BackgroundTasks
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, func
|
||||
from sqlalchemy.orm import selectinload
|
||||
import json
|
||||
import asyncio
|
||||
from typing import Optional
|
||||
@@ -125,7 +126,7 @@ async def get_project_chapters(
|
||||
request: Request,
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""获取指定项目的所有章节(路径参数版本)"""
|
||||
"""获取指定项目的所有章节(带大纲信息)"""
|
||||
# 验证用户权限
|
||||
user_id = getattr(request.state, 'user_id', None)
|
||||
await verify_project_access(project_id, user_id, db)
|
||||
@@ -136,7 +137,7 @@ async def get_project_chapters(
|
||||
)
|
||||
total = count_result.scalar_one()
|
||||
|
||||
# 获取章节列表
|
||||
# 获取章节列表,同时加载关联的大纲信息
|
||||
result = await db.execute(
|
||||
select(Chapter)
|
||||
.where(Chapter.project_id == project_id)
|
||||
@@ -144,7 +145,46 @@ async def get_project_chapters(
|
||||
)
|
||||
chapters = result.scalars().all()
|
||||
|
||||
return ChapterListResponse(total=total, items=chapters)
|
||||
# 获取所有大纲信息(用于填充outline_title)
|
||||
outline_ids = [ch.outline_id for ch in chapters if ch.outline_id]
|
||||
outlines_map = {}
|
||||
if outline_ids:
|
||||
outlines_result = await db.execute(
|
||||
select(Outline).where(Outline.id.in_(outline_ids))
|
||||
)
|
||||
outlines_map = {o.id: o for o in outlines_result.scalars().all()}
|
||||
|
||||
# 为所有章节添加大纲信息(统一处理)
|
||||
chapters_with_outline = []
|
||||
for chapter in chapters:
|
||||
chapter_dict = {
|
||||
"id": chapter.id,
|
||||
"project_id": chapter.project_id,
|
||||
"chapter_number": chapter.chapter_number,
|
||||
"title": chapter.title,
|
||||
"content": chapter.content,
|
||||
"summary": chapter.summary,
|
||||
"word_count": chapter.word_count,
|
||||
"status": chapter.status,
|
||||
"outline_id": chapter.outline_id,
|
||||
"sub_index": chapter.sub_index,
|
||||
"expansion_plan": chapter.expansion_plan,
|
||||
"created_at": chapter.created_at,
|
||||
"updated_at": chapter.updated_at,
|
||||
}
|
||||
|
||||
# 添加大纲信息
|
||||
if chapter.outline_id and chapter.outline_id in outlines_map:
|
||||
outline = outlines_map[chapter.outline_id]
|
||||
chapter_dict["outline_title"] = outline.title
|
||||
chapter_dict["outline_order"] = outline.order_index
|
||||
else:
|
||||
chapter_dict["outline_title"] = None
|
||||
chapter_dict["outline_order"] = None
|
||||
|
||||
chapters_with_outline.append(chapter_dict)
|
||||
|
||||
return ChapterListResponse(total=total, items=chapters_with_outline)
|
||||
|
||||
|
||||
@router.get("/{chapter_id}", response_model=ChapterResponse, summary="获取章节详情")
|
||||
|
||||
+959
-164
File diff suppressed because it is too large
Load Diff
@@ -354,7 +354,9 @@ async def export_project_chapters(
|
||||
txt_content.append("\n" + "=" * 80 + "\n\n")
|
||||
|
||||
for chapter in chapters:
|
||||
txt_content.append(f"第 {chapter.chapter_number} 章 {chapter.title}")
|
||||
# 处理子章节序号显示
|
||||
chapter_display = f"{chapter.chapter_number}-{chapter.sub_index}" if chapter.sub_index and chapter.sub_index > 1 else str(chapter.chapter_number)
|
||||
txt_content.append(f"第 {chapter_display} 章 {chapter.title}")
|
||||
txt_content.append("-" * 80)
|
||||
txt_content.append("") # 空行
|
||||
|
||||
|
||||
@@ -17,8 +17,16 @@ class Chapter(Base):
|
||||
summary = Column(Text, comment="章节摘要")
|
||||
word_count = Column(Integer, default=0, comment="字数统计")
|
||||
status = Column(String(20), default="draft", comment="章节状态")
|
||||
|
||||
# 大纲关联字段(实现一对多关系)
|
||||
outline_id = Column(String(36), ForeignKey("outlines.id", ondelete="SET NULL"), nullable=True, comment="关联的大纲ID")
|
||||
sub_index = Column(Integer, default=1, comment="大纲下的子章节序号")
|
||||
|
||||
# 大纲展开规划数据(JSON格式)
|
||||
expansion_plan = Column(Text, comment="展开规划详情(JSON): 包含key_events, character_focus, emotional_tone等")
|
||||
|
||||
created_at = Column(DateTime, server_default=func.now(), comment="创建时间")
|
||||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now(), comment="更新时间")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Chapter(id={self.id}, chapter_number={self.chapter_number}, title={self.title})>"
|
||||
return f"<Chapter(id={self.id}, chapter_number={self.chapter_number}, title={self.title}, outline_id={self.outline_id})>"
|
||||
@@ -12,6 +12,9 @@ class ChapterBase(BaseModel):
|
||||
summary: Optional[str] = Field(None, description="章节摘要")
|
||||
word_count: Optional[int] = Field(0, description="字数")
|
||||
status: Optional[str] = Field("draft", description="章节状态")
|
||||
outline_id: Optional[str] = Field(None, description="关联的大纲ID")
|
||||
sub_index: Optional[int] = Field(1, description="大纲下的子章节序号")
|
||||
expansion_plan: Optional[str] = Field(None, description="展开规划详情(JSON)")
|
||||
|
||||
|
||||
class ChapterCreate(BaseModel):
|
||||
@@ -22,6 +25,9 @@ class ChapterCreate(BaseModel):
|
||||
content: Optional[str] = Field(None, description="章节内容")
|
||||
summary: Optional[str] = Field(None, description="章节摘要")
|
||||
status: Optional[str] = Field("draft", description="章节状态")
|
||||
outline_id: Optional[str] = Field(None, description="关联的大纲ID")
|
||||
sub_index: Optional[int] = Field(1, description="大纲下的子章节序号")
|
||||
expansion_plan: Optional[str] = Field(None, description="展开规划详情(JSON)")
|
||||
|
||||
|
||||
class ChapterUpdate(BaseModel):
|
||||
@@ -44,6 +50,11 @@ class ChapterResponse(BaseModel):
|
||||
summary: Optional[str] = None
|
||||
word_count: int = 0
|
||||
status: str
|
||||
outline_id: Optional[str] = None
|
||||
sub_index: Optional[int] = 1
|
||||
expansion_plan: Optional[str] = None
|
||||
outline_title: Optional[str] = None # 大纲标题(从Outline表联查)
|
||||
outline_order: Optional[int] = None # 大纲排序序号(从Outline表联查)
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
@@ -78,12 +78,70 @@ class OutlineListResponse(BaseModel):
|
||||
items: list[OutlineResponse]
|
||||
|
||||
|
||||
class OutlineReorderItem(BaseModel):
|
||||
"""单个大纲重排序项"""
|
||||
id: str = Field(..., description="大纲ID")
|
||||
order_index: int = Field(..., description="新的序号", ge=1)
|
||||
class ChapterPlanItem(BaseModel):
|
||||
"""单个章节规划项"""
|
||||
sub_index: int = Field(..., description="子章节序号", ge=1)
|
||||
title: str = Field(..., description="章节标题")
|
||||
plot_summary: str = Field(..., description="剧情摘要(200-300字)")
|
||||
key_events: list[str] = Field(..., description="关键事件列表")
|
||||
character_focus: list[str] = Field(..., description="主要涉及的角色")
|
||||
emotional_tone: str = Field(..., description="情感基调")
|
||||
narrative_goal: str = Field(..., description="叙事目标")
|
||||
conflict_type: str = Field(..., description="冲突类型")
|
||||
estimated_words: int = Field(3000, description="预计字数", ge=1000)
|
||||
scenes: Optional[list[str]] = Field(None, description="场景列表(可选)")
|
||||
|
||||
|
||||
class OutlineReorderRequest(BaseModel):
|
||||
"""大纲批量重排序请求"""
|
||||
orders: list[OutlineReorderItem] = Field(..., description="排序列表")
|
||||
class OutlineExpansionRequest(BaseModel):
|
||||
"""大纲展开为多章节的请求模型(outline_id从路径参数获取)"""
|
||||
target_chapter_count: int = Field(3, description="目标章节数", ge=1, le=10)
|
||||
expansion_strategy: str = Field("balanced", description="展开策略: balanced(均衡), climax(高潮重点), detail(细节丰富)")
|
||||
enable_scene_analysis: bool = Field(False, description="是否包含场景规划")
|
||||
auto_create_chapters: bool = Field(True, description="是否自动创建章节记录")
|
||||
provider: Optional[str] = Field(None, description="AI提供商")
|
||||
model: Optional[str] = Field(None, description="AI模型")
|
||||
|
||||
|
||||
class OutlineExpansionResponse(BaseModel):
|
||||
"""大纲展开响应模型"""
|
||||
outline_id: str = Field(..., description="大纲ID")
|
||||
outline_title: str = Field(..., description="大纲标题")
|
||||
target_chapter_count: int = Field(..., description="目标章节数")
|
||||
actual_chapter_count: int = Field(..., description="实际生成的章节数")
|
||||
expansion_strategy: str = Field(..., description="使用的展开策略")
|
||||
chapter_plans: list[ChapterPlanItem] = Field(..., description="章节规划列表")
|
||||
created_chapters: Optional[list] = Field(None, description="已创建的章节列表")
|
||||
|
||||
|
||||
class BatchOutlineExpansionRequest(BaseModel):
|
||||
"""批量大纲展开请求模型"""
|
||||
project_id: str = Field(..., description="项目ID")
|
||||
outline_ids: Optional[list[str]] = Field(None, description="要展开的大纲ID列表(为空则展开所有)")
|
||||
chapters_per_outline: int = Field(3, description="每个大纲的目标章节数", ge=1, le=10)
|
||||
expansion_strategy: str = Field("balanced", description="展开策略")
|
||||
enable_scene_analysis: bool = Field(False, description="是否包含场景规划")
|
||||
auto_create_chapters: bool = Field(True, description="是否自动创建章节记录")
|
||||
provider: Optional[str] = Field(None, description="AI提供商")
|
||||
model: Optional[str] = Field(None, description="AI模型")
|
||||
|
||||
|
||||
class BatchOutlineExpansionResponse(BaseModel):
|
||||
"""批量大纲展开响应模型"""
|
||||
project_id: str = Field(..., description="项目ID")
|
||||
total_outlines_expanded: int = Field(..., description="总共展开的大纲数")
|
||||
total_chapters_created: int = Field(..., description="总共创建的章节数")
|
||||
expansion_results: list[OutlineExpansionResponse] = Field(..., description="展开结果列表")
|
||||
skipped_outlines: Optional[list[dict]] = Field(None, description="跳过的大纲列表(已展开)")
|
||||
|
||||
|
||||
class CreateChaptersFromPlansRequest(BaseModel):
|
||||
"""根据已有规划创建章节的请求模型"""
|
||||
chapter_plans: list[ChapterPlanItem] = Field(..., description="章节规划列表(来自之前的AI生成结果)")
|
||||
|
||||
|
||||
class CreateChaptersFromPlansResponse(BaseModel):
|
||||
"""根据已有规划创建章节的响应模型"""
|
||||
outline_id: str = Field(..., description="大纲ID")
|
||||
outline_title: str = Field(..., description="大纲标题")
|
||||
chapters_created: int = Field(..., description="创建的章节数")
|
||||
created_chapters: list = Field(..., description="创建的章节列表")
|
||||
@@ -0,0 +1,723 @@
|
||||
"""大纲剧情展开服务 - 将大纲节点展开为多个章节"""
|
||||
from typing import List, Dict, Any, Optional
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, func
|
||||
import json
|
||||
|
||||
from app.models.outline import Outline
|
||||
from app.models.project import Project
|
||||
from app.models.character import Character
|
||||
from app.models.chapter import Chapter
|
||||
from app.services.ai_service import AIService
|
||||
from app.logger import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class PlotExpansionService:
|
||||
"""大纲剧情展开服务"""
|
||||
|
||||
def __init__(self, ai_service: AIService):
|
||||
self.ai_service = ai_service
|
||||
|
||||
async def analyze_outline_for_chapters(
|
||||
self,
|
||||
outline: Outline,
|
||||
project: Project,
|
||||
db: AsyncSession,
|
||||
target_chapter_count: int = 3,
|
||||
expansion_strategy: str = "balanced",
|
||||
enable_scene_analysis: bool = True,
|
||||
provider: Optional[str] = None,
|
||||
model: Optional[str] = None,
|
||||
batch_size: int = 5,
|
||||
progress_callback: Optional[callable] = None
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
分析单个大纲,生成多章节规划(支持分批生成)
|
||||
|
||||
Args:
|
||||
outline: 大纲对象
|
||||
project: 项目对象
|
||||
db: 数据库会话
|
||||
target_chapter_count: 目标生成章节数
|
||||
expansion_strategy: 展开策略(balanced/climax/detail)
|
||||
enable_scene_analysis: 是否启用场景级分析
|
||||
provider: AI提供商
|
||||
model: AI模型
|
||||
batch_size: 每批生成的章节数(默认5章)
|
||||
progress_callback: 进度回调函数(可选)
|
||||
|
||||
Returns:
|
||||
章节规划列表
|
||||
"""
|
||||
logger.info(f"开始分析大纲 {outline.id},目标生成 {target_chapter_count} 章")
|
||||
|
||||
# 如果章节数较少,直接生成
|
||||
if target_chapter_count <= batch_size:
|
||||
return await self._generate_chapters_single_batch(
|
||||
outline=outline,
|
||||
project=project,
|
||||
db=db,
|
||||
target_chapter_count=target_chapter_count,
|
||||
expansion_strategy=expansion_strategy,
|
||||
enable_scene_analysis=enable_scene_analysis,
|
||||
provider=provider,
|
||||
model=model
|
||||
)
|
||||
|
||||
# 章节数较多,分批生成
|
||||
logger.info(f"章节数({target_chapter_count})超过批次大小({batch_size}),启用分批生成")
|
||||
return await self._generate_chapters_in_batches(
|
||||
outline=outline,
|
||||
project=project,
|
||||
db=db,
|
||||
target_chapter_count=target_chapter_count,
|
||||
expansion_strategy=expansion_strategy,
|
||||
enable_scene_analysis=enable_scene_analysis,
|
||||
provider=provider,
|
||||
model=model,
|
||||
batch_size=batch_size,
|
||||
progress_callback=progress_callback
|
||||
)
|
||||
|
||||
async def _generate_chapters_single_batch(
|
||||
self,
|
||||
outline: Outline,
|
||||
project: Project,
|
||||
db: AsyncSession,
|
||||
target_chapter_count: int,
|
||||
expansion_strategy: str,
|
||||
enable_scene_analysis: bool,
|
||||
provider: Optional[str],
|
||||
model: Optional[str]
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""单批次生成章节规划"""
|
||||
# 获取角色信息
|
||||
characters_result = await db.execute(
|
||||
select(Character).where(Character.project_id == project.id)
|
||||
)
|
||||
characters = characters_result.scalars().all()
|
||||
characters_info = "\n".join([
|
||||
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
|
||||
f"{char.personality[:100] if char.personality else '暂无描述'}"
|
||||
for char in characters
|
||||
])
|
||||
|
||||
# 获取大纲上下文(前后大纲)
|
||||
context_info = await self._get_outline_context(outline, project.id, db)
|
||||
|
||||
# 构建分析提示词
|
||||
prompt = self._build_expansion_prompt(
|
||||
outline=outline,
|
||||
project=project,
|
||||
characters_info=characters_info,
|
||||
context_info=context_info,
|
||||
target_chapter_count=target_chapter_count,
|
||||
expansion_strategy=expansion_strategy,
|
||||
enable_scene_analysis=enable_scene_analysis
|
||||
)
|
||||
|
||||
# 调用AI生成章节规划
|
||||
logger.info(f"调用AI生成章节规划...")
|
||||
ai_response = await self.ai_service.generate_text(
|
||||
prompt=prompt,
|
||||
provider=provider,
|
||||
model=model
|
||||
)
|
||||
|
||||
# 提取内容
|
||||
ai_content = ai_response.get("content", "") if isinstance(ai_response, dict) else ai_response
|
||||
|
||||
# 解析AI响应
|
||||
chapter_plans = self._parse_expansion_response(ai_content, outline.id)
|
||||
|
||||
logger.info(f"成功生成 {len(chapter_plans)} 个章节规划")
|
||||
return chapter_plans
|
||||
|
||||
async def _generate_chapters_in_batches(
|
||||
self,
|
||||
outline: Outline,
|
||||
project: Project,
|
||||
db: AsyncSession,
|
||||
target_chapter_count: int,
|
||||
expansion_strategy: str,
|
||||
enable_scene_analysis: bool,
|
||||
provider: Optional[str],
|
||||
model: Optional[str],
|
||||
batch_size: int,
|
||||
progress_callback: Optional[callable]
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""分批生成章节规划"""
|
||||
# 计算批次数
|
||||
total_batches = (target_chapter_count + batch_size - 1) // batch_size
|
||||
logger.info(f"分批生成计划: 总共{target_chapter_count}章,分{total_batches}批,每批{batch_size}章")
|
||||
|
||||
# 获取角色信息(所有批次共用)
|
||||
characters_result = await db.execute(
|
||||
select(Character).where(Character.project_id == project.id)
|
||||
)
|
||||
characters = characters_result.scalars().all()
|
||||
characters_info = "\n".join([
|
||||
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
|
||||
f"{char.personality[:100] if char.personality else '暂无描述'}"
|
||||
for char in characters
|
||||
])
|
||||
|
||||
# 获取大纲上下文
|
||||
context_info = await self._get_outline_context(outline, project.id, db)
|
||||
|
||||
all_chapter_plans = []
|
||||
|
||||
for batch_num in range(total_batches):
|
||||
# 计算当前批次的章节数
|
||||
remaining_chapters = target_chapter_count - len(all_chapter_plans)
|
||||
current_batch_size = min(batch_size, remaining_chapters)
|
||||
current_start_index = len(all_chapter_plans) + 1
|
||||
|
||||
logger.info(f"开始生成第{batch_num + 1}/{total_batches}批,章节范围: {current_start_index}-{current_start_index + current_batch_size - 1}")
|
||||
|
||||
# 回调通知进度
|
||||
if progress_callback:
|
||||
await progress_callback(batch_num + 1, total_batches, current_start_index, current_batch_size)
|
||||
|
||||
# 构建当前批次的提示词(包含已生成章节的上下文)
|
||||
prompt = self._build_batch_expansion_prompt(
|
||||
outline=outline,
|
||||
project=project,
|
||||
characters_info=characters_info,
|
||||
context_info=context_info,
|
||||
target_chapter_count=current_batch_size,
|
||||
expansion_strategy=expansion_strategy,
|
||||
enable_scene_analysis=enable_scene_analysis,
|
||||
start_index=current_start_index,
|
||||
previous_chapters=all_chapter_plans,
|
||||
total_chapters=target_chapter_count
|
||||
)
|
||||
|
||||
# 调用AI生成当前批次
|
||||
logger.info(f"调用AI生成第{batch_num + 1}批...")
|
||||
ai_response = await self.ai_service.generate_text(
|
||||
prompt=prompt,
|
||||
provider=provider,
|
||||
model=model
|
||||
)
|
||||
|
||||
# 提取内容
|
||||
ai_content = ai_response.get("content", "") if isinstance(ai_response, dict) else ai_response
|
||||
|
||||
# 解析AI响应
|
||||
batch_plans = self._parse_expansion_response(ai_content, outline.id)
|
||||
|
||||
# 调整sub_index以保持连续性
|
||||
for i, plan in enumerate(batch_plans):
|
||||
plan["sub_index"] = current_start_index + i
|
||||
|
||||
all_chapter_plans.extend(batch_plans)
|
||||
|
||||
logger.info(f"第{batch_num + 1}批生成完成,本批生成{len(batch_plans)}章,累计{len(all_chapter_plans)}章")
|
||||
|
||||
logger.info(f"分批生成完成,共生成 {len(all_chapter_plans)} 个章节规划")
|
||||
return all_chapter_plans
|
||||
|
||||
async def batch_expand_outlines(
|
||||
self,
|
||||
project_id: str,
|
||||
db: AsyncSession,
|
||||
ai_service: AIService,
|
||||
target_chapters_per_outline: int = 3,
|
||||
expansion_strategy: str = "balanced",
|
||||
provider: Optional[str] = None,
|
||||
model: Optional[str] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
批量展开所有大纲为章节
|
||||
|
||||
Returns:
|
||||
{
|
||||
"total_outlines": 总大纲数,
|
||||
"total_chapters_planned": 规划的总章节数,
|
||||
"expansions": [每个大纲的展开结果]
|
||||
}
|
||||
"""
|
||||
logger.info(f"开始批量展开项目 {project_id} 的所有大纲")
|
||||
|
||||
# 获取项目
|
||||
project_result = await db.execute(
|
||||
select(Project).where(Project.id == project_id)
|
||||
)
|
||||
project = project_result.scalar_one_or_none()
|
||||
if not project:
|
||||
raise ValueError(f"项目 {project_id} 不存在")
|
||||
|
||||
# 获取所有大纲
|
||||
outlines_result = await db.execute(
|
||||
select(Outline)
|
||||
.where(Outline.project_id == project_id)
|
||||
.order_by(Outline.order_index)
|
||||
)
|
||||
outlines = outlines_result.scalars().all()
|
||||
|
||||
if not outlines:
|
||||
logger.warning(f"项目 {project_id} 没有大纲")
|
||||
return {
|
||||
"total_outlines": 0,
|
||||
"total_chapters_planned": 0,
|
||||
"expansions": []
|
||||
}
|
||||
|
||||
# 逐个展开大纲
|
||||
expansions = []
|
||||
total_chapters = 0
|
||||
|
||||
for outline in outlines:
|
||||
try:
|
||||
chapter_plans = await self.analyze_outline_for_chapters(
|
||||
outline=outline,
|
||||
project=project,
|
||||
db=db,
|
||||
target_chapter_count=target_chapters_per_outline,
|
||||
expansion_strategy=expansion_strategy,
|
||||
provider=provider,
|
||||
model=model
|
||||
)
|
||||
|
||||
expansions.append({
|
||||
"outline_id": outline.id,
|
||||
"outline_title": outline.title,
|
||||
"chapter_plans": chapter_plans,
|
||||
"chapter_count": len(chapter_plans)
|
||||
})
|
||||
|
||||
total_chapters += len(chapter_plans)
|
||||
logger.info(f"大纲 {outline.title} 展开为 {len(chapter_plans)} 章")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"展开大纲 {outline.id} 失败: {str(e)}")
|
||||
expansions.append({
|
||||
"outline_id": outline.id,
|
||||
"outline_title": outline.title,
|
||||
"error": str(e),
|
||||
"chapter_count": 0
|
||||
})
|
||||
|
||||
result = {
|
||||
"total_outlines": len(outlines),
|
||||
"total_chapters_planned": total_chapters,
|
||||
"expansions": expansions
|
||||
}
|
||||
|
||||
logger.info(f"批量展开完成: {len(outlines)} 个大纲 → {total_chapters} 个章节规划")
|
||||
return result
|
||||
|
||||
async def create_chapters_from_plans(
|
||||
self,
|
||||
outline_id: str,
|
||||
chapter_plans: List[Dict[str, Any]],
|
||||
project_id: str,
|
||||
db: AsyncSession,
|
||||
start_chapter_number: int = None
|
||||
) -> List[Chapter]:
|
||||
"""
|
||||
根据章节规划创建实际的章节记录
|
||||
|
||||
Args:
|
||||
outline_id: 大纲ID
|
||||
chapter_plans: 章节规划列表
|
||||
project_id: 项目ID
|
||||
db: 数据库会话
|
||||
start_chapter_number: 起始章节号(如果为None,则自动计算)
|
||||
|
||||
Returns:
|
||||
创建的章节列表
|
||||
"""
|
||||
logger.info(f"根据规划创建 {len(chapter_plans)} 个章节记录")
|
||||
|
||||
# 如果没有指定起始章节号,自动计算
|
||||
if start_chapter_number is None:
|
||||
# 查询项目中已有章节的最大序号
|
||||
max_number_result = await db.execute(
|
||||
select(func.max(Chapter.chapter_number))
|
||||
.where(Chapter.project_id == project_id)
|
||||
)
|
||||
max_number = max_number_result.scalar()
|
||||
start_chapter_number = (max_number or 0) + 1
|
||||
logger.info(f"自动计算起始章节号: {start_chapter_number} (当前最大序号: {max_number})")
|
||||
|
||||
chapters = []
|
||||
for idx, plan in enumerate(chapter_plans):
|
||||
# 保存完整的展开规划数据(JSON格式)
|
||||
expansion_plan_json = json.dumps({
|
||||
"key_events": plan.get("key_events", []),
|
||||
"character_focus": plan.get("character_focus", []),
|
||||
"emotional_tone": plan.get("emotional_tone", ""),
|
||||
"narrative_goal": plan.get("narrative_goal", ""),
|
||||
"conflict_type": plan.get("conflict_type", ""),
|
||||
"estimated_words": plan.get("estimated_words", 3000),
|
||||
"scenes": plan.get("scenes", []) if plan.get("scenes") else None
|
||||
}, ensure_ascii=False)
|
||||
|
||||
chapter = Chapter(
|
||||
project_id=project_id,
|
||||
outline_id=outline_id,
|
||||
chapter_number=start_chapter_number + idx,
|
||||
sub_index=plan.get("sub_index", idx + 1),
|
||||
title=plan.get("title", f"第{start_chapter_number + idx}章"),
|
||||
summary=plan.get("plot_summary", ""),
|
||||
expansion_plan=expansion_plan_json,
|
||||
status="draft"
|
||||
)
|
||||
db.add(chapter)
|
||||
chapters.append(chapter)
|
||||
|
||||
await db.commit()
|
||||
|
||||
for chapter in chapters:
|
||||
await db.refresh(chapter)
|
||||
|
||||
logger.info(f"成功创建 {len(chapters)} 个章节记录(已保存展开规划数据)")
|
||||
return chapters
|
||||
|
||||
async def _get_outline_context(
|
||||
self,
|
||||
outline: Outline,
|
||||
project_id: str,
|
||||
db: AsyncSession
|
||||
) -> str:
|
||||
"""获取大纲的上下文(前后大纲)"""
|
||||
# 获取前一个大纲
|
||||
prev_result = await db.execute(
|
||||
select(Outline)
|
||||
.where(
|
||||
Outline.project_id == project_id,
|
||||
Outline.order_index < outline.order_index
|
||||
)
|
||||
.order_by(Outline.order_index.desc())
|
||||
.limit(1)
|
||||
)
|
||||
prev_outline = prev_result.scalar_one_or_none()
|
||||
|
||||
# 获取后一个大纲
|
||||
next_result = await db.execute(
|
||||
select(Outline)
|
||||
.where(
|
||||
Outline.project_id == project_id,
|
||||
Outline.order_index > outline.order_index
|
||||
)
|
||||
.order_by(Outline.order_index)
|
||||
.limit(1)
|
||||
)
|
||||
next_outline = next_result.scalar_one_or_none()
|
||||
|
||||
context = ""
|
||||
if prev_outline:
|
||||
context += f"【前一节】{prev_outline.title}: {prev_outline.content[:200]}...\n\n"
|
||||
if next_outline:
|
||||
context += f"【后一节】{next_outline.title}: {next_outline.content[:200]}...\n"
|
||||
|
||||
return context if context else "(无前后文)"
|
||||
|
||||
def _build_expansion_prompt(
|
||||
self,
|
||||
outline: Outline,
|
||||
project: Project,
|
||||
characters_info: str,
|
||||
context_info: str,
|
||||
target_chapter_count: int,
|
||||
expansion_strategy: str,
|
||||
enable_scene_analysis: bool
|
||||
) -> str:
|
||||
"""构建大纲展开提示词"""
|
||||
|
||||
strategy_desc = {
|
||||
"balanced": "均衡展开:每章剧情量相当,节奏平稳",
|
||||
"climax": "高潮重点:重点章节剧情丰富,其他章节简洁过渡",
|
||||
"detail": "细节丰富:每章都深入描写,场景和情感细腻"
|
||||
}
|
||||
|
||||
strategy_instruction = strategy_desc.get(expansion_strategy, strategy_desc["balanced"])
|
||||
|
||||
# 场景字段(避免f-string中的反斜杠)
|
||||
scene_field = ',\n "main_scenes": ["场景1", "场景2"]' if enable_scene_analysis else ''
|
||||
|
||||
scene_instruction = ""
|
||||
if enable_scene_analysis:
|
||||
scene_instruction = """
|
||||
5. 场景分析(每章需包含):
|
||||
- 主要场景地点
|
||||
- 场景氛围
|
||||
- 关键道具/环境元素
|
||||
"""
|
||||
|
||||
prompt = f"""你是专业的小说情节架构师。请分析以下大纲节点,将其展开为 {target_chapter_count} 个章节的详细规划。
|
||||
|
||||
【项目信息】
|
||||
小说名称:{project.title}
|
||||
类型:{project.genre or '通用'}
|
||||
主题:{project.theme or '未设定'}
|
||||
叙事视角:{project.narrative_perspective or '第三人称'}
|
||||
|
||||
【世界观背景】
|
||||
时间背景:{project.world_time_period or '未设定'}
|
||||
地理位置:{project.world_location or '未设定'}
|
||||
氛围基调:{project.world_atmosphere or '未设定'}
|
||||
|
||||
【角色信息】
|
||||
{characters_info or '暂无角色'}
|
||||
|
||||
【当前大纲节点 - 展开对象】
|
||||
序号:第 {outline.order_index} 节
|
||||
标题:{outline.title}
|
||||
内容:{outline.content}
|
||||
|
||||
【上下文参考】
|
||||
{context_info}
|
||||
|
||||
【展开策略】
|
||||
{strategy_instruction}
|
||||
|
||||
【⚠️ 重要约束 - 必须严格遵守】
|
||||
1. **内容边界约束**:
|
||||
- ✅ 只能展开【当前大纲节点】中明确描述的内容
|
||||
- ❌ 绝对不能推进到后续大纲的内容(如果有【后一节】信息)
|
||||
- ❌ 不要让剧情快速推进,要深化而非跨越
|
||||
|
||||
2. **展开原则**:
|
||||
- 将当前大纲的单一事件拆解为多个细节丰富的章节
|
||||
- 深入挖掘情感、心理、环境、对话等细节
|
||||
- 放慢叙事节奏,让读者充分体验当前阶段的剧情
|
||||
- 每个章节都应该是当前大纲内容的不同侧面或阶段
|
||||
|
||||
3. **如何避免剧情越界**:
|
||||
- 如果当前大纲描述"主角遇到困境",展开时应详写困境的发现、分析、情感冲击等
|
||||
- 不要直接写到"解决困境",除非原大纲明确包含解决过程
|
||||
- 如果看到【后一节】的内容,那些是禁区,绝不提前展开
|
||||
|
||||
【任务要求】
|
||||
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}
|
||||
4. 确保章节间:
|
||||
- 衔接自然流畅
|
||||
- 剧情递进合理(但不超出当前大纲边界)
|
||||
- 节奏张弛有度
|
||||
- 每章都有明确的叙事价值
|
||||
- 最后一章结束时,剧情发展程度应恰好完成当前大纲描述的内容,不多不少
|
||||
|
||||
【输出格式】
|
||||
请严格按照以下JSON数组格式输出,不要添加任何其他文字:
|
||||
[
|
||||
{{
|
||||
"sub_index": 1,
|
||||
"title": "章节标题",
|
||||
"plot_summary": "该章详细剧情摘要...",
|
||||
"key_events": ["关键事件1", "关键事件2", "关键事件3"],
|
||||
"character_focus": ["角色A", "角色B"],
|
||||
"emotional_tone": "情感基调",
|
||||
"narrative_goal": "叙事目标",
|
||||
"conflict_type": "冲突类型",
|
||||
"estimated_words": 3000{scene_field}
|
||||
}}
|
||||
]
|
||||
|
||||
请开始分析并生成章节规划:
|
||||
"""
|
||||
return prompt
|
||||
|
||||
def _build_batch_expansion_prompt(
|
||||
self,
|
||||
outline: Outline,
|
||||
project: Project,
|
||||
characters_info: str,
|
||||
context_info: str,
|
||||
target_chapter_count: int,
|
||||
expansion_strategy: str,
|
||||
enable_scene_analysis: bool,
|
||||
start_index: int,
|
||||
previous_chapters: List[Dict[str, Any]],
|
||||
total_chapters: int
|
||||
) -> str:
|
||||
"""构建分批展开提示词"""
|
||||
|
||||
strategy_desc = {
|
||||
"balanced": "均衡展开:每章剧情量相当,节奏平稳",
|
||||
"climax": "高潮重点:重点章节剧情丰富,其他章节简洁过渡",
|
||||
"detail": "细节丰富:每章都深入描写,场景和情感细腻"
|
||||
}
|
||||
|
||||
strategy_instruction = strategy_desc.get(expansion_strategy, strategy_desc["balanced"])
|
||||
|
||||
# 场景字段
|
||||
scene_field = ',\n "main_scenes": ["场景1", "场景2"]' if enable_scene_analysis else ''
|
||||
|
||||
scene_instruction = ""
|
||||
if enable_scene_analysis:
|
||||
scene_instruction = """
|
||||
5. 场景分析(每章需包含):
|
||||
- 主要场景地点
|
||||
- 场景氛围
|
||||
- 关键道具/环境元素
|
||||
"""
|
||||
|
||||
# 构建已生成章节的摘要
|
||||
previous_context = ""
|
||||
if previous_chapters:
|
||||
previous_summaries = []
|
||||
for ch in previous_chapters[-3:]: # 只显示最近3章
|
||||
previous_summaries.append(
|
||||
f"第{ch['sub_index']}节《{ch['title']}》: {ch['plot_summary'][:100]}..."
|
||||
)
|
||||
previous_context = f"""
|
||||
【已生成章节概要】(接续生成,注意衔接)
|
||||
{chr(10).join(previous_summaries)}
|
||||
|
||||
⚠️ 当前是第{start_index}-{start_index + target_chapter_count - 1}节(共{total_chapters}节中的一部分)
|
||||
"""
|
||||
|
||||
prompt = f"""你是专业的小说情节架构师。请继续分析以下大纲节点,将其展开为第{start_index}-{start_index + target_chapter_count - 1}节(共{target_chapter_count}个章节)的详细规划。
|
||||
|
||||
【项目信息】
|
||||
小说名称:{project.title}
|
||||
类型:{project.genre or '通用'}
|
||||
主题:{project.theme or '未设定'}
|
||||
叙事视角:{project.narrative_perspective or '第三人称'}
|
||||
|
||||
【世界观背景】
|
||||
时间背景:{project.world_time_period or '未设定'}
|
||||
地理位置:{project.world_location or '未设定'}
|
||||
氛围基调:{project.world_atmosphere or '未设定'}
|
||||
|
||||
【角色信息】
|
||||
{characters_info or '暂无角色'}
|
||||
|
||||
【当前大纲节点 - 展开对象】
|
||||
序号:第 {outline.order_index} 节
|
||||
标题:{outline.title}
|
||||
内容:{outline.content}
|
||||
|
||||
【上下文参考】
|
||||
{context_info}
|
||||
{previous_context}
|
||||
|
||||
【展开策略】
|
||||
{strategy_instruction}
|
||||
|
||||
【⚠️ 重要约束 - 必须严格遵守】
|
||||
1. **内容边界约束**:
|
||||
- ✅ 只能展开【当前大纲节点】中明确描述的内容
|
||||
- ❌ 绝对不能推进到后续大纲的内容(如果有【后一节】信息)
|
||||
- ❌ 不要让剧情快速推进,要深化而非跨越
|
||||
|
||||
2. **分批连续性约束**:
|
||||
- 这是第{start_index}-{start_index + target_chapter_count - 1}节,是整个展开的一部分
|
||||
- 必须与前面已生成的章节自然衔接
|
||||
- 从第{start_index}节开始编号(sub_index从{start_index}开始)
|
||||
- 继续深化当前大纲的内容,保持叙事连贯性
|
||||
|
||||
3. **展开原则**:
|
||||
- 将当前大纲的单一事件拆解为多个细节丰富的章节
|
||||
- 深入挖掘情感、心理、环境、对话等细节
|
||||
- 放慢叙事节奏,让读者充分体验当前阶段的剧情
|
||||
- 每个章节都应该是当前大纲内容的不同侧面或阶段
|
||||
|
||||
【任务要求】
|
||||
1. 深度分析该大纲的剧情容量和叙事节奏
|
||||
2. 识别关键剧情点、冲突点和情感转折点(仅限当前大纲范围内)
|
||||
3. 生成第{start_index}-{start_index + target_chapter_count - 1}节的章节规划,每章需包含:
|
||||
- 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}
|
||||
4. 确保章节间:
|
||||
- 与前面章节衔接自然流畅
|
||||
- 剧情递进合理(但不超出当前大纲边界)
|
||||
- 节奏张弛有度
|
||||
- 每章都有明确的叙事价值
|
||||
|
||||
【输出格式】
|
||||
请严格按照以下JSON数组格式输出,不要添加任何其他文字:
|
||||
[
|
||||
{{
|
||||
"sub_index": {start_index},
|
||||
"title": "章节标题",
|
||||
"plot_summary": "该章详细剧情摘要...",
|
||||
"key_events": ["关键事件1", "关键事件2", "关键事件3"],
|
||||
"character_focus": ["角色A", "角色B"],
|
||||
"emotional_tone": "情感基调",
|
||||
"narrative_goal": "叙事目标",
|
||||
"conflict_type": "冲突类型",
|
||||
"estimated_words": 3000{scene_field}
|
||||
}}
|
||||
]
|
||||
|
||||
请开始分析并生成第{start_index}-{start_index + target_chapter_count - 1}节的章节规划:
|
||||
"""
|
||||
return prompt
|
||||
|
||||
def _parse_expansion_response(
|
||||
self,
|
||||
ai_response: str,
|
||||
outline_id: str
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""解析AI的展开响应"""
|
||||
try:
|
||||
# 清理响应文本
|
||||
cleaned_text = ai_response.strip()
|
||||
if cleaned_text.startswith('```json'):
|
||||
cleaned_text = cleaned_text[7:]
|
||||
if cleaned_text.startswith('```'):
|
||||
cleaned_text = cleaned_text[3:]
|
||||
if cleaned_text.endswith('```'):
|
||||
cleaned_text = cleaned_text[:-3]
|
||||
cleaned_text = cleaned_text.strip()
|
||||
|
||||
# 解析JSON
|
||||
chapter_plans = json.loads(cleaned_text)
|
||||
|
||||
# 确保是列表
|
||||
if not isinstance(chapter_plans, list):
|
||||
chapter_plans = [chapter_plans]
|
||||
|
||||
# 为每个章节规划添加outline_id
|
||||
for plan in chapter_plans:
|
||||
plan["outline_id"] = outline_id
|
||||
|
||||
return chapter_plans
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"解析AI响应失败: {e}, 响应内容: {ai_response[:500]}")
|
||||
# 返回一个基础规划
|
||||
return [{
|
||||
"outline_id": outline_id,
|
||||
"sub_index": 1,
|
||||
"title": "AI解析失败的默认章节",
|
||||
"plot_summary": ai_response[:500],
|
||||
"key_events": ["解析失败"],
|
||||
"character_focus": [],
|
||||
"emotional_tone": "未知",
|
||||
"narrative_goal": "需要重新生成",
|
||||
"conflict_type": "未知",
|
||||
"estimated_words": 3000
|
||||
}]
|
||||
|
||||
|
||||
# 工厂函数
|
||||
def create_plot_expansion_service(ai_service: AIService) -> PlotExpansionService:
|
||||
"""创建剧情展开服务实例"""
|
||||
return PlotExpansionService(ai_service)
|
||||
@@ -789,6 +789,81 @@ class PromptService:
|
||||
1. 只返回纯JSON对象,不要有```json```这样的标记
|
||||
2. 文本中不要使用中文引号(""),改用【】或《》
|
||||
3. 不要有任何额外的文字说明"""
|
||||
|
||||
# 大纲展开为多章节的提示词
|
||||
OUTLINE_EXPANSION = """你是专业的小说情节架构师。请分析以下大纲节点,将其展开为 {target_chapters} 个章节的详细规划。
|
||||
|
||||
【项目信息】
|
||||
小说名称:{title}
|
||||
类型:{genre}
|
||||
主题:{theme}
|
||||
叙事视角:{narrative_perspective}
|
||||
|
||||
【世界观背景】
|
||||
时间背景:{time_period}
|
||||
地理位置:{location}
|
||||
氛围基调:{atmosphere}
|
||||
世界规则:{rules}
|
||||
|
||||
【角色信息】
|
||||
{characters_info}
|
||||
|
||||
【大纲节点】
|
||||
序号:第 {outline_order} 节
|
||||
标题:{outline_title}
|
||||
内容:{outline_content}
|
||||
|
||||
【上下文】
|
||||
{context_info}
|
||||
|
||||
【展开策略】
|
||||
{strategy_instruction}
|
||||
|
||||
【任务要求】
|
||||
1. 深度分析该大纲的剧情容量和叙事节奏
|
||||
2. 识别关键剧情点、冲突点和情感转折点
|
||||
3. 将大纲拆解为 {target_chapters} 个章节,每章需包含:
|
||||
- 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}
|
||||
4. 确保章节间:
|
||||
- 衔接自然流畅
|
||||
- 剧情递进合理
|
||||
- 节奏张弛有度
|
||||
- 每章都有明确的叙事价值
|
||||
|
||||
**重要格式要求:**
|
||||
1. 只返回纯JSON数组格式,不要包含任何markdown标记、代码块标记或其他说明文字
|
||||
2. 不要在JSON字符串值中使用中文引号(""''),请使用【】或《》
|
||||
3. 文本描述中的专有名词使用【】标记
|
||||
|
||||
请严格按照以下JSON数组格式输出:
|
||||
[
|
||||
{{
|
||||
"sub_index": 1,
|
||||
"title": "章节标题",
|
||||
"plot_summary": "该章详细剧情摘要(200-300字)...",
|
||||
"key_events": ["关键事件1", "关键事件2", "关键事件3"],
|
||||
"character_focus": ["角色A", "角色B"],
|
||||
"emotional_tone": "情感基调",
|
||||
"narrative_goal": "叙事目标",
|
||||
"conflict_type": "冲突类型",
|
||||
"estimated_words": 3000{scene_field}
|
||||
}}
|
||||
]
|
||||
|
||||
再次强调:
|
||||
1. 只返回纯JSON数组,不要有```json```这样的标记
|
||||
2. 数组中要包含{target_chapters}个章节对象
|
||||
3. 每个plot_summary必须是200-300字的详细描述
|
||||
4. 文本中不要使用中文引号(""),改用【】或《》"""
|
||||
|
||||
@staticmethod
|
||||
def format_prompt(template: str, **kwargs) -> str:
|
||||
@@ -1106,6 +1181,72 @@ class PromptService:
|
||||
project_context=project_context,
|
||||
user_input=user_input
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_outline_expansion_prompt(cls, title: str, genre: str, theme: str,
|
||||
narrative_perspective: str, time_period: str,
|
||||
location: str, atmosphere: str, rules: str,
|
||||
characters_info: str, outline_order: int,
|
||||
outline_title: str, outline_content: str,
|
||||
context_info: str, strategy: str = "balanced",
|
||||
target_chapters: int = 3,
|
||||
include_scenes: bool = False) -> str:
|
||||
"""
|
||||
获取大纲展开为多章节的提示词
|
||||
|
||||
Args:
|
||||
title: 小说名称
|
||||
genre: 类型
|
||||
theme: 主题
|
||||
narrative_perspective: 叙事视角
|
||||
time_period: 时间背景
|
||||
location: 地理位置
|
||||
atmosphere: 氛围基调
|
||||
rules: 世界规则
|
||||
characters_info: 角色信息
|
||||
outline_order: 大纲序号
|
||||
outline_title: 大纲标题
|
||||
outline_content: 大纲内容
|
||||
context_info: 上下文信息
|
||||
strategy: 展开策略 (balanced/climax/detail)
|
||||
target_chapters: 目标章节数
|
||||
include_scenes: 是否包含场景字段
|
||||
"""
|
||||
# 根据策略生成指导说明
|
||||
strategy_instructions = {
|
||||
"balanced": "采用均衡策略:将大纲内容平均分配到各章节,保持节奏均匀,每章剧情密度相当。",
|
||||
"climax": "采用高潮重点策略:识别大纲中的高潮部分,为其分配更多章节进行细致展开,其他部分适当精简。",
|
||||
"detail": "采用细节丰富策略:深挖大纲中的每个细节,为每个关键事件、情感转折都安排足够的叙事空间。"
|
||||
}
|
||||
strategy_instruction = strategy_instructions.get(strategy, strategy_instructions["balanced"])
|
||||
|
||||
# 场景相关的指令和字段
|
||||
scene_instruction = ""
|
||||
scene_field = ""
|
||||
if include_scenes:
|
||||
scene_instruction = "\n - scenes: 场景列表(2-4个具体场景描述)"
|
||||
scene_field = ',\n "scenes": ["场景1", "场景2"]'
|
||||
|
||||
return cls.format_prompt(
|
||||
cls.OUTLINE_EXPANSION,
|
||||
title=title,
|
||||
genre=genre,
|
||||
theme=theme,
|
||||
narrative_perspective=narrative_perspective,
|
||||
time_period=time_period,
|
||||
location=location,
|
||||
atmosphere=atmosphere,
|
||||
rules=rules,
|
||||
characters_info=characters_info,
|
||||
outline_order=outline_order,
|
||||
outline_title=outline_title,
|
||||
outline_content=outline_content,
|
||||
context_info=context_info,
|
||||
strategy_instruction=strategy_instruction,
|
||||
target_chapters=target_chapters,
|
||||
scene_instruction=scene_instruction,
|
||||
scene_field=scene_field
|
||||
)
|
||||
|
||||
|
||||
# 创建全局提示词服务实例
|
||||
|
||||
Reference in New Issue
Block a user