利用Python和AI編寫小説的工作流教程

概述

本教程將展示如何使用Python和現代AI技術構建一個完整的小説創作工作流,從構思到完整章節生成。

技術棧

  • Python 3.8+
  • OpenAI API / 本地LLM模型
  • LangChain框架
  • 文本處理庫

安裝依賴

# requirements.txt
openai==1.12.0
langchain==0.1.0
langchain-openai==0.0.2
python-dotenv==1.0.0
tiktoken==0.5.1
numpy==1.24.0
pandas==2.0.0
plotly==5.17.0

安裝命令:

pip install -r requirements.txt

工作流設計

1. 環境設置

# config.py
import os
from dotenv import load_dotenv
from typing import Dict, List, Optional

load_dotenv()

class Config:
# API配置
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
OPENAI_MODEL = "gpt-4-turbo-preview"# 或使用本地模型

# 路徑配置
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
DATA_DIR = os.path.join(PROJECT_ROOT, "data")
OUTPUT_DIR = os.path.join(PROJECT_ROOT, "output")

# 創建目錄
os.makedirs(DATA_DIR, exist_ok=True)
os.makedirs(OUTPUT_DIR, exist_ok=True)

# 寫作參數
MAX_TOKENS = 2000
TEMPERATURE = 0.7

2. 核心AI寫作引擎

# ai_writer.py
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema import HumanMessage, SystemMessage
from typing import List, Dict, Any
import json

class AIWriter:
def __init__(self, config):
self.config = config
self.llm = ChatOpenAI(
model_name=config.OPENAI_MODEL,
temperature=config.TEMPERATURE,
max_tokens=config.MAX_TOKENS,
api_key=config.OPENAI_API_KEY
)

def generate_idea(self, genre: str, themes: List[str]) -> Dict:
"""生成小説創意"""
prompt = ChatPromptTemplate.from_messages([
SystemMessage(content="你是一位經驗豐富的小説家,擅長創作引人入勝的故事。"),
HumanMessage(content=f"""
請為一部{genre}類型的小説生成創意,主題包括:{', '.join(themes)}。
請提供以下內容:
1. 小説標題
2. 一句話簡介(不超過50字)
3. 核心衝突
4. 三個主要角色及其特點
5. 故事背景設定

請以JSON格式返回:
{{
"title": "小説標題",
"logline": "一句話簡介",
"conflict": "核心衝突",
"characters": [
{{"name": "角色名", "age": 年齡, "personality": "性格特點", "role": "角色作用"}}
],
"setting": "背景設定"
}}
""")
])

response = self.llm.invoke(prompt)
return json.loads(response.content)

def outline_chapter(self, novel_info: Dict, chapter_num: int) -> Dict:
"""生成章節大綱"""
prompt = f"""
根據以下小説信息:
標題:{novel_info['title']}
簡介:{novel_info['logline']}
核心衝突:{novel_info['conflict']}

請為第{chapter_num}章生成詳細大綱,包括:
1. 章節標題
2. 主要情節點(3-5個)
3. 出現的角色
4. 場景設置
5. 本章在整體故事中的作用

以JSON格式返回。
"""

response = self.llm.invoke([
SystemMessage(content="你是專業的編劇,擅長設計戲劇性情節。"),
HumanMessage(content=prompt)
])

return json.loads(response.content)

def write_scene(self, scene_context: Dict) -> str:
"""根據場景上下文寫作具體內容"""
prompt = ChatPromptTemplate.from_messages([
SystemMessage(content="""
你是一位文筆優美的小説家。請以生動的語言、豐富的細節和恰當的對話寫作以下場景。
注意保持角色性格一致性,推進情節發展。
"""),
HumanMessage(content=f"""
場景信息:
時間:{scene_context['time']}
地點:{scene_context['location']}
角色:{', '.join(scene_context['characters'])}
情節要點:{scene_context['plot_points']}
前情提要:{scene_context.get('previous_context', '無')}

請寫出這個場景的完整內容(1000-1500字)。
""")
])

response = self.llm.invoke(prompt)
return response.content

def revise_text(self, text: str, instructions: str) -> str:
"""修改文本"""
prompt = f"""
請根據以下要求修改文本:

原文:
{text}

修改要求:
{instructions}

請提供修改後的完整文本。
"""

response = self.llm.invoke([
SystemMessage(content="你是專業的文學編輯,擅長潤色和修改文本。"),
HumanMessage(content=prompt)
])

return response.content

3. 項目管理類

# novel_project.py
import json
from datetime import datetime
from pathlib import Path
from typing import List, Dict, Any

