内容
# 📖 波尔菲里·彼得罗维奇:有声书助手
一款适用于 **小智 ESP32-S3 设备**(通过 MCP WebSocket 协议)的 **有声书助手**。可以朗读 FB2、EPUB 和 TXT 格式的书籍,并带有来自维克托·佩列文小说中的人物波尔菲里·彼得罗维奇的 **恰当的评论**。
> **以波尔菲里·彼得罗维奇命名** —— 来自佩列文作品的讽刺、烟瘾哲学家,而不是陀思妥耶夫斯基的调查员。

## 功能特点
- 🎙️ **语音控制** — “读本书”,“下一章”,“停止”,“重新开始”,“前往第 5 章”
- 📚 **多格式支持** — FB2,EPUB,TXT
- 💬 **智能评论** — 波尔菲里每隔 N 句(可配置)评论一次,带有讽刺性观察
- 🎭 **富有表现力的语调** — 评论使用感叹词,反问句,破折号和省略号,以自然的方式与书文本区分开来
- 🔖 **书签** — 记录你停止阅读的地方,即使在重启后也能继续
- ⚡ **立即恢复** — 预加载的书籍从内存中恢复,无需重新解析
- 📦 **缓存** — 解析后的书籍和句子分割缓存,实现即时导航
- 🔄 **13 个 MCP 工具** — 完整的语音命令集,用于完全控制书籍
## 语音命令
| 命令 | 示例 | 描述 |
|---------|---------|-------------|
| `read_book` | “读《战争与和平》” | 加载并开始阅读 |
| `get_next_sentence` | (自动调用) | 获取下一句 |
| `pause_reading` | “暂停” | 暂停 + 保存书签 |
| `resume_reading` | “继续” | 继续阅读 |
| `stop_reading` | “停止” | 停止 + 保存书签 |
| `start_over` | “重新开始” | 从头开始 |
| `go_to_chapter` | “前往第 5 章” | 跳转到指定章节 |
| `next_chapter` | “下一章” | 跳到下一章 |
| `prev_chapter` | “上一章” | 返回上一章 |
| `commentary_on` | “启用评论” | 打开波尔菲里的评论 |
| `commentary_off` | “禁用评论” | 关闭评论 |
| `list_books` | “有哪些书可用?” | 列出图书馆中的书籍 |
| `clear_cache` | “清除缓存” | 重置内部缓存 |
## 技术栈
- **Python 3.10+** — 基于 asyncio 的服务器
- **WebSocket** — 小智 MCP 协议(JSON-RPC 2.0)
- **DeepSeek API** — 评论生成(可配置模型)
- **小智云TTS** — 设备端语音合成
## 项目结构
```
porfiry_audiobook/
├── main.py # MCP WebSocket 客户端,工具注册
├── state_manager.py # 有限状态机(空闲 → 阅读 → 暂停)
├── audio_streamer.py # 句子流式传输 + 评论插入
├── config.py # 基于环境的配置
├── commentator.py # DeepSeek 驱动的评论生成
├── book_parser.py # FB2/EPUB/TXT 解析器
├── book_library.py # 书籍发现和元数据扫描
├── bookmark_store.py # 基于 JSON 的书签持久化
├── text_splitter.py # 智能句子分割
├── books/ # 📁 放置你的书籍在这里
├── output/ # 📁 生成的文件(书签,日志)
└── .env # 🔒 你的配置(不提交)
```
## 架构
### 协议
通过 WebSocket(`wss://api.xiaozhi.me/mcp/`)连接到小智 MCP。设备的 LLM 调用我们注册的工具,我们返回文本响应,由设备通过其自己的 TTS(小智云)朗读。
### 拉取式流媒体 — 每次一个句子
与推送式架构(服务器推送文本)不同,这里使用 **拉取模型**:
1. 设备 LLM 调用 `read_book("Crime and Punishment")` — 服务器加载书籍
2. `read_book` 响应包括章节公告 **和第一个句子** — 设备立即开始朗读
3. LLM 反复调用 `get_next_sentence()` — 每次调用返回 **一个句子**(或一个评论)
4. 设备通过小智云 TTS 合成语音
5. 服务器在当前章节完成后自动进入下一章节
6. 阅读继续,直到用户说“停止”或书籍结束
这种一次一个句子的方式使设备的 LLM 对阅读流程具有细粒度控制,并与小智 MCP 协议可靠地协同工作。
### 评论与语调
波尔菲里的评论通过 DeepSeek API 生成,并作为单独的响应返回,在下一句之前。评论系统指示 AI 使用 **表达性语调模式**:感叹词(「Хм...」, 「Ну-ну...」, 「Ах, вот оно что...」),反问句,破折号,省略号,感叹号。这使得评论自然地与书文本区分开来,即使没有特殊的 TTS 处理。
评论生成有 **5 秒超时** — 如果 DeepSeek API 速度慢,阅读将继续进行,无需评论。
### 状态机
```
┌─────────────┐
start ──────>│ 空闲 │<──────┘
└──────┬──────┘
│
read_book()
│
v
┌─────────────┐
│ 阅读中 │────── stop ────> 空闲(保存书签)
└──────┬──────┘
│
pause()
│
v
┌─────────────┐
│ 已暂停 │────── resume ──> 阅读中
└─────────────┘
```
### 缓存
- **书籍缓存**:按文件名缓存已解析的 `Book` 对象 — 重新阅读同一本书即时完成
- **句子缓存**:按 `(filename, chapter_index)` 缓存分割的句子 — 章节导航即时完成
- 两个缓存都可以通过 `clear_cache` 命令清除
### 快速元数据扫描
`BookLibrary` 从 FB2/EPUB 文件中提取元数据(标题,作者)**而无需解析整个书籍** — 只读取 XML 头或 OPF 元数据。TXT 文件使用文件名作为标题。
## 配置 (.env)
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `XIAOZHI_MCP_TOKEN` | — | 小智 MCP 的 WebSocket Token |
| `DEEPSEEK_API_KEY` | — | DeepSeek API 密钥,用于评论 |
| `DEEPSEEK_MODEL` | `deepseek-chat` | DeepSeek 模型名称 |
| `BOOKS_DIR` | `./books` | 书籍目录 |
| `BOOKMARKS_FILE` | `./output/bookmarks.json` | 书签文件 |
| `COMMENTARY_ENABLED` | `true` | 默认启用评论 |
| `COMMENTARY_FREQUENCY` | `4` | 每隔 N 句评论一次 |
| `LOG_LEVEL` | `INFO` | 日志级别 |
## 安装
### 1. 克隆
```bash
git clone https://github.com/yourusername/porfiry_audiobook.git
cd porfiry_audiobook
```
### 2. 虚拟环境
```bash
python3 -m venv venv
source venv/bin/activate
```
### 3. 安装依赖
```bash
pip install -r requirements.txt
```
### 4. 配置
```bash
cp .env.example .env
# 编辑 .env 文件,填写你的密钥:
# XIAOZHI_MCP_TOKEN — 来自小智控制台
# DEEPSEEK_API_KEY — 来自 DeepSeek 平台
```
### 5. 添加书籍
将 `.fb2`, `.epub` 或 `.txt` 文件放在 `books/` 目录下:
```bash
cp ~/Downloads/my_book.fb2 books/
```
### 6. 运行
```bash
python main.py
```
服务器连接到小智 MCP 并注册其工具。对你的设备说:“Прочитай книгу Война и мир”(或“读《战争与和平》”)。
## 支持的格式
| 格式 | 扩展名 | 功能 |
|--------|-----------|----------|
| **FB2** | `.fb2` | 完整支持:章节,段落,元数据 |
| **EPUB** | `.epub` | 完整支持:章节,段落,元数据 |
| **TXT** | `.txt` | 简单:整个文件作为一章,文件名作为标题 |
## 许可证
MIT