├── .env.example ├── .gitignore ├── LICENSE ├── README.md ├── requirements.txt ├── src ├── __init__.py ├── agent.py └── prompts.py └── story_creation_example.py /.env.example: -------------------------------------------------------------------------------- 1 | # 浦语模型配置 2 | # 从 https://internlm-chat.intern-ai.org.cn/ 获取 3 | PUYU_API_KEY=your_puyu_api_key_here 4 | PUYU_BASE_URL=https://internlm-chat.intern-ai.org.cn/puyu/api/v1/ 5 | 6 | # 智谱AI配置 7 | # 从 https://open.bigmodel.cn/ 获取 8 | GLM_API_KEY=your_glm_api_key_here 9 | GLM_BASE_URL=https://open.bigmodel.cn/api/paas/v4/chat/completions 10 | 11 | # 日志级别设置 (可选) 12 | # 可选值: DEBUG, INFO, WARNING, ERROR, CRITICAL 13 | LOG_LEVEL=INFO 14 | 15 | # 输出配置 (可选) 16 | OUTPUT_DIR=output -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | story_output.json 3 | __pycache__/ 4 | *.pyc 5 | # 输出文件夹 6 | output/ 7 | # 提示词文件(如果需要保密) 8 | story_prompts.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 xiaoxiaoxiaotao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 智能小说创作助手 2 | 3 | 一个基于大型语言模型的智能小说创作工具,支持多种AI模型,能够自动生成完整的小说内容。 4 | 5 | ## 目录 6 | 7 | - [功能特点](#功能特点) 8 | - [安装说明](#安装说明) 9 | - [使用方法](#使用方法) 10 | - [输出说明](#输出说明) 11 | - [项目结构](#项目结构) 12 | - [自定义开发](#自定义开发) 13 | - [注意事项](#注意事项) 14 | - [许可证](#许可证) 15 | - [贡献指南](#贡献指南) 16 | - [更新日志](#更新日志) 17 | - [联系方式](#联系方式) 18 | 19 | ## 功能特点 20 | 21 | - 支持多种AI模型(浦语、智谱) 22 | - 自动生成故事主题和核心思想 23 | - 创建多维度的人物角色(包括主角、反派和配角) 24 | - 生成详细的故事大纲 25 | - 自动生成富有诗意的章节标题 26 | - 分段生成完整的故事内容 27 | - 自动保存JSON和TXT格式的输出 28 | 29 | ## 安装说明 30 | 31 | ### 步骤1: 克隆项目 32 | 33 | ```bash 34 | git clone https://github.com/xiaoxiaoxiaotao/novel-ai-agent-Chinese.git 35 | cd novel_ai_agent 36 | ``` 37 | 38 | ### 步骤2: 安装依赖 39 | 40 | ```bash 41 | pip install -r requirements.txt 42 | ``` 43 | 44 | ### 步骤3: 配置API密钥 45 | 46 | 复制`.env.example`为`.env`并在其中填入你的API密钥: 47 | 48 | #### 浦语模型配置 49 | 50 | ```plaintext 51 | PUYU_API_KEY=your_puyu_api_key 52 | PUYU_BASE_URL=your_puyu_base_url 53 | ``` 54 | 55 | #### 智谱AI配置 56 | 57 | ```plaintext 58 | GLM_API_KEY=your_glm_api_key 59 | GLM_BASE_URL=your_glm_base_url 60 | ``` 61 | 62 | ## 使用方法 63 | 64 | - **使用浦语模型创作故事** 65 | 66 | ```bash 67 | python story_creation_example.py --model puyu 68 | ``` 69 | 70 | - **使用智谱模型创作故事** 71 | 72 | ```bash 73 | python story_creation_example.py --model glm 74 | ``` 75 | 76 | ## 输出说明 77 | 78 | 程序会在`output`目录下生成两个文件: 79 | 80 | 1. **JSON文件(`story_output_{model_type}_{timestamp}.json`)**: 81 | - 包含完整的故事元数据,如主题、角色设定、大纲等。 82 | 83 | 2. **TXT文件(`story_{model_type}_{timestamp}.txt`)**: 84 | - 包含可读的故事内容,包括章节标题和正文。 85 | 86 | ## 项目结构 87 | 88 | ``` 89 | novel_ai_agent/ 90 | ├── src/ 91 | │ ├── __init__.py 92 | │ ├── agent.py # AI代理核心逻辑 93 | │ └── prompts.py # 提示词模板 94 | ├── output/ # 输出文件目录 95 | ├── .env # 环境配置文件 96 | ├── .gitignore # Git忽略文件 97 | ├── requirements.txt # 项目依赖 98 | ├── README.md # 项目说明 99 | └── story_creation_example.py # 示例脚本 100 | ``` 101 | 102 | ## 自定义开发 103 | 104 | - **修改提示词**:编辑`src/prompts.py`中的提示词模板以调整故事风格、长度等参数。 105 | - **调整输出格式**:修改`story_creation_example.py`中的输出处理逻辑来自定义输出文件的格式和内容。 106 | - **添加新的模型支持**:在`src/agent.py`中扩展`NovelAIAgent`类并添加新的API调用方式。 107 | 108 | ## 注意事项 109 | 110 | - 确保API密钥有效且有足够的配额。 111 | - 生成的内容可能需要人工审核。 112 | - 建议保留生成的JSON文件以便后续修改。 113 | - 注意遵守API使用条款和版权规定。 114 | 115 | ## 许可证 116 | 117 | 本项目采用MIT License许可协议。详情参见[LICENSE](LICENSE)文件。 118 | 119 | ## 贡献指南 120 | 121 | 欢迎提交Issue和Pull Request来改进项目。 122 | 123 | ## 更新日志 124 | 125 | ### v1.0.0 (2025-01-01) 126 | 127 | - 初始版本发布。 128 | - 支持浦语和智谱AI模型。 129 | - 实现基本的故事生成功能。 130 | 131 | ## 联系方式 132 | 133 | 如有问题或建议,请提交Issue或联系开发者。 -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | openai>=1.0.0 2 | python-dotenv>=0.19.0 3 | aiohttp>=3.8.0 4 | async-timeout>=4.0.0 5 | typing-extensions>=4.0.0 -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | # 空文件,用于标记包 -------------------------------------------------------------------------------- /src/agent.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Optional, List 2 | from openai import OpenAI 3 | from zhipuai import ZhipuAI 4 | import logging 5 | from .prompts import ( 6 | THEME_ANALYSIS_PROMPT, 7 | CHARACTER_DESIGN_PROMPT, 8 | STORY_OUTLINE_PROMPT, 9 | CONTENT_CREATION_SYSTEM_PROMPT, 10 | CHAPTER_SYNOPSIS_PROMPT, 11 | SETTING_GENERATION_PROMPT, 12 | TONE_ANALYSIS_PROMPT 13 | ) 14 | import json 15 | 16 | # 配置日志 17 | logging.basicConfig( 18 | level=logging.INFO, 19 | format='%(asctime)s - %(levelname)s - %(message)s' 20 | ) 21 | logger = logging.getLogger(__name__) 22 | 23 | class NovelAIAgent: 24 | def __init__(self, api_key: str, base_url: Optional[str] = None, model_type: str = "puyu"): 25 | """初始化小说创作智能代理""" 26 | logger.info(f"初始化NovelAIAgent... (model_type: {model_type})") 27 | 28 | self.model_type = model_type 29 | if model_type == "puyu": 30 | self.client = OpenAI(api_key=api_key, base_url=base_url) 31 | self.model = "internlm2.5-latest" 32 | else: # zhipu models 33 | self.client = ZhipuAI(api_key=api_key) 34 | # 根据任务复杂度选择不同的模型 35 | self.models = { 36 | "complex": "glm-4-plus", # 最复杂的任务:故事大纲、人物设计等 37 | "medium": "glm-4-air", # 中等复杂度:章节梗概、主题分析等 38 | "simple": "glm-4-flash" # 简单任务:具体内容生成等 39 | } 40 | self.model = self.models["medium"] # 设置默认模型 41 | 42 | logger.info(f"API配置完成: model={self.model}") 43 | 44 | self.current_story = { 45 | "title": "", 46 | "themes": [], 47 | "setting": "", 48 | "characters": "", 49 | "tone": "", 50 | "outline": "", 51 | "synopses": "", 52 | "content": [] 53 | } 54 | 55 | async def _call_api(self, messages: List[Dict], complexity: str = "medium") -> str: 56 | """根据任务复杂度调用不同的模型""" 57 | try: 58 | if self.model_type == "puyu": 59 | response = self.client.chat.completions.create( 60 | model=self.model, 61 | messages=messages 62 | ) 63 | else: # zhipu models 64 | model = self.models.get(complexity, self.models["medium"]) 65 | response = self.client.chat.completions.create( 66 | model=model, 67 | messages=messages 68 | ) 69 | return response.choices[0].message.content 70 | except Exception as e: 71 | logger.error(f"API调用出错: {str(e)}") 72 | raise 73 | 74 | async def _generate_chapter_synopses(self, meta_info: Dict) -> str: 75 | """分阶段生成章节梗概""" 76 | try: 77 | logger.info("开始生成章节梗概...") 78 | all_synopses = [] 79 | stages = ["起", "承", "转", "合", "终"] 80 | 81 | for stage_index, stage in enumerate(stages, 1): 82 | logger.info(f"正在生成第{stage_index}阶段({stage})的章节梗概...") 83 | 84 | # 计算本阶段的章节编号范围 85 | start_chapter = (stage_index - 1) * 10 + 1 86 | end_chapter = start_chapter + 9 87 | 88 | response = await self._call_api([ 89 | {"role": "system", "content": "你是一位优秀的故事规划师,擅长设计扣人心弦的情节。"}, 90 | {"role": "user", "content": f""" 91 | 请为小说的第{stage_index}阶段({stage})创作10个章节的详细梗概。 92 | 93 | 小说基本信息: 94 | 标题:{meta_info.get('title', '未命名')} 95 | 主题:{', '.join(meta_info.get('themes', []))} 96 | 世界观:{meta_info.get('setting', '')} 97 | 主要人物:{meta_info.get('characters', '')} 98 | 99 | 本阶段要求: 100 | 1. 创作第{start_chapter}章到第{end_chapter}章的梗概 101 | 2. 每章梗概200字左右 102 | 3. 体现阶段性特点: 103 | - 起:引入故事、展示天赋、初入宗门 104 | - 承:磨练成长、结交好友、树敌对手 105 | - 转:身世之谜、实力暴涨、危机显现 106 | - 合:真相揭露、大战爆发、逆境突破 107 | - 终:终极决战、拯救世界、圆满收官 108 | 109 | 4. 爽点要求: 110 | - 每章都要有意外或反转 111 | - 实力要循序渐进提升 112 | - 设置合理的打脸情节 113 | - 制造期待和悬念 114 | 115 | 请按以下格式输出10个章节的梗概: 116 | 117 | 【第{start_chapter}章:章节标题】 118 | [详细梗概,包含地点、人物、事件、转折] 119 | 120 | 【第{start_chapter + 1}章:章节标题】 121 | [详细梗概] 122 | ... 123 | """}], complexity="complex") 124 | 125 | all_synopses.append(response) 126 | logger.info(f"第{stage_index}阶段章节梗概生成完成") 127 | 128 | # 合并所有阶段的梗概 129 | complete_synopses = "\n\n".join(all_synopses) 130 | logger.info("所有章节梗概生成完成") 131 | return complete_synopses 132 | 133 | except Exception as e: 134 | logger.error(f"生成章节梗概时出错: {str(e)}") 135 | raise 136 | 137 | async def _generate_chapters_content(self, meta_info: Dict, chapter_synopses: str) -> List[str]: 138 | """生成所有章节的具体内容,返回章节列表""" 139 | try: 140 | logger.info("开始生成章节内容...") 141 | chapters = [] 142 | 143 | # 字数要求配置 144 | REQUIRED_WORDS = 3000 # 要求模型生成的字数 145 | MIN_WORDS = 2000 # 实际检查的最小字数 146 | 147 | # 解析章节梗概 148 | synopses_list = chapter_synopses.split("【第") 149 | synopses_list = [s for s in synopses_list if s.strip()] # 移除空字符串 150 | 151 | for i, synopsis in enumerate(synopses_list, 1): 152 | logger.info(f"正在生成第{i}章内容...") 153 | 154 | # 提取章节标题和梗概内容 155 | try: 156 | chapter_parts = synopsis.split("】\n", 1) 157 | title_parts = chapter_parts[0].split("章:", 1) 158 | chapter_title = f"第{title_parts[0]}章:{title_parts[1] if len(title_parts) > 1 else '未命名'}" 159 | chapter_content = chapter_parts[1].strip() if len(chapter_parts) > 1 else "" 160 | except Exception as e: 161 | logger.warning(f"解析章节{i}梗概时出错: {str(e)}") 162 | chapter_title = f"第{i}章" 163 | chapter_content = synopsis 164 | 165 | # 添加重试机制 166 | max_retries = 3 167 | retry_count = 0 168 | content = "" 169 | 170 | while retry_count < max_retries: 171 | response = await self._call_api([ 172 | {"role": "system", "content": CONTENT_CREATION_SYSTEM_PROMPT}, 173 | {"role": "user", "content": f""" 174 | 请根据以下信息创作小说章节的具体内容: 175 | 176 | 小说基本信息: 177 | 标题:{meta_info.get('title', '未命名')} 178 | 主题:{', '.join(meta_info.get('themes', []))} 179 | 基调:{meta_info.get('tone', '')} 180 | 181 | 本章信息: 182 | {chapter_title} 183 | 184 | 本章梗概: 185 | {chapter_content} 186 | 187 | 主要人物: 188 | {meta_info.get('characters', '')} 189 | 190 | 创作要求: 191 | 1. 字数要求:必须超过{REQUIRED_WORDS}字,建议2500-3500字 192 | - 如果内容不足{REQUIRED_WORDS}字,请继续补充 193 | - 保持情节的完整性和连贯性 194 | - 不要为凑字数而冗长 195 | 196 | 2. 情节结构: 197 | - 反转与震撼(35%):意外展现、打脸装逼 198 | - 期待与升级(35%):能力提升、资源获取 199 | - 场景描写(20%):震撼场面、众人反应 200 | - 对话设计(10%):金句对决、暗藏玄机 201 | 202 | 3. 爽感要求: 203 | - 情节要出人意料 204 | - 打脸要干脆利落 205 | - 升级要令人期待 206 | - 反转要令人震惊 207 | 208 | 4. 写作要点: 209 | - 合理铺垫伏笔 210 | - 制造期待感 211 | - 突出震撼效果 212 | - 保持爽感节奏 213 | 214 | 请直接开始创作本章正文,确保字数超过{REQUIRED_WORDS}字: 215 | """}], complexity="complex") 216 | 217 | content = response.strip() 218 | word_count = len(content) 219 | 220 | if word_count >= MIN_WORDS: # 使用较低的阈值进行检查 221 | logger.info(f"第{i}章生成成功,字数:{word_count}") 222 | break 223 | else: 224 | retry_count += 1 225 | logger.warning(f"第{i}章字数不足({word_count}字),第{retry_count}次重试...") 226 | 227 | if word_count < MIN_WORDS: 228 | logger.error(f"第{i}章生成失败,字数不足:{word_count}字") 229 | 230 | # 格式化章节内容,确保标题格式统一 231 | formatted_chapter = f"{chapter_title}\n\n{content}\n" 232 | chapters.append(formatted_chapter) 233 | logger.info(f"第{i}章内容生成完成") 234 | 235 | logger.info("所有章节内容生成完成") 236 | return chapters 237 | except Exception as e: 238 | logger.error(f"生成章节内容时出错: {str(e)}") 239 | raise 240 | 241 | async def create_story(self, prompt: str) -> Dict: 242 | """创建完整的故事""" 243 | try: 244 | logger.info("开始创建新故事...") 245 | 246 | # 1. 分析主题(复杂任务) 247 | logger.info("Step 1/6: 分析故事主题...") 248 | themes_content = await self._call_api([ 249 | {"role": "system", "content": THEME_ANALYSIS_PROMPT}, 250 | {"role": "user", "content": prompt} 251 | ], complexity="complex") 252 | 253 | # 解析主题 254 | themes = [theme.strip() for theme in themes_content.split('\n') if theme.strip()] 255 | self.current_story["themes"] = themes 256 | logger.info(f"主题分析完成: {themes}") 257 | 258 | # 2. 创建世界观设定(复杂任务) 259 | logger.info("Step 2/6: 创建世界观设定...") 260 | setting = await self._call_api([ 261 | {"role": "system", "content": SETTING_GENERATION_PROMPT}, 262 | {"role": "user", "content": f"故事提示:{prompt}\n主题:{themes}"} 263 | ], complexity="complex") 264 | 265 | self.current_story["setting"] = setting 266 | logger.info("世界观设定完成") 267 | 268 | # 3. 设计角色(复杂任务) 269 | logger.info("Step 3/6: 设计角色...") 270 | characters = await self._call_api([ 271 | {"role": "system", "content": CHARACTER_DESIGN_PROMPT}, 272 | {"role": "user", "content": f"故事提示:{prompt}\n主题:{themes}\n世界观:{setting}"} 273 | ], complexity="complex") 274 | 275 | self.current_story["characters"] = characters 276 | logger.info("角色设计完成") 277 | 278 | # 4. 创建故事大纲(复杂任务) 279 | logger.info("Step 4/6: 创建故事大纲...") 280 | outline = await self._call_api([ 281 | {"role": "system", "content": STORY_OUTLINE_PROMPT}, 282 | {"role": "user", "content": f"故事提示:{prompt}\n主题:{themes}\n世界观:{setting}\n角色:{characters}"} 283 | ], complexity="complex") 284 | 285 | self.current_story["outline"] = outline 286 | logger.info("故事大纲创建完成") 287 | 288 | # 5. 生成章节梗概(复杂任务) 289 | logger.info("Step 5/6: 生成章节梗概...") 290 | chapter_synopses = await self._generate_chapter_synopses(self.current_story) 291 | self.current_story["synopses"] = chapter_synopses 292 | 293 | # 6. 生成具体内容(复杂任务) 294 | logger.info("Step 6/6: 生成详细故事内容...") 295 | story_content = await self._generate_chapters_content( 296 | meta_info=self.current_story, 297 | chapter_synopses=chapter_synopses 298 | ) 299 | self.current_story["content"] = story_content 300 | 301 | logger.info("故事创作完成") 302 | return self.current_story 303 | 304 | except Exception as e: 305 | logger.error(f"故事创作过程出错: {str(e)}") 306 | raise 307 | 308 | def create_agent(model_type: str, api_key: str, base_url: Optional[str] = None) -> NovelAIAgent: 309 | """创建AI代理""" 310 | return NovelAIAgent(api_key=api_key, base_url=base_url, model_type=model_type) -------------------------------------------------------------------------------- /src/prompts.py: -------------------------------------------------------------------------------- 1 | """ 2 | 存储所有用于故事生成的prompt模板 3 | """ 4 | 5 | from typing import Dict 6 | 7 | THEME_ANALYSIS_PROMPT = """作为一位深刻的文学分析家,请从提供的故事提示中提炼出核心主题和哲学思考。 8 | 要求: 9 | 1. 每个主题必须以简洁的一句话表达 10 | 2. 主题应该包含: 11 | - 核心矛盾(如人性与科技的冲突) 12 | - 伦理困境(如个人利益与集体利益的选择) 13 | - 哲学思考(如存在主义、自由意志等) 14 | 3. 确保主题与故事提示紧密相关 15 | 4. 每个主题都要有现实意义和普遍性 16 | 17 | 请严格按照以下格式返回3-5个主题: 18 | 1. [第一个主题] 19 | 2. [第二个主题] 20 | 3. [第三个主题] 21 | ... 22 | """ 23 | 24 | CHARACTER_DESIGN_PROMPT = """作为一位角色设计大师,请创建一个复杂的角色关系网。要求: 25 | 26 | 1. 主要角色(至少10个): 27 | A. 主角: 28 | - 基本设定(身份背景、性格特点) 29 | - 成长轨迹(每阶段的变化) 30 | - 核心能力(战斗特色、独特技能) 31 | - 关键道具(玉佩等重要物品) 32 | 33 | B. 各阶段BOSS(5个): 34 | - 身份背景和实力 35 | - 与主角的对立点 36 | - 掌握的特殊能力 37 | - 背后的势力 38 | - 与真相的关联 39 | 40 | C. 重要盟友(至少4个): 41 | - 与主角的相遇契机 42 | - 独特的能力特点 43 | - 性格和行为模式 44 | - 对主角的帮助 45 | - 个人的故事线 46 | 47 | 2. 次要角色(至少30个): 48 | A. 宗门人物(至少10个): 49 | - 长老、师兄师姐 50 | - 对手和竞争者 51 | - 暗中帮助者 52 | 53 | B. 各方势力人物(至少10个): 54 | - 其他宗门高手 55 | - 神秘组织成员 56 | - 江湖势力首领 57 | 58 | C. 普通配角(至少10个): 59 | - 商人、镖师 60 | - 普通百姓 61 | - 关键线索提供者 62 | 63 | 3. 角色关系网: 64 | A. 明面关系: 65 | - 师徒关系 66 | - 朋友关系 67 | - 对手关系 68 | 69 | B. 隐藏关系: 70 | - 血缘联系 71 | - 过往恩怨 72 | - 势力归属 73 | 74 | C. 动态关系: 75 | - 敌人转盟友 76 | - 盟友变对手 77 | - 身份反转 78 | 79 | 4. 每个阶段的人物配置: 80 | 第一阶段: 81 | - 本阶段BOSS:xxx(特点、实力) 82 | - 新登场盟友:xxx(特点、作用) 83 | - 重要配角:xxx(特点、关系) 84 | 85 | 第二阶段: 86 | [类似结构,依次类推...] 87 | 88 | 5. 注意事项: 89 | - 每个角色要有独特性格 90 | - 安排合理的实力层级 91 | - 设计复杂的人际关系 92 | - 为后续反转埋伏笔 93 | - 主要角色要有成长空间 94 | """ 95 | 96 | STORY_OUTLINE_PROMPT = """作为一位优秀的故事架构师,请设计一个结构完整的长篇故事大纲。要求: 97 | 98 | 1. 整体结构设计(硬性要求): 99 | - 分为5个阶段,每阶段10章,共50章 100 | - 每个阶段设置一个最终BOSS 101 | - 每阶段引入新的盟友和对手 102 | - 逐步揭示更大的阴谋 103 | 104 | 2. 每阶段设计要求: 105 | 第一阶段(1-10章)- 初入修行: 106 | - 发现火焰能力 107 | - 进入宗门历练 108 | - 结识初始同伴 109 | - 对抗第一个BOSS 110 | - 获得重要线索 111 | 112 | 第二阶段(11-20章)- 实力提升: 113 | - 新的修炼体系 114 | - 结识高阶盟友 115 | - 经历重要考验 116 | - 击败新的BOSS 117 | - 揭示更大阴谋 118 | 119 | 第三阶段(21-30章)- 真相显现: 120 | - 身世之谜浮现 121 | - 新的能力觉醒 122 | - 盟友阵营扩大 123 | - 挑战强大BOSS 124 | - 发现惊天秘密 125 | 126 | 第四阶段(31-40章)- 势力对抗: 127 | - 各方势力汇聚 128 | - 朋友敌人反转 129 | - 关键秘密揭晓 130 | - 决战阶段BOSS 131 | - 准备最终一战 132 | 133 | 第五阶段(41-50章)- 终极决战: 134 | - 集结所有力量 135 | - 真相完全揭露 136 | - 终极BOSS现身 137 | - 最后的大决战 138 | - 完美的结局 139 | 140 | 3. 每章要求: 141 | - 字数:不少于2500字 142 | - 包含具体时间地点 143 | - 明确的情节推进 144 | - 人物互动丰富 145 | - 为后续铺垫 146 | 147 | 4. 爽点设计: 148 | - 每章都有精彩战斗或反转 149 | - 实力提升要循序渐进 150 | - 设置合理的打脸情节 151 | - 制造期待和悬念 152 | - BOSS战要出人意料 153 | 154 | 5. 注意事项: 155 | - 每个阶段都要有明确目标 156 | - 各条线索要环环相扣 157 | - BOSS难度要递进提升 158 | - 盟友要适时帮助 159 | - 为反转预留空间 160 | """ 161 | 162 | CONTENT_CREATION_SYSTEM_PROMPT = """你是一位擅长创作爽文的小说家,特别注重通过情节反转和人物逆袭来制造震撼效果。创作要求: 163 | 164 | 1. 情节设计(重要性排序): 165 | - 反转与震撼(35%): 166 | * 出人意料的能力展现 167 | * 身份地位的突然转变 168 | * 打脸装逼打击对手 169 | * 令众人震惊的场面 170 | * 意想不到的剧情转折 171 | 172 | - 期待与升级(35%): 173 | * 能力的不断提升 174 | * 资源的持续积累 175 | * 身份地位的逐步提高 176 | * 真相的逐步揭露 177 | * 为下一个高潮铺垫 178 | 179 | - 场景描写(20%): 180 | * 震撼性的场面描绘 181 | * 众人反应的细节 182 | * 气氛的渲染烘托 183 | * 环境与情节的呼应 184 | 185 | - 对话设计(10%): 186 | * 犀利的对话交锋 187 | * 有力的反击台词 188 | * 装逼打脸的金句 189 | * 引发期待的暗示 190 | 191 | 2. 爽感营造技巧: 192 | - 反转设计: 193 | * 表面平淡下的实力隐藏 194 | * 关键时刻的能力爆发 195 | * 身份地位的突然揭露 196 | * 局势的戏剧性逆转 197 | 198 | - 升级节奏: 199 | * 循序渐进的实力提升 200 | * 一波接一波的打脸 201 | * 层层递进的矛盾升级 202 | * 不断加码的奖励收获 203 | 204 | 3. 人物塑造: 205 | - 主角特点: 206 | * 智商在线,有谋略 207 | * 能力突出,有亮点 208 | * 隐藏实力,有底牌 209 | * 进步神速,有潜力 210 | 211 | - 配角设计: 212 | * 对手要够嚣张跋扈 213 | * 路人要有震惊反应 214 | * 支持者要适时现身 215 | * 反派要有实力威胁 216 | 217 | 4. 情节节奏: 218 | - 高潮设计: 219 | * 实力展现要震撼 220 | * 打脸场面要痛快 221 | * 反转剧情要意外 222 | * 升级奖励要给力 223 | 224 | - 铺垫过渡: 225 | * 为反转做好铺垫 226 | * 为升级预留空间 227 | * 为打脸设置情境 228 | * 为高潮做好准备 229 | 230 | 5. 注意事项: 231 | - 反转要有合理性 232 | - 升级要有阶段感 233 | - 打脸要有快感 234 | - 期待要有延续性 235 | - 避免过度夸张 236 | """ 237 | 238 | def get_chapter_content_prompt(meta_info: Dict, chapter_synopsis: str, chapter_num: int) -> str: 239 | """生成具体章节内容的prompt""" 240 | return f"""作为小说创作者,请基于以下信息创作第{chapter_num}章的具体内容。 241 | 242 | 小说基本信息: 243 | 1. 标题:{meta_info['title']} 244 | 2. 主题:{meta_info['theme']} 245 | 3. 世界观设定: 246 | {meta_info['setting']} 247 | 248 | 4. 主要人物: 249 | {meta_info['characters']} 250 | 251 | 5. 写作基调: 252 | {meta_info['tone']} 253 | 254 | 本章信息: 255 | 1. 章节标题:{meta_info['chapters'][chapter_num-1]['title']} 256 | 2. 本章梗概: 257 | {chapter_synopsis} 258 | 259 | 写作要求: 260 | 1. 字数控制在2500字左右 261 | 2. 场景描写要具体,注意细节 262 | 3. 对话要自然,展现人物性格 263 | 4. 情节发展要符合人物性格和故事逻辑 264 | 5. 要有适当的悬念和铺垫 265 | 6. 注意与前后章节的连贯性 266 | 7. 符合整体的写作基调 267 | 268 | 请创作本章的具体内容。 269 | """ 270 | 271 | SECTION_TITLE_PROMPT = """请为这个故事的五个主要部分生成富有诗意和象征意义的标题。每个标题要: 272 | 1. 简洁有力(2-3个字) 273 | 2. 与内容相呼应 274 | 3. 有意象和象征 275 | 4. 能反映情节发展 276 | 5. 前后有关联性 277 | 278 | 已知五个部分的主要内容: 279 | 1. 起因部分:介绍背景、人物、引发事件 280 | 2. 经过部分:矛盾初显、关系交织 281 | 3. 发展部分:冲突加剧、形势恶化 282 | 4. 高潮部分:终极对决、重大转折 283 | 5. 结局部分:问题解决、回归平静 284 | 285 | 请按照以下格式返回: 286 | { 287 | "起因": "《标题》", 288 | "经过": "《标题》", 289 | "发展": "《标题》", 290 | "高潮": "《标题》", 291 | "结局": "《标题》" 292 | } 293 | """ 294 | 295 | TITLE_GENERATION_PROMPT = """作为一位专业的小说标题设计师,请为这个故事创作一个引人注目的标题。要求: 296 | 297 | 1. 标题要: 298 | - 简洁有力(不超过10个字) 299 | - 富有诗意和想象力 300 | - 体现故事的核心主题 301 | - 引发读者好奇 302 | - 暗示故事的基调 303 | 304 | 2. 同时提供: 305 | - 中文标题 306 | - 英文标题(如果适用) 307 | - 副标题(可选) 308 | 309 | 请按照以下格式返回: 310 | { 311 | "main_title": "主标题", 312 | "sub_title": "副标题(可选)", 313 | "english_title": "English Title" 314 | } 315 | """ 316 | 317 | SETTING_GENERATION_PROMPT = """作为一位世界观构建大师,请为这个故事创建详细的背景设定。要求: 318 | 319 | 1. 时间背景: 320 | - 具体年代或时期 321 | - 重要的历史事件或背景 322 | - 社会发展阶段 323 | 324 | 2. 地理环境: 325 | - 具体地点描述 326 | - 环境特征 327 | - 气候条件 328 | - 标志性场所 329 | 330 | 3. 社会背景: 331 | - 社会制度 332 | - 科技水平 333 | - 文化特征 334 | - 主要矛盾 335 | 336 | 4. 特殊元素: 337 | - 独特的设定 338 | - 重要的道具或系统 339 | - 世界运行规则 340 | 341 | 请以散文形式详细描述以上各个方面,确保前后连贯,细节丰富。 342 | """ 343 | 344 | DIALOGUE_GENERATION_PROMPT = """作为对话写作专家,请创作生动的对话场景。要求: 345 | 346 | 1. 对话要求: 347 | - 展现人物性格 348 | - 推动情节发展 349 | - 制造戏剧冲突 350 | - 揭示人物关系 351 | - 埋设关键信息 352 | 353 | 2. 对话技巧: 354 | - 个性化的说话方式 355 | - 适当的动作描写 356 | - 自然的语气语调 357 | - 暗示和潜台词 358 | - 节奏的控制 359 | 360 | 3. 场景描写: 361 | - 环境氛围 362 | - 人物表情 363 | - 肢体语言 364 | - 细节刻画 365 | """ 366 | 367 | PACING_CONTROL_PROMPT = """作为节奏掌控大师,请设计故事的节奏安排: 368 | 369 | 1. 总体节奏: 370 | - 开端:徐缓铺陈 371 | - 发展:逐步加快 372 | - 高潮:紧张激烈 373 | - 结局:舒缓回落 374 | 375 | 2. 场景节奏: 376 | - 重要场景详写 377 | - 过渡场景略写 378 | - 紧张与舒缓交替 379 | - 适时留白 380 | 381 | 3. 情节推进: 382 | - 悬念的设置 383 | - 冲突的递进 384 | - 高潮的预设 385 | - 结局的伏笔 386 | """ 387 | 388 | CHAPTER_SYNOPSIS_PROMPT = """作为一位故事规划大师,请根据已生成的目录,为每个章节创作详细的梗概。要求: 389 | 390 | 1. 内容要求: 391 | - 每个章节的梗概要包含完整的情节发展 392 | - 明确的时间、地点和场景转换 393 | - 具体的人物行动和互动 394 | - 重要的对话和心理描写 395 | - 与整体故事的关联 396 | 397 | 2. 结构要求: 398 | - 每个章节的梗概约300-500字 399 | - 要有清晰的起承转合 400 | - 要体现章节主题 401 | - 要为具体写作预留发挥空间 402 | 403 | 3. 连贯性要求: 404 | - 前后章节要紧密联系 405 | - 人物形象要连贯 406 | - 情节发展要合理 407 | - 伏笔和悬念要统一布局 408 | 409 | 4. 节奏要求: 410 | - 把控整体故事节奏 411 | - 安排情节的轻重缓急 412 | - 设计高潮和转折点 413 | - 注意场景的切换节奏 414 | 415 | 已有目录: 416 | {chapter_titles} 417 | 418 | 请按以下格式输出所有章节的梗概: 419 | 420 | 【第一章:xxx】 421 | [详细梗概,包含时间地点、人物行动、关键对话等] 422 | 423 | 【第二章:xxx】 424 | [详细梗概] 425 | 426 | ... 427 | 428 | 注意: 429 | 1. 所有章节的梗概要在一次生成中完成,确保整体连贯 430 | 2. 每个梗概要为后续写作提供足够的细节 431 | 3. 要体现故事的整体节奏和主题 432 | 4. 为每个章节预设合理的冲突和转折 433 | """ 434 | 435 | TONE_ANALYSIS_PROMPT = """作为一位文学风格分析专家,请分析这个故事应该采用的基调。要求: 436 | 437 | 1. 确定整体写作风格: 438 | - 叙事语气(如:热血、冷静、幽默等) 439 | - 情感色彩(如:温暖、压抑、激昂等) 440 | - 表达方式(如:直白、含蓄、夸张等) 441 | 442 | 2. 分析适合的写作手法: 443 | - 描写方式 444 | - 对话风格 445 | - 节奏控制 446 | - 氛围营造 447 | 448 | 请用简洁的语言描述故事的基调,确保与主题和内容相匹配。 449 | """ -------------------------------------------------------------------------------- /story_creation_example.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import os 3 | from dotenv import load_dotenv 4 | from src.agent import NovelAIAgent, create_agent 5 | import json 6 | import logging 7 | from datetime import datetime 8 | import argparse 9 | 10 | # 加载.env文件 11 | load_dotenv() 12 | 13 | logger = logging.getLogger(__name__) 14 | 15 | # 模型配置 16 | MODEL_CONFIGS = { 17 | "puyu": { 18 | "api_key": os.getenv("PUYU_API_KEY"), 19 | "base_url": os.getenv("PUYU_BASE_URL"), 20 | "model_type": "puyu", 21 | "model": "internlm2.5-latest" 22 | }, 23 | "glm": { 24 | "api_key": os.getenv("GLM_API_KEY"), 25 | "base_url": os.getenv("GLM_BASE_URL"), 26 | "model_type": "glm", 27 | "model": "glm-4-flash" 28 | } 29 | } 30 | 31 | def load_story_prompt(genre: str) -> str: 32 | """加载并处理故事提示词""" 33 | try: 34 | with open("story_prompts.txt", "r", encoding="utf-8") as f: 35 | template = f.read() 36 | 37 | # 替换题材 38 | prompt = template.replace("{题材}", genre) 39 | 40 | # 构建完整的提示词 41 | formatted_prompt = f""" 42 | 请根据以下模板创作一个{genre}小说: 43 | 44 | {prompt} 45 | 46 | 请确保故事情节合理,人物性格鲜明,冲突激烈,结构完整。 47 | """ 48 | return formatted_prompt 49 | 50 | except Exception as e: 51 | logger.error(f"加载故事提示词时出错: {str(e)}") 52 | raise 53 | 54 | async def create_sample_story(model_type: str = "puyu", genre: str = "科幻"): 55 | # 获取对应的模型配置 56 | config = MODEL_CONFIGS.get(model_type) 57 | if not config: 58 | raise ValueError(f"不支持的模型类型: {model_type},请选择 'puyu' 或 'glm'") 59 | 60 | if not config["api_key"]: 61 | raise ValueError(f"请在.env文件中设置{model_type.upper()}_API_KEY") 62 | 63 | # 初始化AI代理 64 | agent = NovelAIAgent( 65 | api_key=config["api_key"], 66 | base_url=config.get("base_url"), 67 | model_type=model_type 68 | ) 69 | 70 | # 加载故事提示词 71 | prompt = load_story_prompt(genre) 72 | 73 | # 创建故事 74 | story = await agent.create_story(prompt) 75 | 76 | # 创建输出目录结构 77 | timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") 78 | output_base = os.path.join("output", f"story_{model_type}_{timestamp}") 79 | os.makedirs(output_base, exist_ok=True) 80 | chapters_dir = os.path.join(output_base, "chapters") 81 | os.makedirs(chapters_dir, exist_ok=True) 82 | 83 | # 准备元数据 84 | meta_info = { 85 | "model_type": model_type, 86 | "model_name": config["model"], 87 | "creation_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), 88 | "title": story.get("title", ""), 89 | "theme": story.get("themes", []), 90 | "setting": story.get("setting", ""), 91 | "characters": story.get("characters", ""), 92 | "tone": story.get("tone", ""), 93 | "outline": story.get("outline", ""), 94 | "chapters": story.get("synopses", []) 95 | } 96 | 97 | # 保存元数据 98 | with open(os.path.join(output_base, "meta.json"), "w", encoding="utf-8") as f: 99 | json.dump(meta_info, f, ensure_ascii=False, indent=2) 100 | 101 | # 保存目录结构 102 | with open(os.path.join(output_base, "table_of_contents.md"), "w", encoding="utf-8") as f: 103 | f.write("# 故事目录\n\n") 104 | f.write(story["outline"].split("## 详细大纲")[0]) # 只保存目录部分 105 | 106 | # 保存每章内容 107 | if isinstance(story["content"], list): 108 | for i, chapter in enumerate(story["content"], 1): 109 | chapter_file = os.path.join(chapters_dir, f"chapter_{i:03d}.txt") 110 | with open(chapter_file, "w", encoding="utf-8") as f: 111 | f.write(chapter) 112 | else: 113 | logger.error("故事内容格式错误:不是章节列表") 114 | # 如果内容不是列表,将整个内容保存为单个文件 115 | with open(os.path.join(output_base, "full_story.txt"), "w", encoding="utf-8") as f: 116 | f.write(story["content"]) 117 | 118 | logger.info(f"故事创作完成,输出目录:{output_base}") 119 | 120 | def main(): 121 | # 解析命令行参数 122 | parser = argparse.ArgumentParser(description='AI小说创作工具') 123 | parser.add_argument('--model', type=str, choices=['puyu', 'glm'], 124 | default='puyu', help='选择使用的模型 (puyu 或 glm)') 125 | parser.add_argument('--genre', type=str, default='科幻', 126 | help='小说题材 (如:科幻、奇幻、悬疑等)') 127 | args = parser.parse_args() 128 | 129 | # 在Windows系统上运行异步代码 130 | if os.name == 'nt': 131 | asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) 132 | 133 | # 运行异步函数 134 | asyncio.run(create_sample_story(args.model, args.genre)) 135 | 136 | if __name__ == "__main__": 137 | main() --------------------------------------------------------------------------------