class NovelProject:
def __init__(self, title: str, writer):
self.title = title
self.writer = writer
self.metadata = {
"title": title,
"created_date": datetime.now().isoformat(),
"last_modified": datetime.now().isoformat(),
"total_chapters": 0,
"status": "planning"
}

# 項目結構
self.outline = {}
self.characters = []
self.chapters = []
self.scenes = []

def create_outline(self, genre: str, themes: List[str]) -> Dict:
"""創建小説大綱"""
idea = self.writer.generate_idea(genre, themes)

self.metadata.update({
"genre": genre,
"themes": themes,
"logline": idea["logline"],
"conflict": idea["conflict"]
})

self.outline = idea
self.characters = idea["characters"]
self.metadata["status"] = "outlined"

self.save_project()
return idea

def plan_chapters(self, num_chapters: int) -> List[Dict]:
"""規劃章節"""
chapters = []
for i in range(1, num_chapters + 1):
chapter_outline = self.writer.outline_chapter(self.outline, i)
chapters.append(chapter_outline)

self.chapters = chapters
self.metadata["total_chapters"] = num_chapters
self.metadata["status"] = "chapter_planned"

self.save_project()
return chapters

def write_chapter(self, chapter_num: int) -> Dict:
"""寫作具體章節"""
if chapter_num > len(self.chapters):
raise ValueError(f"章節{chapter_num}未規劃")

chapter = self.chapters[chapter_num - 1]
scenes = []

# 將章節大綱分解為場景
plot_points = chapter.get("plot_points", [])
for i, plot_point in enumerate(plot_points):
scene_context = {
"time": chapter.get("time", "未指定"),
"location": chapter.get("location", "未指定"),
"characters": chapter.get("characters", []),
"plot_points": plot_point,
"previous_context": " | ".join(plot_points[:i]) if i > 0 else "章節開始"
}

scene_text = self.writer.write_scene(scene_context)
scenes.append({
"scene_num": i + 1,
"context": scene_context,
"text": scene_text
})

chapter_content = {
"chapter_num": chapter_num,
"title": chapter.get("title", f"第{chapter_num}章"),
"scenes": scenes,
"full_text": "\n\n".join([s["text"] for s in scenes])
}

# 更新章節狀態
if "content" not in self.chapters[chapter_num - 1]:
self.chapters[chapter_num - 1]["content"] = chapter_content
self.chapters[chapter_num - 1]["status"] = "written"
self.chapters[chapter_num - 1]["written_date"] = datetime.now().isoformat()

self.save_project()
return chapter_content

def revise_chapter(self, chapter_num: int, instructions: str) -> Dict:
"""修改章節"""
chapter = self.chapters[chapter_num - 1]
original_text = chapter["content"]["full_text"]

revised_text = self.writer.revise_text(original_text, instructions)

chapter["content"]["full_text"] = revised_text
chapter["content"]["revisions"] = chapter.get("revisions", []) + [{
"date": datetime.now().isoformat(),
"instructions": instructions,
"original_snippet": original_text[:500] + "..."
}]

self.save_project()
return chapter["content"]

def export_novel(self, format: str = "txt") -> Path:
"""導出完整小説"""
output_path = Path(self.writer.config.OUTPUT_DIR) / f"{self.title}.{format}"

content_parts = []

# 添加封面信息
content_parts.append(f"《{self.title}》")
content_parts.append(f"簡介:{self.metadata.get('logline', '')}")
content_parts.append("=" * 50 + "\n")

# 添加章節內容
for i, chapter in enumerate(self.chapters):
if "content" in chapter:
content_parts.append(f"\n第{i+1}章 {chapter.get('title', '')}")
content_parts.append("-" * 30)
content_parts.append(chapter["content"]["full_text"])

full_content = "\n".join(content_parts)

with open(output_path, "w", encoding="utf-8") as f:
f.write(full_content)

return output_path

def save_project(self):
"""保存項目狀態"""
project_data = {
"metadata": self.metadata,
"outline": self.outline,
"characters": self.characters,
"chapters": self.chapters
}

project_path = Path(self.writer.config.DATA_DIR) / f"{self.title}_project.json"
with open(project_path, "w", encoding="utf-8") as f:
json.dump(project_data, f, ensure_ascii=False, indent=2)

@classmethod
def load_project(cls, title: str, writer):
"""加載已有項目"""
project_path = Path(writer.config.DATA_DIR) / f"{title}_project.json"

with open(project_path, "r", encoding="utf-8") as f:
project_data = json.load(f)

project = cls(title, writer)
project.metadata = project_data["metadata"]
project.outline = project_data["outline"]
project.characters = project_data["characters"]
project.chapters = project_data["chapters"]

return project

4. 交互式命令行界面

# cli.py
import argparse
import sys
from config import Config
from ai_writer import AIWriter
from novel_project import NovelProject

