4145 lines
153 KiB
Python
4145 lines
153 KiB
Python
import os
|
||
import re
|
||
import json
|
||
import time
|
||
import random
|
||
import logging
|
||
import signal
|
||
import threading
|
||
from dataclasses import dataclass, asdict
|
||
from typing import Optional, Dict, Any, List, Tuple
|
||
from datetime import datetime
|
||
from pathlib import Path
|
||
|
||
import dashscope
|
||
from dashscope import Generation
|
||
|
||
# 导入新模块
|
||
try:
|
||
from NovelConfig import AdvancedNovelConfig, get_preset_config, list_available_presets, list_available_genres
|
||
ENHANCED_FEATURES = True
|
||
except ImportError:
|
||
ENHANCED_FEATURES = False
|
||
# logger还没有定义,先不记录日志
|
||
|
||
try:
|
||
from dotenv import load_dotenv
|
||
load_dotenv()
|
||
except ImportError:
|
||
pass
|
||
|
||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 现在可以记录增强功能的状态
|
||
if not ENHANCED_FEATURES:
|
||
logger.warning("增强功能模块未找到,使用基础功能")
|
||
|
||
API_KEY = os.getenv('DASHSCOPE_API_KEY', '') or os.getenv('QWEN_API_KEY', '')
|
||
if API_KEY:
|
||
dashscope.api_key = API_KEY
|
||
|
||
|
||
# 反同质化系统
|
||
ANTI_REPETITION_SYSTEM = {
|
||
"plot_patterns": {
|
||
"conflict_types": [
|
||
"内心挣扎型", "道德选择型", "能力极限型", "关系背叛型",
|
||
"身份揭秘型", "时间压力型", "资源争夺型", "信念冲突型"
|
||
],
|
||
"resolution_methods": [
|
||
"智慧解决", "牺牲换取", "意外转折", "合作共赢",
|
||
"代价妥协", "创新突破", "情感感化", "力量压制"
|
||
],
|
||
"story_structures": [
|
||
"线性推进", "双线并行", "倒叙揭秘", "多视角切换",
|
||
"时间跳跃", "梦境现实", "回忆穿插", "悬念设置"
|
||
]
|
||
},
|
||
"dialogue_variety": {
|
||
"emotional_expressions": {
|
||
"愤怒": ["怒火中烧", "咬牙切齿", "拳头紧握", "眼中喷火", "暴跳如雷"],
|
||
"喜悦": ["眉开眼笑", "心花怒放", "手舞足蹈", "笑逐颜开", "欣喜若狂"],
|
||
"悲伤": ["泪如雨下", "心如刀割", "黯然神伤", "痛不欲生", "肝肠寸断"],
|
||
"紧张": ["心跳加速", "手心出汗", "屏息凝神", "如坐针毡", "忐忑不安"],
|
||
"惊讶": ["目瞪口呆", "大吃一惊", "瞠目结舌", "惊愕不已", "震惊莫名"]
|
||
},
|
||
"speech_patterns": {
|
||
"直接型": ["直截了当", "开门见山", "毫不掩饰", "坦率表达"],
|
||
"委婉型": ["拐弯抹角", "欲言又止", "暗示暗喻", "含蓄表达"],
|
||
"幽默型": ["调侃玩笑", "自嘲解围", "机智应对", "轻松化解"],
|
||
"严肃型": ["郑重其事", "一本正经", "严厉警告", "庄重宣告"]
|
||
}
|
||
},
|
||
"scene_diversity": {
|
||
"settings": [
|
||
"古朴庭院", "繁华街市", "幽深洞穴", "高耸山峰", "神秘遗迹",
|
||
"宁静湖泊", "茂密森林", "荒芜沙漠", "冰雪世界", "云海仙境"
|
||
],
|
||
"atmospheres": [
|
||
"紧张刺激", "温馨和谐", "神秘诡异", "壮丽磅礴", "凄美哀伤",
|
||
"热血沸腾", "宁静致远", "危机四伏", "希望满怀", "绝望深渊"
|
||
],
|
||
"weather_moods": [
|
||
"阳光明媚", "细雨绵绵", "狂风暴雨", "雪花纷飞", "雾气朦胧",
|
||
"星光璀璨", "月色如水", "乌云密布", "彩虹初现", "晨曦微露"
|
||
]
|
||
},
|
||
"character_development": {
|
||
"growth_triggers": [
|
||
"重大失败", "意外发现", "他人牺牲", "价值冲突", "能力觉醒",
|
||
"关系变化", "环境压力", "内心顿悟", "外界刺激", "责任觉醒"
|
||
],
|
||
"personality_shifts": [
|
||
"从冲动到冷静", "从自私到无私", "从怯懦到勇敢", "从固执到灵活",
|
||
"从孤僻到开朗", "从悲观到乐观", "从依赖到独立", "从骄傲到谦逊"
|
||
]
|
||
},
|
||
"forbidden_repetitions": [
|
||
# 从n4发现的高频重复短语
|
||
"手牵手,迎着朝阳", "相视一笑", "心中充满了", "只要彼此相伴",
|
||
"没有什么是克服不了的", "新的征程", "书写属于他们的传奇",
|
||
"太棒了,你们干得真漂亮", "我们一起加油", "放心,我们",
|
||
"### 尾声:梦想与希望", "尾声:梦想与希望", "梦想与希望",
|
||
"时光荏苒", "他们的名字成为了", "无人不知、无人不晓",
|
||
"在修炼道路上不断前行", "得到了升华", "真正的力量",
|
||
"不仅仅来自于", "更来自于", "内心的坚韧和勇气",
|
||
|
||
# n4中严重重复的内容
|
||
"心中涌动着复杂的情绪", "眼神中闪烁着坚定", "深吸一口气",
|
||
"点了点头", "轻声说道", "坚定地说道", "沉默片刻",
|
||
"摸了摸下巴", "紧握长剑", "走上前来", "转过身",
|
||
"他想起父亲临终前的话语", "真正的能力,不是来自武力,而是内心的平和与坚定",
|
||
"这句话一直萦绕在他心头", "但如何找到这种力量,他却始终迷茫",
|
||
"真正的力量不仅仅是外在的能力,更是心灵的韧性与胆识",
|
||
"只有内心强大,才能面对一切困难", "心中仿佛有了一丝明悟",
|
||
"他决定,要找到属于自己的力量", "无论前方有多少困难",
|
||
"他都不会再感到迷茫", "因为他已经找到了属于自己的力量",
|
||
|
||
# 重复的对话模式
|
||
"邓华,你在想什么?", "我认为,真正的力量", "现在不是思考的时候",
|
||
"我们必须尽快解决眼前的问题", "好,我们上!", "你没事吧?",
|
||
"没事,我们必须坚持下去", "这股能量似乎有一个核心",
|
||
"我们必须找到并摧毁它", "在那里!", "成功了!",
|
||
|
||
# 重复的场景描写
|
||
"洞穴内潮湿阴冷", "石壁上布满了青苔", "空气中弥漫着一股霉味",
|
||
"这里太安静了", "我总感觉有什么东西在暗处窥视我们",
|
||
"这个洞穴似乎有些奇怪", "好像有一种无形的力量在引导我们",
|
||
"看,这些符号", "它们好像是某种古老的文字",
|
||
"这些符号我不认识,但它们似乎指向某个方向",
|
||
"我们跟着这些符号走吧", "也许它们能带我们找到",
|
||
|
||
# 重复的战斗描写
|
||
"大家小心,准备战斗!", "黑暗能量异常强大", "每一击都被反弹回来",
|
||
"邓华感到一阵剧痛", "身体不由自主地后退", "咬紧牙关,勉强站稳",
|
||
"努力集中精神", "闭上眼睛,感受着周围的气流和能量波动",
|
||
"脑海中闪过一道灵光", "挥舞长剑,全力一击", "发出一声震耳欲聋的咆哮",
|
||
|
||
# 重复的情感表达
|
||
"心中充满了平静与满足", "这场旅程不仅让他找到了力量",
|
||
"更让他找到了真正的自我", "未来还有更多的挑战等着他",
|
||
"但他已经准备好迎接这一切", "望向远方,嘴角露出一抹微笑",
|
||
"无论发生什么,我们都会一起面对", "是的,我们会一直并肩作战",
|
||
"交换眼神,默契一笑", "心中充满了坚定",
|
||
|
||
# 重复的章节标题
|
||
"### 幽深洞穴", "### 内心挣扎", "### 牺牲换取", "### 多视角切换",
|
||
"### 情感描写", "### 细节描写", "### 结尾", "### 时间跳跃",
|
||
"### 冲突与信念", "### 智慧的考验", "### 能力觉醒", "### 回到现实",
|
||
"### 未来的征程", "### 终章", "### 月色下的沙漠", "### 意外的发现",
|
||
|
||
# 角色重名问题
|
||
"另一个邓华", "这个邓华", "同名的角色", "两个邓华"
|
||
]
|
||
}
|
||
|
||
|
||
def _extract_high_frequency_phrases(text: str) -> List[str]:
|
||
"""提取文本中的高频短语,用于反重复检查"""
|
||
import re
|
||
from collections import Counter
|
||
|
||
# 提取2-6字的短语
|
||
phrases = []
|
||
|
||
# 使用正则表达式提取中文短语
|
||
chinese_text = re.sub(r'[^\u4e00-\u9fa5,。!?;:、""''()【】《》]', '', text)
|
||
|
||
# 提取2-6字的连续中文短语
|
||
for length in range(2, 7):
|
||
for i in range(len(chinese_text) - length + 1):
|
||
phrase = chinese_text[i:i+length]
|
||
if len(phrase) == length and not re.search(r'[,。!?;:、""''()【】《》]', phrase):
|
||
phrases.append(phrase)
|
||
|
||
# 统计频率并返回高频短语
|
||
phrase_counts = Counter(phrases)
|
||
high_freq_phrases = [phrase for phrase, count in phrase_counts.most_common(50) if count >= 2]
|
||
|
||
return high_freq_phrases
|
||
|
||
|
||
def detect_and_replace_repetitions(text: str) -> str:
|
||
"""超强化版重复检测和替换系统 - 彻底解决重复问题"""
|
||
import re
|
||
import random
|
||
|
||
logger.info("开始执行强化版反重复处理...")
|
||
|
||
# 严格的重复短语检测和替换
|
||
strict_replacements = {
|
||
# 最高频重复短语 - 必须替换
|
||
"轻轻颔首": ["微微点头", "表示赞同", "示意同意", "轻点头颅", "颔首示意"],
|
||
"心头泛起": ["内心涌现", "胸中升起", "心中浮现", "情感涌动", "心境变化"],
|
||
"胸中涌起": ["内心升腾", "心头浮现", "情感翻涌", "心中激荡", "胸怀激荡"],
|
||
"凝视着": ["注视着", "望向", "看向", "眺望", "凝望"],
|
||
"情感汹涌": ["内心激荡", "情绪翻腾", "心潮澎湃", "感情起伏", "心境波动"],
|
||
"眼神中闪烁着": ["目光中透出", "眼中流露出", "眸子里显现", "眼底浮现", "眼中显露"],
|
||
"深吸一口气": ["缓缓吸气", "长长吐息", "调整呼吸", "平复心情", "稳定情绪"],
|
||
"点了点头": ["轻轻颔首", "微微点头", "表示赞同", "示意同意", "颔首回应"],
|
||
"整理衣襟": ["拂拭衣袖", "理了理衣服", "整理着装", "抚平衣物", "拉直衣角"],
|
||
"站起身": ["起身", "站了起来", "从座位上起来", "离开座位", "起立"],
|
||
"走上前": ["迈步向前", "上前几步", "走近过来", "靠近过来", "向前走去"],
|
||
"转过身": ["回过身", "转身", "掉转身子", "回身", "转向"],
|
||
"抬起头": ["仰起头", "抬头", "昂首", "举目", "抬眸"],
|
||
"低下头": ["垂下头", "低头", "俯首", "低眉", "垂眸"],
|
||
"握紧拳头": ["攥紧双拳", "紧握双手", "拳头紧握", "手握成拳", "双拳紧攥"],
|
||
|
||
# 情感表达重复
|
||
"心中充满了": ["内心满怀", "胸中涌起", "心头泛起", "情感汹涌", "心境充盈"],
|
||
"眼中充满了": ["目光中满含", "眼底尽是", "眸中盛满", "眼神里透着", "眼中蕴含"],
|
||
"脸上露出": ["面上显现", "神情中带着", "表情流露出", "面容上浮现", "脸上显现"],
|
||
|
||
# 对话引导重复
|
||
"轻声说道": ["低声道", "柔声说", "细声道", "温和地说", "轻柔地说"],
|
||
"坚定地说道": ["断然道", "毅然说", "决然道", "斩钉截铁地说", "坚决地说"],
|
||
"沉默片刻": ["静默良久", "默然不语", "陷入沉思", "思索片刻", "短暂沉默"],
|
||
|
||
# 场景描写重复
|
||
"空气中弥漫着": ["空中飘散着", "四周充斥着", "周围弥散着", "气息中带着", "空间里充满"],
|
||
"阳光透过": ["光线穿过", "日光从", "阳光从", "光芒透过", "光线透射"],
|
||
"微风轻拂": ["清风徐来", "风儿轻抚", "微风拂过", "清风吹过", "风轻轻吹"],
|
||
|
||
# 心理活动重复
|
||
"心中暗想": ["暗自思忖", "心里琢磨", "默默思考", "内心盘算", "私下思量"],
|
||
"陷入沉思": ["深入思考", "沉浸思索", "专心思虑", "认真思考", "仔细思量"],
|
||
"若有所思": ["若有所悟", "似有所感", "若有所得", "似有领悟", "若有感触"],
|
||
|
||
# 动作描写重复
|
||
"缓缓走来": ["慢慢走近", "徐徐而来", "缓步走来", "慢步走来", "从容走来"],
|
||
"快步走向": ["急步走向", "匆忙走向", "快速走向", "疾步走向", "迅速走向"],
|
||
"紧紧握住": ["牢牢抓住", "紧紧抓着", "死死握着", "用力握住", "紧握不放"],
|
||
|
||
# 时间转换重复
|
||
"与此同时": ["同一时间", "就在此时", "此时此刻", "正在这时", "恰在此时"],
|
||
"就在这时": ["正在此刻", "恰在这时", "此时", "这一刻", "当时"],
|
||
"不久之后": ["过了一会", "片刻之后", "稍后", "随后", "接着"],
|
||
|
||
# 结尾表达重复
|
||
"望向远方": ["眺望远处", "看向远方", "凝望远处", "遥望远方", "目视远方"],
|
||
"嘴角露出一抹微笑": ["脸上浮现笑意", "嘴角上扬", "露出淡淡笑容", "面带微笑", "笑容浮现"],
|
||
"心中充满了坚定": ["内心无比坚定", "心志坚如磐石", "意志坚定不移", "决心无比坚定", "信念坚定不摇"]
|
||
}
|
||
|
||
# 统计替换次数
|
||
total_replacements = 0
|
||
|
||
# 执行严格替换
|
||
for original, alternatives in strict_replacements.items():
|
||
count = text.count(original)
|
||
if count > 1:
|
||
# 保留1个原文,其余全部替换
|
||
replace_times = count - 1
|
||
for i in range(replace_times):
|
||
replacement = alternatives[i % len(alternatives)] # 循环使用替换词
|
||
text = text.replace(original, replacement, 1)
|
||
total_replacements += 1
|
||
|
||
# 检测并删除重复的段落
|
||
paragraphs = [p.strip() for p in text.split('\n\n') if p.strip() and len(p.strip()) > 30]
|
||
paragraphs_to_remove = set()
|
||
|
||
for i in range(len(paragraphs)):
|
||
for j in range(i + 1, len(paragraphs)):
|
||
similarity = _calculate_similarity(paragraphs[i], paragraphs[j])
|
||
if similarity > 0.7: # 降低阈值,更严格检测
|
||
paragraphs_to_remove.add(j)
|
||
|
||
if paragraphs_to_remove:
|
||
filtered_paragraphs = [p for i, p in enumerate(paragraphs) if i not in paragraphs_to_remove]
|
||
text = '\n\n'.join(filtered_paragraphs)
|
||
logger.info(f"删除了 {len(paragraphs_to_remove)} 个重复段落")
|
||
|
||
# 检测重复的句子模式
|
||
sentences = re.split(r'[。!?]', text)
|
||
sentence_counts = {}
|
||
for sentence in sentences:
|
||
sentence = sentence.strip()
|
||
if len(sentence) > 10: # 只检查较长的句子
|
||
if sentence in sentence_counts:
|
||
sentence_counts[sentence] += 1
|
||
else:
|
||
sentence_counts[sentence] = 1
|
||
|
||
# 删除重复超过2次的句子
|
||
for sentence, count in sentence_counts.items():
|
||
if count > 2:
|
||
# 保留2个,删除其余的
|
||
for _ in range(count - 2):
|
||
text = text.replace(sentence, '', 1)
|
||
|
||
logger.info(f"反重复处理完成,共执行 {total_replacements} 次替换")
|
||
return text
|
||
|
||
|
||
def _calculate_similarity(text1: str, text2: str) -> float:
|
||
"""计算两个文本的相似度"""
|
||
if not text1 or not text2:
|
||
return 0.0
|
||
|
||
# 简单的字符级相似度计算
|
||
words1 = set(text1)
|
||
words2 = set(text2)
|
||
|
||
intersection = len(words1.intersection(words2))
|
||
union = len(words1.union(words2))
|
||
|
||
return intersection / union if union > 0 else 0.0
|
||
|
||
|
||
def generate_anti_repetition_guidance(genre: str, chapter_count: int) -> str:
|
||
"""生成强化版反同质化指导"""
|
||
import random
|
||
|
||
# 根据章节数选择不同的冲突类型和解决方式
|
||
conflict_type = random.choice(ANTI_REPETITION_SYSTEM["plot_patterns"]["conflict_types"])
|
||
resolution_method = random.choice(ANTI_REPETITION_SYSTEM["plot_patterns"]["resolution_methods"])
|
||
story_structure = random.choice(ANTI_REPETITION_SYSTEM["plot_patterns"]["story_structures"])
|
||
|
||
# 选择场景和氛围
|
||
setting = random.choice(ANTI_REPETITION_SYSTEM["scene_diversity"]["settings"])
|
||
atmosphere = random.choice(ANTI_REPETITION_SYSTEM["scene_diversity"]["atmospheres"])
|
||
weather = random.choice(ANTI_REPETITION_SYSTEM["scene_diversity"]["weather_moods"])
|
||
|
||
# 选择角色发展触发器
|
||
growth_trigger = random.choice(ANTI_REPETITION_SYSTEM["character_development"]["growth_triggers"])
|
||
|
||
forbidden_phrases = "、".join(ANTI_REPETITION_SYSTEM["forbidden_repetitions"])
|
||
|
||
return f"""
|
||
【强化版反同质化创作指导 - 第{chapter_count}轮】
|
||
|
||
## 🚫 严格禁止的重复内容(违反将重新生成):
|
||
|
||
### 禁用短语列表:
|
||
{forbidden_phrases}
|
||
|
||
### 禁用句式模式:
|
||
- "他想起父亲临终前的话语"(已出现多次)
|
||
- "真正的力量不仅仅是外在的能力,更是心灵的韧性与胆识"(完全重复)
|
||
- "只有内心强大,才能面对一切困难"(高频重复)
|
||
- "心中涌动着复杂的情绪"(过度使用)
|
||
- "眼神中闪烁着坚定"(重复表达)
|
||
- "深吸一口气"(动作重复)
|
||
- "点了点头"(动作重复)
|
||
- "摸了摸下巴"(动作重复)
|
||
|
||
### 禁用对话模式:
|
||
- "邓华,你在想什么?"(重复询问)
|
||
- "我认为,真正的力量..."(重复说教)
|
||
- "现在不是思考的时候"(重复催促)
|
||
- "我们必须..."(重复表态)
|
||
|
||
### 禁用情节模式:
|
||
- 反复进入洞穴探险
|
||
- 反复遇到黑暗能量
|
||
- 反复的内心挣扎独白
|
||
- 反复的哲理对话
|
||
- 反复的战斗→受伤→坚持模式
|
||
|
||
## ✅ 本轮创作要求:
|
||
|
||
### 1. 情节创新要求:
|
||
- **冲突类型**:{conflict_type} - 必须是全新的冲突,不能重复之前的模式
|
||
- **解决方式**:{resolution_method} - 避免总是用战斗或说教解决问题
|
||
- **叙事结构**:{story_structure} - 打破线性推进,尝试新的叙事方式
|
||
- **场景设定**:{setting},营造{atmosphere}的氛围,天气:{weather}
|
||
- **角色成长触发**:通过{growth_trigger}推动角色发展
|
||
|
||
### 2. 对话创新要求:
|
||
- 每个角色必须有独特的说话方式,不能千篇一律
|
||
- 对话要推进情节,不能是无意义的哲理讨论
|
||
- 避免重复的对话开头:"邓华,你..."
|
||
- 增加角色间的分歧和冲突,不要总是和谐一致
|
||
- 用对话展现角色性格差异,而非重复相同观点
|
||
|
||
### 3. 动作描写创新:
|
||
- 禁止使用:摸下巴、深吸气、点头、转身、走上前等重复动作
|
||
- 用新的动作:整理衣襟、凝视远方、轻抚剑柄、踱步思考等
|
||
- 每个角色要有专属的习惯动作
|
||
- 动作要符合当时的情绪和情境
|
||
|
||
### 4. 情感表达创新:
|
||
- 禁止"心中充满了"、"眼神中闪烁着"等空泛表达
|
||
- 用具体的生理反应:心跳加速、手心出汗、喉咙发紧等
|
||
- 用环境细节反映内心:注意到平时忽略的细节等
|
||
- 用行为变化体现情感:说话速度、音调变化等
|
||
|
||
### 5. 场景转换要求:
|
||
- 不能再进入洞穴、山峰等重复场景
|
||
- 尝试新环境:城镇街道、学院内部、商铺、客栈等
|
||
- 每个场景要有独特的氛围和细节
|
||
- 场景转换要自然,有明确的时间和空间逻辑
|
||
|
||
### 6. 角色互动创新:
|
||
- 增加角色间的真实冲突,不要总是和谐
|
||
- 让配角有自己的想法和立场,不要只是附和主角
|
||
- 增加误解、分歧、争论等真实的人际关系
|
||
- 让角色关系有发展和变化
|
||
|
||
## 🎯 质量控制要求:
|
||
|
||
### 内容完整性:
|
||
- 每1000字必须推进主线情节
|
||
- 不能原地打转或重复之前的内容
|
||
- 每个章节都要有新的信息或发展
|
||
- 避免为了凑字数而重复描写
|
||
|
||
### 逻辑一致性:
|
||
- 角色行为要符合已建立的性格
|
||
- 时间线要清晰,不能前后矛盾
|
||
- 世界观设定要保持一致
|
||
- 因果关系要合理
|
||
|
||
### 创新性要求:
|
||
- 每轮续写都要有新的元素
|
||
- 不能重复使用相同的情节模式
|
||
- 角色要有新的发现或成长
|
||
- 故事要向前推进,不能停滞
|
||
|
||
## ⚠️ 特别警告:
|
||
|
||
1. **绝对禁止重复结尾**:不能出现多个"尾声"、"终章"、"结语"
|
||
2. **严禁角色重名**:不能再出现"另一个邓华"这种混乱设定
|
||
3. **禁止重复哲理**:不要反复讨论"真正的力量是什么"
|
||
4. **避免套话对话**:每句对话都要有具体目的,不能是空洞的鼓励
|
||
5. **杜绝场景重复**:不能再写洞穴探险、山峰沉思等重复场景
|
||
|
||
## 📝 写作检查清单:
|
||
|
||
续写前必须确认:
|
||
- [ ] 本段内容是否与之前重复?
|
||
- [ ] 角色对话是否有新意?
|
||
- [ ] 情节是否向前推进?
|
||
- [ ] 是否避免了禁用短语?
|
||
- [ ] 场景是否有新鲜感?
|
||
- [ ] 角色行为是否符合逻辑?
|
||
|
||
**记住:宁可写得少一些,也不要重复之前的内容!**
|
||
"""
|
||
|
||
|
||
# 人物随机种子系统
|
||
CHARACTER_SEED_POOLS = {
|
||
"names": {
|
||
"male": [
|
||
# 传统名字
|
||
"林浩", "张明", "王强", "李伟", "陈杰", "刘涛", "赵磊", "孙宇", "周峰", "吴斌",
|
||
"马超", "朱辉", "胡军", "郭亮", "何东", "高飞", "梁凯", "宋阳", "邓华", "冯毅",
|
||
# 文艺名字
|
||
"苏墨", "顾言", "萧然", "慕容", "司徒", "欧阳", "上官", "诸葛", "轩辕", "夏侯",
|
||
"白羽", "墨轩", "云天", "风华", "星辰", "月影", "寒冰", "烈火", "雷霆", "山河",
|
||
# 现代名字
|
||
"陈逸飞", "林子轩", "张浩宇", "王梓豪", "李俊杰", "赵子涵", "孙嘉诚", "周志强",
|
||
"吴天宇", "马俊熙", "朱子墨", "胡浩然", "郭子轩", "何志远", "高梓轩", "梁浩宇",
|
||
# 古风名字
|
||
"慕容白", "司马青", "欧阳墨", "上官寒", "诸葛风", "轩辕天", "夏侯雪", "独孤求",
|
||
"令狐冲", "东方不败", "西门吹雪", "南宫飞", "北冥有鱼", "叶孤城", "花满楼", "陆小凤",
|
||
# 霸气名字
|
||
"龙傲天", "叶天帝", "萧炎", "林动", "石昊", "牧尘", "萧晨", "秦尘", "叶辰", "林凡",
|
||
"王腾", "姬昊", "帝天", "古青", "萧叶", "楚风", "洛尘", "凌霄", "战天", "破军",
|
||
# 温和名字
|
||
"温如玉", "君莫笑", "顾清欢", "沈清秋", "谢怜", "花城", "魏无羡", "蓝忘机", "江澄", "聂怀桑"
|
||
],
|
||
"female": [
|
||
# 传统名字
|
||
"李薇", "王雪", "张丽", "陈美", "刘芳", "赵敏", "孙娜", "周静", "吴琳", "马欣",
|
||
"朱莉", "胡蓉", "郭婷", "何洁", "高雅", "梁慧", "宋颖", "邓萍", "冯娟", "田甜",
|
||
# 古典美名
|
||
"苏若汐", "顾倾城", "萧雨涵", "慕容雪", "司徒月", "欧阳晴", "上官婉儿", "诸葛青青",
|
||
"轩辕紫", "夏侯霜", "白素贞", "墨兰", "云裳", "风铃", "星月", "影儿", "寒梅", "烈焰",
|
||
# 现代名字
|
||
"陈诗涵", "林雨萱", "张梓涵", "王欣怡", "李思雨", "赵雨桐", "孙梦琪", "周诗雅",
|
||
"吴雨欣", "马梓萱", "朱雨涵", "胡诗涵", "郭梦瑶", "何雨桐", "高梓涵", "梁诗雨",
|
||
# 仙气名字
|
||
"慕容仙", "司马灵", "欧阳雪", "上官月", "诸葛云", "轩辕霜", "夏侯冰", "独孤雁",
|
||
"令狐蓉", "东方玉", "西门雪", "南宫舞", "北冥雪", "叶轻舞", "花无缺", "陆雪琪",
|
||
# 霸气女名
|
||
"龙女帝", "叶青帝", "萧薰儿", "林青檀", "石紫嫣", "牧锦", "萧潇", "秦瑶", "叶倾仙", "林仙儿",
|
||
"王语嫣", "姬如雪", "帝女花", "古薰儿", "萧雪", "楚倾歌", "洛神", "凌雪", "战姬", "破晓",
|
||
# 温婉名字
|
||
"温柔", "君如意", "顾盼兮", "沈佳期", "谢安然", "花解语", "魏璎珞", "蓝湛", "江雪", "聂小倩"
|
||
]
|
||
},
|
||
"personalities": [
|
||
# 基础性格
|
||
"内向谨慎", "外向开朗", "理性冷静", "感性温暖", "倔强坚持", "灵活变通",
|
||
"完美主义", "随性自由", "责任感强", "创新冒险", "传统保守", "叛逆独立",
|
||
"乐观积极", "悲观现实", "敏感细腻", "粗犷豪爽", "温和包容", "激进直接",
|
||
# 复合性格
|
||
"外冷内热", "表面温和内心坚定", "看似散漫实则专注", "外表柔弱内心强大",
|
||
"表面冷漠实则关怀", "看似冲动实则深思", "外表严肃内心温柔", "表面随和内有原则",
|
||
"看似懒散实则敏锐", "外表高冷内心火热", "表面谦逊实则自信", "看似天真实则睿智",
|
||
# 特殊性格
|
||
"神秘莫测", "古灵精怪", "腹黑毒舌", "天然呆萌", "高冷禁欲", "温润如玉",
|
||
"霸道强势", "温柔体贴", "机智狡黠", "单纯善良", "成熟稳重", "活泼可爱",
|
||
"孤傲不群", "平易近人", "心机深沉", "直率真诚", "优雅高贵", "接地气亲民"
|
||
],
|
||
"backgrounds": [
|
||
# 家庭背景
|
||
"贫困家庭出身", "中产阶级家庭", "富裕家庭背景", "单亲家庭成长", "留守儿童经历",
|
||
"城市精英家庭", "农村朴实家庭", "书香门第", "商人家庭", "军人家庭",
|
||
"艺术家庭", "学者家庭", "普通工人家庭", "创业家庭", "移民家庭",
|
||
# 特殊背景
|
||
"孤儿院长大", "被收养的孩子", "家族企业继承人", "没落贵族后裔", "新兴富豪之子",
|
||
"外交官家庭", "医生世家", "教师家庭", "警察家庭", "消防员家庭",
|
||
"农民工家庭", "个体户家庭", "公务员家庭", "科研工作者家庭", "文艺工作者家庭",
|
||
# 复杂背景
|
||
"父母离异重组家庭", "隔代抚养", "寄养家庭", "国际化家庭", "军政世家",
|
||
"商政联姻家庭", "学术世家", "医学世家", "法律世家", "金融世家"
|
||
],
|
||
"occupations": {
|
||
"都市逆袭": [
|
||
# 基础职业
|
||
"程序员", "设计师", "销售", "会计", "文员", "服务员", "快递员", "保安", "清洁工", "司机",
|
||
# 新兴职业
|
||
"网络主播", "自媒体博主", "电商运营", "游戏策划", "UI设计师", "产品经理", "数据分析师",
|
||
"新媒体运营", "短视频制作", "网络作家", "独立开发者", "自由摄影师", "健身教练", "营养师",
|
||
# 传统职业
|
||
"厨师", "理发师", "修理工", "装修工", "外卖员", "网约车司机", "便利店员工", "超市收银员"
|
||
],
|
||
"甜宠先婚后爱": [
|
||
# 高端职业
|
||
"律师", "医生", "教师", "设计师", "编辑", "心理咨询师", "金融分析师", "市场营销", "人力资源", "建筑师",
|
||
# 创意职业
|
||
"服装设计师", "珠宝设计师", "室内设计师", "广告创意", "品牌策划", "公关经理", "活动策划",
|
||
"艺术策展人", "画廊经理", "时尚编辑", "美食评论家", "旅游博主", "生活方式博主",
|
||
# 商业职业
|
||
"投资顾问", "企业顾问", "管理培训师", "商业分析师", "风险评估师", "项目经理"
|
||
],
|
||
"悬疑推理": [
|
||
# 调查类
|
||
"侦探", "警察", "法医", "记者", "律师", "心理学家", "私家侦探", "档案管理员", "图书管理员", "研究员",
|
||
# 专业类
|
||
"犯罪心理学家", "司法鉴定师", "网络安全专家", "数据恢复专家", "监控分析师", "情报分析员",
|
||
"保险调查员", "企业内审", "反欺诈专家", "密码学专家", "行为分析师", "痕迹检验师"
|
||
],
|
||
"末世生存": [
|
||
# 军事类
|
||
"军人", "特种兵", "雇佣兵", "保镖", "安保人员", "武器专家", "战术专家", "军事顾问",
|
||
# 技能类
|
||
"医生", "工程师", "农民", "猎人", "厨师", "机械师", "电工", "护士", "教师", "建筑师",
|
||
# 生存类
|
||
"野外生存专家", "应急救援员", "消防员", "搜救队员", "登山向导", "户外教练", "求生专家"
|
||
],
|
||
"玄幻升级": [
|
||
# 修炼类
|
||
"修炼者", "炼药师", "阵法师", "剑修", "体修", "术法师", "驯兽师", "符箓师", "器修", "丹师",
|
||
# 特殊类
|
||
"占卜师", "风水师", "驱魔师", "召唤师", "元素法师", "幻术师", "毒师", "医师", "乐师", "棋师",
|
||
# 门派类
|
||
"宗门弟子", "散修", "魔修", "佛修", "道修", "儒修", "武修", "法修", "鬼修", "妖修"
|
||
]
|
||
},
|
||
"flaws": [
|
||
# 性格缺陷
|
||
"过度自尊", "缺乏自信", "控制欲强", "优柔寡断", "情绪化", "固执己见",
|
||
"完美主义", "拖延症", "社交恐惧", "信任困难", "冲动鲁莽", "过度敏感",
|
||
"逃避现实", "依赖他人", "嫉妒心强", "报复心理", "自我中心", "悲观消极",
|
||
# 行为缺陷
|
||
"说话不经大脑", "容易被激怒", "记仇", "小心眼", "爱面子", "死要面子活受罪",
|
||
"三分钟热度", "半途而废", "眼高手低", "好高骛远", "自以为是", "刚愎自用",
|
||
"多疑猜忌", "患得患失", "犹豫不决", "瞻前顾后", "畏首畏尾", "胆小怕事",
|
||
# 深层缺陷
|
||
"童年阴影", "被抛弃恐惧", "失败恐惧", "成功恐惧", "亲密关系恐惧", "承诺恐惧",
|
||
"被评判恐惧", "不完美恐惧", "被拒绝恐惧", "孤独恐惧", "死亡恐惧", "未知恐惧"
|
||
],
|
||
"desires": [
|
||
# 基本需求
|
||
"获得认可", "追求自由", "寻找真爱", "实现梦想", "保护家人", "证明自己",
|
||
"获得财富", "追求权力", "寻求安全", "渴望冒险", "追求知识", "获得尊重",
|
||
"寻找归属", "实现价值", "获得救赎", "追求完美", "寻求刺激", "获得平静",
|
||
# 深层渴望
|
||
"被无条件爱着", "找到人生意义", "超越父母期待", "打破家族诅咒", "改变世界",
|
||
"留下传奇", "获得永生", "掌控命运", "拯救他人", "复仇雪恨", "弥补遗憾",
|
||
"重新开始", "找回初心", "守护信念", "追求真理", "探索未知", "创造奇迹",
|
||
# 情感需求
|
||
"被理解", "被信任", "被需要", "被依赖", "被崇拜", "被原谅", "被接纳",
|
||
"被保护", "被珍惜", "被记住", "被感激", "被尊敬", "被欣赏", "被支持"
|
||
],
|
||
"genre_specific": {
|
||
"悬疑推理": {
|
||
"special_traits": [
|
||
"观察敏锐", "逻辑严密", "记忆超群", "直觉准确", "心理洞察", "细节控",
|
||
"推理天才", "破案如神", "洞察人心", "记忆宫殿", "逻辑怪物", "真相猎手",
|
||
"证据收集狂", "线索敏感", "疑点发现者", "谎言识别器", "动机分析师", "时间线专家"
|
||
],
|
||
"investigation_skills": [
|
||
"现场勘查", "证据分析", "心理分析", "逻辑推理", "信息整合", "人际交往",
|
||
"审讯技巧", "跟踪监视", "伪装潜入", "密码破译", "痕迹还原", "时间推算",
|
||
"动机分析", "关系梳理", "数据挖掘", "网络追踪", "心理画像", "行为预测"
|
||
]
|
||
},
|
||
"末世生存": {
|
||
"survival_skills": [
|
||
"野外求生", "医疗急救", "机械维修", "食物获取", "武器制作", "团队领导",
|
||
"资源搜集", "陷阱制作", "庇护所建造", "水源净化", "食物保存", "伤口处理",
|
||
"药物调配", "工具制作", "火种保存", "方向辨识", "天气预测", "危险预警"
|
||
],
|
||
"mental_traits": [
|
||
"意志坚强", "适应力强", "危机意识", "团队合作", "资源管理", "道德坚持",
|
||
"冷静理智", "果断决策", "牺牲精神", "保护欲强", "生存本能", "危机应对",
|
||
"压力承受", "情绪控制", "希望维持", "信念坚定", "责任担当", "人性坚守"
|
||
]
|
||
},
|
||
"玄幻升级": {
|
||
"cultivation_types": [
|
||
"剑道天才", "体质特殊", "悟性超群", "意志坚定", "机缘深厚", "血脉觉醒",
|
||
"天生道体", "先天灵根", "混沌体质", "不灭金身", "万法不侵", "天眼通",
|
||
"神识强大", "元神特异", "灵魂变异", "天赋异禀", "根骨奇佳", "慧根深厚"
|
||
],
|
||
"spiritual_roots": [
|
||
"金系", "木系", "水系", "火系", "土系", "雷系", "风系", "冰系", "暗系", "光系",
|
||
"混沌", "阴阳", "五行", "时间", "空间", "生命", "死亡", "毁灭", "创造", "轮回",
|
||
"因果", "命运", "虚无", "真实", "梦幻", "永恒", "瞬息", "无限", "有限", "超脱"
|
||
],
|
||
"special_abilities": [
|
||
"过目不忘", "举一反三", "触类旁通", "融会贯通", "无师自通", "天人合一",
|
||
"心有灵犀", "预知未来", "回溯过去", "洞察本质", "看破虚妄", "参透天机"
|
||
]
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
def generate_random_character_seed(character_type: str = "main", genre: str = "都市逆袭") -> Dict[str, Any]:
|
||
"""生成随机角色种子 - 增强版"""
|
||
seed = random.randint(1000, 9999)
|
||
random.seed(seed)
|
||
|
||
gender = random.choice(["male", "female"])
|
||
name = random.choice(CHARACTER_SEED_POOLS["names"][gender])
|
||
personality = random.choice(CHARACTER_SEED_POOLS["personalities"])
|
||
background = random.choice(CHARACTER_SEED_POOLS["backgrounds"])
|
||
|
||
# 根据题材选择职业
|
||
if genre in CHARACTER_SEED_POOLS["occupations"]:
|
||
occupation = random.choice(CHARACTER_SEED_POOLS["occupations"][genre])
|
||
else:
|
||
occupation = random.choice(CHARACTER_SEED_POOLS["occupations"]["都市逆袭"])
|
||
|
||
flaw = random.choice(CHARACTER_SEED_POOLS["flaws"])
|
||
desire = random.choice(CHARACTER_SEED_POOLS["desires"])
|
||
|
||
# 添加题材特定属性
|
||
special_attrs = {}
|
||
if genre in CHARACTER_SEED_POOLS["genre_specific"]:
|
||
genre_data = CHARACTER_SEED_POOLS["genre_specific"][genre]
|
||
for attr_type, attr_list in genre_data.items():
|
||
special_attrs[attr_type] = random.choice(attr_list)
|
||
|
||
# 生成角色关系倾向
|
||
relationship_style = random.choice([
|
||
"独来独往型", "团队合作型", "领导者型", "追随者型", "调解者型", "挑战者型",
|
||
"保护者型", "被保护型", "导师型", "学生型", "竞争者型", "支持者型"
|
||
])
|
||
|
||
# 生成说话风格
|
||
speech_patterns = [
|
||
"简洁直接", "温和委婉", "幽默风趣", "严肃正经", "文雅书面", "口语化",
|
||
"逻辑性强", "感性表达", "反问较多", "喜欢举例", "爱用比喻", "习惯沉默"
|
||
]
|
||
speech_style = random.choice(speech_patterns)
|
||
|
||
# 生成习惯动作
|
||
habit_actions = [
|
||
"思考时摸下巴", "紧张时咬唇", "兴奋时搓手", "生气时皱眉", "无聊时转笔",
|
||
"专注时眯眼", "困惑时挠头", "开心时笑眼", "担心时踱步", "放松时伸懒腰",
|
||
"思考时凝视远方", "紧张时整理衣襟", "习惯性轻抚剑柄", "喜欢双手抱胸"
|
||
]
|
||
habit_action = random.choice(habit_actions)
|
||
|
||
# 重置随机种子以避免影响其他随机生成
|
||
random.seed()
|
||
|
||
result = {
|
||
"seed": seed,
|
||
"name": name,
|
||
"gender": gender,
|
||
"personality": personality,
|
||
"background": background,
|
||
"occupation": occupation,
|
||
"flaw": flaw,
|
||
"desire": desire,
|
||
"character_type": character_type,
|
||
"genre": genre,
|
||
"relationship_style": relationship_style,
|
||
"speech_style": speech_style,
|
||
"habit_action": habit_action
|
||
}
|
||
|
||
# 添加特殊属性
|
||
result.update(special_attrs)
|
||
|
||
return result
|
||
|
||
|
||
def generate_character_team(genre: str, team_size: int = 4) -> List[Dict[str, Any]]:
|
||
"""生成一个角色团队,确保角色间有互补性和冲突性"""
|
||
team = []
|
||
used_names = set()
|
||
used_personalities = set()
|
||
|
||
# 生成主角
|
||
main_char = generate_random_character_seed("main", genre)
|
||
while main_char["name"] in used_names:
|
||
main_char = generate_random_character_seed("main", genre)
|
||
|
||
team.append(main_char)
|
||
used_names.add(main_char["name"])
|
||
used_personalities.add(main_char["personality"])
|
||
|
||
# 生成配角,确保多样性
|
||
for i in range(team_size - 1):
|
||
attempts = 0
|
||
while attempts < 20: # 最多尝试20次
|
||
char = generate_random_character_seed("supporting", genre)
|
||
|
||
# 确保名字不重复
|
||
if char["name"] in used_names:
|
||
attempts += 1
|
||
continue
|
||
|
||
# 尽量确保性格不重复(但不强制)
|
||
if len(used_personalities) < 10 and char["personality"] in used_personalities:
|
||
attempts += 1
|
||
continue
|
||
|
||
team.append(char)
|
||
used_names.add(char["name"])
|
||
used_personalities.add(char["personality"])
|
||
break
|
||
else:
|
||
# 如果20次都没找到合适的,就用最后一个生成的
|
||
team.append(char)
|
||
|
||
return team
|
||
|
||
|
||
@dataclass
|
||
class ViralNovelConfig:
|
||
genre: str
|
||
target_total_chars: int # 改为最小字数要求,不是硬性上限
|
||
model: str = "qwen-turbo"
|
||
temperature: float = 0.8
|
||
enable_logic_check: bool = True
|
||
max_logic_iterations: int = 3
|
||
enable_progress_save: bool = True
|
||
enable_content_filter: bool = True
|
||
max_retry_attempts: int = 5
|
||
# 新增:故事完整性控制
|
||
min_chars_before_ending: int = 15000 # 最少15000字才允许结尾
|
||
max_chars_limit: int = 50000 # 最大字数限制,防止无限生成
|
||
story_completeness_check: bool = True # 启用故事完整性检查
|
||
|
||
|
||
@dataclass
|
||
class GenerationProgress:
|
||
"""生成进度数据"""
|
||
project_dir: str
|
||
stage: str # "bible", "title", "story", "refine", "complete"
|
||
progress_percent: float
|
||
bible: Optional[Dict[str, Any]] = None
|
||
title: Optional[str] = None
|
||
story: Optional[str] = None
|
||
start_time: Optional[str] = None
|
||
last_update: Optional[str] = None
|
||
error_count: int = 0
|
||
|
||
|
||
GENRE_TEMPLATES: Dict[str, Dict[str, Any]] = {
|
||
"都市逆袭": {
|
||
"core": "底层主角通过智慧和机遇逐步崛起,每一步都有合理动机和代价",
|
||
"character_depth": {
|
||
"主角": "有明确过往创伤、性格缺陷、成长动机,不是完美人设",
|
||
"反派": "有自己的逻辑和立场,不是纯粹的恶人",
|
||
"配角": "各有独特性格和说话方式,不是工具人"
|
||
},
|
||
"plot_structure": {
|
||
"铺垫比例": "30%用于人物背景和世界观建立",
|
||
"冲突升级": "每个冲突都有前因后果,不突兀",
|
||
"情感线": "与主线紧密结合,不是装饰品"
|
||
},
|
||
"writing_focus": "重点描写人物内心活动、动机变化、情感冲突"
|
||
},
|
||
"甜宠先婚后爱": {
|
||
"core": "两个有血有肉的人从陌生到相知相爱的心理变化过程",
|
||
"character_depth": {
|
||
"女主": "有自己的事业追求、性格弱点、成长历程",
|
||
"男主": "不是霸总模板,有温柔也有脆弱的一面",
|
||
"配角": "闺蜜、同事、家人都有独特个性"
|
||
},
|
||
"plot_structure": {
|
||
"情感递进": "从排斥→好奇→心动→深爱,每一步都有具体事件触发",
|
||
"误会设置": "基于性格差异的合理误会,不是狗血剧情",
|
||
"甜蜜时刻": "细节描写,不是空洞的'很甜'"
|
||
},
|
||
"writing_focus": "重点描写微表情、小动作、内心独白、情感变化"
|
||
},
|
||
"悬疑推理": {
|
||
"core": "通过逻辑推理和线索收集,逐步揭开真相的智力较量过程",
|
||
"character_depth": {
|
||
"主角": "敏锐观察力、理性思维、有推理天赋但也有盲点",
|
||
"反派": "智商在线、动机复杂、手法精巧的对手",
|
||
"配角": "各有隐秘、可能是证人也可能是嫌疑人"
|
||
},
|
||
"plot_structure": {
|
||
"线索布局": "40%用于线索埋设和氛围营造",
|
||
"推理过程": "逻辑严密、步步深入、有误导也有突破",
|
||
"真相揭示": "意外但合理,所有线索都能串联"
|
||
},
|
||
"writing_focus": "重点描写细节观察、逻辑推理、心理博弈、氛围营造"
|
||
},
|
||
"末世生存": {
|
||
"core": "在绝望环境中求生,展现人性光辉与黑暗的生存史诗",
|
||
"character_depth": {
|
||
"主角": "有生存技能、道德底线、但也会面临艰难选择",
|
||
"幸存者": "各有背景、技能、价值观,形成复杂的生存群体",
|
||
"反派": "为生存不择手段、或被环境扭曲的人"
|
||
},
|
||
"plot_structure": {
|
||
"世界构建": "30%用于末世环境和生存规则建立",
|
||
"生存挑战": "资源争夺、环境威胁、人性考验层层递进",
|
||
"希望主线": "在绝望中寻找重建文明的可能"
|
||
},
|
||
"writing_focus": "重点描写生存技巧、环境威胁、人性冲突、团队协作"
|
||
},
|
||
"玄幻升级": {
|
||
"core": "在奇幻世界中通过修炼和历练不断突破自我的成长之路",
|
||
"character_depth": {
|
||
"主角": "有修炼天赋、坚定意志、但也有成长瓶颈和内心挣扎",
|
||
"师父": "智慧深邃、有自己的过往和秘密",
|
||
"同门": "各有特长、性格、竞争与友谊并存"
|
||
},
|
||
"plot_structure": {
|
||
"世界观建设": "35%用于修炼体系和世界规则建立",
|
||
"升级历程": "境界突破有合理过程,不是简单的数值增长",
|
||
"试炼考验": "每次升级都伴随着心性和实力的双重考验"
|
||
},
|
||
"writing_focus": "重点描写修炼感悟、战斗技巧、师徒情谊、境界突破"
|
||
}
|
||
}
|
||
|
||
|
||
def _get_advanced_writing_system() -> str:
|
||
"""获取高级写作系统 - 解决流水账、突兀、重复、矛盾、角色符号化问题"""
|
||
return """
|
||
【高级写作系统 - 解决常见AI写作问题】
|
||
|
||
## 一、反流水账写作法
|
||
|
||
### 1. 情感驱动叙事
|
||
❌ 流水账:他走进办公室,坐下来,打开电脑,开始工作。
|
||
✅ 情感驱动:李明推开办公室的门,熟悉的咖啡香味扑面而来。他深吸一口气,试图平复刚才在电梯里遇到前女友时的心跳。桌上那份离职申请书静静躺着,像是在嘲笑他的犹豫不决。
|
||
|
||
### 2. 冲突张力写法
|
||
- 每个场景都要有内在冲突(内心挣扎、两难选择、情感冲突)
|
||
- 每个对话都要有潜台词(表面说A,实际想B)
|
||
- 每个动作都要有情感色彩(不是机械动作,是情感表达)
|
||
|
||
### 3. 细节质感提升
|
||
- 用具体细节替代抽象描述
|
||
- 用感官体验替代概念表达
|
||
- 用对比反差制造印象深刻的画面
|
||
|
||
## 二、铺垫与因果系统
|
||
|
||
### 1. 三层铺垫法
|
||
**远程铺垫**(开篇埋下)→ **中程铺垫**(中段呼应)→ **近程铺垫**(爆发前预兆)
|
||
|
||
示例:
|
||
- 远程:开篇提到主角怕黑(童年创伤)
|
||
- 中程:几次在黑暗中的不适反应
|
||
- 近程:关键时刻必须进入黑暗空间,内心挣扎
|
||
- 爆发:克服恐惧,完成蜕变
|
||
|
||
### 2. 因果链条设计
|
||
每个重要情节都要有:
|
||
- **动机**:为什么这样做?
|
||
- **阻碍**:遇到什么困难?
|
||
- **选择**:如何决定?
|
||
- **后果**:产生什么影响?
|
||
- **反思**:角色如何成长?
|
||
|
||
### 3. 情节预告系统
|
||
- 用细节暗示即将发生的事
|
||
- 用角色的不安情绪预告危机
|
||
- 用环境描写烘托气氛变化
|
||
|
||
## 三、防重复系统(严格执行)
|
||
|
||
### 1. 词汇变化库(必须使用)
|
||
- **情感词汇轮换**:
|
||
- 愤怒:恼火→暴躁→怒不可遏→勃然大怒→怒火中烧
|
||
- 高兴:开心→欣喜→狂欢→兴奋不已→心花怒放
|
||
- 紧张:不安→焦虑→惶恐→心惊胆战→忐忑不安
|
||
- 悲伤:难过→沮丧→痛苦→心如刀割→悲痛欲绝
|
||
|
||
- **动作词汇轮换**:
|
||
- 走:踱步→疾行→拖着脚步→大步流星→蹑手蹑脚
|
||
- 看:凝视→瞥见→注视→扫视→凝望→窥视
|
||
- 说:低语→呢喃→高声→轻声→大声→细声
|
||
|
||
- **表情词汇轮换**:
|
||
- 笑:微笑→轻笑→大笑→苦笑→冷笑→狞笑
|
||
- 皱眉:蹙眉→紧锁眉头→眉头紧皱→愁眉不展
|
||
|
||
### 2. 句式变化法(强制要求)
|
||
- **禁止重复的句式**:
|
||
- 避免连续使用"他知道"、"他想"、"他觉得"
|
||
- 避免重复"...的时候"、"就在这时"、"与此同时"
|
||
- 避免重复"心中一沉"、"深吸一口气"、"点了点头"
|
||
|
||
- **句式轮换策略**:
|
||
- 长短句交替:长句描写,短句冲击
|
||
- 语气变化:陈述→疑问→感叹→反问
|
||
- 视角切换:第三人称→内心独白→环境描写
|
||
|
||
### 3. 情节螺旋上升(避免重复情节)
|
||
- 相似情况下角色反应的变化
|
||
- 同类冲突的不同层次处理
|
||
- 重复中的递进和深化
|
||
|
||
## 四、逻辑一致性系统
|
||
|
||
### 1. 角色行为逻辑
|
||
- 每个角色都有核心性格特征(3-5个关键词)
|
||
- 所有行为都要符合性格逻辑
|
||
- 性格改变必须有充分的事件触发
|
||
|
||
### 2. 世界观一致性
|
||
- 建立明确的世界规则
|
||
- 所有情节都在规则内发生
|
||
- 规则改变必须有合理解释
|
||
|
||
### 3. 时间线管理
|
||
- 明确每个事件的时间点
|
||
- 确保前后事件的逻辑顺序
|
||
- 避免时间矛盾
|
||
|
||
## 五、角色立体化系统
|
||
|
||
### 1. 角色三维构建法
|
||
**外在层**:外貌、职业、社会身份
|
||
**行为层**:说话方式、行为习惯、反应模式
|
||
**内核层**:价值观、恐惧、渴望、创伤
|
||
|
||
### 2. 角色差异化标签
|
||
每个角色必须有:
|
||
- **独特口头禅**:体现性格的说话习惯
|
||
- **专属动作**:紧张时的小动作、思考时的习惯
|
||
- **情感触发点**:什么最容易让他们情绪波动
|
||
- **核心矛盾**:内心最大的冲突是什么
|
||
|
||
### 3. 角色关系网络
|
||
- 每个角色与主角的关系都不同
|
||
- 角色之间也要有独立的关系线
|
||
- 通过关系冲突推动情节发展
|
||
|
||
## 六、情感共鸣写作法
|
||
|
||
### 1. 共同体验挖掘
|
||
- 找到读者都有过的情感体验
|
||
- 用具体场景唤起共鸣
|
||
- 避免过于特殊的经历
|
||
|
||
### 2. 情感层次递进
|
||
- 表面情感→深层情感→核心情感
|
||
- 从个人情感→关系情感→社会情感
|
||
- 从当下情感→过往情感→未来期待
|
||
|
||
### 3. 情感冲突设计
|
||
- 理智与情感的冲突
|
||
- 不同价值观的冲突
|
||
- 现实与理想的冲突
|
||
|
||
## 七、节奏控制系统
|
||
|
||
### 1. 张弛有度
|
||
- 高潮后必须有缓冲
|
||
- 平静中埋下不安因子
|
||
- 用小冲突串联大冲突
|
||
|
||
### 2. 信息投放节奏
|
||
- 关键信息分批透露
|
||
- 用悬念控制阅读节奏
|
||
- 在读者期待最高时给予满足
|
||
|
||
### 3. 情感波动曲线
|
||
- 设计情感的起伏变化
|
||
- 避免情感平线或过山车
|
||
- 让读者的心情跟着角色走
|
||
|
||
## 八、具体写作指令
|
||
|
||
### 开篇500字要求:
|
||
1. 建立主角的核心冲突(内心挣扎)
|
||
2. 展示独特的世界观细节
|
||
3. 埋下至少2个伏笔
|
||
4. 让读者对主角产生情感连接
|
||
5. 制造悬念或期待
|
||
|
||
### 每1000字检查点:
|
||
1. 是否推进了主线情节?
|
||
2. 是否深化了角色关系?
|
||
3. 是否有新的情感层次?
|
||
4. 是否避免了重复表达?
|
||
5. 是否保持了逻辑一致?
|
||
|
||
### 对话写作要求(必须严格遵守):
|
||
1. **对话占比要求**:对话内容必须占总文本的30%以上
|
||
2. **对话格式要求**:所有对话必须使用中文引号标记,格式如下:
|
||
- 直接对话:"角色说的话"
|
||
- 带说话人:"角色说的话,"他说道。
|
||
- 示例:"林浩,你在这儿干嘛呢?"李薇的声音中带着一丝调侃。
|
||
3. **角色语言差异化**:每个角色说话方式必须不同
|
||
- 主角:内敛、谨慎、有礼貌
|
||
- 导师型角色:睿智、引导性、温和
|
||
- 对手型角色:强势、直接、带攻击性
|
||
- 朋友型角色:轻松、支持性、亲近
|
||
4. **对话功能性**:
|
||
- 推进情节发展(不是闲聊)
|
||
- 展现角色性格和关系
|
||
- 传递重要信息
|
||
- 制造冲突和张力
|
||
5. **对话真实性**:
|
||
- 符合角色身份和教育背景
|
||
- 体现当时的情绪状态
|
||
- 有潜台词和言外之意
|
||
- 自然流畅,不生硬
|
||
|
||
### 强制对话场景:
|
||
- 每个重要情节转折都必须有对话
|
||
- 角色初次见面必须有对话
|
||
- 冲突场面必须有对话交锋
|
||
- 情感变化时必须有内心或外在对话
|
||
- 每1000字至少要有3-5段对话
|
||
|
||
### 场景转换要求:
|
||
1. 用情感状态连接场景
|
||
2. 通过环境变化体现内心变化
|
||
3. 避免突兀的跳跃
|
||
4. 保持叙事的连贯性
|
||
5. 用细节暗示时间流逝
|
||
"""
|
||
|
||
|
||
def _get_logic_consistency_system() -> str:
|
||
"""逻辑一致性检查和修复系统"""
|
||
return """
|
||
【逻辑一致性检查系统 - 确保故事逻辑严密】
|
||
|
||
## 一、角色行为一致性检查
|
||
|
||
### 1. 核心性格特征追踪
|
||
- 每个角色必须有3-5个核心性格关键词
|
||
- 所有行为、对话、反应都必须符合这些特征
|
||
- 性格改变必须有充分的事件触发和心理过程
|
||
|
||
### 2. 角色能力边界检查
|
||
- 角色的知识水平、技能范围要保持一致
|
||
- 不能突然掌握之前没有的能力
|
||
- 角色的社会地位、经济条件要前后一致
|
||
|
||
### 3. 角色关系动态追踪
|
||
- 角色之间的关系变化要有合理过程
|
||
- 情感变化要有具体的触发事件
|
||
- 对话语气要符合当前关系状态
|
||
|
||
## 二、时间线逻辑检查
|
||
|
||
### 1. 事件时序验证
|
||
- 所有事件按时间顺序排列
|
||
- 检查是否有时间倒流或跳跃
|
||
- 确保事件持续时间合理
|
||
|
||
### 2. 因果关系验证
|
||
- 每个结果都有明确的原因
|
||
- 原因发生在结果之前
|
||
- 因果链条完整无断裂
|
||
|
||
### 3. 角色行程合理性
|
||
- 角色移动时间要符合实际
|
||
- 不能同时出现在不同地点
|
||
- 角色的精力和时间分配要合理
|
||
|
||
## 三、世界观规则检查
|
||
|
||
### 1. 物理规则一致性
|
||
- 世界的基本物理法则要统一
|
||
- 特殊设定要前后呼应
|
||
- 不能违反已建立的世界规则
|
||
|
||
### 2. 社会规则一致性
|
||
- 社会制度、法律、习俗要统一
|
||
- 角色的社会行为要符合设定
|
||
- 经济、政治背景要保持一致
|
||
|
||
### 3. 技术水平一致性
|
||
- 科技发展水平要统一
|
||
- 不能出现超越时代的技术
|
||
- 信息传播方式要符合设定
|
||
|
||
## 四、情节逻辑检查
|
||
|
||
### 1. 动机合理性检查
|
||
- 每个角色的行为都有明确动机
|
||
- 动机要符合角色性格和处境
|
||
- 动机强度要与行为程度匹配
|
||
|
||
### 2. 冲突升级合理性
|
||
- 冲突升级要有合理的触发点
|
||
- 冲突强度要逐步递进
|
||
- 解决方案要符合角色能力
|
||
|
||
### 3. 转折点合理性
|
||
- 重大转折要有充分铺垫
|
||
- 转折的触发因素要合理
|
||
- 转折后的发展要符合逻辑
|
||
|
||
## 五、对话逻辑检查
|
||
|
||
### 1. 信息传递一致性
|
||
- 角色知道的信息要前后一致
|
||
- 不能突然知道不该知道的事
|
||
- 信息获取渠道要合理
|
||
|
||
### 2. 语言风格一致性
|
||
- 每个角色的说话方式要固定
|
||
- 教育背景影响语言水平
|
||
- 情绪状态影响说话方式
|
||
|
||
### 3. 对话内容合理性
|
||
- 对话要推进情节或展现性格
|
||
- 避免无意义的闲聊
|
||
- 对话长度要符合情境
|
||
|
||
## 六、逻辑检查执行流程
|
||
|
||
### 1. 分段检查(每1000字)
|
||
- 提取关键信息点
|
||
- 与前文进行对比验证
|
||
- 标记潜在的逻辑问题
|
||
|
||
### 2. 全文检查(完成后)
|
||
- 构建完整的角色档案
|
||
- 绘制时间线图谱
|
||
- 验证世界观一致性
|
||
|
||
### 3. 问题修复流程
|
||
- 识别逻辑矛盾点
|
||
- 分析矛盾原因
|
||
- 提供修复方案
|
||
- 重新生成问题段落
|
||
|
||
## 七、逻辑检查标准
|
||
|
||
### 严重逻辑错误(必须修复):
|
||
- 角色性格严重OOC
|
||
- 时间线明显错误
|
||
- 世界观规则冲突
|
||
- 因果关系断裂
|
||
|
||
### 轻微逻辑问题(建议修复):
|
||
- 细节前后不一致
|
||
- 角色反应略显突兀
|
||
- 信息传递不够清晰
|
||
- 动机解释不够充分
|
||
|
||
### 检查通过标准:
|
||
- 无严重逻辑错误
|
||
- 轻微问题少于3个
|
||
- 角色行为符合设定
|
||
- 情节发展合理自然
|
||
"""
|
||
|
||
|
||
def _get_enhanced_character_system() -> str:
|
||
"""增强版角色发展系统"""
|
||
return """
|
||
【增强版角色发展系统 - 打造真正立体的角色】
|
||
|
||
## 一、角色核心档案建立
|
||
|
||
### 1. 基础信息档案
|
||
```
|
||
角色姓名:[具体姓名]
|
||
年龄:[具体年龄]
|
||
职业:[具体职业及工作内容]
|
||
外貌特征:[3-5个显著特征]
|
||
家庭背景:[家庭结构、经济状况、教育背景]
|
||
```
|
||
|
||
### 2. 性格核心标签(必须具体化)
|
||
```
|
||
核心性格:[3个核心特征,如:敏感、倔强、善良]
|
||
性格缺陷:[1-2个明显缺陷,如:过度自尊、不信任他人]
|
||
隐藏特质:[1个不易察觉的特质,如:内心脆弱、渴望被理解]
|
||
价值观:[核心价值观,如:公平正义、家庭至上]
|
||
恐惧点:[最害怕的事物,如:被抛弃、失去控制]
|
||
```
|
||
|
||
### 3. 行为模式档案
|
||
```
|
||
说话习惯:[口头禅、语气特点、用词偏好]
|
||
思考方式:[理性型/感性型、决策模式]
|
||
压力反应:[遇到困难时的第一反应]
|
||
情感表达:[如何表达喜怒哀乐]
|
||
人际模式:[与不同类型人的相处方式]
|
||
```
|
||
|
||
## 二、角色成长轨迹设计
|
||
|
||
### 1. 过往经历塑造
|
||
```
|
||
童年关键事件:[影响性格形成的重要事件]
|
||
青少年转折:[价值观形成的关键时期]
|
||
成年重要经历:[影响现在行为的重要经历]
|
||
创伤事件:[造成心理阴影的事件]
|
||
成功经验:[建立自信的成功经历]
|
||
```
|
||
|
||
### 2. 当前状态分析
|
||
```
|
||
现在的困境:[面临的主要问题]
|
||
内心冲突:[价值观冲突、情感冲突]
|
||
未实现的愿望:[内心深处的渴望]
|
||
人际关系状况:[与重要他人的关系]
|
||
自我认知:[对自己的看法]
|
||
```
|
||
|
||
### 3. 成长路径规划
|
||
```
|
||
成长起点:[故事开始时的状态]
|
||
成长触发:[促使改变的关键事件]
|
||
成长阻碍:[阻止改变的内外因素]
|
||
成长转折:[突破的关键时刻]
|
||
成长结果:[最终达到的状态]
|
||
```
|
||
|
||
## 三、角色差异化系统
|
||
|
||
### 1. 语言个性化标签
|
||
```
|
||
知识分子型:
|
||
- 用词:准确、书面化
|
||
- 句式:复杂、逻辑性强
|
||
- 特点:喜欢引用、分析
|
||
|
||
平民百姓型:
|
||
- 用词:口语化、直接
|
||
- 句式:简单、生活化
|
||
- 特点:用比喻、俗语
|
||
|
||
年轻活泼型:
|
||
- 用词:网络用语、新潮
|
||
- 句式:短句、语气词多
|
||
- 特点:表达活泼、情绪化
|
||
|
||
商务精英型:
|
||
- 用词:专业、效率导向
|
||
- 句式:简洁、目标明确
|
||
- 特点:数字化、结果导向
|
||
```
|
||
|
||
### 2. 情绪表达差异化
|
||
```
|
||
内向型角色:
|
||
- 情绪内敛,通过细节表达
|
||
- 多用内心独白
|
||
- 行为含蓄,眼神、微表情丰富
|
||
|
||
外向型角色:
|
||
- 情绪外放,直接表达
|
||
- 多用肢体语言
|
||
- 行为夸张,声音、动作明显
|
||
|
||
理性型角色:
|
||
- 用逻辑分析情感
|
||
- 控制情绪表达
|
||
- 通过行为体现理性思考
|
||
|
||
感性型角色:
|
||
- 情感丰富,表达直接
|
||
- 容易情绪波动
|
||
- 通过感受描述事物
|
||
```
|
||
|
||
## 四、角色关系动态系统
|
||
|
||
### 1. 关系发展阶段
|
||
```
|
||
初识阶段:
|
||
- 第一印象形成
|
||
- 基于外在判断
|
||
- 保持距离和礼貌
|
||
|
||
了解阶段:
|
||
- 发现共同点和差异
|
||
- 开始深入交流
|
||
- 建立初步信任
|
||
|
||
深入阶段:
|
||
- 分享内心想法
|
||
- 产生情感连接
|
||
- 关系质量提升
|
||
|
||
冲突阶段:
|
||
- 价值观差异显现
|
||
- 利益冲突出现
|
||
- 关系面临考验
|
||
|
||
和解/决裂阶段:
|
||
- 选择妥协或坚持
|
||
- 关系重新定义
|
||
- 达到新的平衡
|
||
```
|
||
|
||
### 2. 关系影响因素
|
||
```
|
||
性格匹配度:
|
||
- 互补型:性格互补,相互吸引
|
||
- 相似型:价值观相近,容易理解
|
||
- 冲突型:差异过大,容易摩擦
|
||
|
||
利益关系:
|
||
- 合作关系:共同目标,互相支持
|
||
- 竞争关系:利益冲突,相互制约
|
||
- 依赖关系:一方需要另一方
|
||
|
||
情感基础:
|
||
- 亲情:血缘关系,天然纽带
|
||
- 友情:选择关系,基于认同
|
||
- 爱情:情感关系,基于吸引
|
||
```
|
||
|
||
## 五、角色一致性维护
|
||
|
||
### 1. 行为一致性检查清单
|
||
- [ ] 角色反应符合性格设定
|
||
- [ ] 对话风格保持一致
|
||
- [ ] 价值观体现在行为中
|
||
- [ ] 能力水平前后一致
|
||
- [ ] 知识背景没有矛盾
|
||
|
||
### 2. 成长合理性检查清单
|
||
- [ ] 性格改变有充分触发
|
||
- [ ] 改变过程符合心理规律
|
||
- [ ] 保留核心性格特征
|
||
- [ ] 成长速度合理
|
||
- [ ] 新行为有学习过程
|
||
|
||
### 3. 关系发展检查清单
|
||
- [ ] 关系变化有具体事件
|
||
- [ ] 情感发展符合逻辑
|
||
- [ ] 对话体现关系状态
|
||
- [ ] 行为符合关系定位
|
||
- [ ] 冲突和和解都有原因
|
||
"""
|
||
|
||
|
||
def _get_enhanced_plot_system() -> str:
|
||
"""增强版情节发展系统"""
|
||
return """
|
||
【增强版情节发展系统 - 构建引人入胜的故事】
|
||
|
||
## 一、情节结构优化
|
||
|
||
### 1. 开篇吸引力公式
|
||
```
|
||
开篇必备要素(前500字):
|
||
1. 冲突预告:暗示即将到来的主要冲突
|
||
2. 角色困境:展示主角面临的具体问题
|
||
3. 情感钩子:让读者对角色产生情感连接
|
||
4. 悬念设置:提出让读者好奇的问题
|
||
5. 世界观展示:通过细节展现故事背景
|
||
```
|
||
|
||
### 2. 中段推进策略
|
||
```
|
||
每章节必备:
|
||
- 新信息:推进主线或揭示背景
|
||
- 新冲突:增加故事张力
|
||
- 角色发展:展现角色成长或变化
|
||
- 关系变化:推进人物关系发展
|
||
- 悬念延续:保持读者阅读兴趣
|
||
```
|
||
|
||
### 3. 高潮设计原则
|
||
```
|
||
高潮构成要素:
|
||
- 所有矛盾集中爆发
|
||
- 主角面临最大考验
|
||
- 情感达到最高点
|
||
- 价值观接受终极检验
|
||
- 为结局做好铺垫
|
||
```
|
||
|
||
## 二、冲突层次设计
|
||
|
||
### 1. 外在冲突(表层)
|
||
```
|
||
人与人的冲突:
|
||
- 利益冲突:资源争夺、地位竞争
|
||
- 价值观冲突:理念差异、立场对立
|
||
- 情感冲突:爱恨情仇、背叛伤害
|
||
|
||
人与环境的冲突:
|
||
- 自然环境:天灾、恶劣条件
|
||
- 社会环境:制度限制、社会压力
|
||
- 时代背景:历史变迁、技术冲击
|
||
```
|
||
|
||
### 2. 内在冲突(深层)
|
||
```
|
||
价值观冲突:
|
||
- 理想与现实的冲突
|
||
- 个人利益与集体利益
|
||
- 传统观念与新思想
|
||
|
||
情感冲突:
|
||
- 爱与恨的纠缠
|
||
- 理智与情感的对立
|
||
- 责任与欲望的冲突
|
||
|
||
身份冲突:
|
||
- 真实自我与社会期待
|
||
- 过去身份与现在角色
|
||
- 多重身份的矛盾
|
||
```
|
||
|
||
### 3. 冲突升级路径
|
||
```
|
||
第一层:日常摩擦
|
||
- 小误会、小分歧
|
||
- 轻微的不满情绪
|
||
- 可以轻易化解
|
||
|
||
第二层:明显对立
|
||
- 立场分歧明确
|
||
- 情绪开始激化
|
||
- 需要努力才能和解
|
||
|
||
第三层:激烈冲突
|
||
- 矛盾公开化
|
||
- 情绪失控
|
||
- 关系面临破裂
|
||
|
||
第四层:不可调和
|
||
- 价值观根本对立
|
||
- 伤害已经造成
|
||
- 只能选择一方
|
||
```
|
||
|
||
## 三、铺垫技巧升级
|
||
|
||
### 1. 多层次铺垫系统
|
||
```
|
||
远程铺垫(开篇-中段):
|
||
- 在日常对话中埋下关键信息
|
||
- 通过角色回忆暗示过往
|
||
- 用环境描写预示未来
|
||
|
||
中程铺垫(中段-高潮前):
|
||
- 通过配角行为暗示变化
|
||
- 用角色情绪变化预告转折
|
||
- 通过细节描写营造氛围
|
||
|
||
近程铺垫(高潮前):
|
||
- 角色内心不安加剧
|
||
- 环境细节变化明显
|
||
- 对话中透露关键信息
|
||
```
|
||
|
||
### 2. 伏笔回收技巧
|
||
```
|
||
信息伏笔:
|
||
- 开篇提到的细节在关键时刻发挥作用
|
||
- 看似无关的对话包含重要信息
|
||
- 角色的无意行为暗示真相
|
||
|
||
情感伏笔:
|
||
- 角色的情感变化有迹可循
|
||
- 关系发展的转折有预兆
|
||
- 内心冲突的爆发有征兆
|
||
|
||
物品伏笔:
|
||
- 重要物品多次出现
|
||
- 看似普通的物品有特殊意义
|
||
- 物品的出现推动情节发展
|
||
```
|
||
|
||
## 四、节奏控制系统
|
||
|
||
### 1. 张弛有度原则
|
||
```
|
||
紧张节奏段落:
|
||
- 冲突激化时刻
|
||
- 悬念揭晓前夕
|
||
- 角色面临选择
|
||
- 危机处理过程
|
||
|
||
缓和节奏段落:
|
||
- 冲突后的反思
|
||
- 角色情感交流
|
||
- 日常生活描写
|
||
- 背景信息补充
|
||
```
|
||
|
||
### 2. 信息投放节奏
|
||
```
|
||
信息投放策略:
|
||
- 关键信息分批透露
|
||
- 在读者好奇心最强时给出答案
|
||
- 用新信息引发新疑问
|
||
- 保持信息的新鲜感
|
||
```
|
||
|
||
## 五、情节逻辑检查
|
||
|
||
### 1. 因果关系验证
|
||
```
|
||
每个重要情节点检查:
|
||
- 这个结果的原因是什么?
|
||
- 原因是否充分?
|
||
- 时间顺序是否正确?
|
||
- 是否有其他可能的结果?
|
||
- 角色的反应是否合理?
|
||
```
|
||
|
||
### 2. 动机合理性检查
|
||
```
|
||
角色行为动机检查:
|
||
- 动机是否明确?
|
||
- 动机是否符合角色性格?
|
||
- 动机强度是否匹配行为?
|
||
- 是否有更简单的解决方案?
|
||
- 其他角色的反应是否合理?
|
||
```
|
||
|
||
### 3. 转折点合理性
|
||
```
|
||
重大转折检查:
|
||
- 转折是否有充分铺垫?
|
||
- 触发因素是否合理?
|
||
- 角色反应是否符合性格?
|
||
- 转折后的发展是否自然?
|
||
- 是否推进了主线发展?
|
||
```
|
||
"""
|
||
"""角色发展系统"""
|
||
return """
|
||
【角色发展系统 - 让每个角色都活起来】
|
||
|
||
## 主角塑造公式
|
||
|
||
### 1. 核心设定
|
||
- **致命弱点**:性格上的最大缺陷(如:过度自尊、不信任他人、逃避责任)
|
||
- **内心渴望**:最深层的需求(如:被认可、找到归属、证明自己)
|
||
- **外在目标**:表面上想要达成的事(如:升职、复仇、找到真爱)
|
||
- **成长弧线**:从缺陷到成长的完整路径
|
||
|
||
### 2. 背景故事模板
|
||
- **童年创伤**:塑造性格的关键事件
|
||
- **重要关系**:影响人生观的重要人物
|
||
- **转折时刻**:改变人生轨迹的事件
|
||
- **未完成的事**:内心的遗憾或执念
|
||
|
||
### 3. 行为模式设计
|
||
- **压力反应**:遇到困难时的第一反应
|
||
- **情感表达**:如何表达喜怒哀乐
|
||
- **决策方式**:理性型还是感性型
|
||
- **人际模式**:如何与不同类型的人相处
|
||
|
||
## 配角立体化方案
|
||
|
||
### 1. 功能性角色的人格化
|
||
**导师型角色**:
|
||
- 不是完美的智者,有自己的局限性
|
||
- 有过失败的经历,所以更珍惜成功
|
||
- 对主角的指导带有个人色彩
|
||
|
||
**对手型角色**:
|
||
- 有合理的动机和立场
|
||
- 在某些方面比主角更优秀
|
||
- 有自己的成长和变化
|
||
|
||
**朋友型角色**:
|
||
- 有独立的人生目标
|
||
- 与主角的友谊有起伏
|
||
- 有自己的性格特点和说话方式
|
||
|
||
### 2. 角色关系动态化
|
||
- 关系会随着情节发展而变化
|
||
- 每个角色都有自己的成长线
|
||
- 角色之间的冲突和和解都有原因
|
||
|
||
## 对话个性化系统
|
||
|
||
### 1. 语言习惯设计
|
||
**知识分子型**:用词准确,逻辑清晰,偶尔引用典故
|
||
**平民型**:口语化,直接,用比喻和俗语
|
||
**年轻人型**:网络用语,语气词多,表达活泼
|
||
**商人型**:实用主义,喜欢用数字和比较
|
||
|
||
### 2. 情绪表达差异
|
||
**内向型**:情绪内敛,通过细节表达
|
||
**外向型**:情绪外放,直接表达感受
|
||
**理性型**:用逻辑分析情感
|
||
**感性型**:用感受描述事物
|
||
|
||
### 3. 潜台词设计
|
||
- 表面说的话 vs 内心想的话
|
||
- 通过语气、停顿、用词暗示真实想法
|
||
- 让读者能读出言外之意
|
||
"""
|
||
|
||
|
||
def _get_emotional_resonance_system() -> str:
|
||
"""情感共鸣系统 - 让读者产生强烈代入感"""
|
||
return """
|
||
【情感共鸣系统 - 触动人心的写作技巧】
|
||
|
||
## 一、普遍情感体验挖掘
|
||
|
||
### 1. 核心人性情感
|
||
```
|
||
恐惧类情感:
|
||
- 被抛弃的恐惧:害怕失去重要的人
|
||
- 失败的恐惧:担心达不到期望
|
||
- 未知的恐惧:对不确定未来的焦虑
|
||
- 被误解的恐惧:担心他人不理解自己
|
||
|
||
渴望类情感:
|
||
- 被认可的渴望:希望得到他人肯定
|
||
- 归属感的渴望:想要找到自己的位置
|
||
- 成长的渴望:希望变得更好
|
||
- 爱与被爱的渴望:情感连接的需求
|
||
|
||
挫折类情感:
|
||
- 努力无果的挫败:付出得不到回报
|
||
- 选择困难的痛苦:两难境地的煎熬
|
||
- 时间流逝的无奈:错过机会的遗憾
|
||
- 能力不足的焦虑:想做却做不到的痛苦
|
||
```
|
||
|
||
### 2. 生活场景共鸣点
|
||
```
|
||
职场共鸣:
|
||
- 第一天上班的紧张
|
||
- 被领导批评的委屈
|
||
- 同事关系的复杂
|
||
- 升职加薪的期待
|
||
|
||
情感共鸣:
|
||
- 暗恋时的小心翼翼
|
||
- 分手时的痛苦挣扎
|
||
- 友情背叛的失望
|
||
- 家人关爱的温暖
|
||
|
||
成长共鸣:
|
||
- 离开家乡的不舍
|
||
- 独自面对困难的勇气
|
||
- 犯错后的自责
|
||
- 突破自我的成就感
|
||
```
|
||
|
||
## 二、细节化情感表达
|
||
|
||
### 1. 微表情描写
|
||
```
|
||
紧张时:
|
||
- 不自觉地咬下唇
|
||
- 手指轻敲桌面
|
||
- 眼神闪躲不敢直视
|
||
- 声音微微颤抖
|
||
|
||
开心时:
|
||
- 眼角不自觉上扬
|
||
- 步伐轻快有力
|
||
- 忍不住哼小曲
|
||
- 整个人都在发光
|
||
|
||
难过时:
|
||
- 肩膀微微下垂
|
||
- 眼中蒙上一层雾气
|
||
- 声音变得很轻很轻
|
||
- 习惯性地抱住自己
|
||
```
|
||
|
||
### 2. 内心独白技巧
|
||
```
|
||
真实的内心声音:
|
||
- "为什么我总是这样?"(自我质疑)
|
||
- "如果当时我..."(后悔反思)
|
||
- "也许他是对的"(动摇犹豫)
|
||
- "我一定要证明给他们看"(决心坚定)
|
||
|
||
情感层次递进:
|
||
表面想法 → 深层担忧 → 核心恐惧
|
||
"我不在乎" → "其实我很在意" → "我害怕被拒绝"
|
||
```
|
||
|
||
### 3. 感官细节运用
|
||
```
|
||
视觉细节:
|
||
- 阳光透过窗帘洒在地板上的斑驳光影
|
||
- 她眼中闪烁的泪光像星星一样
|
||
- 他紧握的拳头上青筋暴起
|
||
|
||
听觉细节:
|
||
- 心跳声在安静的房间里格外清晰
|
||
- 远处传来的音乐声若有若无
|
||
- 她轻柔的呼吸声让人安心
|
||
|
||
触觉细节:
|
||
- 冰冷的门把手让人瞬间清醒
|
||
- 温暖的拥抱驱散了所有寒意
|
||
- 粗糙的手掌传递着坚定的力量
|
||
```
|
||
|
||
## 三、情感冲突设计
|
||
|
||
### 1. 内心矛盾冲突
|
||
```
|
||
理智与情感:
|
||
- 知道应该放手,但舍不得
|
||
- 明白这样不对,但控制不住
|
||
- 理性分析很清楚,感情上接受不了
|
||
|
||
责任与欲望:
|
||
- 想要追求梦想,但不能抛下家庭
|
||
- 渴望自由,但承担着责任
|
||
- 个人幸福与他人期待的冲突
|
||
|
||
过去与现在:
|
||
- 想要忘记过去,但阴影挥之不去
|
||
- 怀念过去,但必须面对现实
|
||
- 过去的选择影响现在的生活
|
||
```
|
||
|
||
### 2. 关系情感冲突
|
||
```
|
||
爱恨交织:
|
||
- 爱一个人,但被他伤害
|
||
- 恨一个人,但又离不开他
|
||
- 想要报复,但又心软
|
||
|
||
期待与失望:
|
||
- 对某人抱有期待,但总是失望
|
||
- 想要被理解,但总是被误解
|
||
- 努力付出,但得不到回应
|
||
|
||
依赖与独立:
|
||
- 想要独立,但习惯了依赖
|
||
- 需要帮助,但不愿意承认
|
||
- 保护与被保护的角色转换
|
||
```
|
||
|
||
## 四、共鸣触发技巧
|
||
|
||
### 1. 对比反差法
|
||
```
|
||
强烈对比:
|
||
- 表面坚强 vs 内心脆弱
|
||
- 外表光鲜 vs 内心孤独
|
||
- 众人羡慕 vs 自己痛苦
|
||
|
||
时间对比:
|
||
- 过去的天真 vs 现在的成熟
|
||
- 曾经的梦想 vs 现实的妥协
|
||
- 当初的承诺 vs 如今的改变
|
||
```
|
||
|
||
### 2. 细节共鸣法
|
||
```
|
||
生活化细节:
|
||
- 深夜一个人吃泡面的孤独
|
||
- 收到工资时的小确幸
|
||
- 被人理解时的感动
|
||
- 犯错时的羞愧难当
|
||
|
||
情感化细节:
|
||
- 想哭却哭不出来的憋屈
|
||
- 突然想起某个人的瞬间
|
||
- 听到熟悉歌曲时的回忆涌现
|
||
- 一个人走夜路时的恐惧
|
||
```
|
||
|
||
### 3. 成长共鸣法
|
||
```
|
||
成长痛点:
|
||
- 第一次离开父母的不安
|
||
- 意识到自己长大的瞬间
|
||
- 发现父母也会老的震撼
|
||
- 承担责任时的重量感
|
||
|
||
蜕变时刻:
|
||
- 从依赖到独立的转变
|
||
- 从天真到成熟的过程
|
||
- 从逃避到面对的勇气
|
||
- 从自卑到自信的建立
|
||
```
|
||
|
||
## 五、情感表达层次
|
||
|
||
### 1. 表层情感(直接表达)
|
||
- 直接的情绪词汇:开心、难过、愤怒
|
||
- 明显的行为表现:哭泣、大笑、发怒
|
||
- 清晰的态度表达:喜欢、讨厌、支持
|
||
|
||
### 2. 深层情感(间接暗示)
|
||
- 通过行为暗示:不自觉的小动作
|
||
- 通过对话暗示:言不由衷的话语
|
||
- 通过环境暗示:周围事物的变化
|
||
|
||
### 3. 核心情感(深层挖掘)
|
||
- 童年创伤的影响:深层恐惧的根源
|
||
- 价值观冲突:内心最深的矛盾
|
||
- 存在意义:对人生的终极思考
|
||
|
||
## 六、读者代入感营造
|
||
|
||
### 1. 第一人称视角运用
|
||
```
|
||
内心独白:
|
||
- "我告诉自己要坚强,但眼泪还是不争气地掉下来"
|
||
- "那一刻,我突然明白了什么叫做心痛"
|
||
- "我想我永远不会忘记那个下午"
|
||
|
||
感官体验:
|
||
- "我感到胸口一阵闷痛"
|
||
- "我听到自己心跳的声音"
|
||
- "我闻到了熟悉的香水味"
|
||
```
|
||
|
||
### 2. 情感节奏控制
|
||
```
|
||
情感起伏:
|
||
平静 → 波动 → 高潮 → 回落 → 新的波动
|
||
|
||
情感层次:
|
||
表面平静 → 内心波澜 → 情感爆发 → 深层反思
|
||
```
|
||
|
||
### 3. 共同记忆唤起
|
||
```
|
||
集体记忆:
|
||
- 学生时代的青涩回忆
|
||
- 第一次工作的紧张兴奋
|
||
- 恋爱时的甜蜜痛苦
|
||
- 成年后的责任重担
|
||
|
||
时代记忆:
|
||
- 特定年代的流行歌曲
|
||
- 共同经历的社会事件
|
||
- 集体的文化符号
|
||
- 时代特色的生活方式
|
||
```
|
||
"""
|
||
|
||
|
||
def _get_advanced_dialogue_system() -> str:
|
||
"""高级对话系统 - 让每句话都有深意"""
|
||
return """
|
||
【高级对话系统 - 让对话活起来】
|
||
|
||
## 一、潜台词设计技巧
|
||
|
||
### 1. 表里不一型对话
|
||
```
|
||
表面说法 → 真实想法:
|
||
"我很好" → 其实我很痛苦
|
||
"随便你" → 我很在意你的选择
|
||
"没关系" → 我很受伤
|
||
"我不在乎" → 我非常在乎
|
||
"你去吧" → 我希望你留下来
|
||
```
|
||
|
||
### 2. 情感层次对话
|
||
```
|
||
第一层(表面):礼貌客套
|
||
"谢谢你的关心"
|
||
|
||
第二层(暗示):有所保留
|
||
"我会处理好的"(不想让你担心)
|
||
|
||
第三层(深层):真实情感
|
||
"其实我很害怕"(内心独白)
|
||
```
|
||
|
||
### 3. 关系状态对话
|
||
```
|
||
亲密关系:
|
||
- 可以有停顿、省略
|
||
- 一个眼神就能理解
|
||
- 用昵称、专属词汇
|
||
|
||
疏远关系:
|
||
- 客套、正式用词
|
||
- 避免深入话题
|
||
- 保持安全距离
|
||
|
||
紧张关系:
|
||
- 话中有话
|
||
- 试探性发言
|
||
- 防御性回应
|
||
```
|
||
|
||
## 二、角色语言个性化
|
||
|
||
### 1. 教育背景影响语言
|
||
```
|
||
高学历角色:
|
||
- 用词准确、书面化
|
||
- 逻辑性强、条理清晰
|
||
- 偶尔引用典故、专业术语
|
||
- 例:"从逻辑上分析,这个方案确实存在漏洞"
|
||
|
||
普通教育角色:
|
||
- 口语化、生活化
|
||
- 直接表达、简单明了
|
||
- 用比喻、俗语
|
||
- 例:"这事儿就像烂泥扶不上墙"
|
||
|
||
低学历角色:
|
||
- 方言、口语重
|
||
- 表达直接、情绪化
|
||
- 语法不够规范
|
||
- 例:"这咋整啊,我心里没底"
|
||
```
|
||
|
||
### 2. 年龄特征语言
|
||
```
|
||
青少年:
|
||
- 网络用语、流行词汇
|
||
- 语气词多:"超级"、"特别"
|
||
- 情绪化表达
|
||
- 例:"这也太离谱了吧!"
|
||
|
||
中年人:
|
||
- 稳重、考虑周全
|
||
- 责任感强的表达
|
||
- 经验性判断
|
||
- 例:"年轻人,这事得从长计议"
|
||
|
||
老年人:
|
||
- 人生感悟、经验之谈
|
||
- 怀旧、对比过去
|
||
- 语速较慢、用词谨慎
|
||
- 例:"我这把年纪了,什么没见过"
|
||
```
|
||
|
||
### 3. 职业特色语言
|
||
```
|
||
医生:
|
||
- 专业术语自然运用
|
||
- 理性分析、客观判断
|
||
- 关注健康、风险
|
||
- 例:"从症状来看,需要进一步检查"
|
||
|
||
教师:
|
||
- 习惯性解释、引导
|
||
- 用词规范、逻辑清晰
|
||
- 关注成长、教育
|
||
- 例:"这个问题很好,我们来分析一下"
|
||
|
||
商人:
|
||
- 效率导向、结果导向
|
||
- 成本效益思维
|
||
- 人际关系敏感
|
||
- 例:"这个项目的投入产出比如何?"
|
||
```
|
||
|
||
## 三、对话推进技巧
|
||
|
||
### 1. 信息递进式对话
|
||
```
|
||
第一轮:试探
|
||
A:"你最近怎么样?"
|
||
B:"还行吧。"
|
||
|
||
第二轮:深入
|
||
A:"看你好像有心事?"
|
||
B:"也没什么大不了的。"
|
||
|
||
第三轮:突破
|
||
A:"是不是工作上的事?"
|
||
B:"你怎么知道的?"(开始敞开心扉)
|
||
```
|
||
|
||
### 2. 冲突升级式对话
|
||
```
|
||
第一阶段:分歧出现
|
||
A:"我觉得这样不对。"
|
||
B:"为什么不对?"
|
||
|
||
第二阶段:立场明确
|
||
A:"这违背了我们的原则。"
|
||
B:"原则?什么时候你这么在意原则了?"
|
||
|
||
第三阶段:情绪爆发
|
||
A:"我一直都很在意!"
|
||
B:"那你之前为什么不说?"
|
||
|
||
第四阶段:深层矛盾
|
||
A:"因为我以为你会理解!"
|
||
B:"理解?我怎么理解一个从不跟我沟通的人?"
|
||
```
|
||
|
||
### 3. 情感递进式对话
|
||
```
|
||
疏远 → 试探 → 接近 → 亲密
|
||
|
||
疏远阶段:
|
||
"你好。"(客套)
|
||
|
||
试探阶段:
|
||
"你还记得我们以前..."(回忆)
|
||
|
||
接近阶段:
|
||
"其实我一直想跟你说..."(敞开)
|
||
|
||
亲密阶段:
|
||
"我知道你懂我的意思。"(默契)
|
||
```
|
||
|
||
## 四、对话节奏控制
|
||
|
||
### 1. 快节奏对话(紧张、激烈)
|
||
```
|
||
特点:
|
||
- 短句、断句多
|
||
- 语气急促
|
||
- 互相打断
|
||
- 情绪激动
|
||
|
||
示例:
|
||
A:"你怎么能这样?"
|
||
B:"我怎么了?"
|
||
A:"你明知道——"
|
||
B:"我明知道什么?"
|
||
A:"别装了!"
|
||
```
|
||
|
||
### 2. 慢节奏对话(深沉、思考)
|
||
```
|
||
特点:
|
||
- 长句、停顿多
|
||
- 语气平缓
|
||
- 深思熟虑
|
||
- 情感深沉
|
||
|
||
示例:
|
||
A:"你知道吗...有时候我会想,如果当初我们选择了不同的路,现在会是什么样子。"
|
||
B:"(沉默片刻)也许...也许我们都会更快乐一些。"
|
||
```
|
||
|
||
### 3. 变化节奏对话(情感转折)
|
||
```
|
||
从慢到快:
|
||
A:"我一直在想一个问题..."(慢)
|
||
B:"什么问题?"
|
||
A:"为什么你要骗我?!"(快,情绪爆发)
|
||
|
||
从快到慢:
|
||
A:"你必须告诉我真相!"(快)
|
||
B:"好吧...其实..."(慢,开始坦白)
|
||
```
|
||
|
||
## 五、对话功能设计
|
||
|
||
### 1. 推进情节的对话
|
||
```
|
||
信息揭示:
|
||
- 通过对话透露关键信息
|
||
- 推动故事发展
|
||
- 解答读者疑问
|
||
|
||
冲突制造:
|
||
- 通过对话引发矛盾
|
||
- 加剧紧张关系
|
||
- 制造戏剧冲突
|
||
|
||
转折触发:
|
||
- 关键对话改变局势
|
||
- 重要决定的宣布
|
||
- 意外信息的披露
|
||
```
|
||
|
||
### 2. 展现性格的对话
|
||
```
|
||
直接展现:
|
||
- 通过说话方式展现性格
|
||
- 通过观点表达展现价值观
|
||
- 通过反应方式展现特质
|
||
|
||
间接展现:
|
||
- 通过对话选择展现优先级
|
||
- 通过语气变化展现情绪
|
||
- 通过沉默展现内心状态
|
||
```
|
||
|
||
### 3. 营造氛围的对话
|
||
```
|
||
紧张氛围:
|
||
- 短促、急迫的对话
|
||
- 充满质疑和怀疑
|
||
- 话中有话的试探
|
||
|
||
温馨氛围:
|
||
- 温和、关怀的语调
|
||
- 分享和倾听
|
||
- 理解和支持的表达
|
||
|
||
悬疑氛围:
|
||
- 模糊、暗示性的话语
|
||
- 欲言又止的表达
|
||
- 意味深长的沉默
|
||
```
|
||
|
||
## 六、对话真实感营造
|
||
|
||
### 1. 生活化表达
|
||
```
|
||
口语化特征:
|
||
- 语气词:"嗯"、"啊"、"呢"
|
||
- 重复:"真的真的"、"好好好"
|
||
- 省略:"知道了"(省略主语)
|
||
- 感叹:"天哪!"、"我的妈呀!"
|
||
|
||
情境化表达:
|
||
- 根据场合调整语言
|
||
- 考虑说话环境
|
||
- 符合当时情绪状态
|
||
```
|
||
|
||
### 2. 个性化标签
|
||
```
|
||
每个角色的专属特征:
|
||
- 口头禅:"说实话"、"你懂的"
|
||
- 语言习惯:爱用反问、喜欢举例
|
||
- 表达方式:直接/委婉、理性/感性
|
||
- 词汇偏好:书面语/口语、正式/随意
|
||
```
|
||
|
||
### 3. 情感真实性
|
||
```
|
||
情感与语言的匹配:
|
||
- 开心时:语调上扬、语速加快
|
||
- 难过时:语调低沉、语速缓慢
|
||
- 愤怒时:语调激烈、用词强硬
|
||
- 紧张时:语调不稳、结结巴巴
|
||
```
|
||
"""
|
||
"""情节发展系统"""
|
||
return """
|
||
【情节发展系统 - 告别突兀和流水账】
|
||
|
||
## 情节推进三原则
|
||
|
||
### 1. 因果链原则
|
||
每个情节点都要回答:
|
||
- **为什么发生**:有什么原因导致?
|
||
- **如何发生**:具体过程是什么?
|
||
- **产生什么后果**:对角色和故事有什么影响?
|
||
|
||
### 2. 情感驱动原则
|
||
- 情节的推进来自角色的情感需求
|
||
- 外在事件触发内在情感变化
|
||
- 情感变化推动行为选择
|
||
|
||
### 3. 冲突升级原则
|
||
- 小冲突→中冲突→大冲突
|
||
- 外在冲突→内在冲突→价值观冲突
|
||
- 个人冲突→关系冲突→社会冲突
|
||
|
||
## 铺垫技巧系统
|
||
|
||
### 1. 细节铺垫法
|
||
- 在日常对话中透露关键信息
|
||
- 通过环境描写暗示即将发生的事
|
||
- 用角色的小动作预示内心变化
|
||
|
||
### 2. 情绪铺垫法
|
||
- 用气氛烘托即将到来的转折
|
||
- 通过角色的不安情绪制造悬念
|
||
- 用对比手法突出变化
|
||
|
||
### 3. 伏笔回收法
|
||
- 开篇埋下的线索在中后段回收
|
||
- 看似无关的细节在关键时刻发挥作用
|
||
- 角色的过往经历影响现在的选择
|
||
|
||
## 冲突设计模板
|
||
|
||
### 1. 内心冲突类型
|
||
- **价值观冲突**:正义 vs 利益
|
||
- **情感冲突**:爱情 vs 友情
|
||
- **选择冲突**:安全 vs 冒险
|
||
- **身份冲突**:真实自我 vs 社会期待
|
||
|
||
### 2. 关系冲突类型
|
||
- **误解型**:基于信息不对称的冲突
|
||
- **立场型**:基于不同立场的冲突
|
||
- **竞争型**:基于资源争夺的冲突
|
||
- **成长型**:基于成长差异的冲突
|
||
|
||
### 3. 外在冲突类型
|
||
- **环境阻碍**:客观条件的限制
|
||
- **时间压力**:deadline带来的紧迫感
|
||
- **资源稀缺**:有限资源的争夺
|
||
- **规则限制**:制度或法律的约束
|
||
|
||
## 情节转折技巧
|
||
|
||
### 1. 反转设计
|
||
- **信息反转**:之前的认知被颠覆
|
||
- **身份反转**:角色身份的意外揭示
|
||
- **立场反转**:敌友关系的转换
|
||
- **结果反转**:预期结果的意外改变
|
||
|
||
### 2. 高潮设计
|
||
- 所有矛盾在此刻集中爆发
|
||
- 主角面临最大的选择和考验
|
||
- 情感达到最高点
|
||
- 为结局做好铺垫
|
||
|
||
### 3. 结局设计
|
||
- 回应开篇提出的问题
|
||
- 展示角色的成长和变化
|
||
- 给读者满足感和思考空间
|
||
- 为可能的续集留下空间
|
||
"""
|
||
|
||
|
||
def list_genres() -> List[str]:
|
||
return list(GENRE_TEMPLATES.keys())
|
||
|
||
|
||
def _force_story_expansion(config: ViralNovelConfig, bible: Dict[str, Any], existing_story: str, target_chars: int) -> str:
|
||
"""强制扩展故事到目标字数,降低完整性检查标准"""
|
||
|
||
current_length = _count_effective_characters(existing_story)
|
||
needed_chars = target_chars - current_length
|
||
|
||
if needed_chars <= 0:
|
||
return existing_story
|
||
|
||
logger.info(f"强制扩展模式:需要增加约{needed_chars}字")
|
||
|
||
# 获取写作系统
|
||
writing_system = _get_advanced_writing_system()
|
||
character_system = _get_enhanced_character_system()
|
||
plot_system = _get_enhanced_plot_system()
|
||
|
||
# 分析已有内容
|
||
story_summary = existing_story[-3000:] if len(existing_story) > 3000 else existing_story
|
||
bible_text = json.dumps(bible, ensure_ascii=False, indent=2)
|
||
|
||
prompt = f"""你需要为以下小说进行扩展,确保达到目标字数。当前故事可能看起来接近完整,但需要进一步丰富内容。
|
||
|
||
{writing_system}
|
||
|
||
{character_system}
|
||
|
||
{plot_system}
|
||
|
||
【原始设定】
|
||
{bible_text}
|
||
|
||
【已有故事内容(最后部分)】
|
||
...{story_summary}
|
||
|
||
【扩展要求】
|
||
1. 目标增加字数:约{needed_chars}字
|
||
2. 可以通过以下方式扩展:
|
||
- 增加角色间的深度对话和互动
|
||
- 丰富场景描写和环境细节
|
||
- 添加角色的内心独白和情感变化
|
||
- 增加支线情节或回忆片段
|
||
- 扩展现有情节的细节描写
|
||
3. 保持与现有内容的连贯性
|
||
4. 即使故事看似完整,也要找到合理的扩展点
|
||
5. 可以添加新的小冲突或情节转折来丰富内容
|
||
|
||
请直接输出扩展内容,不要任何说明或标记:"""
|
||
|
||
max_tokens = min(int(needed_chars * 3), 8192)
|
||
|
||
try:
|
||
response = _call_generation(
|
||
model=config.model,
|
||
messages=[
|
||
{"role": "system", "content": "你是世界顶级的小说家,擅长在保持故事完整性的同时丰富内容细节。"},
|
||
{"role": "user", "content": prompt}
|
||
],
|
||
temperature=getattr(config, 'temperature', 0.9), # 稍微提高创造性
|
||
max_tokens=max_tokens
|
||
)
|
||
|
||
expansion = _clean_extra_lines(_safe_get_content(response))
|
||
|
||
# 检查扩展内容质量
|
||
if len(expansion.strip()) < 200:
|
||
logger.warning("强制扩展内容过短,返回原故事")
|
||
return existing_story
|
||
|
||
# 合并内容
|
||
combined_story = existing_story + "\n\n" + expansion
|
||
|
||
# 应用反重复处理
|
||
final_story = detect_and_replace_repetitions(combined_story)
|
||
|
||
final_length = _count_effective_characters(final_story)
|
||
logger.info(f"强制扩展完成,最终字数: {final_length}")
|
||
|
||
return final_story
|
||
|
||
except Exception as e:
|
||
logger.error(f"强制扩展过程中出错: {e}")
|
||
return existing_story
|
||
|
||
|
||
def _continue_story_generation(config: ViralNovelConfig, bible: Dict[str, Any], existing_story: str) -> str:
|
||
"""智能续写故事,基于故事完整性而非固定字数"""
|
||
|
||
current_length = _count_effective_characters(existing_story)
|
||
logger.info(f"开始智能续写,当前字数: {current_length}")
|
||
|
||
# 检查当前故事阶段
|
||
story_stage = _analyze_story_stage(existing_story, bible)
|
||
logger.info(f"故事阶段分析: {story_stage}")
|
||
|
||
# 检查是否已经完整 - 但如果字数不足,优先续写
|
||
is_complete, completeness_reason = _check_story_completeness(existing_story, bible, config.min_chars_before_ending)
|
||
|
||
# 如果字数严重不足,即使看起来完整也要继续写
|
||
if current_length < config.min_chars_before_ending * 0.8: # 如果少于最小要求的80%
|
||
logger.info(f"字数不足最小要求的80%({current_length} < {config.min_chars_before_ending * 0.8}),强制续写")
|
||
is_complete = False
|
||
completeness_reason = f"字数不足,需要继续扩展(当前{current_length}字)"
|
||
|
||
if is_complete and current_length >= config.min_chars_before_ending:
|
||
logger.info(f"故事已完整且字数充足: {completeness_reason}")
|
||
return existing_story
|
||
|
||
# 如果字数已经很多但故事不完整,优先完善结构而非增加字数
|
||
if current_length > config.max_chars_limit:
|
||
logger.info("字数已达上限,专注于完善故事结构")
|
||
target_addition = min(2000, config.max_chars_limit - current_length)
|
||
else:
|
||
# 根据故事阶段决定续写长度
|
||
if "开端" in story_stage or "发展" in story_stage:
|
||
target_addition = min(5000, config.max_chars_limit - current_length)
|
||
elif "高潮" in story_stage:
|
||
target_addition = min(3000, config.max_chars_limit - current_length)
|
||
else: # 收尾阶段
|
||
target_addition = min(2000, config.max_chars_limit - current_length)
|
||
|
||
if target_addition <= 0:
|
||
logger.info("已达字数上限,不再续写")
|
||
return existing_story
|
||
|
||
# 获取写作系统
|
||
writing_system = _get_advanced_writing_system()
|
||
character_system = _get_enhanced_character_system()
|
||
plot_system = _get_enhanced_plot_system()
|
||
|
||
# 分析已有内容,提取关键信息
|
||
story_summary = existing_story[-2000:] if len(existing_story) > 2000 else existing_story
|
||
|
||
bible_text = json.dumps(bible, ensure_ascii=False, indent=2)
|
||
|
||
prompt = f"""你需要为以下小说续写内容,确保故事的连贯性和完整性。
|
||
|
||
{writing_system}
|
||
|
||
{character_system}
|
||
|
||
{plot_system}
|
||
|
||
【原始设定】
|
||
{bible_text}
|
||
|
||
【当前故事发展阶段】
|
||
{story_stage}
|
||
|
||
【已有故事内容(最后部分)】
|
||
...{story_summary}
|
||
|
||
【续写要求】
|
||
1. 无缝衔接已有内容,保持人物性格和情节逻辑一致
|
||
2. 根据当前阶段推进故事发展,不要重复已有情节
|
||
3. 目标续写长度:约{target_addition}字
|
||
4. 如果故事接近完整,应该开始收尾而不是无限扩展
|
||
5. 确保情节有意义的推进,避免无意义的重复描写
|
||
|
||
请直接输出续写内容,不要任何说明或标记:"""
|
||
|
||
max_tokens = min(int(target_addition * 2.5), 4096)
|
||
|
||
try:
|
||
response = _call_generation(
|
||
model=config.model,
|
||
messages=[
|
||
{"role": "system", "content": "你是世界顶级的小说家,精通故事续写和情节发展。"},
|
||
{"role": "user", "content": prompt}
|
||
],
|
||
temperature=getattr(config, 'temperature', 0.8),
|
||
max_tokens=max_tokens
|
||
)
|
||
|
||
continuation = _clean_extra_lines(_safe_get_content(response))
|
||
|
||
# 检查续写内容质量
|
||
if len(continuation.strip()) < 100:
|
||
logger.warning("续写内容过短,可能生成失败")
|
||
return existing_story
|
||
|
||
# 合并内容
|
||
combined_story = existing_story + "\n\n" + continuation
|
||
|
||
# 应用反重复处理
|
||
final_story = detect_and_replace_repetitions(combined_story)
|
||
|
||
final_length = _count_effective_characters(final_story)
|
||
logger.info(f"续写完成,最终字数: {final_length}")
|
||
|
||
return final_story
|
||
|
||
except Exception as e:
|
||
logger.error(f"续写过程中出错: {e}")
|
||
return existing_story
|
||
|
||
|
||
def generate_viral_novel_with_progress(genre: str,
|
||
target_total_chars: int,
|
||
model: str = "qwen-turbo",
|
||
temperature: float = 0.8,
|
||
enable_logic_check: bool = True,
|
||
project_dir: str = None,
|
||
resume: bool = False) -> Dict[str, str]:
|
||
"""带进度保存的小说生成"""
|
||
global _current_progress
|
||
|
||
config = ViralNovelConfig(
|
||
genre=genre,
|
||
target_total_chars=int(target_total_chars),
|
||
model=model,
|
||
temperature=temperature,
|
||
enable_logic_check=enable_logic_check,
|
||
min_chars_before_ending=max(15000, int(target_total_chars * 0.6)), # 至少60%的目标字数
|
||
max_chars_limit=int(target_total_chars * 2.5), # 最大不超过目标的2.5倍
|
||
story_completeness_check=True
|
||
)
|
||
|
||
# 确定项目目录
|
||
if not project_dir:
|
||
base_dir = os.path.dirname(os.path.abspath(__file__))
|
||
project_dir = _next_project_dir(base_dir)
|
||
|
||
# 尝试恢复进度
|
||
progress = None
|
||
if resume:
|
||
progress = _load_generation_progress(project_dir)
|
||
|
||
if not progress:
|
||
progress = GenerationProgress(
|
||
project_dir=project_dir,
|
||
stage="start",
|
||
progress_percent=0.0,
|
||
start_time=datetime.now().isoformat()
|
||
)
|
||
|
||
_current_progress = progress
|
||
|
||
try:
|
||
# 阶段1: 构建故事圣经
|
||
if progress.stage in ["start", "bible"]:
|
||
progress.stage = "bible"
|
||
progress.progress_percent = 10.0
|
||
_save_generation_progress(progress)
|
||
|
||
if not progress.bible:
|
||
progress.bible = _build_detailed_bible(config)
|
||
progress.progress_percent = 25.0
|
||
_save_generation_progress(progress)
|
||
print("✓ 角色设定完成")
|
||
|
||
# 阶段2: 生成标题
|
||
if progress.stage in ["bible", "title"]:
|
||
progress.stage = "title"
|
||
progress.progress_percent = 30.0
|
||
_save_generation_progress(progress)
|
||
|
||
if not progress.title:
|
||
progress.title = _generate_title(config, progress.bible)
|
||
|
||
progress.progress_percent = 35.0
|
||
_save_generation_progress(progress)
|
||
print(f"✓ 标题生成: 《{progress.title}》")
|
||
|
||
# 阶段3: 生成故事主体
|
||
if progress.stage in ["title", "story"]:
|
||
progress.stage = "story"
|
||
progress.progress_percent = 40.0
|
||
_save_generation_progress(progress)
|
||
|
||
if not progress.story:
|
||
if ENHANCED_FEATURES and config.enable_logic_check:
|
||
progress.story = _enhanced_story_generation_with_logic_check(config, progress.bible)
|
||
print("✓ 故事主体完成(已通过逻辑检查)")
|
||
else:
|
||
progress.story = _generate_story_with_depth_fallback(config, progress.bible)
|
||
print("✓ 故事主体完成")
|
||
|
||
progress.progress_percent = 75.0
|
||
_save_generation_progress(progress)
|
||
|
||
# 阶段4: 智能扩展(基于故事完整性)
|
||
if progress.stage in ["story", "refine"]:
|
||
progress.stage = "refine"
|
||
progress.progress_percent = 80.0
|
||
_save_generation_progress(progress)
|
||
|
||
# 检查故事完整性而非固定字数
|
||
current_length = _count_effective_characters(progress.story)
|
||
logger.info(f"开始智能扩展阶段,当前字数: {current_length}")
|
||
|
||
# 循环续写直到达到最小要求
|
||
max_iterations = 5 # 最多尝试5次续写
|
||
iteration = 0
|
||
|
||
while iteration < max_iterations:
|
||
iteration += 1
|
||
logger.info(f"第{iteration}次续写尝试...")
|
||
|
||
# 使用新的智能续写函数
|
||
new_story = _continue_story_generation(config, progress.bible, progress.story)
|
||
|
||
# 检查是否有实际的内容增加
|
||
new_length = _count_effective_characters(new_story)
|
||
if new_length <= current_length + 100: # 如果增加的内容少于100字,说明续写失败
|
||
logger.warning(f"第{iteration}次续写增加内容过少,停止续写")
|
||
break
|
||
|
||
progress.story = new_story
|
||
current_length = new_length
|
||
logger.info(f"第{iteration}次续写完成,当前字数: {current_length}")
|
||
|
||
# 检查是否达到最小要求
|
||
if current_length >= config.min_chars_before_ending:
|
||
logger.info(f"已达到最小字数要求: {current_length} >= {config.min_chars_before_ending}")
|
||
break
|
||
|
||
# 更新进度
|
||
progress.progress_percent = 80.0 + (iteration / max_iterations) * 10.0
|
||
_save_generation_progress(progress)
|
||
|
||
# 最终检查
|
||
final_length = _count_effective_characters(progress.story)
|
||
is_complete, completeness_reason = _check_story_completeness(
|
||
progress.story, progress.bible, config.min_chars_before_ending
|
||
)
|
||
|
||
if is_complete:
|
||
logger.info(f"故事完整:{completeness_reason},最终字数: {final_length}")
|
||
else:
|
||
logger.warning(f"故事可能不够完整:{completeness_reason},最终字数: {final_length}")
|
||
|
||
# 如果仍然不足最小要求,强制再次扩展
|
||
if final_length < config.min_chars_before_ending:
|
||
logger.info("字数仍不足最小要求,进行强制扩展...")
|
||
# 降低完整性检查标准,强制续写
|
||
forced_story = _force_story_expansion(config, progress.bible, progress.story, config.min_chars_before_ending)
|
||
if forced_story != progress.story:
|
||
progress.story = forced_story
|
||
final_length = _count_effective_characters(progress.story)
|
||
logger.info(f"强制扩展完成,最终字数: {final_length}")
|
||
|
||
progress.progress_percent = 95.0
|
||
_save_generation_progress(progress)
|
||
print("✓ 内容优化完成")
|
||
|
||
# 内容安全检查
|
||
if config.enable_content_filter:
|
||
is_safe, issues = _content_safety_check(progress.story)
|
||
if not is_safe:
|
||
logger.warning(f"内容安全检查发现问题: {issues}")
|
||
# 可以选择是否继续或进行内容过滤
|
||
|
||
# 完成
|
||
progress.stage = "complete"
|
||
progress.progress_percent = 100.0
|
||
_save_generation_progress(progress)
|
||
|
||
# 保存最终结果
|
||
_write_outputs(project_dir, progress.title, progress.story)
|
||
|
||
# 清理进度文件
|
||
_cleanup_progress_file(project_dir)
|
||
|
||
full_text = f"《{progress.title}》\n\n{progress.story}"
|
||
return {
|
||
"title": progress.title,
|
||
"text": full_text,
|
||
"project_dir": project_dir
|
||
}
|
||
|
||
except Exception as e:
|
||
logger.error(f"生成过程出错: {str(e)}")
|
||
# 保存当前进度
|
||
if progress:
|
||
progress.error_count += 1
|
||
_save_generation_progress(progress)
|
||
raise e
|
||
|
||
|
||
def _save_generation_progress(progress: GenerationProgress):
|
||
"""保存生成进度"""
|
||
if not progress.project_dir:
|
||
return
|
||
|
||
try:
|
||
os.makedirs(progress.project_dir, exist_ok=True)
|
||
progress_file = os.path.join(progress.project_dir, ".generation_progress.json")
|
||
|
||
progress.last_update = datetime.now().isoformat()
|
||
|
||
with open(progress_file, 'w', encoding='utf-8') as f:
|
||
json.dump(asdict(progress), f, ensure_ascii=False, indent=2)
|
||
|
||
logger.info(f"进度已保存: {progress.stage} ({progress.progress_percent:.1f}%)")
|
||
except Exception as e:
|
||
logger.warning(f"保存进度失败: {str(e)}")
|
||
|
||
|
||
def _load_generation_progress(project_dir: str) -> Optional[GenerationProgress]:
|
||
"""加载生成进度"""
|
||
try:
|
||
progress_file = os.path.join(project_dir, ".generation_progress.json")
|
||
if not os.path.exists(progress_file):
|
||
return None
|
||
|
||
with open(progress_file, 'r', encoding='utf-8') as f:
|
||
data = json.load(f)
|
||
|
||
progress = GenerationProgress(**data)
|
||
logger.info(f"已加载进度: {progress.stage} ({progress.progress_percent:.1f}%)")
|
||
return progress
|
||
except Exception as e:
|
||
logger.warning(f"加载进度失败: {str(e)}")
|
||
return None
|
||
|
||
|
||
def _cleanup_progress_file(project_dir: str):
|
||
"""清理进度文件"""
|
||
try:
|
||
progress_file = os.path.join(project_dir, ".generation_progress.json")
|
||
if os.path.exists(progress_file):
|
||
os.remove(progress_file)
|
||
except Exception as e:
|
||
logger.warning(f"清理进度文件失败: {str(e)}")
|
||
|
||
|
||
def _content_safety_check(text: str) -> Tuple[bool, List[str]]:
|
||
"""内容安全检查,包括对话质量检查"""
|
||
issues = []
|
||
|
||
# 敏感词检查(简单示例,实际应用中需要更完善的词库)
|
||
sensitive_words = [
|
||
"暴力", "血腥", "恐怖", "政治敏感", "违法", "犯罪",
|
||
# 可以根据需要扩展
|
||
]
|
||
|
||
for word in sensitive_words:
|
||
if word in text:
|
||
issues.append(f"包含敏感词汇: {word}")
|
||
|
||
# 长度检查
|
||
if len(text) < 100:
|
||
issues.append("内容过短,可能生成失败")
|
||
|
||
# 字数达标检查
|
||
effective_chars = _count_effective_characters(text)
|
||
if effective_chars < 20000: # 低于20000字认为不达标
|
||
issues.append(f"字数不足: 当前{effective_chars}字,建议至少20000字")
|
||
|
||
# 对话比例检查
|
||
dialogue_count = text.count('"') + text.count('"') + text.count('"') + text.count('「') + text.count('『')
|
||
total_chars = len(text)
|
||
if total_chars > 0:
|
||
dialogue_ratio = (dialogue_count / total_chars) * 100
|
||
if dialogue_ratio < 5: # 对话比例低于5%
|
||
issues.append(f"对话比例过低: {dialogue_ratio:.1f}%,建议增加角色对话")
|
||
|
||
# 重复内容检查(加强版)
|
||
lines = text.split('\n')
|
||
line_counts = {}
|
||
for line in lines:
|
||
line = line.strip()
|
||
if len(line) > 10: # 只检查较长的行
|
||
line_counts[line] = line_counts.get(line, 0) + 1
|
||
|
||
repeated_lines = [line for line, count in line_counts.items() if count > 3]
|
||
if repeated_lines:
|
||
issues.append(f"发现重复内容: {len(repeated_lines)}行")
|
||
|
||
# 重复词汇检查
|
||
common_phrases = ["他知道", "心中一沉", "深吸一口气", "点了点头", "说道", "与此同时"]
|
||
for phrase in common_phrases:
|
||
count = text.count(phrase)
|
||
if count > 10: # 超过10次认为过度重复
|
||
issues.append(f"词汇重复过多: '{phrase}' 出现{count}次")
|
||
|
||
# 结构完整性检查
|
||
if "第一章" not in text and "第1章" not in text and "章" not in text:
|
||
issues.append("缺少章节结构,建议添加章节划分")
|
||
|
||
is_safe = len(issues) == 0
|
||
return is_safe, issues
|
||
|
||
|
||
def _handle_api_error(error: Exception, attempt: int, max_attempts: int) -> bool:
|
||
"""处理API错误 - 增强版"""
|
||
error_msg = str(error).lower()
|
||
|
||
# 检查是否是超时错误
|
||
if "timeout" in error_msg or "timed out" in error_msg:
|
||
wait_time = min(30 * (2 ** attempt), 180) # 超时错误等待更久,最多3分钟
|
||
logger.warning(f"API超时,等待 {wait_time} 秒后重试...")
|
||
time.sleep(wait_time)
|
||
return True
|
||
|
||
# 检查是否是限流错误
|
||
if "rate limit" in error_msg or "too many requests" in error_msg:
|
||
wait_time = min(60 * (2 ** attempt), 300) # 最多等待5分钟
|
||
logger.warning(f"API限流,等待 {wait_time} 秒后重试...")
|
||
time.sleep(wait_time)
|
||
return True
|
||
|
||
# 检查是否是网络错误
|
||
if "network" in error_msg or "connection" in error_msg:
|
||
wait_time = min(15 * (2 ** attempt), 90) # 网络错误等待时间适中
|
||
logger.warning(f"网络错误,等待 {wait_time} 秒后重试...")
|
||
time.sleep(wait_time)
|
||
return True
|
||
|
||
# 检查是否是服务器错误
|
||
if "server error" in error_msg or "internal error" in error_msg or "500" in error_msg:
|
||
wait_time = min(45 * (2 ** attempt), 240) # 服务器错误等待较久
|
||
logger.warning(f"服务器错误,等待 {wait_time} 秒后重试...")
|
||
time.sleep(wait_time)
|
||
return True
|
||
|
||
# 检查是否是服务不可用
|
||
if "service unavailable" in error_msg or "503" in error_msg:
|
||
wait_time = min(60 * (2 ** attempt), 300)
|
||
logger.warning(f"服务不可用,等待 {wait_time} 秒后重试...")
|
||
time.sleep(wait_time)
|
||
return True
|
||
|
||
# 其他错误不重试
|
||
return False
|
||
|
||
|
||
def _enhanced_call_generation(model: str, messages: list, temperature: float,
|
||
max_tokens: Optional[int] = None, max_attempts: int = 8):
|
||
"""增强版API调用,包含更强的超时和错误处理"""
|
||
last_error: Optional[Exception] = None
|
||
|
||
for attempt in range(max_attempts):
|
||
try:
|
||
logger.info(f"API调用尝试 {attempt + 1}/{max_attempts}")
|
||
|
||
# 设置更长的超时时间
|
||
import dashscope
|
||
|
||
response = Generation.call(
|
||
model=model,
|
||
messages=messages,
|
||
temperature=temperature,
|
||
max_tokens=max_tokens,
|
||
result_format='message'
|
||
)
|
||
|
||
if response.status_code == 200:
|
||
logger.info(f"API调用成功 (尝试 {attempt + 1})")
|
||
return response
|
||
else:
|
||
error_msg = f"API调用失败: {response.message}"
|
||
last_error = Exception(error_msg)
|
||
logger.warning(f"API返回错误: {error_msg}")
|
||
|
||
# 尝试处理特定错误
|
||
if not _handle_api_error(last_error, attempt, max_attempts):
|
||
logger.error(f"无法处理的API错误,停止重试: {error_msg}")
|
||
break
|
||
|
||
except Exception as e:
|
||
last_error = e
|
||
error_str = str(e)
|
||
logger.warning(f"第 {attempt + 1} 次尝试失败: {error_str}")
|
||
|
||
# 尝试处理错误
|
||
if attempt < max_attempts - 1:
|
||
if _handle_api_error(e, attempt, max_attempts):
|
||
continue
|
||
else:
|
||
# 简单的指数退避重试
|
||
wait_time = min((2 ** attempt) + random.uniform(0, 1), 30)
|
||
logger.info(f"等待 {wait_time:.1f} 秒后重试...")
|
||
time.sleep(wait_time)
|
||
else:
|
||
logger.error(f"已达到最大重试次数 {max_attempts},停止尝试")
|
||
|
||
# 如果所有尝试都失败了
|
||
error_msg = f"API调用失败: {str(last_error) if last_error else '未知错误'}"
|
||
logger.error(error_msg)
|
||
raise last_error if last_error else Exception("API调用失败")
|
||
|
||
|
||
def _safe_api_call_with_fallback(model: str, messages: list, temperature: float,
|
||
max_tokens: Optional[int] = None) -> str:
|
||
"""安全的API调用,带降级处理"""
|
||
try:
|
||
response = _enhanced_call_generation(model, messages, temperature, max_tokens)
|
||
return _safe_get_content(response)
|
||
except Exception as e:
|
||
error_str = str(e)
|
||
logger.error(f"API调用完全失败: {error_str}")
|
||
|
||
# 如果是超时或网络问题,建议用户稍后重试
|
||
if any(keyword in error_str.lower() for keyword in ['timeout', 'network', 'connection']):
|
||
raise Exception(f"网络连接问题,请检查网络后重试: {error_str}")
|
||
|
||
# 如果是限流问题
|
||
if 'rate limit' in error_str.lower():
|
||
raise Exception(f"API调用频率过高,请稍后重试: {error_str}")
|
||
|
||
# 其他错误
|
||
raise Exception(f"API服务异常,请稍后重试: {error_str}")
|
||
|
||
|
||
# 全局变量用于处理中断信号
|
||
_generation_interrupted = False
|
||
_current_progress: Optional[GenerationProgress] = None
|
||
|
||
def _signal_handler(signum, frame):
|
||
"""处理中断信号"""
|
||
global _generation_interrupted, _current_progress
|
||
_generation_interrupted = True
|
||
|
||
print("\n\n收到中断信号,正在保存进度...")
|
||
if _current_progress:
|
||
_save_generation_progress(_current_progress)
|
||
print("进度已保存,可以稍后继续生成")
|
||
exit(0)
|
||
|
||
# 注册信号处理器
|
||
signal.signal(signal.SIGINT, _signal_handler)
|
||
if hasattr(signal, 'SIGTERM'):
|
||
signal.signal(signal.SIGTERM, _signal_handler)
|
||
|
||
|
||
def _call_generation(model: str, messages: list, temperature: float, max_tokens: Optional[int] = None):
|
||
"""兼容性包装函数"""
|
||
return _enhanced_call_generation(model, messages, temperature, max_tokens)
|
||
|
||
|
||
def _safe_get_content(response) -> str:
|
||
try:
|
||
return response.output.choices[0].message.content.strip()
|
||
except Exception:
|
||
return ""
|
||
|
||
|
||
def _count_effective_characters(text: str) -> int:
|
||
lines = [line.strip() for line in text.split('\n') if line.strip()]
|
||
combined = ''.join(lines)
|
||
chinese_chars = re.findall(r'[\u4e00-\u9fa5]', combined)
|
||
punctuation = re.findall(r'[,。!?;:、""''()【】《》]', combined)
|
||
numbers = re.findall(r'\d', combined)
|
||
return len(chinese_chars) + len(punctuation) + len(numbers)
|
||
|
||
|
||
def _clean_extra_lines(text: str) -> str:
|
||
text = re.sub(r'\n{3,}', '\n\n', text)
|
||
return text.strip()
|
||
|
||
|
||
def _next_project_dir(base_dir: str) -> str:
|
||
existing = []
|
||
for name in os.listdir(base_dir):
|
||
if re.fullmatch(r'n\d+', name) and os.path.isdir(os.path.join(base_dir, name)):
|
||
existing.append(int(name[1:]))
|
||
idx = (max(existing) + 1) if existing else 1
|
||
return os.path.join(base_dir, f"n{idx}")
|
||
|
||
|
||
def _choose_genre_enhanced() -> str:
|
||
"""增强版题材选择"""
|
||
if ENHANCED_FEATURES:
|
||
genres = list_available_genres()
|
||
else:
|
||
genres = list(GENRE_TEMPLATES.keys())
|
||
|
||
print("\n可选题材:")
|
||
for i, g in enumerate(genres, 1):
|
||
print(f"{i}. {g}")
|
||
|
||
while True:
|
||
s = input(f"\n请选择题材 (1-{len(genres)}): ").strip()
|
||
if not s.isdigit():
|
||
print("请输入数字")
|
||
continue
|
||
idx = int(s)
|
||
if 1 <= idx <= len(genres):
|
||
return genres[idx - 1]
|
||
print("无效选择")
|
||
|
||
|
||
def _build_detailed_bible(config: ViralNovelConfig) -> Dict[str, Any]:
|
||
"""构建详细的故事圣经,重点解决角色符号化问题"""
|
||
t = GENRE_TEMPLATES[config.genre]
|
||
|
||
# 生成随机角色种子
|
||
main_char_seed = generate_random_character_seed("main", config.genre)
|
||
supporting_seeds = [
|
||
generate_random_character_seed("supporting", config.genre) for _ in range(3)
|
||
]
|
||
|
||
print(f"🎲 角色随机种子已生成:")
|
||
print(f" 主角种子: {main_char_seed['seed']} - {main_char_seed['name']} ({main_char_seed['personality']}, {main_char_seed['occupation']})")
|
||
print(f" 主角特征: {main_char_seed.get('speech_style', '未知')}说话风格, {main_char_seed.get('habit_action', '无特殊习惯')}")
|
||
for i, seed in enumerate(supporting_seeds, 1):
|
||
print(f" 配角{i}种子: {seed['seed']} - {seed['name']} ({seed['personality']}, {seed['occupation']})")
|
||
print(f" 配角{i}特征: {seed.get('speech_style', '未知')}说话风格, {seed.get('relationship_style', '未知')}人际关系")
|
||
|
||
prompt = f"""你是顶级编剧,专门创作有深度、有灵魂的故事。请为以下题材创建一个详细的故事设定。
|
||
|
||
题材:{config.genre}
|
||
核心理念:{t['core']}
|
||
|
||
【角色随机种子 - 必须严格使用】:
|
||
主角种子:
|
||
- 姓名:{main_char_seed['name']}
|
||
- 性格:{main_char_seed['personality']}
|
||
- 背景:{main_char_seed['background']}
|
||
- 职业:{main_char_seed['occupation']}
|
||
- 性格缺陷:{main_char_seed['flaw']}
|
||
- 内心渴望:{main_char_seed['desire']}
|
||
|
||
配角种子:
|
||
{chr(10).join([f"- {seed['name']}:{seed['personality']},{seed['background']},{seed['occupation']}" for seed in supporting_seeds])}
|
||
|
||
【重要】你必须基于上述随机种子创造真正立体的角色,不是符号化的工具人:
|
||
|
||
1. **主角设定要求**:
|
||
- 严格使用提供的姓名、性格、背景等种子信息
|
||
- 有明确的童年创伤或成长经历
|
||
- 有性格缺陷和内心恐惧
|
||
- 有具体的说话习惯和行为模式
|
||
- 有内心的矛盾和挣扎
|
||
|
||
2. **配角设定要求**:
|
||
- 严格使用提供的配角种子信息
|
||
- 每个配角都有独立的人生目标
|
||
- 有不同的价值观和行为逻辑
|
||
- 有独特的说话方式和性格特征
|
||
- 与主角的关系有深度和变化
|
||
|
||
3. **情节设定要求**:
|
||
- 每个重要情节都有充分的铺垫
|
||
- 冲突来自角色的内心需求
|
||
- 有明确的因果关系链条
|
||
- 避免突兀的剧情转折
|
||
|
||
请输出JSON格式,包含以下字段:
|
||
{{
|
||
"character_seeds": {{
|
||
"main_seed": {main_char_seed['seed']},
|
||
"supporting_seeds": {[seed['seed'] for seed in supporting_seeds]}
|
||
}},
|
||
"title_concept": "故事核心概念(一句话)",
|
||
"theme": "深层主题(人性、成长、价值观等)",
|
||
"main_character": {{
|
||
"name": "{main_char_seed['name']}",
|
||
"age": 年龄,
|
||
"background": "详细背景故事(至少100字,基于种子背景:{main_char_seed['background']})",
|
||
"personality_core": "{main_char_seed['personality']}相关的核心性格特征",
|
||
"fatal_flaw": "{main_char_seed['flaw']}的具体表现",
|
||
"inner_desire": "{main_char_seed['desire']}的深层含义",
|
||
"speech_pattern": "说话习惯和口头禅",
|
||
"behavior_habits": "行为习惯和小动作",
|
||
"growth_arc": "成长轨迹(从什么到什么)"
|
||
}},
|
||
"supporting_characters": [
|
||
{{
|
||
"name": "{supporting_seeds[0]['name']}",
|
||
"role": "在故事中的作用",
|
||
"personality": "{supporting_seeds[0]['personality']}",
|
||
"motivation": "行为动机",
|
||
"speech_style": "说话风格",
|
||
"relationship_with_mc": "与主角的关系动态"
|
||
}},
|
||
{{
|
||
"name": "{supporting_seeds[1]['name']}",
|
||
"role": "在故事中的作用",
|
||
"personality": "{supporting_seeds[1]['personality']}",
|
||
"motivation": "行为动机",
|
||
"speech_style": "说话风格",
|
||
"relationship_with_mc": "与主角的关系动态"
|
||
}},
|
||
{{
|
||
"name": "{supporting_seeds[2]['name']}",
|
||
"role": "在故事中的作用",
|
||
"personality": "{supporting_seeds[2]['personality']}",
|
||
"motivation": "行为动机",
|
||
"speech_style": "说话风格",
|
||
"relationship_with_mc": "与主角的关系动态"
|
||
}}
|
||
],
|
||
"world_setting": {{
|
||
"environment": "故事发生的具体环境",
|
||
"social_context": "社会背景和氛围",
|
||
"rules": "这个世界的运行规则",
|
||
"conflicts": "环境中存在的矛盾"
|
||
}},
|
||
"plot_structure": {{
|
||
"opening_hook": "开篇钩子(具体事件)",
|
||
"inciting_incident": "引发事件(改变主角生活的事件)",
|
||
"first_plot_point": "第一个转折点",
|
||
"midpoint": "中点危机",
|
||
"climax": "高潮冲突",
|
||
"resolution": "解决方案"
|
||
}},
|
||
"emotional_journey": {{
|
||
"opening_emotion": "开篇主角的情感状态",
|
||
"emotional_turns": ["情感转折点1", "情感转折点2", "情感转折点3"],
|
||
"final_emotion": "结尾主角的情感状态"
|
||
}},
|
||
"key_relationships": [
|
||
{{
|
||
"characters": "角色A与角色B",
|
||
"relationship_type": "关系类型",
|
||
"conflict_source": "冲突来源",
|
||
"development": "关系发展轨迹"
|
||
}}
|
||
]
|
||
}}
|
||
"""
|
||
|
||
try:
|
||
response = _enhanced_call_generation(
|
||
model=config.model,
|
||
messages=[
|
||
{"role": "system", "content": "你是世界顶级的故事创作大师,擅长创造深刻、立体、有灵魂的角色和故事。你创作的每个角色都有血有肉,每个情节都有深层逻辑,每个冲突都来自人性的真实需求。"},
|
||
{"role": "user", "content": prompt}
|
||
],
|
||
temperature=0.6,
|
||
max_tokens=4000
|
||
)
|
||
except Exception as e:
|
||
logger.error(f"故事圣经生成失败: {str(e)}")
|
||
raise Exception(f"角色设定生成失败,请检查网络连接后重试: {str(e)}")
|
||
|
||
text = _safe_get_content(response)
|
||
m = re.search(r'\{[\s\S]*\}', text)
|
||
json_text = m.group(0) if m else text
|
||
try:
|
||
data = json.loads(json_text)
|
||
except Exception:
|
||
# 如果JSON解析失败,返回基础结构
|
||
data = {
|
||
"title_concept": "一个关于成长的故事",
|
||
"theme": "人性与成长",
|
||
"main_character": {
|
||
"name": "主角",
|
||
"background": "普通人的不普通经历",
|
||
"personality_core": "坚韧不拔",
|
||
"fatal_flaw": "过度自尊",
|
||
"inner_desire": "被认可",
|
||
"speech_pattern": "简洁直接",
|
||
"behavior_habits": "紧张时握拳",
|
||
"growth_arc": "从自卑到自信"
|
||
},
|
||
"supporting_characters": [],
|
||
"world_setting": {},
|
||
"plot_structure": {},
|
||
"emotional_journey": {},
|
||
"key_relationships": []
|
||
}
|
||
return data
|
||
|
||
|
||
def _generate_title(config: ViralNovelConfig, bible: Dict[str, Any]) -> str:
|
||
"""生成标题"""
|
||
concept = bible.get("title_concept", "")
|
||
theme = bible.get("theme", "")
|
||
|
||
prompt = f"""请为以下故事创作一个吸引人的中文标题。
|
||
|
||
故事概念:{concept}
|
||
深层主题:{theme}
|
||
题材:{config.genre}
|
||
|
||
要求:
|
||
1. 4-8个中文字
|
||
2. 体现故事的核心冲突或情感
|
||
3. 朗朗上口,有记忆点
|
||
4. 不要使用书名号
|
||
5. 原创,不模仿现有作品
|
||
|
||
只输出标题本身:
|
||
"""
|
||
|
||
try:
|
||
response = _enhanced_call_generation(
|
||
model=config.model,
|
||
messages=[
|
||
{"role": "system", "content": "你是专业的文学编辑,擅长为深度故事创作有内涵的标题。"},
|
||
{"role": "user", "content": prompt}
|
||
],
|
||
temperature=0.7,
|
||
max_tokens=50
|
||
)
|
||
except Exception as e:
|
||
logger.error(f"标题生成失败: {str(e)}")
|
||
# 标题生成失败时使用默认标题
|
||
logger.warning("使用默认标题")
|
||
return "未命名作品"
|
||
|
||
title = _safe_get_content(response)
|
||
title = title.replace('《', '').replace('》', '').replace('"', '').replace('"', '').replace(''', '').replace(''', '').strip()
|
||
title = re.sub(r'\s+', '', title)
|
||
return title[:15] if title else "未命名"
|
||
|
||
|
||
def _check_logic_consistency(text: str, bible: Dict[str, Any], config: ViralNovelConfig) -> Tuple[bool, List[str], str]:
|
||
"""
|
||
检查文本的逻辑一致性
|
||
返回: (是否通过检查, 问题列表, 修复建议)
|
||
"""
|
||
if not config.enable_logic_check:
|
||
return True, [], ""
|
||
|
||
logger.info("开始逻辑一致性检查...")
|
||
|
||
# 构建检查提示词
|
||
prompt = f"""请对以下小说文本进行逻辑一致性检查,重点检查:
|
||
|
||
【检查维度】
|
||
1. 角色行为一致性:
|
||
- 角色行为是否符合已设定的性格特征
|
||
- 角色能力是否前后一致
|
||
- 角色知识水平是否合理
|
||
|
||
2. 时间线逻辑:
|
||
- 事件发生顺序是否合理
|
||
- 时间跨度是否现实
|
||
- 因果关系是否正确
|
||
|
||
3. 世界观一致性:
|
||
- 是否违反已建立的世界规则
|
||
- 社会背景是否前后统一
|
||
- 技术水平是否一致
|
||
|
||
4. 情节逻辑:
|
||
- 角色动机是否充分
|
||
- 冲突升级是否合理
|
||
- 转折点是否有铺垫
|
||
|
||
5. 对话逻辑:
|
||
- 角色是否知道不该知道的信息
|
||
- 说话方式是否符合角色设定
|
||
- 对话内容是否推进情节
|
||
|
||
【角色设定参考】
|
||
{json.dumps(bible.get('main_character', {}), ensure_ascii=False, indent=2)}
|
||
|
||
【配角设定参考】
|
||
{json.dumps(bible.get('supporting_characters', []), ensure_ascii=False, indent=2)}
|
||
|
||
【世界观设定参考】
|
||
{json.dumps(bible.get('world_setting', {}), ensure_ascii=False, indent=2)}
|
||
|
||
【待检查文本】
|
||
{text}
|
||
|
||
请以JSON格式回复:
|
||
{{
|
||
"overall_pass": true/false,
|
||
"critical_issues": ["严重逻辑错误1", "严重逻辑错误2"],
|
||
"minor_issues": ["轻微问题1", "轻微问题2"],
|
||
"suggestions": ["修复建议1", "修复建议2"],
|
||
"score": 评分(0-100),
|
||
"details": {{
|
||
"character_consistency": 评分(0-100),
|
||
"timeline_logic": 评分(0-100),
|
||
"world_consistency": 评分(0-100),
|
||
"plot_logic": 评分(0-100),
|
||
"dialogue_logic": 评分(0-100)
|
||
}}
|
||
}}"""
|
||
|
||
try:
|
||
response = _call_generation(
|
||
model=config.model,
|
||
messages=[
|
||
{"role": "system", "content": "你是一位专业的逻辑分析师,擅长发现故事中的逻辑漏洞和不一致之处。你的分析必须客观、准确、有建设性。"},
|
||
{"role": "user", "content": prompt}
|
||
],
|
||
temperature=0.3,
|
||
max_tokens=2000
|
||
)
|
||
|
||
result_text = _safe_get_content(response)
|
||
|
||
# 尝试解析JSON
|
||
try:
|
||
# 提取JSON部分
|
||
json_match = re.search(r'\{[\s\S]*\}', result_text)
|
||
if json_match:
|
||
result_data = json.loads(json_match.group(0))
|
||
|
||
overall_pass = result_data.get("overall_pass", True)
|
||
critical_issues = result_data.get("critical_issues", [])
|
||
minor_issues = result_data.get("minor_issues", [])
|
||
suggestions = result_data.get("suggestions", [])
|
||
|
||
# 如果有严重问题,检查不通过
|
||
if critical_issues:
|
||
overall_pass = False
|
||
|
||
all_issues = critical_issues + minor_issues
|
||
fix_suggestions = "\n".join(suggestions) if suggestions else ""
|
||
|
||
logger.info(f"逻辑检查完成,发现 {len(critical_issues)} 个严重问题,{len(minor_issues)} 个轻微问题")
|
||
return overall_pass, all_issues, fix_suggestions
|
||
else:
|
||
logger.warning("无法解析逻辑检查结果,跳过检查")
|
||
return True, [], ""
|
||
|
||
except json.JSONDecodeError:
|
||
logger.warning("逻辑检查结果JSON解析失败,跳过检查")
|
||
return True, [], ""
|
||
|
||
except Exception as e:
|
||
logger.error(f"逻辑检查过程出错: {str(e)}")
|
||
return True, [], ""
|
||
|
||
|
||
def _fix_logic_issues(text: str, issues: List[str], suggestions: str, bible: Dict[str, Any], config: ViralNovelConfig) -> str:
|
||
"""
|
||
根据逻辑检查结果修复问题
|
||
"""
|
||
if not issues or not suggestions:
|
||
return text
|
||
|
||
logger.info(f"开始修复 {len(issues)} 个逻辑问题...")
|
||
|
||
# 构建修复提示词
|
||
issues_text = "\n".join(f"- {issue}" for issue in issues)
|
||
|
||
prompt = f"""请修复以下文本中的逻辑问题,要求:
|
||
|
||
【发现的逻辑问题】
|
||
{issues_text}
|
||
|
||
【修复建议】
|
||
{suggestions}
|
||
|
||
【修复要求】
|
||
1. 保持原文的核心情节和主要内容不变
|
||
2. 只修复逻辑问题,不要大幅改动
|
||
3. 确保修复后的内容符合角色设定和世界观
|
||
4. 保持文本的流畅性和可读性
|
||
5. 修复后的内容要逻辑自洽
|
||
|
||
【角色设定参考】
|
||
{json.dumps(bible.get('main_character', {}), ensure_ascii=False, indent=2)}
|
||
|
||
【需要修复的文本】
|
||
{text}
|
||
|
||
请直接输出修复后的完整文本,不要添加任何说明:"""
|
||
|
||
try:
|
||
response = _call_generation(
|
||
model=config.model,
|
||
messages=[
|
||
{"role": "system", "content": "你是一位专业的文本编辑,擅长修复逻辑问题而不改变故事的核心内容。你的修复必须精准、合理、保持原文风格。"},
|
||
{"role": "user", "content": prompt}
|
||
],
|
||
temperature=0.5,
|
||
max_tokens=min(len(text) * 2, 8192)
|
||
)
|
||
|
||
fixed_text = _safe_get_content(response)
|
||
if fixed_text and len(fixed_text) > len(text) * 0.5: # 确保修复后的文本不会太短
|
||
logger.info("逻辑问题修复完成")
|
||
return _clean_extra_lines(fixed_text)
|
||
else:
|
||
logger.warning("修复结果异常,返回原文")
|
||
return text
|
||
|
||
except Exception as e:
|
||
logger.error(f"逻辑修复过程出错: {str(e)}")
|
||
return text
|
||
|
||
|
||
def _enhanced_story_generation_with_logic_check(config: ViralNovelConfig, bible: Dict[str, Any]) -> str:
|
||
"""
|
||
增强版故事生成,包含逻辑检查和修复
|
||
"""
|
||
# 获取增强的写作系统
|
||
writing_system = _get_advanced_writing_system()
|
||
character_system = _get_enhanced_character_system()
|
||
plot_system = _get_enhanced_plot_system()
|
||
logic_system = _get_logic_consistency_system()
|
||
|
||
# 生成反同质化指导
|
||
anti_repetition_guide = generate_anti_repetition_guidance(config.genre, 1)
|
||
|
||
bible_text = json.dumps(bible, ensure_ascii=False, indent=2)
|
||
|
||
prompt = f"""你将创作一篇高质量的原创小说,必须解决AI写作的常见问题并确保逻辑严密。
|
||
|
||
{anti_repetition_guide}
|
||
|
||
{writing_system}
|
||
|
||
{character_system}
|
||
|
||
{plot_system}
|
||
|
||
{logic_system}
|
||
|
||
【故事设定】
|
||
{bible_text}
|
||
|
||
【创作要求 - 必须严格遵守】
|
||
|
||
1. **逻辑严密要求**:
|
||
- 角色行为必须符合性格设定,不能OOC
|
||
- 时间线必须清晰合理,不能前后矛盾
|
||
- 世界观规则必须统一,不能自相矛盾
|
||
- 因果关系必须明确,每个结果都有充分原因
|
||
- 角色知识和能力必须前后一致
|
||
|
||
2. **反流水账要求**:
|
||
- 每个场景都要有情感张力和内心冲突
|
||
- 用内心独白展现角色思考过程
|
||
- 通过具体细节暗示而非直接说明
|
||
- 让每个动作都有情感色彩和深层含义
|
||
|
||
3. **铺垫与因果要求**:
|
||
- 重要情节前必须有多层次铺垫
|
||
- 每个转折都要有合理原因和充分动机
|
||
- 用细节和情绪变化暗示即将发生的事
|
||
- 角色的选择要有内心挣扎和思考过程
|
||
|
||
4. **防重复要求**:
|
||
- 同一情感用不同方式表达(愤怒→恼火→暴躁→怒不可遏)
|
||
- 同一场景从不同角度描写(视觉→听觉→心理感受)
|
||
- 词汇和句式要有变化,避免重复表达
|
||
- 情节要螺旋上升,在重复中递进和深化
|
||
|
||
5. **角色立体化要求**:
|
||
- 每个角色都有独特的说话方式和行为模式
|
||
- 通过行为展现性格,不要直接说明
|
||
- 角色要有内心冲突和成长轨迹
|
||
- 配角也要有自己的目标和动机,不是工具人
|
||
|
||
6. **情感共鸣要求**:
|
||
- 挖掘普遍的人性体验和情感共鸣点
|
||
- 用具体细节唤起读者情感
|
||
- 展现角色的脆弱和坚强,让人物有血有肉
|
||
- 让读者能感同身受,产生代入感
|
||
|
||
【具体写作指令】
|
||
|
||
**开篇500字必须包含**:
|
||
1. 主角的核心冲突和内心困境
|
||
2. 独特的环境细节和世界观展示
|
||
3. 至少2个伏笔为后续情节埋线
|
||
4. 让读者对主角产生情感连接的细节
|
||
5. 制造悬念或期待,吸引读者继续阅读
|
||
|
||
**每1000字自检要求**:
|
||
1. 是否推进了情感发展和角色关系?
|
||
2. 是否深化了角色的内心层次?
|
||
3. 是否有新的冲突或转折?
|
||
4. 是否避免了重复表达?
|
||
5. 是否保持了逻辑一致性?
|
||
|
||
**对话写作要求**:
|
||
1. 每个角色说话方式不同(用词、语气、句式)
|
||
2. 对话要有潜台词(表面说A,实际想B)
|
||
3. 通过对话推进情节和展现角色性格
|
||
4. 符合角色的情感状态和人际关系
|
||
5. 自然口语化,不要书面语
|
||
|
||
**场景转换要求**:
|
||
1. 用角色的情感状态连接不同场景
|
||
2. 通过环境变化体现角色内心变化
|
||
3. 用过渡句避免突兀跳跃
|
||
4. 保持时间和空间的连贯性
|
||
5. 每个场景都要有存在的必要性
|
||
|
||
目标字数:至少 {config.target_total_chars} 字
|
||
|
||
请直接输出小说正文,不要任何说明或标记:"""
|
||
|
||
max_tokens = min(int(config.target_total_chars * 2.5), 8192)
|
||
|
||
# 生成初始故事
|
||
response = _call_generation(
|
||
model=config.model,
|
||
messages=[
|
||
{"role": "system", "content": "你是世界顶级的小说家,精通人性洞察和情感描写。你的作品逻辑严密,从不流水账,每个角色都活灵活现,每个情节都有深层逻辑,每个细节都服务于情感表达。你擅长通过细腻的心理描写和生动的细节描写创造强烈的代入感。"},
|
||
{"role": "user", "content": prompt}
|
||
],
|
||
temperature=config.temperature,
|
||
max_tokens=max_tokens
|
||
)
|
||
|
||
story = _clean_extra_lines(_safe_get_content(response))
|
||
|
||
# 应用反重复处理
|
||
story = detect_and_replace_repetitions(story)
|
||
|
||
# 进行逻辑检查和修复
|
||
if config.enable_logic_check:
|
||
for iteration in range(config.max_logic_iterations):
|
||
logger.info(f"进行第 {iteration + 1} 轮逻辑检查...")
|
||
|
||
# 检查逻辑一致性
|
||
is_consistent, issues, suggestions = _check_logic_consistency(story, bible, config)
|
||
|
||
if is_consistent:
|
||
logger.info("逻辑检查通过")
|
||
break
|
||
else:
|
||
logger.info(f"发现 {len(issues)} 个逻辑问题,开始修复...")
|
||
story = _fix_logic_issues(story, issues, suggestions, bible, config)
|
||
|
||
# 如果是最后一轮,即使有问题也不再修复
|
||
if iteration == config.max_logic_iterations - 1:
|
||
logger.warning("已达到最大修复轮数,停止逻辑修复")
|
||
|
||
return story
|
||
"""生成有深度的故事,解决所有常见问题"""
|
||
|
||
writing_system = _get_advanced_writing_system()
|
||
character_system = _get_character_development_system()
|
||
plot_system = _get_plot_development_system()
|
||
|
||
bible_text = json.dumps(bible, ensure_ascii=False, indent=2)
|
||
|
||
prompt = f"""你将创作一篇高质量的原创小说,必须解决AI写作的常见问题。
|
||
|
||
{writing_system}
|
||
|
||
{character_system}
|
||
|
||
{plot_system}
|
||
|
||
【故事设定】
|
||
{bible_text}
|
||
|
||
【创作要求 - 必须严格遵守】
|
||
|
||
1. **字数要求**:
|
||
- 必须生成至少8000字的内容
|
||
- 内容要丰富充实,不能敷衍了事
|
||
- 每个章节都要有足够的篇幅展开
|
||
|
||
2. **对话要求(重要)**:
|
||
- 对话内容必须占总文本的30%以上
|
||
- 每个角色说话方式必须不同
|
||
- 通过对话推进情节,展现性格
|
||
- 每1000字至少要有5-8段对话
|
||
|
||
3. **反流水账要求**:
|
||
- 每个场景都要有情感张力和内心冲突
|
||
- 用内心独白展现角色思考过程
|
||
- 通过具体细节暗示而非直接说明
|
||
- 让每个动作都有情感色彩和深层含义
|
||
|
||
4. **铺垫与因果要求**:
|
||
- 重要情节前必须有多层次铺垫
|
||
- 每个转折都要有合理原因和充分动机
|
||
- 用细节和情绪变化暗示即将发生的事
|
||
- 角色的选择要有内心挣扎和思考过程
|
||
|
||
5. **防重复要求(严格执行)**:
|
||
- 禁止重复使用"他知道"、"心中一沉"、"深吸一口气"等词汇
|
||
- 同一情感用不同方式表达(愤怒→恼火→暴躁→怒不可遏)
|
||
- 同一场景从不同角度描写(视觉→听觉→心理感受)
|
||
- 词汇和句式要有变化,避免重复表达
|
||
- 词汇和句式要有变化
|
||
- 情节要螺旋上升,不原地打转
|
||
|
||
4. **逻辑一致性要求**:
|
||
- 角色行为符合性格设定
|
||
- 前后情节不能矛盾
|
||
- 时间线要清晰合理
|
||
- 世界观规则要统一
|
||
|
||
5. **角色立体化要求**:
|
||
- 每个角色都有独特的说话方式
|
||
- 通过行为展现性格,不要直接说明
|
||
- 角色要有内心冲突和成长
|
||
- 配角也要有自己的目标和动机
|
||
|
||
6. **情感共鸣要求**:
|
||
- 挖掘普遍的人性体验
|
||
- 用具体细节唤起情感
|
||
- 展现角色的脆弱和坚强
|
||
- 让读者能感同身受
|
||
|
||
【具体写作指令】
|
||
|
||
**开篇500字必须包含**:
|
||
1. 主角的核心冲突(内心挣扎)
|
||
2. 独特的环境细节(不是泛泛描述)
|
||
3. 至少2个伏笔(为后续情节埋线)
|
||
4. 让读者对主角产生情感连接的细节
|
||
5. 制造悬念或期待
|
||
|
||
**每1000字检查**:
|
||
1. 是否推进了情感发展?
|
||
2. 是否深化了角色关系?
|
||
3. 是否有新的内心层次?
|
||
4. 是否避免了重复?
|
||
5. 是否保持了逻辑?
|
||
|
||
**对话要求**:
|
||
1. 每个角色说话方式不同(用词、语气、句式)
|
||
2. 对话要有潜台词(表面说A,实际想B)
|
||
3. 通过对话推进情节和展现性格
|
||
4. 符合角色的情感状态和关系
|
||
5. 自然口语化,不要书面语
|
||
|
||
**场景转换要求**:
|
||
1. 用角色的情感状态连接场景
|
||
2. 通过环境变化体现内心变化
|
||
3. 用过渡句避免突兀跳跃
|
||
4. 保持时间和空间的连贯性
|
||
|
||
目标字数:至少 {config.target_total_chars} 字
|
||
|
||
请直接输出小说正文,不要任何说明或标记:
|
||
"""
|
||
|
||
max_tokens = min(int(config.target_total_chars * 2.5), 8192)
|
||
response = _call_generation(
|
||
model=config.model,
|
||
messages=[
|
||
{"role": "system", "content": "你是世界顶级的小说家,精通人性洞察和情感描写。你的作品从不流水账,每个角色都活灵活现,每个情节都有深层逻辑,每个细节都服务于情感表达。你擅长通过细腻的心理描写和生动的细节描写创造强烈的代入感。"},
|
||
{"role": "user", "content": prompt}
|
||
],
|
||
temperature=config.temperature,
|
||
max_tokens=max_tokens
|
||
)
|
||
|
||
story = _clean_extra_lines(_safe_get_content(response))
|
||
return story
|
||
|
||
|
||
def _refine_and_expand(config: ViralNovelConfig, bible: Dict[str, Any], story: str) -> str:
|
||
"""精炼和扩展故事,确保质量和字数 - 优化版"""
|
||
current_length = _count_effective_characters(story)
|
||
|
||
if current_length >= config.target_total_chars:
|
||
return story
|
||
|
||
# 需要补充的字数
|
||
shortage = config.target_total_chars - current_length
|
||
logger.info(f"开始续写,当前字数: {current_length}, 目标字数: {config.target_total_chars}, 需要补充: {shortage}")
|
||
|
||
writing_system = _get_advanced_writing_system()
|
||
bible_text = json.dumps(bible, ensure_ascii=False, indent=2)
|
||
|
||
# 减少续写轮数,提高每轮质量
|
||
max_attempts = 6 # 从10次减少到6次
|
||
min_words_per_round = 2000 # 每轮最少2000字
|
||
|
||
for attempt in range(max_attempts):
|
||
if current_length >= config.target_total_chars:
|
||
logger.info(f"已达到目标字数: {current_length}")
|
||
break
|
||
|
||
# 计算本轮需要写的字数
|
||
remaining_rounds = max_attempts - attempt
|
||
words_this_round = max(min_words_per_round, shortage // remaining_rounds)
|
||
words_this_round = min(words_this_round, 8000) # 单轮最多8000字
|
||
|
||
# 获取故事结尾部分作为上下文
|
||
context = _get_smart_context(story, bible, shortage)
|
||
|
||
# 生成强化版反同质化指导
|
||
anti_repetition_guide = generate_anti_repetition_guidance(config.genre, attempt + 2)
|
||
|
||
# 检查当前故事是否已经有结尾
|
||
has_ending = _check_story_ending(story)
|
||
|
||
# 提取当前故事中的高频短语,用于生成时避免
|
||
existing_phrases = _extract_high_frequency_phrases(story)
|
||
forbidden_phrases_text = "、".join(existing_phrases[:15]) if existing_phrases else "无"
|
||
|
||
prompt = f"""请继续这个故事,必须补充至少 {words_this_round} 字的高质量内容。
|
||
|
||
⚠️ 【严格禁止重复以下已出现的高频短语】:
|
||
{forbidden_phrases_text}
|
||
|
||
⚠️ 【绝对禁止的表达方式】:
|
||
- 心头泛起、胸中涌起、情感汹涌、心中充满了
|
||
- 轻轻颔首、点了点头、深吸一口气、整理衣襟
|
||
- 凝视着、注视着、眼神中闪烁着、目光中透出
|
||
- 站起身、走上前、转过身、抬起头、低下头
|
||
- 与此同时、就在这时、不久之后、片刻之后
|
||
|
||
{anti_repetition_guide}
|
||
|
||
{writing_system}
|
||
|
||
【故事设定】
|
||
{bible_text}
|
||
|
||
【当前状态分析】
|
||
- 故事是否已有结尾:{'是' if has_ending else '否'}
|
||
- 本轮续写目标:{words_this_round} 字
|
||
- 剩余续写轮数:{remaining_rounds}
|
||
|
||
【续写要求 - 严格执行】
|
||
|
||
1. **字数要求**:
|
||
- 必须写够 {words_this_round} 字,不能敷衍了事
|
||
- 内容要丰富充实,有实质性推进
|
||
|
||
2. **内容质量要求**:
|
||
- 必须推进主线情节,不能原地打转
|
||
- 增加新的冲突、发现或角色发展
|
||
- 每个场景都要有存在的必要性
|
||
- 避免为了凑字数而重复描写
|
||
|
||
3. **对话要求**:
|
||
- 对话占比必须达到30%以上
|
||
- 每个角色说话方式必须不同
|
||
- 对话要推进情节,不能是空洞的哲理讨论
|
||
- 格式:所有对话使用中文引号 "角色说的话"
|
||
|
||
4. **反重复要求**:
|
||
- 绝对禁止重复之前出现过的情节
|
||
- 不能重复使用相同的场景(洞穴、山峰等)
|
||
- 避免重复的对话模式和动作描写
|
||
- 每个段落都要有新意
|
||
|
||
5. **结尾控制**:
|
||
{'- 故事已有结尾,不要再写结尾内容' if has_ending else '- 如果是最后一轮续写,可以适当收尾'}
|
||
- 不要出现"尾声"、"终章"等标题
|
||
- 让故事自然发展,不要强行煽情
|
||
|
||
6. **场景创新**:
|
||
- 尝试新的场景:城镇、学院、商铺、客栈等
|
||
- 每个场景要有独特的氛围和细节
|
||
- 通过环境变化体现角色内心变化
|
||
|
||
7. **角色互动**:
|
||
- 增加角色间的真实冲突和分歧
|
||
- 让配角有自己的想法,不只是附和主角
|
||
- 展现角色关系的发展和变化
|
||
|
||
【上文结尾】
|
||
{context}
|
||
|
||
【写作提示】
|
||
- 从上文自然衔接,不要突兀跳跃
|
||
- 用具体的事件推进情节
|
||
- 通过角色行为展现性格
|
||
- 用细节描写增加真实感
|
||
- 让每个对话都有目的性
|
||
|
||
请直接输出续写内容,确保质量和字数:
|
||
"""
|
||
|
||
# 根据需要补充的字数动态调整max_tokens
|
||
max_tokens = min(words_this_round * 3, 8192) # 给足够空间
|
||
|
||
logger.info(f"第 {attempt + 1} 轮续写,目标: {words_this_round} 字,max_tokens: {max_tokens}")
|
||
|
||
try:
|
||
response = _enhanced_call_generation(
|
||
model=config.model,
|
||
messages=[
|
||
{"role": "system", "content": "你是世界顶级的小说家,擅长写长篇小说。你必须严格按照字数要求进行创作,确保内容丰富、对话充分、情节精彩。\n\n【核心原则】:绝不能重复之前的内容,每一段都要有新的价值。\n\n【严格禁止】:\n- 任何形式的重复短语和表达\n- 套路化的动作描写(如:轻轻颔首、心头泛起、凝视着等)\n- 空洞的情感表达(如:心中充满了、眼神中闪烁着等)\n- 重复的对话模式和场景设置\n\n【必须做到】:\n- 每个动作、表情、心理活动都用全新的表达方式\n- 对话要有个性化,推进情节\n- 场景描写要具体生动,避免抽象概念\n- 情节必须向前推进,不能原地打转"},
|
||
{"role": "user", "content": prompt}
|
||
],
|
||
temperature=config.temperature,
|
||
max_tokens=max_tokens
|
||
)
|
||
|
||
additional_text = _clean_extra_lines(_safe_get_content(response))
|
||
|
||
# 严格检查生成内容是否包含禁用短语
|
||
forbidden_check_phrases = [
|
||
"轻轻颔首", "心头泛起", "胸中涌起", "情感汹涌", "凝视着",
|
||
"眼神中闪烁着", "深吸一口气", "点了点头", "整理衣襟",
|
||
"心中充满了", "眼中充满了", "站起身", "走上前", "转过身"
|
||
]
|
||
|
||
violation_count = 0
|
||
for phrase in forbidden_check_phrases:
|
||
if phrase in additional_text:
|
||
violation_count += 1
|
||
logger.warning(f"检测到禁用短语: {phrase}")
|
||
|
||
# 如果违规短语过多,重新生成
|
||
if violation_count >= 3:
|
||
logger.warning(f"生成内容包含 {violation_count} 个禁用短语,重新生成...")
|
||
if attempt < max_attempts - 1: # 不是最后一次尝试
|
||
continue # 重新生成
|
||
else:
|
||
logger.warning("已达最大重试次数,强制应用反重复处理")
|
||
|
||
except Exception as e:
|
||
error_str = str(e)
|
||
logger.error(f"第 {attempt + 1} 轮续写API调用失败: {error_str}")
|
||
|
||
# 如果是网络或超时问题,给用户明确提示
|
||
if any(keyword in error_str.lower() for keyword in ['timeout', 'network', 'connection']):
|
||
logger.error("网络连接问题,建议:")
|
||
logger.error("1. 检查网络连接")
|
||
logger.error("2. 稍后重试")
|
||
logger.error("3. 进度已保存,可以继续生成")
|
||
raise Exception(f"网络连接超时,进度已保存至 {config.target_total_chars*0.8:.0f}%,请稍后继续")
|
||
|
||
# 如果是限流问题
|
||
if 'rate limit' in error_str.lower():
|
||
logger.error("API调用频率过高,建议等待几分钟后重试")
|
||
raise Exception("API调用频率限制,请等待几分钟后重试")
|
||
|
||
# 其他错误,尝试继续下一轮
|
||
if attempt < max_attempts - 2: # 还有至少2轮机会
|
||
logger.warning(f"第 {attempt + 1} 轮续写失败,尝试下一轮")
|
||
continue
|
||
else:
|
||
raise Exception(f"续写过程中遇到问题: {error_str}")
|
||
|
||
if not additional_text or len(additional_text) < 500: # 如果续写内容太少
|
||
logger.warning(f"第 {attempt + 1} 轮续写内容不足,跳过")
|
||
continue
|
||
|
||
# 应用强化版反重复处理
|
||
additional_text = detect_and_replace_repetitions(additional_text)
|
||
|
||
# 检查是否有重复内容
|
||
if _check_content_repetition(story, additional_text):
|
||
logger.warning(f"第 {attempt + 1} 轮续写发现重复内容,跳过")
|
||
continue
|
||
|
||
story = _clean_extra_lines(story + "\n\n" + additional_text)
|
||
new_length = _count_effective_characters(story)
|
||
added_length = new_length - current_length
|
||
current_length = new_length
|
||
shortage = config.target_total_chars - current_length
|
||
|
||
logger.info(f"第 {attempt + 1} 轮续写完成,新增字数: {added_length}, 当前总字数: {current_length}, 剩余: {shortage}")
|
||
|
||
# 如果单次续写字数太少且还有很多轮次,继续
|
||
if added_length < min_words_per_round and attempt < max_attempts - 2:
|
||
logger.info("单次续写字数不足,继续下一轮")
|
||
continue
|
||
|
||
final_length = _count_effective_characters(story)
|
||
completion_rate = (final_length / config.target_total_chars) * 100
|
||
|
||
if completion_rate < 80:
|
||
logger.warning(f"续写完成,但字数不足。最终字数: {final_length}, 完成度: {completion_rate:.1f}%")
|
||
else:
|
||
logger.info(f"续写成功完成。最终字数: {final_length}, 完成度: {completion_rate:.1f}%")
|
||
|
||
return detect_and_replace_repetitions(story)
|
||
|
||
|
||
def _analyze_story_stage(story: str, bible: Dict[str, Any]) -> str:
|
||
"""分析当前故事发展到哪个阶段"""
|
||
|
||
current_length = _count_effective_characters(story)
|
||
|
||
# 基于字数的粗略阶段判断
|
||
if current_length < 5000:
|
||
stage = "开端阶段"
|
||
description = "故事刚刚开始,需要建立世界观、介绍主要角色、设置初始冲突"
|
||
elif current_length < 12000:
|
||
stage = "发展阶段"
|
||
description = "情节逐步展开,角色关系发展,冲突逐渐升级"
|
||
elif current_length < 20000:
|
||
stage = "深化阶段"
|
||
description = "主要冲突全面展开,角色面临重大挑战,情节复杂化"
|
||
elif current_length < 30000:
|
||
stage = "高潮阶段"
|
||
description = "故事达到高潮,主要冲突激化,角色做出关键决定"
|
||
else:
|
||
stage = "收尾阶段"
|
||
description = "冲突解决,角色成长完成,可以考虑适当收尾"
|
||
|
||
# 检查故事中的关键词来判断更精确的阶段
|
||
story_lower = story.lower()
|
||
|
||
# 检查是否有结尾迹象
|
||
ending_signs = ["解决", "战胜", "成功", "完成", "实现", "胜利", "和解", "团聚"]
|
||
has_resolution = any(sign in story[-1000:] for sign in ending_signs)
|
||
|
||
if has_resolution and current_length > 15000:
|
||
stage = "自然结尾阶段"
|
||
description = "主要冲突已解决,故事可以自然结束,但需要适当的情感升华和收尾"
|
||
|
||
return f"{stage}:{description}(当前字数:{current_length})"
|
||
|
||
|
||
def _check_story_completeness(story: str, bible: Dict[str, Any], min_chars: int) -> Tuple[bool, str]:
|
||
"""检查故事是否真正完整,而不仅仅是字数达标"""
|
||
|
||
current_length = _count_effective_characters(story)
|
||
|
||
# 1. 字数检查:至少要达到最小字数要求
|
||
if current_length < min_chars:
|
||
return False, f"字数不足,当前{current_length}字,最少需要{min_chars}字"
|
||
|
||
# 2. 检查是否有明显的结尾标志(但不是重复的套路结尾)
|
||
ending_indicators = [
|
||
"### 尾声", "### 终章", "### 结语", "### 完结", "### 结局",
|
||
"故事到此结束", "全文完", "(完)", "【完】"
|
||
]
|
||
|
||
has_ending_marker = any(indicator in story for indicator in ending_indicators)
|
||
|
||
# 3. 检查故事结构完整性
|
||
plot_elements = {
|
||
"开端": ["开始", "起初", "最初", "一开始", "故事开始"],
|
||
"发展": ["接着", "然后", "随后", "后来", "接下来", "与此同时"],
|
||
"高潮": ["关键时刻", "决定性", "最终", "终于", "关键", "决战", "最后的"],
|
||
"结局": ["最终", "终于", "最后", "结束", "完成", "实现", "成功"]
|
||
}
|
||
|
||
structure_score = 0
|
||
for element, keywords in plot_elements.items():
|
||
if any(keyword in story for keyword in keywords):
|
||
structure_score += 1
|
||
|
||
# 4. 检查主要情节线是否解决
|
||
main_conflicts = bible.get("plot_structure", {})
|
||
if main_conflicts:
|
||
climax_keywords = ["解决", "战胜", "克服", "成功", "胜利", "完成", "实现"]
|
||
has_resolution = any(keyword in story[-2000:] for keyword in climax_keywords) # 检查最后2000字
|
||
else:
|
||
has_resolution = True # 如果没有明确的冲突设定,默认为已解决
|
||
|
||
# 5. 综合判断
|
||
if structure_score >= 3 and has_resolution:
|
||
if has_ending_marker:
|
||
return True, "故事结构完整,有明确结尾"
|
||
elif current_length > min_chars * 1.5: # 如果字数超过最小要求的1.5倍
|
||
return True, "故事内容充实,可以自然结束"
|
||
else:
|
||
return False, "故事发展充分但缺少明确结尾"
|
||
else:
|
||
return False, f"故事结构不完整(结构得分:{structure_score}/4,冲突解决:{has_resolution})"
|
||
|
||
|
||
def _check_story_ending(story: str) -> bool:
|
||
"""检查故事是否已经有结尾"""
|
||
ending_indicators = [
|
||
"### 尾声", "### 终章", "### 结语", "### 完结",
|
||
"这就是", "的故事", "传奇故事", "未来还有更多的挑战",
|
||
"他已经找到了属于自己的力量", "准备好迎接这一切"
|
||
]
|
||
|
||
# 检查最后2000字符
|
||
ending_part = story[-2000:] if len(story) > 2000 else story
|
||
|
||
for indicator in ending_indicators:
|
||
if indicator in ending_part:
|
||
return True
|
||
|
||
return False
|
||
|
||
|
||
def _check_content_repetition(existing_story: str, new_content: str) -> bool:
|
||
"""检查新内容是否与现有故事重复"""
|
||
# 简单的重复检查
|
||
new_sentences = [s.strip() for s in new_content.split('。') if len(s.strip()) > 10]
|
||
existing_sentences = [s.strip() for s in existing_story.split('。') if len(s.strip()) > 10]
|
||
|
||
repetition_count = 0
|
||
for new_sentence in new_sentences:
|
||
for existing_sentence in existing_sentences:
|
||
similarity = _calculate_similarity(new_sentence, existing_sentence)
|
||
if similarity > 0.7: # 相似度超过70%
|
||
repetition_count += 1
|
||
break
|
||
|
||
# 如果超过30%的句子重复,认为是重复内容
|
||
repetition_rate = repetition_count / len(new_sentences) if new_sentences else 0
|
||
return repetition_rate > 0.3
|
||
|
||
|
||
def _get_smart_context(story: str, bible: Dict[str, Any], shortage: int) -> str:
|
||
"""智能获取上下文,简化版本"""
|
||
# 基础上下文长度
|
||
base_context_length = min(2000, len(story))
|
||
|
||
# 根据需要补充的字数调整上下文长度
|
||
if shortage > 3000:
|
||
context_length = min(3000, len(story))
|
||
elif shortage > 1500:
|
||
context_length = min(2500, len(story))
|
||
else:
|
||
context_length = base_context_length
|
||
|
||
# 获取结尾部分
|
||
context = story[-context_length:] if len(story) > context_length else story
|
||
|
||
# 添加关键角色信息
|
||
if bible and "main_character" in bible:
|
||
main_char = bible["main_character"]
|
||
char_info = f"\n【主角设定】\n姓名: {main_char.get('name', '主角')}\n性格: {main_char.get('personality_core', '')}\n动机: {main_char.get('inner_desire', '')}\n"
|
||
context = char_info + "\n" + context
|
||
|
||
return context
|
||
|
||
|
||
def _write_outputs(project_dir: str, title: str, story: str):
|
||
os.makedirs(project_dir, exist_ok=True)
|
||
story_path = os.path.join(project_dir, f"novel{os.path.basename(project_dir)[1:]}.txt")
|
||
|
||
with open(story_path, "w", encoding="utf-8") as f:
|
||
f.write(f"《{title}》\n\n{story}")
|
||
|
||
|
||
def main():
|
||
|
||
api_key = os.getenv('DASHSCOPE_API_KEY', '') or os.getenv('QWEN_API_KEY', '')
|
||
if not api_key:
|
||
print("\n❌ 未检测到 API Key。请设置环境变量 DASHSCOPE_API_KEY 或 QWEN_API_KEY")
|
||
return
|
||
|
||
# 检查是否有未完成的生成任务
|
||
base_dir = os.path.dirname(os.path.abspath(__file__))
|
||
resume_option = False
|
||
resume_dir = None
|
||
|
||
# 查找未完成的任务
|
||
for i in range(1, 100):
|
||
check_dir = os.path.join(base_dir, f"n{i}")
|
||
if os.path.exists(os.path.join(check_dir, ".generation_progress.json")):
|
||
progress = _load_generation_progress(check_dir)
|
||
if progress and progress.stage != "complete":
|
||
print(f"\n发现未完成的生成任务:")
|
||
print(f" 目录: {check_dir}")
|
||
print(f" 阶段: {progress.stage}")
|
||
print(f" 进度: {progress.progress_percent:.1f}%")
|
||
|
||
resume_choice = input("是否继续未完成的任务?(y/n,默认n): ").strip().lower()
|
||
if resume_choice == 'y':
|
||
resume_option = True
|
||
resume_dir = check_dir
|
||
break
|
||
|
||
if resume_option and resume_dir:
|
||
# 继续未完成的任务
|
||
try:
|
||
print(f"\n继续生成任务...")
|
||
result = generate_viral_novel_with_progress(
|
||
genre="", # 从进度中恢复
|
||
target_total_chars=0, # 从进度中恢复
|
||
project_dir=resume_dir,
|
||
resume=True
|
||
)
|
||
|
||
print("\n" + "=" * 70)
|
||
print("🎉 续写完成!")
|
||
print("=" * 70)
|
||
print(f"📖 标题: 《{result['title']}》")
|
||
print(f"📁 输出目录: {result['project_dir']}")
|
||
|
||
except Exception as e:
|
||
print(f"\n❌ 续写过程出错: {str(e)}")
|
||
logger.error(f"续写过程出错: {str(e)}", exc_info=True)
|
||
|
||
return
|
||
|
||
# 新的生成任务
|
||
# 直接使用精品长篇配置
|
||
if ENHANCED_FEATURES:
|
||
config = get_preset_config("精品长篇")
|
||
|
||
# 允许用户选择题材
|
||
config.genre = _choose_genre_enhanced()
|
||
else:
|
||
# 基础配置
|
||
genre = _choose_genre_enhanced()
|
||
target = input("\n请输入目标有效字数(建议8000-20000):").strip()
|
||
if not target.isdigit() or int(target) <= 0:
|
||
print("无效字数")
|
||
return
|
||
|
||
config = ViralNovelConfig(
|
||
genre=genre,
|
||
target_total_chars=int(target)
|
||
)
|
||
|
||
project_dir = _next_project_dir(os.path.dirname(os.path.abspath(__file__)))
|
||
|
||
print(f"\n开始生成高质量小说...")
|
||
print(f"题材: {config.genre}")
|
||
print(f"最小字数: {config.target_total_chars}(故事完整性优先,不设上限)")
|
||
print(f"使用模型: {config.model}")
|
||
if hasattr(config, 'temperature'):
|
||
print(f"创作温度: {config.temperature}")
|
||
if hasattr(config, 'enable_logic_check'):
|
||
print(f"逻辑检查: {'启用' if config.enable_logic_check else '禁用'}")
|
||
print(f"输出目录: {os.path.basename(project_dir)}/")
|
||
print(f"进度保存: 启用(支持中断恢复)")
|
||
print("\n正在创建立体角色和深度情节...")
|
||
print("提示: 可以随时按 Ctrl+C 中断,进度会自动保存")
|
||
|
||
try:
|
||
start_time = time.time()
|
||
|
||
# 使用新的带进度保存的生成函数
|
||
result = generate_viral_novel_with_progress(
|
||
genre=config.genre,
|
||
target_total_chars=config.target_total_chars,
|
||
model=config.model,
|
||
temperature=getattr(config, 'temperature', 0.8),
|
||
enable_logic_check=getattr(config, 'enable_logic_check', True),
|
||
project_dir=project_dir
|
||
)
|
||
|
||
generation_time = time.time() - start_time
|
||
final_len = _count_effective_characters(result['text'])
|
||
completion_rate = (final_len / config.target_total_chars) * 100
|
||
|
||
print("\n" + "=" * 70)
|
||
if completion_rate >= 80:
|
||
print("🎉 高质量小说生成完成!")
|
||
else:
|
||
print("⚠️ 小说生成完成(字数不足)")
|
||
print("=" * 70)
|
||
print(f"📖 标题: 《{result['title']}》")
|
||
print(f"📊 有效字数: {final_len}")
|
||
|
||
# 根据完成度显示不同的信息
|
||
if completion_rate >= 100:
|
||
print(f"📈 完成度: {completion_rate:.1f}% ✅ 超额完成")
|
||
elif completion_rate >= 80:
|
||
print(f"📈 完成度: {completion_rate:.1f}% ✅ 达标")
|
||
elif completion_rate >= 60:
|
||
print(f"📈 完成度: {completion_rate:.1f}% ⚠️ 基本达标")
|
||
else:
|
||
print(f"📈 完成度: {completion_rate:.1f}% ❌ 严重不足")
|
||
|
||
print(f"📁 输出目录: {result['project_dir']}")
|
||
print(f"🤖 使用模型: {config.model}")
|
||
print(f"⏱️ 生成耗时: {generation_time:.1f}秒")
|
||
|
||
print("=" * 70)
|
||
|
||
except KeyboardInterrupt:
|
||
print("\n\n⚠️ 用户中断生成")
|
||
print("📁 进度已自动保存,可以稍后继续")
|
||
|
||
except Exception as e:
|
||
error_str = str(e)
|
||
print(f"\n❌ 生成过程出错: {error_str}")
|
||
|
||
if "网络连接" in error_str or "超时" in error_str or "timeout" in error_str.lower():
|
||
print("\n🔧 网络问题解决建议:")
|
||
print("1. 检查网络连接是否稳定")
|
||
print("2. 等待几分钟后重试")
|
||
print("3. 如果问题持续,可能是API服务繁忙")
|
||
print("4. 进度已保存,下次运行时选择继续未完成的任务")
|
||
|
||
elif "rate limit" in error_str.lower():
|
||
print("\n⏰ API调用频率限制:")
|
||
print("1. 请等待几分钟后重试")
|
||
print("2. 避免频繁调用API")
|
||
print("3. 进度已保存,可以稍后继续")
|
||
|
||
else:
|
||
print(f"\n🔧 其他错误,请检查:")
|
||
print("1. API密钥是否正确")
|
||
print("2. 网络连接是否正常")
|
||
print("3. 是否有足够的磁盘空间")
|
||
|
||
logger.error(f"生成过程出错: {error_str}", exc_info=True)
|
||
print("📁 进度已保存,可以稍后继续生成")
|
||
|
||
|
||
def _generate_story_with_depth_fallback(config, bible: Dict[str, Any]) -> str:
|
||
"""备用的故事生成方法(兼容旧版本)"""
|
||
writing_system = _get_advanced_writing_system()
|
||
character_system = _get_enhanced_character_system()
|
||
plot_system = _get_enhanced_plot_system()
|
||
|
||
bible_text = json.dumps(bible, ensure_ascii=False, indent=2)
|
||
|
||
prompt = f"""你将创作一篇高质量的原创小说,必须解决AI写作的常见问题。
|
||
|
||
{writing_system}
|
||
|
||
{character_system}
|
||
|
||
{plot_system}
|
||
|
||
【故事设定】
|
||
{bible_text}
|
||
|
||
目标字数:至少 {config.target_total_chars} 字
|
||
|
||
请直接输出小说正文,不要任何说明或标记:"""
|
||
|
||
max_tokens = min(int(config.target_total_chars * 2.5), 8192)
|
||
|
||
response = _call_generation(
|
||
model=config.model,
|
||
messages=[
|
||
{"role": "system", "content": "你是世界顶级的小说家,精通人性洞察和情感描写。"},
|
||
{"role": "user", "content": prompt}
|
||
],
|
||
temperature=getattr(config, 'temperature', 0.8),
|
||
max_tokens=max_tokens
|
||
)
|
||
|
||
return _clean_extra_lines(_safe_get_content(response))
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main() |