更新打包脚本文件、README、requirements.txt;略微改动UI

This commit is contained in:
YILING0013
2025-02-01 00:23:00 +08:00
parent 15186fa19e
commit 2a5602009b
6 changed files with 198 additions and 150 deletions
+151 -107
View File
@@ -1,67 +1,85 @@
**自动小说生成工具**
**核心功能:** # **自动小说生成工具**
**小说设定**(世界观、人物、剧情结构) > 一款基于大模型的多功能小说生成器,帮助你快速生成连贯、可控、可审校的长篇故事。
**章节生成**(多步生成,确保逻辑连贯)
**角色状态 & 伏笔管理**(追踪人物发展)
**向量检索**(保证长篇小说上下文一致)
**自定义知识库**(上传本地参考文档)
**一致性检查**(防止剧情冲突)
**GUI 友好交互**(可配置 & 直观操作)
# **部署与使用指南** <div align="center">
## **1. 环境要求** | 功能点 | 描述 |
在开始之前,请确保你的系统满足以下要求: |-------|-----|
- **Python 3.8+** | 🚀 **小说设定** | 世界观 / 人物 / 剧情结构 |
- **pip 已安装**Python 包管理器) | 📝 **章节生成** | 多步生成,确保逻辑连贯 |
- **API 访问权限**(支持OpenAI API方式的任何AI,中文推荐使用DeepSeek) | 👥 **角色状态 & 伏笔管理** | 追踪人物发展、重要事件 |
| 🔍 **向量检索** | 保证长篇小说上下文一致 |
| 📁 **自定义知识库** | 可上传本地参考文档 |
| ✅ **一致性检查** | 自动识别剧情冲突或矛盾 |
| 🖥 **GUI 友好交互** | 所见即所得,配置 & 操作简便 |
</div>
--- ---
## **2. 安装依赖** ## **目录**
1. [环境要求](#环境要求)
**进入项目目录,执行** 2. [安装依赖](#安装依赖)
```bash 3. [项目结构](#项目结构)
pip install -r requirements.txt 4. [配置 API Key](#配置-api-key)
``` 5. [运行程序](#运行程序)
### 安装语句切分模型punkt(可选,默认程序运行后会自动加载) 6. [使用指南](#使用指南)
**Python环境终端输入**: 7. [生成文件管理](#生成文件管理)
```bash 8. [常见问题](#常见问题)
python
import nltk
nltk.download('punkt')
```
等待下载完成(很小,下载很快的)
**至此,环境配置完成。**
--- ---
## **3. 项目结构** ## **环境要求**
克隆或下载本项目后,你会看到如下结构 在开始之前,请确保你的系统满足以下条件
```plaintext - 推荐**Python 3.10+**
- 已安装 **pip**Python 包管理器)
- 拥有 **API Key**(如 OpenAI 或 DeepSeek)或 **支持OpenAI调用方式的本地接口**
---
## **安装依赖**
1. 在项目根目录下,打开终端或命令行
2. 执行以下命令安装所有依赖:
```bash
pip install -r requirements.txt
```
3. (可选)若需要手动安装 `nltk` 的 `punkt` 数据包:
```bash
python
>>> import nltk
>>> nltk.download('punkt')
```
---
## **项目结构**
克隆或下载本项目后,你将看到如下目录结构:
```
. .
├── main.py # 入口文件运行 GUI ├── main.py # 入口文件, 运行 GUI
├── ui.py # 图形界面 ├── ui.py # 图形界面
├── novel_generator.py # 章节生成核心逻辑 ├── novel_generator.py # 章节生成核心逻辑
├── consistency_checker.py # 一致性检查 (防止剧情冲突) ├── consistency_checker.py # 一致性检查, 防止剧情冲突
├── prompt_definitions.py # 预定义的 AI 提示词 |—— chapter_directory_parser.py #格式化目录
├── utils.py # 通用工具函数 (文件操作) ├── prompt_definitions.py # 定义 AI 提示词
├── config_manager.py # 处理配置信息 (API Key, Base URL) ├── utils.py # 常用工具函数, 文件操作
├── config_manager.py # 管理配置 (API Key, Base URL)
├── config.json # 用户配置文件 (可选) ├── config.json # 用户配置文件 (可选)
└── vectorstore/ # (可选) 存储向量数据库 └── vectorstore/ # (可选) 本地向量数据库存储
``` ```
> `vectorstore/` 文件夹将在程序运行后自动生成,存储向量检索的缓存数据。
--- ---
## **4. 配置 API Key** ## **配置 API Key**
运行前,你需要**配置 API Key** 以便调用 OpenAI 或本地 LLM。 你可以通过以下方式指定或修改 API Key:
### **方式 1手动修改 `config.json`** ### 方式 1**修改 `config.json`**
`config.json` 文件中填入 打开 `config.json`,将对应字段替换为你的配置
```json ```json
{ {
"api_key": "your_openai_api_key", "api_key": "your_openai_api_key",
@@ -75,93 +93,119 @@ nltk.download('punkt')
} }
``` ```
### **方式 2:通过 GUI 直接输入** ### 方式 2**通过 GUI 输入**
- 启动程序后,在 GUI 中输入 `API Key` 并选择 `Base URL`,然后**点击 "保存配置"** 以存储到 `config.json` 1. 运行 `main.py` 后会弹出图形界面
2. 在界面输入 `API Key`、`Base URL`、模型名称、Temperature 等
3. 点击 **“保存配置”** 按钮,自动生成或更新 `config.json`
--- ---
## **5. 运行程序** ## **运行程序**
### **方式 1:使用 `Python` 运行**
### **方式 1:使用 Python 解释器**
```bash ```bash
python main.py python main.py
``` ```
程序启动,你会看到一个图形界面,方便用户交互 执行后,GUI 将会启动,你可以在图形界面中进行各项操作
### **方式 2:打包为可执行文件**
如果你想在无 Python 环境的机器上使用本工具,可以使用 **PyInstaller** 进行打包:
### **方式 2:打包成可执行文件**
如果你希望**打包成可执行文件**(避免 Python 依赖),可以使用 `PyInstaller`
```bash ```bash
pip install pyinstaller pip install pyinstaller
pyinstaller --onefile --windowed main.py
```
这样会在 `dist/` 目录下生成 `main.exe`Windows)或 `main`Linux/macOS)。
或者使用提供的`main.spec`,执行以下打包指令:
```bash
pyinstaller main.spec pyinstaller main.spec
``` ```
打包完成后,会在 `dist/` 目录下生成可执行文件(如 Windows 下的 `main.exe`)。
--- ---
## **6. 使用指南** ## **使用指南**
### **步骤 1:设置小说参数**
在 GUI 界面:
1. **输入 API Key & Base URL**(或使用 `config.json`)。
2. **选择模型**(如 `gpt-4o`)。
3. **输入小说主题**(如 "未来世界中的 AI 革命")。
4. **选择小说类型**(如 "科幻"、"奇幻"、"悬疑")。
5. **设置章节数和每章字数**(如 10 章,每章 3000 字)。
6. **选择存储路径**(建议创建 `novels/` 目录)。
### **步骤 2:生成小说设定 & 目录** 1. **启动后,先完成基本参数设置:**
点击 **"1. 生成设定 & 目录"**,系统将: - **API Key & Base URL**(如 `https://api.openai.com/v1`
- 生成**世界观设定**`Novel_setting.txt`)。 - **模型名称**(如 `gpt-3.5-turbo`、`gpt-4o` 等)
- 生成**章节目录**`Novel_directory.txt`)。 - **Temperature**(0~1,决定文字创意程度)
- **主题(Topic)**(如 “废土世界的 AI 叛乱”)
- **类型(Genre)**(如 “科幻”/“魔幻”/“都市幻想”)
- **章节数**、**每章字数**(如 10 章,每章约 3000 字)
- **保存路径**(建议创建一个新的输出文件夹)
### **步骤 3:生成章节** 2. **点击「1. 生成设定 & 目录」**
点击 **"2. 生成单章"**,系统将 - 系统将基于主题、类型等生成
- 读取**上一章节内容**`lastchapter.txt`)。 - `Novel_setting.txt`**世界观 & 整体设定**
- 通过**向量检索**查找相关背景信息。 - `Novel_directory.txt`:**章节目录**(含标题、简要提示)
- **动态调整角色状态**`character_state.txt` - 可以在生成后的文件中查看、修改或补充世界观设定和目录标题
- **生成完整章节**,并保存到 `chapters/chapter_X.txt`
### **步骤 4:一致性检查(可选)** 3. **点击「2. 生成章节草稿」**
点击 **"3. 一致性审校"**,系统将 - 在生成章节之前,你可以
- 检查**角色行为、剧情逻辑**是否前后矛盾。 - **设置章节号**(如要写第 1 章,就填 `1`
- 识别是否有**未解伏笔**,保证故事合理性。 - **在“本章指导”输入框**中,填写对本章剧情的任何期望或指导
- 点击按钮后,系统将:
- 自动读取前文与 `Novel_directory.txt` 的标题与简述
- 调用向量检索回顾剧情,确保上下文连贯
- 生成本章大纲 (`outline_X.txt`) 及正文 (`chapter_X.txt`)
- 生成完成后,可在左侧查看本章草稿内容。
4. **手动检查 & 编辑**(可选)
- 你可以在文本编辑器中修改 `chapter_X.txt` 的内容,使之更符合个人审美或剧情要求。
5. **点击「3. 定稿当前章节」**
- 系统将:
- **更新全局摘要**`global_summary.txt`
- **更新角色状态**`character_state.txt`,包含人物发展、物品变动、剧情线索等)
- **更新向量检索库**,保证后续章节能够调用最新信息。
6. **一致性检查(可选)**
- 点击「4. 一致性审校」进行冲突检测,比如**角色逻辑、剧情前后矛盾**等。
- 若有冲突,会在日志区输出详细说明。
7. **重复第 3~5 步** 直到所有章节完成!
--- ---
## **7. 生成文件管理** ## **生成文件管理**
所有生成的文件存储在你选择的目录下:
```plaintext 在你指定的输出文件夹中,程序会自动生成并维护以下文件/目录:
```
output_directory/ output_directory/
├── Novel_setting.txt # 小说世界观 & 角色设定 ├── Novel_setting.txt # 世界观 & 整体设定
├── Novel_directory.txt # 章节目录 ├── Novel_directory.txt # 小说章节目录
├── lastchapter.txt # 最新章节 (供 AI 参考) ├── character_state.txt # 角色状态/物品/伏笔等追踪
├── character_state.txt # 角色状态 (道具、情感、技能) ├── global_summary.txt # 整体剧情摘要 (用于后续上下文)
├── global_summary.txt # 小说摘要 ├── outlines/ # 存放各章大纲 (outline_1.txt 等)
── chapters/ # 所有章节 ── chapters/ # 存放每一章成稿 (chapter_1.txt 等)
├── chapter_1.txt └── vectorstore/ # 向量检索数据库 (可清空重置)
├── chapter_2.txt
├── chapter_3.txt
└── ...
``` ```
- **`character_state.txt`**:记录角色的动机、能力、持有物品等关键信息
- **`global_summary.txt`**:每次定稿后都会更新,保证剧情可被后续章节引用
- **`chapters/`**:每一章的正文会独立保存,方便你随时手动修改
--- ---
## **8. 可能遇到的问题** ## **常见问题**
### **1. `Chroma' object has no attribute 'persist'`**
**原因:** `Chroma` 版本问题。 #### 1. **`Chroma' object has no attribute 'persist'` 错误**
**解决方案:**
```bash - **原因**Chroma 库版本不匹配
pip uninstall chromadb - **解决办法**
pip install chromadb==0.3.21 # 或尝试其他版本 ```bash
``` pip uninstall chromadb
如果仍然报错,可以在 `novel_generator.py` 里**注释 `store.persist()`**。 pip install chromadb==0.3.21
```
或者在 `novel_generator.py` 中找到 `store.persist()` 并注释掉,如果你的版本不支持此方法。
--- ---
### **2. 生成内容不符合预期** #### 2. **生成内容与预期不符**
**可能的原因:**
- 主题不够清晰,可以在 `topic` 字段中添加详细设定(如 `“废土世界 + AI 叛乱”`)。 - **可能原因**
- 角色设定较少,可手动在 `Novel_setting.txt` 里补充。 1. 主题或类型过于宽泛,模型难以把握重点
2. 角色和世界观设定不够详细,可在 `Novel_setting.txt` 中增补更多细节
- **建议**
- 在 GUI 的“本章指导”输入框中填写更明确的剧情走向或重点描述,让模型更好地理解和发挥
---
若你还有其他问题或需求,欢迎在项目 Issues 中提出。
+19 -22
View File
@@ -1,34 +1,31 @@
# -*- mode: python ; coding: utf-8 -*- # -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_submodules from PyInstaller.utils.hooks import collect_all
a = Analysis( datas = []
['main.py'], binaries = []
pathex=[], hiddenimports = ['typing_extensions',
binaries=[],
datas=[
('vectorstore', 'vectorstore')
],
hiddenimports=['typing_extensions',
'langchain-openai', 'langchain-openai',
'langgraph', 'langgraph',
'openai', 'openai',
'chromadb',
'nltk', 'nltk',
'sentence_transformers', 'sentence_transformers',
'scikit-learn', 'scikit-learn',
'langchain-community', 'langchain-community',
'pydantic', 'pydantic',
'pydantic.deprecated.decorator', 'pydantic.deprecated.decorator',
*collect_submodules('chromadb'), 'tiktoken_ext.openai_public',
'chromadb.utils.embedding_functions.onnx_mini_lm_l6_v2', 'tiktoken_ext'
'chromadb.telemetry.product.posthog', ]
'chromadb.api.segment', tmp_ret = collect_all('chromadb')
'chromadb.db.impl', datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
'chromadb.db.impl.sqlite',
'chromadb.migrations',
'chromadb.migrations.embeddings_queue'
],
a = Analysis(
['main.py'],
pathex=[],
binaries=binaries,
datas=datas,
hiddenimports=hiddenimports,
hookspath=[], hookspath=[],
hooksconfig={}, hooksconfig={},
runtime_hooks=[], runtime_hooks=[],
@@ -43,8 +40,8 @@ exe = EXE(
a.scripts, a.scripts,
[], [],
exclude_binaries=True, exclude_binaries=True,
name='AI_NovelGenerator_V1.1', name='AI_NovelGenerator_V1.2.3',
debug=False, debug=True,
bootloader_ignore_signals=False, bootloader_ignore_signals=False,
strip=False, strip=False,
upx=True, upx=True,
@@ -63,5 +60,5 @@ coll = COLLECT(
strip=False, strip=False,
upx=True, upx=True,
upx_exclude=[], upx_exclude=[],
name='AI_NovelGenerator_V1.1', name='AI_NovelGenerator_V1.2.3'
) )
+11 -4
View File
@@ -42,20 +42,27 @@ def debug_log(prompt: str, response_content: str):
logging.info(f"[Response >>>] {response_content}\n") logging.info(f"[Response >>>] {response_content}\n")
# ============ 向量检索相关 ============ # ============ 向量检索相关 ============
VECTOR_STORE_DIR = "vectorstore" VECTOR_STORE_DIR = os.path.join(os.getcwd(), "vectorstore")
if not os.path.exists(VECTOR_STORE_DIR):
os.makedirs(VECTOR_STORE_DIR)
def clear_vector_store(): def clear_vector_store():
""" """
清空本地向量库(删除 vectorstore 文件夹)。 清空本地向量库(删除 vectorstore 文件夹内的内容)。
需要在UI中加一个二次确认弹窗,防止误删。 需要在UI中加一个二次确认弹窗,防止误删。
""" """
if os.path.exists(VECTOR_STORE_DIR): if os.path.exists(VECTOR_STORE_DIR):
try: try:
import shutil import shutil
shutil.rmtree(VECTOR_STORE_DIR) for filename in os.listdir(VECTOR_STORE_DIR):
file_path = os.path.join(VECTOR_STORE_DIR, filename)
if os.path.isfile(file_path) or os.path.islink(file_path):
os.unlink(file_path)
elif os.path.isdir(file_path):
shutil.rmtree(file_path)
logging.info("Local vector store has been cleared.") logging.info("Local vector store has been cleared.")
except Exception as e: except Exception as e:
logging.warning(f"Failed to remove vector store: {e}") logging.warning(f"Failed to clear vector store: {e}")
else: else:
logging.info("No vector store found to clear.") logging.info("No vector store found to clear.")
+1 -1
View File
@@ -67,7 +67,7 @@ novel_directory_prompt = """\
{number_of_chapters}< text > {number_of_chapters}< text >
请严格按照上述格式输出每一章的名称,最好在重要情节标题后增加提示性简述, 请严格按照上述格式输出每一章的名称,最好在重要情节标题后增加提示性简述,
若要加更详细的简述,用“ - ”分隔,如“第2阴影浮现 - 主要角色冲突爆发”。 若要加更详细的简述,用“ - ”分隔,举例如“第n< text > - 主要角色冲突爆发,角色A发生xx意外”。
请直接输出,不要使用Markdown语法。 请直接输出,不要使用Markdown语法。
""" """
Binary file not shown.
+6 -6
View File
@@ -149,23 +149,23 @@ class NovelGeneratorGUI:
# ============ 功能按钮 ============ # ============ 功能按钮 ============
# (1) 生成设定 & 目录 # (1) 生成设定 & 目录
self.btn_generate_full = ttk.Button(self.right_frame, text="1. 生成设定 & 目录", command=self.generate_full_novel) self.btn_generate_full = ttk.Button(self.right_frame, text="Step1. 生成设定 & 目录", command=self.generate_full_novel)
self.btn_generate_full.grid(row=row_base, column=0, columnspan=2, padx=5, pady=5, sticky="ew") self.btn_generate_full.grid(row=row_base, column=0, columnspan=2, padx=5, pady=5, sticky="ew")
# (2) 生成章节草稿 # (2) 生成章节草稿
self.btn_generate_chapter = ttk.Button(self.right_frame, text="2. 生成章节草稿", command=self.generate_chapter_draft_ui) self.btn_generate_chapter = ttk.Button(self.right_frame, text="Step2. 生成章节草稿", command=self.generate_chapter_draft_ui)
self.btn_generate_chapter.grid(row=row_base+1, column=0, columnspan=2, padx=5, pady=5, sticky="ew") self.btn_generate_chapter.grid(row=row_base+1, column=0, columnspan=2, padx=5, pady=5, sticky="ew")
# (3) 定稿当前章节 # (3) 定稿当前章节
self.btn_finalize_chapter = ttk.Button(self.right_frame, text="3. 定稿当前章节", command=self.finalize_chapter_ui) self.btn_finalize_chapter = ttk.Button(self.right_frame, text="Step3. 定稿当前章节", command=self.finalize_chapter_ui)
self.btn_finalize_chapter.grid(row=row_base+2, column=0, columnspan=2, padx=5, pady=5, sticky="ew") self.btn_finalize_chapter.grid(row=row_base+2, column=0, columnspan=2, padx=5, pady=5, sticky="ew")
# (4) 一致性审校 # (4) 一致性审校
self.btn_check_consistency = ttk.Button(self.right_frame, text="4. 一致性审校", command=self.do_consistency_check) self.btn_check_consistency = ttk.Button(self.right_frame, text="[可选]一致性审校", command=self.do_consistency_check)
self.btn_check_consistency.grid(row=row_base+3, column=0, columnspan=2, padx=5, pady=5, sticky="ew") self.btn_check_consistency.grid(row=row_base+3, column=0, columnspan=2, padx=5, pady=5, sticky="ew")
# (5) 导入知识库文件 # (5) 导入知识库文件
self.btn_import_knowledge = ttk.Button(self.right_frame, text="导入知识库", command=self.import_knowledge_handler) self.btn_import_knowledge = ttk.Button(self.right_frame, text="[可选]导入知识库", command=self.import_knowledge_handler)
self.btn_import_knowledge.grid(row=row_base+4, column=0, columnspan=2, padx=5, pady=5, sticky="ew") self.btn_import_knowledge.grid(row=row_base+4, column=0, columnspan=2, padx=5, pady=5, sticky="ew")
# (6) 清空向量库 # (6) 清空向量库
@@ -321,7 +321,7 @@ class NovelGeneratorGUI:
filepath=filepath filepath=filepath
) )
if draft_text: if draft_text:
self.log(f"✅ 第{chap_num}章草稿生成完成。请在左侧查看。") self.log(f"✅ 第{chap_num}章草稿生成完成。请在下方查看。")
self.chapter_result.delete("1.0", tk.END) self.chapter_result.delete("1.0", tk.END)
self.chapter_result.insert(tk.END, draft_text) self.chapter_result.insert(tk.END, draft_text)
self.chapter_result.see(tk.END) self.chapter_result.see(tk.END)