def main():
parser = argparse.ArgumentParser(description="AI小説寫作工具")
subparsers = parser.add_subparsers(dest="command", help="命令")

# 創建新項目
create_parser = subparsers.add_parser("create", help="創建新小説項目")
create_parser.add_argument("--title", required=True, help="小説標題")
create_parser.add_argument("--genre", required=True, help="小説類型")
create_parser.add_argument("--themes", nargs="+", help="小説主題")
create_parser.add_argument("--chapters", type=int, default=10, help="章節數量")

# 加載項目
load_parser = subparsers.add_parser("load", help="加載已有項目")
load_parser.add_argument("--title", required=True, help="項目標題")

# 寫作命令
write_parser = subparsers.add_parser("write", help="寫作章節")
write_parser.add_argument("--chapter", type=int, required=True, help="章節編號")

# 修改命令
revise_parser = subparsers.add_parser("revise", help="修改章節")
revise_parser.add_argument("--chapter", type=int, required=True)
revise_parser.add_argument("--instructions", required=True)

# 導出命令
export_parser = subparsers.add_parser("export", help="導出小説")

args = parser.parse_args()

config = Config()
writer = AIWriter(config)

if args.command == "create":
# 創建新項目
project = NovelProject(args.title, writer)
print(f"正在創建小説項目: {args.title}")

# 生成大綱
outline = project.create_outline(args.genre, args.themes)
print(f"✓ 大綱已生成: {outline['title']}")
print(f"簡介: {outline['logline']}")

# 規劃章節
chapters = project.plan_chapters(args.chapters)
print(f"✓ {len(chapters)}個章節已規劃")

# 保存項目
project.save_project()
print(f"✓ 項目已保存到: data/{args.title}_project.json")

elif args.command == "load":
# 加載項目
try:
project = NovelProject.load_project(args.title, writer)
print(f"項目加載成功: {project.title}")
print(f"狀態: {project.metadata['status']}")
print(f"已完成章節: {sum(1 for c in project.chapters if c.get('status') == 'written')}")
except FileNotFoundError:
print(f"錯誤: 未找到項目 '{args.title}'")
sys.exit(1)

elif args.command == "write":
# 需要先加載項目
if not hasattr(args, 'title') or not args.title:
print("錯誤: 請先使用'load'命令加載項目")
sys.exit(1)

project = NovelProject.load_project(args.title, writer)
print(f"開始寫作第{args.chapter}章...")

chapter = project.write_chapter(args.chapter)
print(f"✓ 第{args.chapter}章寫作完成")
print(f"標題: {chapter['title']}")
print(f"字數: {len(chapter['full_text'])}")

elif args.command == "revise":
project = NovelProject.load_project(args.title, writer)
print(f"正在修改第{args.chapter}章...")

chapter = project.revise_chapter(args.chapter, args.instructions)
print(f"✓ 第{args.chapter}章修改完成")

elif args.command == "export":
project = NovelProject.load_project(args.title, writer)
print("正在導出小説...")

export_path = project.export_novel()
print(f"✓ 小説已導出到: {export_path}")

else:
parser.print_help()

if __name__ == "__main__":
main()

5. 進階功能:角色一致性維護

# character_manager.py
class CharacterManager:
def __init__(self, writer):
self.writer = writer
self.characters = {}

def add_character(self, character_data: Dict):
"""添加角色"""
char_id = character_data["name"].lower().replace(" ", "_")
self.characters[char_id] = {
"id": char_id,
**character_data,
"appearances": [],
"character_arc": []
}

def get_character_context(self, character_name: str) -> str:
"""獲取角色上下文信息"""
char_id = character_name.lower().replace(" ", "_")
if char_id not in self.characters:
return ""

char = self.characters[char_id]
context = f"""
角色:{char['name']}
年齡:{char.get('age', '未指定')}
性格:{char.get('personality', '未指定')}
背景:{char.get('background', '未指定')}
重要關係:{', '.join(char.get('relationships', []))}
"""

# 添加角色弧信息
if char.get("character_arc"):
context += f"\n角色發展弧:{char['character_arc']}"

return context

def track_appearance(self, character_name: str, chapter_num: int, scene_num: int):
"""追蹤角色出場"""
char_id = character_name.lower().replace(" ", "_")
if char_id in self.characters:
self.characters[char_id]["appearances"].append({
"chapter": chapter_num,
"scene": scene_num
})

def ensure_consistency(self, text: str, character_name: str) -> str:
"""確保角色一致性"""
char_context = self.get_character_context(character_name)

prompt = f"""
請檢查以下文本中角色'{character_name}'的言行是否符合其性格設定:

角色設定:
{char_context}

待檢查文本:
{text}

請指出任何不符合角色設定的地方,並提出修改建議。
"""

response = self.writer.llm.invoke([
SystemMessage(content="你是專業的角色顧問,擅長分析角色一致性。"),
HumanMessage(content=prompt)
])

return response.content

6. 批量寫作腳本

# batch_writer.py
from tqdm import tqdm
import time

def batch_write_novel(project_title: str, start_chapter: int = 1, end_chapter: int = None):
"""批量寫作多個章節"""
config = Config()
writer = AIWriter(config)
project = NovelProject.load_project(project_title, writer)

if end_chapter is None:
end_chapter = project.metadata["total_chapters"]

print(f"開始批量寫作第{start_chapter}章到第{end_chapter}章...")

for chapter_num in tqdm(range(start_chapter, end_chapter + 1)):
try:
# 檢查是否已寫作
if project.chapters[chapter_num - 1].get("status") == "written":
print(f"第{chapter_num}章已存在,跳過...")
continue

# 寫作章節
chapter = project.write_chapter(chapter_num)

print(f"✓ 第{chapter_num}章完成: {chapter['title']}")
print(f"字數: {len(chapter['full_text'])}")

# 避免API限制,添加延遲
time.sleep(2)

except Exception as e:
print(f"✗ 第{chapter_num}章寫作失敗: {str(e)}")
break

print(f"\n批量寫作完成!")
print(f"總章節數: {end_chapter - start_chapter + 1}")

# 導出完整小説
export_path = project.export_novel()
print(f"小説已導出到: {export_path}")

7. 使用示例

# example_usage.py
"""
完整的小説創作示例
"""

from config import Config
from ai_writer import AIWriter
from novel_project import NovelProject
from character_manager import CharacterManager

def main():
# 1. 初始化
config = Config()
writer = AIWriter(config)

# 2. 創建新小説項目
novel_title = "時空旅者的日記"
project = NovelProject(novel_title, writer)

# 3. 生成創意大綱
print("生成小説創意...")
outline = project.create_outline(
genre="科幻懸疑",
themes=["時間旅行", "平行宇宙", "身份認同"]
)

print(f"標題: {outline['title']}")
print(f"簡介: {outline['logline']}")

# 4. 規劃章節
print("\n規劃章節結構...")
chapters = project.plan_chapters(num_chapters=12)

# 5. 初始化角色管理器
char_manager = CharacterManager(writer)
for char in outline['characters']:
char_manager.add_character(char)

# 6. 批量寫作前3章
print("\n開始寫作章節...")
for chapter_num in range(1, 4):
print(f"寫作第{chapter_num}章...")
chapter_content = project.write_chapter(chapter_num)

# 檢查角色一致性
for scene in chapter_content['scenes']:
for char in scene['context']['characters']:
feedback = char_manager.ensure_consistency(
scene['text'],
char
)
# 如果有不一致的地方,可以考慮修訂
if "不符合" in feedback:
print(f"角色'{char}'一致性檢查建議: {feedback[:200]}...")

# 7. 導出小説
print("\n導出小説...")
export_path = project.export_novel()
print(f"小説已導出到: {export_path}")

# 8. 修改示例
print("\n修改第1章,增加懸念...")
revised = project.revise_chapter(
1,
"在章節末尾增加一個懸念,暗示主角發現了一個驚人的秘密"
)
print("修改完成!")

if __name__ == "__main__":
main()

8. 命令行使用示例

# 創建新項目
python cli.py create --title "時空旅者" --genre "科幻" --themes "時間旅行 懸疑" --chapters 10

# 寫作第1章
python cli.py load --title "時空旅者"# 先加載項目
python cli.py write --chapter 1

# 批量寫作(使用腳本)
python batch_writer.py --title "時空旅者" --start 2 --end 5

# 修改章節
python cli.py revise --chapter 3 --instructions "讓對話更緊張刺激"

# 導出小説
python cli.py export --title "時空旅者"

高級功能建議

  1. 多模型支持:集成Claude、本地LLM等
  2. 風格模仿:訓練模型模仿特定作家的風格
  3. 情節分析:使用AI分析情節節奏和張力
  4. 讀者反饋模擬:預測讀者對不同情節的反應
  5. 可視化工具:繪製角色關係圖、情節時間線

注意事項

  1. API成本控制:設置使用限制,監控費用
  2. 內容原創性:AI生成內容可能需要人工修改以確保原創性
  3. 版權問題:注意訓練數據的版權和生成內容的版權歸屬
  4. 倫理考量:避免生成有害或不當內容

這個工作流提供了從創意生成到完整小説創作的完整流程,你可以根據需求調整和擴展各個模塊。