增加语音对话功能
This commit is contained in:
@@ -0,0 +1,680 @@
|
||||
# 软件架构设计文档
|
||||
|
||||
| 字段 | 内容 |
|
||||
|------|------|
|
||||
| 产品名称 | BanDu(伴读)— Zotero 插件 |
|
||||
| 中文名 | 伴读 |
|
||||
| 英文名 | BanDu |
|
||||
| 文档版本 | v0.1 |
|
||||
| 状态 | 草案 |
|
||||
| 关联文档 | [PLAN.md](./PLAN.md)、[PRD.md](./PRD.md) |
|
||||
|
||||
---
|
||||
|
||||
## 1. 架构概述
|
||||
|
||||
### 1.1 架构风格
|
||||
|
||||
**Local-first 桌面插件架构**:核心计算与缓存均在用户机器完成;仅将段落文本与用户问题发送至用户配置的 LLM / TTS 服务。无自建后端(一期)。
|
||||
|
||||
### 1.2 设计原则
|
||||
|
||||
| 原则 | 说明 |
|
||||
|------|------|
|
||||
| 备课一次、缓存复用 | Lecture 持久化;attachment ID + 文件哈希为缓存键 |
|
||||
| 增量交付 | 摘要 → beats → 逐段 → TTS 分阶段就绪,缩短 Time-to-Listen |
|
||||
| UI / 领域 / 集成 分离 | 降低 Zotero API 变动影响 |
|
||||
| Grounding 优先 | 生成内容必须可追溯到段落 ID |
|
||||
| 优雅降级 | refs 无效、无 timestamps、无 BBT 均有 fallback |
|
||||
|
||||
### 1.3 系统上下文
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ 用户 │
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌──────────────▼──────────────┐
|
||||
│ Zotero 7 (宿主) │
|
||||
│ ┌────────────────────────┐ │
|
||||
│ │ BanDu 插件 │ │
|
||||
│ │ UI │ Engine │ Store │ │
|
||||
│ └─────────┬──────────────┘ │
|
||||
└────────────┼────────────────┘
|
||||
│
|
||||
┌─────────────────┼─────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌───────────┐ ┌────────────┐ ┌─────────────┐
|
||||
│ MinerU │ │ LLM API │ │ TTS API │
|
||||
│ (本地子进程)│ │ / Ollama │ │ ElevenLabs等 │
|
||||
└───────────┘ └────────────┘ └─────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────┐
|
||||
│ SQLite + │
|
||||
│ mp3 文件 │
|
||||
└───────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 逻辑分层
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Presentation Layer(表现层) │
|
||||
│ • BanduPane(Item pane 伴读页) │
|
||||
│ • LibraryWindow(伴读库) │
|
||||
│ • SettingsPane(设置) │
|
||||
│ • PlayerController(播放状态机 + <audio>) │
|
||||
└───────────────────────────┬─────────────────────────────┘
|
||||
│
|
||||
┌───────────────────────────▼─────────────────────────────┐
|
||||
│ Application Layer(应用层 / 用例) │
|
||||
│ • PrepareLectureUseCase(备课编排) │
|
||||
│ • PlayUseCase(播放 / 跳播 / 断点) │
|
||||
│ • AskQuestionUseCase(问答 + RAG) │
|
||||
│ • GenerateReportUseCase(报告 + Note 回写) │
|
||||
└───────────────────────────┬─────────────────────────────┘
|
||||
│
|
||||
┌───────────────────────────▼─────────────────────────────┐
|
||||
│ Domain Layer(领域层) │
|
||||
│ • Paper, Paragraph, Chapter │
|
||||
│ • Lecture, Beat, ParagraphExplanation │
|
||||
│ • QARecord, Progress, Highlight │
|
||||
│ • LecturePreparationState(状态机) │
|
||||
└───────────────────────────┬─────────────────────────────┘
|
||||
│
|
||||
┌───────────────────────────▼─────────────────────────────┐
|
||||
│ Infrastructure Layer(基础设施层) │
|
||||
│ • ZoteroAdapter(Item / Attachment / Note) │
|
||||
│ • PdfParser(MinerU 封装) │
|
||||
│ • LlmClient(OpenAI-compatible / Ollama) │
|
||||
│ • TtsClient(多提供商 + timestamps) │
|
||||
│ • SqliteRepository │
|
||||
│ • AudioStorage(mp3 路径管理) │
|
||||
│ • RagIndex(段落检索) │
|
||||
│ • PromptRegistry(模板版本管理) │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 模块说明
|
||||
|
||||
### 3.1 ZoteroAdapter
|
||||
|
||||
**职责**:封装所有 Zotero 7 API 调用。
|
||||
|
||||
| 接口 | 说明 |
|
||||
|------|------|
|
||||
| `getPdfAttachment(itemId)` | 返回主 PDF 路径、attachmentId |
|
||||
| `computeFileHash(path)` | SHA-256 |
|
||||
| `openPdfAtPage(itemId, page)` | 跳转 PDF 阅读器 |
|
||||
| `createBanduNote(itemId, markdown)` | 创建伴读子笔记 |
|
||||
| `getCitationKey(itemId)` | Better BibTeX 可选集成 |
|
||||
| `listLibraryItems()` | 伴读库数据源 |
|
||||
|
||||
**约束**:UI 与用例层不得直接调用 `Zotero.*` 全局对象。
|
||||
|
||||
### 3.2 PdfParser
|
||||
|
||||
**职责**:调用 MinerU(一期),输出规范化段落树。
|
||||
|
||||
**输入**:PDF 本地路径
|
||||
**输出**:`ParseResult { chapters[], paragraphs[], metadata }`
|
||||
|
||||
**段落字段**:
|
||||
|
||||
```typescript
|
||||
interface Paragraph {
|
||||
id: string; // 稳定 ID,如 "p-0017"
|
||||
chapterId: string;
|
||||
orderIndex: number;
|
||||
text: string;
|
||||
page: number;
|
||||
language?: 'en' | 'zh';
|
||||
bulletPoints?: string[]; // >150 词长段拆分要点
|
||||
}
|
||||
```
|
||||
|
||||
**失败策略**:抛出 typed error(`ScannedPdfError`, `ParseTimeoutError`),UI 映射为用户文案。
|
||||
|
||||
### 3.3 PrepareLectureUseCase
|
||||
|
||||
**职责**:编排备课流水线,驱动增量就绪事件。
|
||||
|
||||
详见 §5 流水线与 §6 状态机。
|
||||
|
||||
### 3.4 LlmClient
|
||||
|
||||
**职责**:统一 LLM 调用;支持 streaming。
|
||||
|
||||
| 方法 | 用途 |
|
||||
|------|------|
|
||||
| `generateSummary(paragraphs[])` | 全文摘要 |
|
||||
| `generateBeats(summary, paragraphIndex[])` | 整体讲解 beats |
|
||||
| `generateParagraphExplanation(para, summary, chapterTitle)` | 三层讲解 |
|
||||
| `generateSentenceExplanation(sentence, context)` | 逐句讲解 |
|
||||
| `answerQuestion(context, question)` | 问答 stream |
|
||||
|
||||
**配置**:provider、baseUrl、apiKey、model、temperature(默认 0.2–0.4)。
|
||||
|
||||
### 3.5 TtsClient
|
||||
|
||||
**职责**:文本 → mp3 + timestamps。
|
||||
|
||||
```typescript
|
||||
interface TtsResult {
|
||||
audioPath: string;
|
||||
timestamps: WordTimestamp[]; // 可为空
|
||||
durationMs: number;
|
||||
}
|
||||
|
||||
interface WordTimestamp {
|
||||
text: string;
|
||||
startMs: number;
|
||||
endMs: number;
|
||||
}
|
||||
```
|
||||
|
||||
**策略**:优先 provider 原生 boundaries;缺失则 `HighlightGranularity = SENTENCE`。
|
||||
|
||||
### 3.6 RagIndex
|
||||
|
||||
**职责**:段落级检索,供问答组装上下文。
|
||||
|
||||
**一期实现**:SQLite FTS5 或 BM25;MVP 可不引入 embedding。
|
||||
|
||||
```typescript
|
||||
interface RagIndex {
|
||||
build(paragraphs: Paragraph[]): void;
|
||||
search(query: string, topK: number): Paragraph[];
|
||||
}
|
||||
```
|
||||
|
||||
### 3.7 SqliteRepository + AudioStorage
|
||||
|
||||
**职责**:结构化数据与二进制音频分离存储。
|
||||
|
||||
- SQLite:`{ZoteroDataDir}/bandu/bandu.db`
|
||||
- 音频:`{ZoteroDataDir}/bandu/audio/{cacheKey}/{unitId}.mp3`
|
||||
|
||||
---
|
||||
|
||||
## 4. 数据模型
|
||||
|
||||
### 4.1 ER 关系
|
||||
|
||||
```
|
||||
PaperCache 1──* Paragraph
|
||||
PaperCache 1──1 Lecture
|
||||
Lecture 1──* Beat
|
||||
Lecture 1──* ParagraphExplanation
|
||||
PaperCache 1──* QARecord
|
||||
PaperCache 1──1 Progress
|
||||
PaperCache 1──* Highlight
|
||||
```
|
||||
|
||||
### 4.2 表结构(SQLite)
|
||||
|
||||
```sql
|
||||
-- 论文缓存根表
|
||||
CREATE TABLE paper_cache (
|
||||
id TEXT PRIMARY KEY, -- hash(attachment_id + file_hash)
|
||||
attachment_id TEXT NOT NULL,
|
||||
item_id TEXT NOT NULL,
|
||||
file_hash TEXT NOT NULL,
|
||||
file_path TEXT NOT NULL,
|
||||
title TEXT,
|
||||
language TEXT, -- 'en' | 'zh'
|
||||
parse_status TEXT NOT NULL, --见状态机
|
||||
lecture_status TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL,
|
||||
UNIQUE(attachment_id, file_hash)
|
||||
);
|
||||
|
||||
-- 章节
|
||||
CREATE TABLE chapter (
|
||||
id TEXT PRIMARY KEY,
|
||||
paper_id TEXT NOT NULL REFERENCES paper_cache(id),
|
||||
title TEXT NOT NULL,
|
||||
order_index INTEGER NOT NULL
|
||||
);
|
||||
|
||||
-- 段落
|
||||
CREATE TABLE paragraph (
|
||||
id TEXT PRIMARY KEY,
|
||||
paper_id TEXT NOT NULL REFERENCES paper_cache(id),
|
||||
chapter_id TEXT REFERENCES chapter(id),
|
||||
order_index INTEGER NOT NULL,
|
||||
text TEXT NOT NULL,
|
||||
page INTEGER NOT NULL
|
||||
);
|
||||
|
||||
-- 讲义
|
||||
CREATE TABLE lecture (
|
||||
paper_id TEXT PRIMARY KEY REFERENCES paper_cache(id),
|
||||
summary_json TEXT NOT NULL, -- {problem, method, result, limitation}
|
||||
prompt_version TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
-- 整体讲解 beat
|
||||
CREATE TABLE beat (
|
||||
id TEXT PRIMARY KEY,
|
||||
paper_id TEXT NOT NULL REFERENCES paper_cache(id),
|
||||
order_index INTEGER NOT NULL,
|
||||
title TEXT,
|
||||
type TEXT NOT NULL, -- intro|method|experiment|conclusion|extension
|
||||
script TEXT NOT NULL,
|
||||
refs_json TEXT NOT NULL, -- ["p-0012","p-0013"]
|
||||
audio_path TEXT,
|
||||
timestamps_json TEXT,
|
||||
tts_status TEXT NOT NULL -- pending|ready|failed
|
||||
);
|
||||
|
||||
-- 逐段三层讲解
|
||||
CREATE TABLE paragraph_explanation (
|
||||
paragraph_id TEXT PRIMARY KEY REFERENCES paragraph(id),
|
||||
paper_id TEXT NOT NULL REFERENCES paper_cache(id),
|
||||
layer_what TEXT NOT NULL,
|
||||
layer_how TEXT NOT NULL,
|
||||
layer_where TEXT NOT NULL,
|
||||
audio_path TEXT,
|
||||
timestamps_json TEXT,
|
||||
gen_status TEXT NOT NULL,
|
||||
tts_status TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- 播放进度
|
||||
CREATE TABLE progress (
|
||||
paper_id TEXT PRIMARY KEY REFERENCES paper_cache(id),
|
||||
last_mode TEXT, -- overview|paragraph
|
||||
last_unit_id TEXT,
|
||||
last_position_ms INTEGER DEFAULT 0,
|
||||
listened_units_json TEXT, -- 已听 unit id 列表
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
-- 问答
|
||||
CREATE TABLE qa_record (
|
||||
id TEXT PRIMARY KEY,
|
||||
paper_id TEXT NOT NULL REFERENCES paper_cache(id),
|
||||
anchor_type TEXT NOT NULL, -- beat|paragraph|sentence|global
|
||||
anchor_id TEXT,
|
||||
question TEXT NOT NULL,
|
||||
answer TEXT NOT NULL,
|
||||
audio_path TEXT,
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
-- 重点标记
|
||||
CREATE TABLE highlight (
|
||||
paper_id TEXT NOT NULL REFERENCES paper_cache(id),
|
||||
paragraph_id TEXT NOT NULL REFERENCES paragraph(id),
|
||||
note TEXT,
|
||||
created_at INTEGER NOT NULL,
|
||||
PRIMARY KEY (paper_id, paragraph_id)
|
||||
);
|
||||
|
||||
-- 段落 FTS(RAG)
|
||||
CREATE VIRTUAL TABLE paragraph_fts USING fts5(
|
||||
paragraph_id,
|
||||
paper_id,
|
||||
text,
|
||||
tokenize='unicode61'
|
||||
);
|
||||
```
|
||||
|
||||
### 4.3 缓存键策略
|
||||
|
||||
```
|
||||
cacheKey = SHA256(attachmentId + "|" + fileHash)
|
||||
|
||||
失效条件:
|
||||
• fileHash 变化(PDF 被替换)
|
||||
• 用户「强制重新备课」
|
||||
• prompt_version _major 升级(可选策略:仅影响新生成)
|
||||
```
|
||||
|
||||
### 4.4 Lecture JSON 摘要结构
|
||||
|
||||
```json
|
||||
{
|
||||
"problem": "…",
|
||||
"method": "…",
|
||||
"result": "…",
|
||||
"limitation": "…"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 Beat refs 校验算法
|
||||
|
||||
```
|
||||
输入: beats[], validParagraphIds (Set)
|
||||
对每个 beat:
|
||||
beat.refs = beat.refs.filter(id => validParagraphIds.has(id))
|
||||
if beat.refs.isEmpty:
|
||||
beat.degraded = true // UI: 不展示原文高亮
|
||||
输出: beats[]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 核心流水线
|
||||
|
||||
### 5.1 端到端数据流
|
||||
|
||||
```
|
||||
PDF path
|
||||
→ [PdfParser] → paragraphs[] → SQLite
|
||||
→ [LlmClient.generateSummary] → lecture.summary
|
||||
→ [LlmClient.generateBeats] → beats[] → refs 校验 → SQLite
|
||||
→ (event: beats_ready) → UI 可播整体模式
|
||||
→ [LlmClient.generateParagraphExplanation] × N → SQLite
|
||||
→ (event: paragraph_batch_ready)
|
||||
→ [TtsClient.synthesize] × units → mp3 → SQLite
|
||||
→ (event: unit_audio_ready)
|
||||
```
|
||||
|
||||
### 5.2 增量备课时序
|
||||
|
||||
```
|
||||
时间 ──────────────────────────────────────────────►
|
||||
|
||||
解析 ████████
|
||||
摘要 ███
|
||||
beats ██████ ← beats_ready(~1-2min)
|
||||
逐段讲解 ████████████████████
|
||||
TTS(beats) ████████ ← 整体模式有声音
|
||||
TTS(段落) █████████████████
|
||||
```
|
||||
|
||||
### 5.3 问答上下文组装
|
||||
|
||||
```
|
||||
AskContext {
|
||||
question: string
|
||||
anchor: { type, id, text, explanation? }
|
||||
summary: SummaryJson
|
||||
ragParagraphs: Paragraph[] // topK=5
|
||||
paperTitle: string
|
||||
}
|
||||
|
||||
Prompt 模板顺序:
|
||||
系统角色 → 全文摘要 → RAG 段落 → 锚点原文+讲解 → 用户问题
|
||||
```
|
||||
|
||||
### 5.4 报告生成
|
||||
|
||||
```
|
||||
ReportBuilder.build(paperId):
|
||||
summary ← lecture.summary
|
||||
progress ← progress.listened_units
|
||||
qas ← qa_record ORDER BY created_at
|
||||
highlights ← highlight JOIN paragraph
|
||||
bibtex ← ZoteroAdapter.getCitationKey + export
|
||||
|
||||
MarkdownRenderer → ZoteroAdapter.createBanduNote
|
||||
```
|
||||
|
||||
**报告策略(建议 MVP)**:同一 Item 只保留一个「伴读报告」笔记;再次生成时**更新**同名 Note(`BanDu 伴读报告: {title}`),避免笔记爆炸。
|
||||
|
||||
---
|
||||
|
||||
## 6. 状态机
|
||||
|
||||
### 6.1 论文 / 备课状态(lecture_status)
|
||||
|
||||
```
|
||||
┌─────────┐
|
||||
│ idle │ 未开始
|
||||
└────┬────┘
|
||||
│ start
|
||||
▼
|
||||
┌─────────┐
|
||||
┌────│ parsing │────┐ fail
|
||||
│ └────┬────┘ │
|
||||
│ │ success ▼
|
||||
│ │ ┌─────────┐
|
||||
│ └───►│ parsed │
|
||||
│ └────┬────┘
|
||||
│ │
|
||||
│ ▼
|
||||
│ ┌──────────────┐
|
||||
│ │ summarizing │
|
||||
│ └──────┬───────┘
|
||||
│ │
|
||||
│ ▼
|
||||
│ ┌──────────────┐
|
||||
│ │ beats_generating │
|
||||
│ └──────┬───────┘
|
||||
│ │ beats_ready
|
||||
│ ▼
|
||||
│ ┌────────────────────┐
|
||||
│ │ paragraphs_generating │◄──┐
|
||||
│ └──────────┬─────────┘ │ batch loop
|
||||
│ │ │
|
||||
│ └─────────────┘
|
||||
│ │ all para done
|
||||
│ ▼
|
||||
│ ┌────────────────────┐
|
||||
│ │ tts_generating │
|
||||
│ └──────────┬─────────┘
|
||||
│ │
|
||||
│ ▼
|
||||
│ ┌──────────────┐
|
||||
└─────────────►│ ready │
|
||||
error └──────────────┘
|
||||
┌─────────┐
|
||||
│ failed │
|
||||
└─────────┘
|
||||
```
|
||||
|
||||
**可恢复**:每个阶段完成后持久化;重启插件从 `lecture_status` 继续,跳过已完成步骤。
|
||||
|
||||
### 6.2 播放器状态
|
||||
|
||||
```
|
||||
PlayerState {
|
||||
mode: 'overview' | 'paragraph'
|
||||
currentUnitId: string
|
||||
isPlaying: boolean
|
||||
playbackRate: 1 | 1.5 | 2
|
||||
highlightCursorMs: number
|
||||
}
|
||||
```
|
||||
|
||||
**跳播**:切换 `currentUnitId` → 加载对应 mp3 → seek(0) → 更新 Progress。
|
||||
|
||||
---
|
||||
|
||||
## 7. 接口设计(内部事件)
|
||||
|
||||
插件内使用 EventBus 解耦 UI 与长时间任务:
|
||||
|
||||
| 事件 | 载荷 | 订阅者 |
|
||||
|------|------|--------|
|
||||
| `parse:progress` | `{ percent, message }` | BanduPane |
|
||||
| `lecture:beats_ready` | `{ paperId, beatCount }` | BanduPane, Player |
|
||||
| `lecture:paragraph_ready` | `{ paperId, paragraphId }` | BanduPane |
|
||||
| `tts:unit_ready` | `{ unitId, audioPath }` | Player |
|
||||
| `lecture:failed` | `{ paperId, error }` | BanduPane |
|
||||
| `qa:chunk` | `{ textDelta }` | QAPanel |
|
||||
| `qa:complete` | `{ qaRecordId }` | QAPanel, Report |
|
||||
|
||||
**长时间任务**:在 Zotero 主线程外使用 Web Worker 或子进程(MinerU);LLM/TTS 使用 async fetch,避免阻塞 UI。
|
||||
|
||||
---
|
||||
|
||||
## 8. 外部集成
|
||||
|
||||
### 8.1 LLM
|
||||
|
||||
| 提供商 | 协议 | 用途 |
|
||||
|--------|------|------|
|
||||
| OpenAI 兼容 API | HTTPS SSE | 备课 + 问答 |
|
||||
| Ollama | localhost HTTP | 全本地备选 |
|
||||
|
||||
### 8.2 TTS
|
||||
|
||||
| 提供商 | 选型维度 |
|
||||
|--------|----------|
|
||||
| 火山 / Azure / MiniMax / ElevenLabs | 中文自然度、价格、word timestamps |
|
||||
|
||||
**抽象接口**:
|
||||
|
||||
```typescript
|
||||
interface TtsProvider {
|
||||
name: string;
|
||||
synthesize(text: string, options: TtsOptions): Promise<TtsResult>;
|
||||
supportsTimestamps: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### 8.3 MinerU
|
||||
|
||||
- **集成方式(待 Phase 0 确认)**:子进程 CLI,stdin/stdout JSON
|
||||
- **打包**:插件安装包内含 MinerU 二进制或安装脚本;文档说明 Python 环境依赖
|
||||
|
||||
### 8.4 Better BibTeX(可选)
|
||||
|
||||
- 检测 `Zotero.BetterBibTeX` 是否存在
|
||||
- 不存在:从 Item 字段生成简易 BibTeX 或留空字段
|
||||
|
||||
---
|
||||
|
||||
## 9. 安全与隐私
|
||||
|
||||
| 项 | 方案 |
|
||||
|----|------|
|
||||
| PDF 文件 | 不离开本地 |
|
||||
| 发往 LLM 的数据 | 段落 text + 用户问题 + 摘要;设置页明示 |
|
||||
| API Key | Zotero prefs 加密存储或 OS Keychain |
|
||||
| 日志 | 不记录 Key、不完整记录段落正文(可截断) |
|
||||
| Ollama | 默认 localhost,无外网 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 部署与构建
|
||||
|
||||
### 10.1 技术栈
|
||||
|
||||
| 层 | 选型 |
|
||||
|----|------|
|
||||
| 插件框架 | [zotero-plugin-template](https://github.com/windingwind/zotero-plugin-template) |
|
||||
| 语言 | TypeScript |
|
||||
| UI | HTML + CSS(Fluent 风格贴近 Zotero) |
|
||||
| 音频 | HTML5 `<audio>` |
|
||||
| 数据库 | better-sqlite3 或 Zotero 内置 SQL(待 POC) |
|
||||
| PDF | MinerU(Python,本地 CPU) |
|
||||
|
||||
### 10.2 目录结构(建议)
|
||||
|
||||
```
|
||||
bandu/
|
||||
├── addon/
|
||||
│ ├── bootstrap.js
|
||||
│ ├── manifest.json
|
||||
│ └── prefs.js
|
||||
├── src/
|
||||
│ ├── ui/
|
||||
│ │ ├── BanduPane.tsx
|
||||
│ │ ├── LibraryWindow.tsx
|
||||
│ │ └── SettingsPane.tsx
|
||||
│ ├── application/
|
||||
│ │ ├── PrepareLectureUseCase.ts
|
||||
│ │ ├── PlayUseCase.ts
|
||||
│ │ ├── AskQuestionUseCase.ts
|
||||
│ │ └── GenerateReportUseCase.ts
|
||||
│ ├── domain/
|
||||
│ ├── infrastructure/
|
||||
│ │ ├── zotero/
|
||||
│ │ ├── pdf/
|
||||
│ │ ├── llm/
|
||||
│ │ ├── tts/
|
||||
│ │ └── storage/
|
||||
│ └── prompts/
|
||||
├── tests/
|
||||
└── docs/
|
||||
```
|
||||
|
||||
### 10.3 版本兼容
|
||||
|
||||
- `manifest.json` 声明 `applications.zotero.strict_min_version` / `strict_max_version`
|
||||
- CI 固定 Zotero 7.x 版本做 smoke test
|
||||
|
||||
---
|
||||
|
||||
## 11. Phase 0 技术验证(Go / No-Go)
|
||||
|
||||
| # | 验证项 | 通过标准 | 阻塞 MVP |
|
||||
|---|--------|----------|----------|
|
||||
| P0-1 | Item pane 嵌入自定义页 | 可展示 UI、读取当前 Item | 是 |
|
||||
| P0-2 | 读取 PDF attachment 路径 | 路径有效、哈希可算 | 是 |
|
||||
| P0-3 | `<audio>` 长音频播放 | 10 min+ 无泄漏/崩溃 | 是 |
|
||||
| P0-4 | MinerU 解析 3 篇论文 | 段落切分可用率 ≥ 80% | 是 |
|
||||
| P0-5 | TTS 中文样本 | 自然度可接受 | 是 |
|
||||
| P0-6 | TTS timestamps | 有则记录格式;无则确认句子级降级 | 否(降级) |
|
||||
| P0-7 | Zotero Note Markdown | 报告渲染可接受 | 否 |
|
||||
|
||||
---
|
||||
|
||||
## 12. 性能与扩展
|
||||
|
||||
### 12.1 长论文策略
|
||||
|
||||
| 页数 | 策略 |
|
||||
|------|------|
|
||||
| ≤ 30 | 全量逐段备课 |
|
||||
| 30–80 | 逐段 lazy:按章节或用户点击生成 |
|
||||
| > 80 | 默认仅整体讲解;逐段需用户确认(成本提示) |
|
||||
|
||||
### 12.2 并发控制
|
||||
|
||||
- LLM 备课:beats 串行或小批量并行(≤ 3)避免 rate limit
|
||||
- TTS:队列化,优先 beats 音频
|
||||
- 同一 paper 不允许重复触发备课(互斥锁)
|
||||
|
||||
---
|
||||
|
||||
## 13. 可观测性
|
||||
|
||||
| 类型 | 内容 |
|
||||
|------|------|
|
||||
| 结构化日志 | paperId, stage, durationMs, errorCode |
|
||||
| 用户可见进度 | 解析 %、beats 数、段落 i/n、TTS i/n |
|
||||
| Dogfooding 导出 | 单篇 metrics JSON(可选调试菜单) |
|
||||
|
||||
---
|
||||
|
||||
## 14. 风险与架构对策
|
||||
|
||||
| 风险 | 架构对策 |
|
||||
|------|----------|
|
||||
| Zotero API 变更 | Adapter 层隔离;pin 版本 |
|
||||
| MinerU 打包复杂 | Phase 0 验证;文档化外部依赖安装 |
|
||||
| refs 幻觉 | 校验器 + degraded beat |
|
||||
| TTS 无 timestamps | HighlightGranularity 降级 |
|
||||
| SQLite 锁 | 写操作单线程队列 |
|
||||
| 备课中断 | 状态机 + 阶段持久化 |
|
||||
|
||||
---
|
||||
|
||||
## 15. 开放问题
|
||||
|
||||
1. SQLite 用 `better-sqlite3` 还是 Zotero `Services.storage` — 待插件 POC 验证 WASM/ native 限制
|
||||
2. MinerU 随插件分发 vs 用户自行安装 Python 环境
|
||||
3. 问答 TTS MVP 用流式还是整段合成(影响架构复杂度)
|
||||
4. 伴读库窗口:全库扫描性能(>5000 items 时的索引策略)
|
||||
|
||||
---
|
||||
|
||||
## 16. 文档修订记录
|
||||
|
||||
| 版本 | 日期 | 说明 |
|
||||
|------|------|------|
|
||||
| v0.1 | 2026-08-29 | 自 PLAN.md 拆分为架构初稿 |
|
||||
@@ -0,0 +1,944 @@
|
||||
# ChatPapers 双模式集成架构设计
|
||||
|
||||
| 字段 | 内容 |
|
||||
|------|------|
|
||||
| 产品名称 | ChatPapers for Zotero |
|
||||
| 文档名称 | 双模式集成架构(方案 A) |
|
||||
| 文档版本 | v0.1 |
|
||||
| 状态 | 草案 |
|
||||
| 目标平台 | Zotero 9(macOS / Windows) |
|
||||
| 关联文档 | [双模式 PRD](./DUAL_MODE_PRD.md)、[开发文档](../development.md)、[BanDu 架构参考](./ARCHITECTURE.md) |
|
||||
|
||||
> **本文档定位**:描述 ChatPapers 插件在保留**文字对话**能力的基础上,扩展**语音伴读**(备课 → 听课 → 语音问答)的架构设计。
|
||||
> 产品统一品牌为 **ChatPapers**;两种模式是同一条「与论文对话」产品线的不同交互形态——文字即时聊 vs 语音系统讲。
|
||||
|
||||
---
|
||||
|
||||
## 1. 架构概述
|
||||
|
||||
### 1.1 架构演进
|
||||
|
||||
```
|
||||
v0.2(当前) v1.0(目标)
|
||||
───────────────── ─────────────────────────────────
|
||||
ChatPapers 插件 ChatPapers 插件(单一 .xpi)
|
||||
└── 文字对话 pane ├── 文字对话 pane(已有)
|
||||
PDFWorker 纯文本 │ 即时问答 / 总结 / JSON 会话
|
||||
LLM 流式 │
|
||||
sessions/*.json └── 语音伴读 pane(新增)
|
||||
MinerU 段落树
|
||||
Lecture 备课 + TTS + 播放器
|
||||
SQLite + mp3 缓存
|
||||
共享:LLM · PDF 附件 · 笔记 · BibTeX
|
||||
```
|
||||
|
||||
### 1.2 架构风格
|
||||
|
||||
**Local-first 双模式插件架构**:核心计算与缓存均在用户机器完成;仅将段落文本与用户问题发送至用户配置的 LLM / TTS 服务。无自建后端。
|
||||
|
||||
### 1.3 设计原则
|
||||
|
||||
| 原则 | 说明 |
|
||||
|------|------|
|
||||
| 单一插件、两种对话形态 | 文字对话与语音伴读并存,共享 LLM 与 Zotero 集成 |
|
||||
| 模块隔离 | 语音模块不侵入文字对话代码路径 |
|
||||
| 备课一次、缓存复用 | Lecture 持久化;attachment ID + 文件哈希为缓存键 |
|
||||
| 增量交付 | 摘要 → beats → 逐段 → TTS 分阶段就绪 |
|
||||
| UI / 领域 / 集成 分离 | 降低 Zotero API 变动影响 |
|
||||
| Grounding 优先 | 生成内容必须可追溯到段落 ID |
|
||||
| 优雅降级 | refs 无效、无 timestamps、无 BBT 均有 fallback |
|
||||
|
||||
### 1.4 系统上下文
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ 用户 │
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌──────────────▼──────────────┐
|
||||
│ Zotero 9(宿主) │
|
||||
│ ┌────────────────────────┐ │
|
||||
│ │ ChatPapers 插件 │ │
|
||||
│ │ ┌──────────┬─────────┐ │ │
|
||||
│ │ │文字对话 │语音伴读 │ │ │
|
||||
│ │ │ ChatView │LectureUI│ │ │
|
||||
│ │ └────┬─────┴────┬────┘ │ │
|
||||
│ │ │ Shared │ │ │
|
||||
│ │ └─────┬─────┘ │ │
|
||||
│ └────────────┼────────────┘ │
|
||||
└───────────────┼──────────────┘
|
||||
│
|
||||
┌─────────────────────────┼─────────────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ Zotero │ │ LLM API │ │ TTS API │
|
||||
│ PDFWorker │ │ / Ollama │ │ 火山/Azure等 │
|
||||
│ MinerU │ │ (共享) │ │(语音模式) │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ {Profile}/chatpapers/ │
|
||||
│ sessions/*.json ← 文字对话 │
|
||||
│ lecture/lecture.db ← 语音伴读 │
|
||||
│ lecture/audio/ │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.5 两种对话形态对比
|
||||
|
||||
| 维度 | 文字对话(已有) | 语音伴读(新增) |
|
||||
|------|------------------|------------------|
|
||||
| 用户意图 | 「我现在想问什么」 | 「请 AI 先读完再给我讲」 |
|
||||
| 交互 | 打字 → 文字流式回复 | 听课 → 打字提问 → 文字 + 语音回复 |
|
||||
| PDF 输入 | PDFWorker 整篇纯文本 | MinerU 结构化段落树 |
|
||||
| LLM 用法 | 每轮即时组装 context | 一次性备课 + 按需问答 |
|
||||
| 持久化 | `sessions/{itemKey}.json` | `lecture/lecture.db` + mp3 |
|
||||
| TTS | 无 | 讲解 + 问答回答均支持 |
|
||||
| 笔记 | `ChatPapers:` 前缀 | `ChatPapers 伴读报告:` 前缀 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 整体分层架构
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ Presentation Layer(表现层) │
|
||||
│ 【文字】chatView.ts · multiChatView.ts · readerPane (Chat sec) │
|
||||
│ 【语音】lecturePane.ts · player/ · libraryWindow.ts(P1) │
|
||||
│ 【共享】prefs.ts · preferences.xhtml │
|
||||
└───────────────────────────────┬──────────────────────────────────┘
|
||||
│
|
||||
┌───────────────────────────────▼──────────────────────────────────┐
|
||||
│ Application Layer(应用层 / 用例) │
|
||||
│ 【文字】buildChatMessages · buildMultiChatMessages(已有) │
|
||||
│ 【语音】PrepareLectureUseCase · PlayUseCase │
|
||||
│ AskQuestionUseCase · GenerateReportUseCase │
|
||||
└───────────────────────────────┬──────────────────────────────────┘
|
||||
│
|
||||
┌───────────────────────────────▼──────────────────────────────────┐
|
||||
│ Domain Layer(领域层) │
|
||||
│ 【文字】ChatMessage · ExtractResult(已有) │
|
||||
│ 【语音】PaperCache · Paragraph · Chapter · Lecture · Beat │
|
||||
│ ParagraphExplanation · QARecord · Progress · Highlight │
|
||||
│ LecturePreparationState · PlayerState │
|
||||
└───────────────────────────────┬──────────────────────────────────┘
|
||||
│
|
||||
┌───────────────────────────────▼──────────────────────────────────┐
|
||||
│ Infrastructure Layer(基础设施层) │
|
||||
│ ┌─ 共享 ─────────────────────────────────────────────────────┐ │
|
||||
│ │ llm/client · llm/providers · zotero/notes · zotero/citeExport│ │
|
||||
│ │ pdf/extractor (附件定位) · pdf/selection · utils/* │ │
|
||||
│ └────────────────────────────────────────────────────────────┘ │
|
||||
│ ┌─ 文字专属 ─────────────────────────────────────────────────┐ │
|
||||
│ │ pdf/context · pdf/multiContext · storage/sessions │ │
|
||||
│ └────────────────────────────────────────────────────────────┘ │
|
||||
│ ┌─ 语音专属 ─────────────────────────────────────────────────┐ │
|
||||
│ │ lecture/infrastructure/pdf (MinerU) · tts/ · storage/sqlite │ │
|
||||
│ │ lecture/infrastructure/rag · audioStorage · promptRegistry │ │
|
||||
│ └────────────────────────────────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 现有代码映射
|
||||
|
||||
### 3.1 共享层(已实现,语音模式直接复用)
|
||||
|
||||
| 模块路径 | 职责 | 文字对话 | 语音伴读 |
|
||||
|----------|------|:--------:|:--------:|
|
||||
| `src/hooks.ts` | 生命周期、注册入口 | ✓ | 扩展注册 |
|
||||
| `src/modules/ui/readerPane.ts` | Item pane section 注册 | ✓ | 扩展第二 section |
|
||||
| `src/modules/llm/client.ts` | LLM 流式请求 | ✓ | ✓ 备课 + 问答 |
|
||||
| `src/modules/llm/providers.ts` | Provider 预设 | ✓ | ✓ 只读共享 prefs |
|
||||
| `src/modules/llm/types.ts` | ChatMessage 等类型 | ✓ | 问答层可复用类型 |
|
||||
| `src/modules/pdf/extractor.ts` | PDF 附件定位 | ✓ | ✓ 路径 + 扩展 hash |
|
||||
| `src/modules/pdf/selection.ts` | Reader 选区 | ✓ | ✓ 「解释这句」 |
|
||||
| `src/modules/zotero/notes.ts` | 子笔记创建 | ✓ | ✓ 伴读报告 |
|
||||
| `src/modules/zotero/citeExport.ts` | BibTeX 导出 | ✓ | ✓ 参考文献卡片 |
|
||||
| `src/utils/markdown.ts` | Markdown 渲染 | ✓ | ✓ 报告预览 |
|
||||
| `src/utils/prefs.ts` | Preference 读写 | ✓ | ✓ 扩展 TTS prefs |
|
||||
| `src/utils/abort.ts` | 请求取消 | ✓ | ✓ |
|
||||
|
||||
### 3.2 文字对话专属(已实现,语音模式不得引用)
|
||||
|
||||
| 模块路径 | 职责 |
|
||||
|----------|------|
|
||||
| `src/modules/ui/chatView.ts` | 文字对话 UI |
|
||||
| `src/modules/ui/multiChatView.ts` | 多 PDF 联合对话 |
|
||||
| `src/modules/pdf/context.ts` | 单篇 context 组装 |
|
||||
| `src/modules/pdf/multiContext.ts` | 多篇 context 组装 |
|
||||
| `src/modules/llm/prompts.ts` | 文字对话 system / summary prompt |
|
||||
| `src/modules/llm/history.ts` | 论文材料持久化到消息历史 |
|
||||
| `src/modules/storage/sessions.ts` | JSON 会话文件 |
|
||||
|
||||
### 3.3 语音伴读新建(`src/modules/lecture/`)
|
||||
|
||||
| 模块 | 职责 |
|
||||
|------|------|
|
||||
| `lecture/ui/lecturePane.ts` | Item pane「语音伴读」section |
|
||||
| `lecture/ui/playerController.ts` | 播放状态机 + `<audio>` |
|
||||
| `lecture/ui/unitList.ts` | beats / 段落列表 |
|
||||
| `lecture/ui/qaPanel.ts` | 锚定提问 + 流式 + 重播 |
|
||||
| `lecture/application/prepareLecture.ts` | 备课流水线编排 |
|
||||
| `lecture/application/playLecture.ts` | 播放 / 跳播 / 断点 |
|
||||
| `lecture/application/askQuestion.ts` | 锚定问答 + RAG |
|
||||
| `lecture/application/generateReport.ts` | 报告组装 + Note 回写 |
|
||||
| `lecture/domain/*` | 领域实体与状态机 |
|
||||
| `lecture/infrastructure/pdf/parser.ts` | MinerU 封装 |
|
||||
| `lecture/infrastructure/pdf/fileHash.ts` | SHA-256 |
|
||||
| `lecture/infrastructure/tts/client.ts` | TTS 多提供商 |
|
||||
| `lecture/infrastructure/tts/providers.ts` | TTS 预设 |
|
||||
| `lecture/infrastructure/storage/repository.ts` | SQLite CRUD |
|
||||
| `lecture/infrastructure/storage/audioStorage.ts` | mp3 路径管理 |
|
||||
| `lecture/infrastructure/rag/index.ts` | FTS5 段落检索 |
|
||||
| `lecture/infrastructure/llm/lecturePrompts.ts` | 备课 / beats / 三层讲解 prompt |
|
||||
| `lecture/infrastructure/events.ts` | EventBus |
|
||||
|
||||
---
|
||||
|
||||
## 4. 依赖规则
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ utils/ │
|
||||
└──────┬──────┘
|
||||
│
|
||||
┌────────────────┼────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ llm/ │ │ pdf/ext │ │ zotero/ │
|
||||
└────┬─────┘ └────┬─────┘ └────┬─────┘
|
||||
│ │ │
|
||||
┌────┴───────┐ │ ┌────┴─────┐
|
||||
│ │ │ │ │
|
||||
▼ ▼ ▼ ▼ ▼
|
||||
chatView multiChat selection notes citeExport
|
||||
(文字 UI) (文字 UI) (共享)
|
||||
|
||||
lecture/* ──可引用──► llm/, pdf/extractor, pdf/selection, zotero/, utils/
|
||||
|
||||
chatView / multiChat / context / sessions ──禁止引用──► lecture/*
|
||||
lecture/* ──禁止引用──► chatView, context, sessions, multiContext
|
||||
```
|
||||
|
||||
**运行时隔离**:两个 pane 的 `onAsyncRender` 各自实例化 View,异常在 View 边界捕获,不向上冒泡至 `hooks.ts` 导致整个插件失效。
|
||||
|
||||
---
|
||||
|
||||
## 5. 启动与注册流程
|
||||
|
||||
```typescript
|
||||
// hooks.ts(目标形态,伪代码)
|
||||
async function onStartup() {
|
||||
await Promise.all([Zotero.initializationPromise, ...]);
|
||||
|
||||
initLocale();
|
||||
registerPrefs(); // 扩展 TTS / 教师风格分组
|
||||
registerChatPane(); // 已有:文字对话 section
|
||||
registerLecturePane(); // 新增:语音伴读 section
|
||||
registerReaderSelectionHook();
|
||||
|
||||
addon.data.initialized = true;
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// readerPane.ts 扩展策略
|
||||
export function registerChatPane() { /* 现有,不动 */ }
|
||||
|
||||
export function registerLecturePane() {
|
||||
Zotero.ItemPaneManager.registerSection({
|
||||
paneID: "chatpapers-lecture",
|
||||
pluginID: config.addonID,
|
||||
header: { l10nID: "chatpapers-item-section-lecture-head", ... },
|
||||
onItemChange: ({ item, setEnabled }) => {
|
||||
setEnabled(Boolean(item && findPdfAttachment(item)));
|
||||
},
|
||||
onAsyncRender: async ({ body, item }) => {
|
||||
const view = new LecturePaneView(doc, body, item);
|
||||
await view.mount();
|
||||
},
|
||||
// ...
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 语音伴读模块详细设计
|
||||
|
||||
### 6.1 Zotero 集成适配(复用 + 薄封装)
|
||||
|
||||
语音模块不新建独立 Adapter 文件树,而是在 `lecture/infrastructure/zotero/` 做薄封装,内部调用已有模块:
|
||||
|
||||
| 接口 | 实现 |
|
||||
|------|------|
|
||||
| `getPdfAttachment(item)` | 委托 `findPdfAttachment` |
|
||||
| `computeFileHash(path)` | 新增:`IOUtils.read` + SubtleCrypto / nsICryptoHash |
|
||||
| `openPdfAtPage(itemId, page)` | 新增:Zotero.Reader API |
|
||||
| `createLectureNote(item, markdown)` | 委托 `createChildNote`,标题 `ChatPapers 伴读报告: {title}` |
|
||||
| `exportBibtex(item)` | 委托 `citeExport.formatCiteText` |
|
||||
|
||||
### 6.2 PdfParser(MinerU)
|
||||
|
||||
**与文字对话 PDF 路径分离**:文字对话继续 `PDFWorker.getFullText`;语音伴读走 MinerU。
|
||||
|
||||
**输入**:PDF 本地路径(来自 attachment)
|
||||
**输出**:
|
||||
|
||||
```typescript
|
||||
interface ParseResult {
|
||||
chapters: Chapter[];
|
||||
paragraphs: Paragraph[];
|
||||
metadata: { pageCount: number; detectedLanguage?: "en" | "zh" };
|
||||
}
|
||||
|
||||
interface Paragraph {
|
||||
id: string; // "p-0017"
|
||||
chapterId: string;
|
||||
orderIndex: number;
|
||||
text: string;
|
||||
page: number;
|
||||
language?: "en" | "zh";
|
||||
bulletPoints?: string[];
|
||||
}
|
||||
```
|
||||
|
||||
**失败策略**:
|
||||
|
||||
| 错误类型 | UI 文案 |
|
||||
|----------|---------|
|
||||
| `ScannedPdfError` | 扫描版 PDF,请先 OCR |
|
||||
| `ParseTimeoutError` | 解析超时,请重试 |
|
||||
| `MinerUNotFoundError` | 未检测到 MinerU,见设置页安装说明 |
|
||||
|
||||
### 6.3 LLM 扩展(共享 client,专属 prompt)
|
||||
|
||||
`llm/client.ts` 保持通用 `chatStream`;语音备课在 `lecture/infrastructure/llm/lecturePrompts.ts` 定义模板,通过 client 调用:
|
||||
|
||||
| 方法 | 位置 | 用途 |
|
||||
|------|------|------|
|
||||
| `chatStream` | `llm/client.ts`(已有) | 文字对话 + 语音问答 |
|
||||
| `generateSummary` | `lecture/.../lectureLlm.ts` | 全文摘要四字段 |
|
||||
| `generateBeats` | 同上 | 8–15 beats + refs[] |
|
||||
| `generateParagraphExplanation` | 同上 | 三层讲解 |
|
||||
| `generateSentenceExplanation` | 同上 | 逐句讲解 |
|
||||
| `answerWithRag` | `lecture/application/askQuestion.ts` | 锚定问答 |
|
||||
|
||||
**配置**:语音备课 temperature 0.2–0.4;与文字对话共用 `getRuntimeConfig()`,不新增 LLM prefs 键。
|
||||
|
||||
### 6.4 TtsClient
|
||||
|
||||
```typescript
|
||||
interface TtsResult {
|
||||
audioPath: string;
|
||||
timestamps: WordTimestamp[];
|
||||
durationMs: number;
|
||||
}
|
||||
|
||||
interface WordTimestamp {
|
||||
text: string;
|
||||
startMs: number;
|
||||
endMs: number;
|
||||
}
|
||||
|
||||
interface TtsProvider {
|
||||
name: string;
|
||||
synthesize(text: string, options: TtsOptions): Promise<TtsResult>;
|
||||
supportsTimestamps: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
**高亮降级链**:`WORD` → `SENTENCE` → `PARAGRAPH` → `NONE`(仅进度条)
|
||||
|
||||
**Prefs 键(建议)**:`extensions.zotero.chatpapers.ttsProvider` / `ttsApiKey` / `ttsVoice`
|
||||
|
||||
### 6.5 RagIndex
|
||||
|
||||
```typescript
|
||||
interface RagIndex {
|
||||
build(paperId: string, paragraphs: Paragraph[]): Promise<void>;
|
||||
search(paperId: string, query: string, topK: number): Paragraph[];
|
||||
}
|
||||
```
|
||||
|
||||
一期 SQLite FTS5,不引入 embedding。索引在解析完成后构建,与 `paragraph_fts` 表同步。
|
||||
|
||||
### 6.6 PrepareLectureUseCase
|
||||
|
||||
**职责**:编排备课流水线;发出增量就绪事件;保证同一 paper 互斥(备课锁)。
|
||||
|
||||
**伪流程**:
|
||||
|
||||
```
|
||||
start(paperId):
|
||||
if lecture_status != idle && != failed: return // 防重入
|
||||
set status → parsing
|
||||
paragraphs ← PdfParser.parse(path)
|
||||
persist paragraphs + FTS
|
||||
set status → summarizing
|
||||
summary ← Llm.generateSummary(paragraphs)
|
||||
persist lecture.summary
|
||||
set status → beats_generating
|
||||
beats ← Llm.generateBeats(summary, paragraphIndex)
|
||||
beats ← validateRefs(beats, paragraphIds)
|
||||
persist beats
|
||||
emit lecture:beats_ready // UI 可开整体讲解
|
||||
set status → paragraphs_generating
|
||||
for batch in paragraphs.chunk(5):
|
||||
explanations ← Llm.generateParagraphExplanation(batch)
|
||||
persist + emit lecture:paragraph_ready
|
||||
set status → tts_generating
|
||||
for unit in beats + explanations:
|
||||
audio ← Tts.synthesize(unit.script)
|
||||
persist audio_path + emit tts:unit_ready
|
||||
set status → ready
|
||||
```
|
||||
|
||||
### 6.7 PlayUseCase + PlayerController
|
||||
|
||||
```typescript
|
||||
interface PlayerState {
|
||||
mode: "overview" | "paragraph";
|
||||
currentUnitId: string;
|
||||
isPlaying: boolean;
|
||||
playbackRate: 1 | 1.5 | 2;
|
||||
highlightCursorMs: number;
|
||||
}
|
||||
```
|
||||
|
||||
**跳播**:`currentUnitId` 变更 → 加载 mp3 → `audio.currentTime = 0` → 更新 `progress` 表。
|
||||
|
||||
**断点续播**:pane 打开时读 `progress.last_unit_id` + `last_position_ms`。
|
||||
|
||||
### 6.8 AskQuestionUseCase
|
||||
|
||||
与文字对话的差异:
|
||||
|
||||
| 项 | 文字对话 | 语音伴读问答 |
|
||||
|----|----------|--------------|
|
||||
| 上下文 | 整篇 PDF 文本(可能截断) | 锚点 + 摘要 + RAG topK=5 |
|
||||
| 存储 | sessions JSON | qa_record 表 |
|
||||
| 输出 | 仅文字流 | 文字流 + TTS mp3 |
|
||||
| 锚点 | 选区字符串 | beat / paragraph / sentence / global |
|
||||
|
||||
```typescript
|
||||
interface AskContext {
|
||||
question: string;
|
||||
anchor: {
|
||||
type: "beat" | "paragraph" | "sentence" | "global";
|
||||
id?: string;
|
||||
text?: string;
|
||||
explanation?: string;
|
||||
};
|
||||
summary: SummaryJson;
|
||||
ragParagraphs: Paragraph[];
|
||||
paperTitle: string;
|
||||
}
|
||||
```
|
||||
|
||||
### 6.9 GenerateReportUseCase
|
||||
|
||||
```
|
||||
ReportBuilder.build(paperId):
|
||||
summary ← lecture.summary_json
|
||||
progress ← progress.listened_units_json
|
||||
qas ← qa_record ORDER BY created_at
|
||||
highlights ← highlight JOIN paragraph
|
||||
bibtex ← citeExport
|
||||
|
||||
MarkdownRenderer → createLectureNote(parentItem, markdown)
|
||||
```
|
||||
|
||||
**Note 策略**:同名 `ChatPapers 伴读报告: {title}` 存在则更新,避免笔记爆炸。
|
||||
|
||||
---
|
||||
|
||||
## 7. 数据模型
|
||||
|
||||
### 7.1 存储目录
|
||||
|
||||
```
|
||||
{ZoteroProfile}/chatpapers/
|
||||
├── sessions/ # 文字对话(已有)
|
||||
│ └── {itemKey}-{attachmentKey}.json
|
||||
└── lecture/ # 语音伴读(新增)
|
||||
├── lecture.db # SQLite
|
||||
└── audio/
|
||||
└── {cacheKey}/
|
||||
├── beat-{id}.mp3
|
||||
├── para-{id}.mp3
|
||||
└── qa-{id}.mp3
|
||||
```
|
||||
|
||||
### 7.2 缓存键
|
||||
|
||||
```
|
||||
cacheKey = SHA256(attachmentId + "|" + fileHash)
|
||||
|
||||
失效条件:
|
||||
• fileHash 变化
|
||||
• 用户强制重新备课
|
||||
• lecture prompt_version major 升级(可选)
|
||||
```
|
||||
|
||||
### 7.3 ER 关系
|
||||
|
||||
```
|
||||
PaperCache 1──* Paragraph
|
||||
PaperCache 1──1 Lecture
|
||||
Lecture 1──* Beat
|
||||
Lecture 1──* ParagraphExplanation
|
||||
PaperCache 1──* QARecord
|
||||
PaperCache 1──1 Progress
|
||||
PaperCache 1──* Highlight
|
||||
```
|
||||
|
||||
### 7.4 SQLite 表结构
|
||||
|
||||
```sql
|
||||
-- 论文缓存根表
|
||||
CREATE TABLE paper_cache (
|
||||
id TEXT PRIMARY KEY,
|
||||
attachment_id TEXT NOT NULL,
|
||||
item_id TEXT NOT NULL,
|
||||
file_hash TEXT NOT NULL,
|
||||
file_path TEXT NOT NULL,
|
||||
title TEXT,
|
||||
language TEXT,
|
||||
parse_status TEXT NOT NULL,
|
||||
lecture_status TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL,
|
||||
UNIQUE(attachment_id, file_hash)
|
||||
);
|
||||
|
||||
CREATE TABLE chapter (
|
||||
id TEXT PRIMARY KEY,
|
||||
paper_id TEXT NOT NULL REFERENCES paper_cache(id),
|
||||
title TEXT NOT NULL,
|
||||
order_index INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE paragraph (
|
||||
id TEXT PRIMARY KEY,
|
||||
paper_id TEXT NOT NULL REFERENCES paper_cache(id),
|
||||
chapter_id TEXT REFERENCES chapter(id),
|
||||
order_index INTEGER NOT NULL,
|
||||
text TEXT NOT NULL,
|
||||
page INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE lecture (
|
||||
paper_id TEXT PRIMARY KEY REFERENCES paper_cache(id),
|
||||
summary_json TEXT NOT NULL,
|
||||
prompt_version TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE beat (
|
||||
id TEXT PRIMARY KEY,
|
||||
paper_id TEXT NOT NULL REFERENCES paper_cache(id),
|
||||
order_index INTEGER NOT NULL,
|
||||
title TEXT,
|
||||
type TEXT NOT NULL,
|
||||
script TEXT NOT NULL,
|
||||
refs_json TEXT NOT NULL,
|
||||
audio_path TEXT,
|
||||
timestamps_json TEXT,
|
||||
tts_status TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE paragraph_explanation (
|
||||
paragraph_id TEXT PRIMARY KEY REFERENCES paragraph(id),
|
||||
paper_id TEXT NOT NULL REFERENCES paper_cache(id),
|
||||
layer_what TEXT NOT NULL,
|
||||
layer_how TEXT NOT NULL,
|
||||
layer_where TEXT NOT NULL,
|
||||
audio_path TEXT,
|
||||
timestamps_json TEXT,
|
||||
gen_status TEXT NOT NULL,
|
||||
tts_status TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE progress (
|
||||
paper_id TEXT PRIMARY KEY REFERENCES paper_cache(id),
|
||||
last_mode TEXT,
|
||||
last_unit_id TEXT,
|
||||
last_position_ms INTEGER DEFAULT 0,
|
||||
listened_units_json TEXT,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE qa_record (
|
||||
id TEXT PRIMARY KEY,
|
||||
paper_id TEXT NOT NULL REFERENCES paper_cache(id),
|
||||
anchor_type TEXT NOT NULL,
|
||||
anchor_id TEXT,
|
||||
question TEXT NOT NULL,
|
||||
answer TEXT NOT NULL,
|
||||
audio_path TEXT,
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE highlight (
|
||||
paper_id TEXT NOT NULL REFERENCES paper_cache(id),
|
||||
paragraph_id TEXT NOT NULL REFERENCES paragraph(id),
|
||||
note TEXT,
|
||||
created_at INTEGER NOT NULL,
|
||||
PRIMARY KEY (paper_id, paragraph_id)
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE paragraph_fts USING fts5(
|
||||
paragraph_id,
|
||||
paper_id,
|
||||
text,
|
||||
tokenize='unicode61'
|
||||
);
|
||||
```
|
||||
|
||||
### 7.5 Beat refs 校验
|
||||
|
||||
```
|
||||
输入: beats[], validParagraphIds (Set)
|
||||
对每个 beat:
|
||||
beat.refs = beat.refs.filter(id => validParagraphIds.has(id))
|
||||
if beat.refs.isEmpty:
|
||||
beat.degraded = true
|
||||
输出: beats[]
|
||||
```
|
||||
|
||||
### 7.6 摘要 JSON
|
||||
|
||||
```json
|
||||
{
|
||||
"problem": "…",
|
||||
"method": "…",
|
||||
"result": "…",
|
||||
"limitation": "…"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 核心流水线
|
||||
|
||||
### 8.1 语音伴读端到端
|
||||
|
||||
```
|
||||
PDF attachment path
|
||||
→ findPdfAttachment + computeFileHash
|
||||
→ [MinerU PdfParser] → paragraphs[] → SQLite + FTS
|
||||
→ [generateSummary] → lecture.summary
|
||||
→ [generateBeats] → refs 校验 → SQLite
|
||||
→ emit lecture:beats_ready ──► 整体讲解可播(文本)
|
||||
→ [generateParagraphExplanation] × N → SQLite
|
||||
→ emit lecture:paragraph_ready
|
||||
→ [Tts.synthesize] × units → mp3 → SQLite
|
||||
→ emit tts:unit_ready ──► 播放器加载音频
|
||||
→ 用户提问 → [AskQuestion + RAG] → 文字流 + TTS
|
||||
→ [GenerateReport] → Zotero Note
|
||||
```
|
||||
|
||||
### 8.2 增量备课时序
|
||||
|
||||
```
|
||||
时间 ──────────────────────────────────────────────►
|
||||
|
||||
解析 ████████
|
||||
摘要 ███
|
||||
beats ██████ ← beats_ready(~1-2min,可先听文本/TTS排队)
|
||||
逐段讲解 ████████████████████
|
||||
TTS(beats) ████████
|
||||
TTS(段落) █████████████████
|
||||
```
|
||||
|
||||
### 8.3 文字对话路径(不变)
|
||||
|
||||
```
|
||||
Item → findPdfAttachment → PDFWorker.getFullText
|
||||
→ buildChatMessages → chatStream → ChatView 渲染
|
||||
→ saveSession (JSON)
|
||||
→ createChildNote(可选)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 状态机
|
||||
|
||||
### 9.1 备课状态(lecture_status)
|
||||
|
||||
```
|
||||
idle → parsing → parsed → summarizing → beats_generating
|
||||
→ paragraphs_generating (batch loop) → tts_generating → ready
|
||||
任何阶段 ──error──► failed(可从 failed 或任意已完成阶段 resume)
|
||||
```
|
||||
|
||||
**可恢复**:每阶段结束写库;重启后读 `lecture_status`,跳过已完成步骤。
|
||||
|
||||
### 9.2 播放器状态
|
||||
|
||||
见 §6.7。pane `onDestroy` 时持久化 progress,释放 `<audio>` 引用。
|
||||
|
||||
---
|
||||
|
||||
## 10. 内部事件(EventBus)
|
||||
|
||||
| 事件 | 载荷 | 订阅者 |
|
||||
|------|------|--------|
|
||||
| `parse:progress` | `{ paperId, percent, message }` | LecturePane |
|
||||
| `lecture:beats_ready` | `{ paperId, beatCount }` | LecturePane, Player |
|
||||
| `lecture:paragraph_ready` | `{ paperId, paragraphId }` | LecturePane |
|
||||
| `tts:unit_ready` | `{ paperId, unitId, audioPath }` | Player |
|
||||
| `lecture:failed` | `{ paperId, error }` | LecturePane |
|
||||
| `qa:chunk` | `{ textDelta }` | QAPanel |
|
||||
| `qa:complete` | `{ qaRecordId, audioPath? }` | QAPanel |
|
||||
|
||||
EventBus 实例按 `paperId` 或全局单例均可;MVP 用模块级 `EventTarget` 即可。
|
||||
|
||||
**长时间任务**:
|
||||
- MinerU:子进程(不阻塞 UI)
|
||||
- LLM / TTS:`async fetch` + AbortController
|
||||
- SQLite 写:单线程队列,避免并发锁
|
||||
|
||||
---
|
||||
|
||||
## 11. 设置与 Preference 设计
|
||||
|
||||
### 11.1 分组结构(preferences.xhtml)
|
||||
|
||||
```
|
||||
ChatPapers 设置
|
||||
├── 通用 · LLM ← 两模式共享
|
||||
│ Provider / Base URL / API Key / Model / Temperature
|
||||
├── 文字对话 ← 已有
|
||||
│ System Prompt / Summary Prompt / Answer Language
|
||||
└── 语音伴读 ← 新增
|
||||
TTS Provider / API Key / Voice
|
||||
教师风格(简洁 / 详细)
|
||||
MinerU 路径(若需)
|
||||
数据流向说明(静态文案)
|
||||
```
|
||||
|
||||
### 11.2 Prefs 键命名
|
||||
|
||||
沿用 `extensions.zotero.chatpapers.*` 前缀,不新增 addon ID:
|
||||
|
||||
| 键 | 模式 | 说明 |
|
||||
|----|------|------|
|
||||
| `provider` | 共享 | 已有 |
|
||||
| `apiKey` | 共享 | 已有 |
|
||||
| `systemPrompt` | 文字 | 已有 |
|
||||
| `ttsProvider` | 语音 | 新增 |
|
||||
| `ttsApiKey` | 语音 | 新增 |
|
||||
| `ttsVoice` | 语音 | 新增 |
|
||||
| `lectureTeacherStyle` | 语音 | 新增 |
|
||||
| `mineruPath` | 语音 | 新增,可选 |
|
||||
|
||||
---
|
||||
|
||||
## 12. 目标工程结构
|
||||
|
||||
在 [development.md](../development.md) 现有结构基础上扩展:
|
||||
|
||||
```text
|
||||
src/
|
||||
├── hooks.ts
|
||||
├── modules/
|
||||
│ ├── ui/
|
||||
│ │ ├── readerPane.ts # 注册 Chat + Lecture 两个 section
|
||||
│ │ ├── chatView.ts # 文字对话(不动)
|
||||
│ │ ├── multiChatView.ts # 文字多 PDF(不动)
|
||||
│ │ └── prefs.ts
|
||||
│ ├── pdf/
|
||||
│ │ ├── extractor.ts # 共享:附件定位
|
||||
│ │ ├── context.ts # 文字专属
|
||||
│ │ ├── multiContext.ts # 文字专属
|
||||
│ │ └── selection.ts # 共享
|
||||
│ ├── llm/ # 共享 client + providers
|
||||
│ ├── storage/
|
||||
│ │ └── sessions.ts # 文字专属
|
||||
│ ├── zotero/ # 共享
|
||||
│ └── lecture/ # ★ 语音伴读(新建)
|
||||
│ ├── ui/
|
||||
│ │ ├── lecturePane.ts
|
||||
│ │ ├── playerController.ts
|
||||
│ │ ├── unitList.ts
|
||||
│ │ └── qaPanel.ts
|
||||
│ ├── application/
|
||||
│ │ ├── prepareLecture.ts
|
||||
│ │ ├── playLecture.ts
|
||||
│ │ ├── askQuestion.ts
|
||||
│ │ └── generateReport.ts
|
||||
│ ├── domain/
|
||||
│ │ ├── types.ts
|
||||
│ │ ├── lectureState.ts
|
||||
│ │ └── playerState.ts
|
||||
│ └── infrastructure/
|
||||
│ ├── pdf/
|
||||
│ │ ├── parser.ts
|
||||
│ │ └── fileHash.ts
|
||||
│ ├── tts/
|
||||
│ │ ├── client.ts
|
||||
│ │ └── providers.ts
|
||||
│ ├── storage/
|
||||
│ │ ├── repository.ts
|
||||
│ │ ├── schema.ts
|
||||
│ │ └── audioStorage.ts
|
||||
│ ├── rag/
|
||||
│ │ └── index.ts
|
||||
│ ├── llm/
|
||||
│ │ ├── lectureLlm.ts
|
||||
│ │ └── lecturePrompts.ts
|
||||
│ ├── zotero/
|
||||
│ │ └── adapter.ts
|
||||
│ └── events.ts
|
||||
```
|
||||
|
||||
**样式**:`addon/content/chatpapers.css` 增加 `.chatpapers-lecture-*` 前缀类,与文字对话样式并列。
|
||||
|
||||
**i18n**:`addon/locale/*/chatpapers.ftl` 增加 `item-section-lecture-*` 等键。
|
||||
|
||||
---
|
||||
|
||||
## 13. 外部集成
|
||||
|
||||
### 13.1 LLM(共享)
|
||||
|
||||
| 提供商 | 协议 | 文字对话 | 语音伴读 |
|
||||
|--------|------|:--------:|:--------:|
|
||||
| OpenAI 兼容 | HTTPS SSE | ✓ | ✓ 备课 + 问答 |
|
||||
| Ollama | localhost | ✓ | ✓ |
|
||||
|
||||
### 13.2 TTS(语音专属)
|
||||
|
||||
| 提供商 | 选型维度 |
|
||||
|--------|----------|
|
||||
| 火山 / Azure / MiniMax / ElevenLabs | 中文自然度、价格、word timestamps |
|
||||
|
||||
### 13.3 MinerU(语音专属)
|
||||
|
||||
- 集成:子进程 CLI,stdout JSON
|
||||
- 分发:Phase 0 决定「随插件文档安装」vs「用户自装 Python 环境」
|
||||
|
||||
### 13.4 Better BibTeX(共享,可选)
|
||||
|
||||
语音报告参考文献卡片复用 `citeExport`;逻辑已在 Chat 多 PDF 模式验证。
|
||||
|
||||
---
|
||||
|
||||
## 14. 安全与隐私
|
||||
|
||||
| 项 | 文字对话 | 语音伴读 |
|
||||
|----|----------|----------|
|
||||
| PDF 文件 | 不上传 | 不上传 |
|
||||
| 发往 LLM | 正文 text(可能截断) | 段落 text + 摘要 + 问题 |
|
||||
| 发往 TTS | — | 讲解/回答中文文本 |
|
||||
| API Key | Zotero prefs | LLM 共享;TTS 独立 pref |
|
||||
| 日志 | 不记录 Key | 不记录 Key;段落正文可截断 |
|
||||
|
||||
设置页「数据流向」分组须同时覆盖两种模式。
|
||||
|
||||
---
|
||||
|
||||
## 15. Phase 0 技术验证(Go / No-Go)
|
||||
|
||||
| # | 验证项 | 通过标准 | 阻塞 MVP |
|
||||
|---|--------|----------|----------|
|
||||
| P0-1 | 第二 Item pane 与 Chat 并存 | 切换 tab 互不影响 | 是 |
|
||||
| P0-2 | MinerU 解析 3 篇论文 | 段落可用率 ≥ 80% | 是 |
|
||||
| P0-3 | Zotero 9 插件内 SQLite CRUD | lecture.db 读写正常 | 是 |
|
||||
| P0-4 | `<audio>` 10 min+ 播放 | 无泄漏 / 崩溃 | 是 |
|
||||
| P0-5 | TTS 中文样本 | 自然度可接受 | 是 |
|
||||
| P0-6 | TTS timestamps | 有则记录;无则句子级降级 | 否 |
|
||||
| P0-7 | Chat 模式回归 | 对话 / 总结 / 存笔记不受影响 | 是 |
|
||||
|
||||
P0-1、P0-7 验证**双模式隔离**;P0-2 ~ P0-6 验证**语音链路**。
|
||||
|
||||
---
|
||||
|
||||
## 16. 性能与扩展
|
||||
|
||||
### 16.1 长论文策略
|
||||
|
||||
| 页数 | 策略 |
|
||||
|------|------|
|
||||
| ≤ 30 | 全量逐段备课 |
|
||||
| 30–80 | 逐段 lazy:按章节或用户触发 |
|
||||
| > 80 | 默认仅整体讲解;逐段需用户确认 |
|
||||
|
||||
### 16.2 并发控制
|
||||
|
||||
- 备课 LLM:beats 小批量并行 ≤ 3
|
||||
- TTS:队列化,beats 优先
|
||||
- 同一 paper 备课互斥锁
|
||||
- SQLite 写串行队列
|
||||
|
||||
### 16.3 伴读库窗口(P1)
|
||||
|
||||
`libraryWindow.ts` 扫描 `paper_cache` 表 + Zotero Items;>5000 items 时仅索引「有过备课记录」的 attachment。
|
||||
|
||||
---
|
||||
|
||||
## 17. 可观测性
|
||||
|
||||
| 类型 | 内容 |
|
||||
|------|------|
|
||||
| 结构化日志 | `mode: "lecture"`, paperId, stage, durationMs, errorCode |
|
||||
| 用户可见进度 | 解析 %、beats n/total、TTS n/total |
|
||||
| 调试导出 | 单篇 metrics JSON(开发者菜单,可选) |
|
||||
|
||||
日志前缀建议 `[ChatPapers:Lecture]`,与 `[ChatPapers:Chat]` 区分。
|
||||
|
||||
---
|
||||
|
||||
## 18. 风险与对策
|
||||
|
||||
| 风险 | 对策 |
|
||||
|------|------|
|
||||
| 语音模块影响文字对话稳定性 | 依赖隔离 + pane 级 try/catch + P0-7 回归 |
|
||||
| MinerU 打包复杂 | Phase 0 验证;文档化安装 |
|
||||
| refs 幻觉 | 校验器 + degraded beat |
|
||||
| TTS 无 timestamps | 高亮降级链 |
|
||||
| SQLite 在插件环境不可用 | Phase 0 备选 sql.js / JSON 分文件 |
|
||||
| 双 pane 内存占用 | onDestroy 释放 audio、取消进行中的 fetch |
|
||||
|
||||
---
|
||||
|
||||
## 19. 开放问题
|
||||
|
||||
| # | 问题 | 影响 |
|
||||
|---|------|------|
|
||||
| 1 | SQLite:better-sqlite3 vs sql.js vs 纯 JSON | 存储层选型 |
|
||||
| 2 | MinerU 分发方式 | 安装体验 |
|
||||
| 3 | 问答 TTS:流式 vs 整段合成 | AskQuestion 复杂度 |
|
||||
| 4 | 文字对话是否未来支持 TTS 朗读回复 | v1.1+ 可统一 TtsClient |
|
||||
| 5 | Chat 会话 → 语音问答上下文导入 | v1.1 产品决策 |
|
||||
|
||||
---
|
||||
|
||||
## 20. 文档关系
|
||||
|
||||
```
|
||||
docs/requirements.md ChatPapers 文字对话需求(不变)
|
||||
docs/development.md 现有工程与工具链(不变)
|
||||
docs/newidea/
|
||||
├── PLAN.md / PRD.md BanDu 原始策划(参考)
|
||||
├── ARCHITECTURE.md 独立插件架构(参考)
|
||||
├── DUAL_MODE_PRD.md ★ 双模式集成需求
|
||||
└── DUAL_MODE_ARCHITECTURE.md ★ 本文:双模式集成架构
|
||||
```
|
||||
|
||||
**实施顺序**:需求看 DUAL_MODE_PRD.md → 架构看本文 → 领域细节参考 ARCHITECTURE.md §4–§6。
|
||||
|
||||
---
|
||||
|
||||
## 21. 文档修订记录
|
||||
|
||||
| 版本 | 日期 | 说明 |
|
||||
|------|------|------|
|
||||
| v0.1 | 2026-08-29 | 初稿:ChatPapers 双模式集成架构;语音模块命名 `lecture/` |
|
||||
@@ -0,0 +1,485 @@
|
||||
# ChatPapers 双模式集成需求规格说明书
|
||||
|
||||
| 字段 | 内容 |
|
||||
|------|------|
|
||||
| 文档名称 | 双模式集成 PRD(方案 A) |
|
||||
| 插件名称 | ChatPapers for Zotero |
|
||||
| 新增模式 | BanDu(伴读) |
|
||||
| 文档版本 | v0.1 |
|
||||
| 状态 | 草案 |
|
||||
| 目标平台 | Zotero 9(macOS / Windows) |
|
||||
| 关联文档 | [ChatPapers 需求](../requirements.md)、[BanDu 策划](./PLAN.md)、[BanDu PRD](./PRD.md)、[BanDu 架构](./ARCHITECTURE.md) |
|
||||
|
||||
> **本文档定位**:在**不拆分、不 fork** 的前提下,将 BanDu(伴读)作为 ChatPapers 插件的**第二种工作模式**纳入同一产品。
|
||||
> 原有 ChatPapers 对话模式的需求以 [requirements.md](../requirements.md) 为准,本文仅描述**集成策略、共享边界、伴读模式增量需求**。
|
||||
|
||||
---
|
||||
|
||||
## 1. 产品概述
|
||||
|
||||
### 1.1 一句话定位
|
||||
|
||||
**ChatPapers** = Zotero 内的 AI 论文助手:**对话模式**用于即时问答与总结,**伴读模式**用于 AI 老师式音频授课与知识沉淀——同一插件、同一论文库、两种互补工作流。
|
||||
|
||||
### 1.2 双模式心智模型
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ ChatPapers for Zotero │
|
||||
│ (一个插件 · 一套 Zotero 集成) │
|
||||
└───────────────┬─────────────────┘
|
||||
│
|
||||
┌─────────────────────┴─────────────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────────┐ ┌──────────────────────┐
|
||||
│ 模式 A:对话 Chat │ │ 模式 B:伴读 BanDu │
|
||||
│ 「我现在想问什么」 │ │ 「请老师先备课再讲」 │
|
||||
├──────────────────────┤ ├──────────────────────┤
|
||||
│ 即时问答 / 一键总结 │ │ 备课 → 听课 → 报告 │
|
||||
│ 文字流式回复 │ │ 整体讲解 / 逐段精读 │
|
||||
│ 选区提问 │ │ TTS 音频 + 播放器 │
|
||||
│ 会话 JSON 持久化 │ │ Lecture SQLite 缓存 │
|
||||
└──────────────────────┘ └──────────────────────┘
|
||||
│ │
|
||||
└─────────────────────┬─────────────────────┘
|
||||
▼
|
||||
共享:PDF 附件 · LLM 配置 · 子笔记 · BibTeX
|
||||
```
|
||||
|
||||
### 1.3 为什么采用双模式(方案 A)
|
||||
|
||||
| 考量 | 说明 |
|
||||
|------|------|
|
||||
| 用户价值互补 | Chat 适合「带着问题读」;伴读适合「先听一遍再精读」 |
|
||||
| 工程复用 | LLM 客户端、PDF 附件解析、笔记回写、引用导出、设置页已在 Chat 模式实现 |
|
||||
| 安装成本 | 用户只装一个插件,不重复配置 Provider / API Key |
|
||||
| 数据闭环 | 伴读报告与 Chat 笔记均写入同一 Zotero Item,写作时可统一检索 |
|
||||
| 品牌演进 | 短期保留 ChatPapers 主品牌;伴读作为子品牌「BanDu」在 UI 内露出 |
|
||||
|
||||
### 1.4 产品目标(伴读模式增量)
|
||||
|
||||
| 目标 | 说明 |
|
||||
|------|------|
|
||||
| G-B1 降低读论文门槛 | 10–20 分钟整体讲解把握论文核心 |
|
||||
| G-B2 支持深度精读 | 逐段三层讲解 + 按需逐句解释 |
|
||||
| G-B3 闭环沉淀知识 | 伴读报告、问答、重点标记写入 Zotero 子笔记 |
|
||||
| G-B4 零迁移成本 | 论文库沿用 Zotero,PDF 不出本地 |
|
||||
|
||||
### 1.5 成功指标(伴读 MVP 验证期)
|
||||
|
||||
| 指标 | 目标 |
|
||||
|------|------|
|
||||
| 整体讲解完成率 | ≥ 60% 启动后听完 |
|
||||
| 单篇备课可听时间 | 摘要 + beats 生成后 ≤ 2 min 可开听 |
|
||||
| 问答首字延迟 | ≤ 3 s(文字) |
|
||||
| 解析可用率 | 典型 8 页 NLP 论文 ≥ 90% 段落无需手动修正 |
|
||||
| 模式切换无摩擦 | 同一 Item 可在 Chat / 伴读 pane 间切换,互不丢状态 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 范围定义
|
||||
|
||||
### 2.1 一期包含(伴读模式 MVP)
|
||||
|
||||
- 在现有 ChatPapers 插件内新增 Item pane **「伴读」** 标签页(与现有 **「ChatPapers」** 标签页并存)
|
||||
- 读取 Zotero PDF 附件 → 本地结构化解析 → 段落树
|
||||
- 备课流水线:全文摘要 + 整体讲解 beats + 逐段三层讲解;增量备课
|
||||
- TTS 合成 + 本地 mp3 缓存
|
||||
- 播放器:整体讲解 / 逐段精读、跳播、语速、文字面板高亮、PDF 页跳转
|
||||
- 文字提问(锚定 beat / 段落 / 句子 / 全局)→ 文字 + 语音回答
|
||||
- 伴读报告 + 参考文献卡片 → Zotero 子笔记
|
||||
- 伴读专属设置项(TTS、教师风格);LLM 设置与 Chat 模式**共享**
|
||||
- 断点续播、本地 Lecture 缓存
|
||||
|
||||
### 2.2 一期明确不包含
|
||||
|
||||
| 功能 | 计划版本 | 备注 |
|
||||
|------|----------|------|
|
||||
| 语音输入(ASR)、barge-in | v1.1 | 两模式均不涉及 |
|
||||
| 章节边界软提示、难点句清单 | v1.1 | 伴读专属 |
|
||||
| PDF viewer 内段落级高亮 | v1.1 | 伴读专属 |
|
||||
| 检验模式(老师考读者) | v2 | 伴读专属 |
|
||||
| Chat 模式重构或合并 UI | — | Chat 保持独立 pane |
|
||||
| 独立 BanDu 插件安装包 | — | 不 fork |
|
||||
|
||||
### 2.3 共享能力边界(不重复建设)
|
||||
|
||||
以下能力由 ChatPapers 基座提供,伴读模式**直接复用**,不在伴读 MVP 中重写:
|
||||
|
||||
| 共享模块 | 现有实现 | 伴读用法 |
|
||||
|----------|----------|----------|
|
||||
| Item pane 注册机制 | `readerPane.ts` | 注册第二个 section |
|
||||
| PDF 附件定位 | `extractor.findPdfAttachment` | 获取路径;伴读另需 file hash |
|
||||
| LLM 流式客户端 | `llm/client.ts` | 备课 + 问答 |
|
||||
| Provider 预设 | `llm/providers.ts` | 共享同一套 LLM 配置 |
|
||||
| 子笔记创建 | `zotero/notes.ts` | 伴读报告写入 |
|
||||
| BibTeX / 引用导出 | `zotero/citeExport.ts` | 参考文献卡片 |
|
||||
| PDF 选区读取 | `pdf/selection.ts` | 「解释这句」入口 |
|
||||
| 偏好设置框架 | `ui/prefs.ts` | 扩展 TTS / 教师风格分组 |
|
||||
| Markdown 渲染 | `utils/markdown.ts` | 报告预览(可选) |
|
||||
|
||||
### 2.4 模式专属能力(伴读新建)
|
||||
|
||||
| 模块 | 说明 | 代码归属(建议) |
|
||||
|------|------|------------------|
|
||||
| 结构化 PDF 解析 | MinerU → 段落树 | `src/modules/bandu/infrastructure/pdf/` |
|
||||
| 备课流水线 | 摘要 / beats / 三层讲解 | `src/modules/bandu/application/` |
|
||||
| TTS 客户端 | mp3 + timestamps | `src/modules/bandu/infrastructure/tts/` |
|
||||
| SQLite 存储 | Lecture / 进度 / Q&A | `src/modules/bandu/infrastructure/storage/` |
|
||||
| RAG 检索 | FTS5 段落检索 | `src/modules/bandu/infrastructure/rag/` |
|
||||
| 播放器 UI | `<audio>` + 状态机 | `src/modules/bandu/ui/` |
|
||||
| 伴读 pane | BanduPane | `src/modules/bandu/ui/banduPane.ts` |
|
||||
|
||||
---
|
||||
|
||||
## 3. 用户故事
|
||||
|
||||
### 3.1 跨模式用户故事
|
||||
|
||||
| ID | 作为… | 我想要… | 以便… | 优先级 |
|
||||
|----|-------|---------|-------|--------|
|
||||
| US-X01 | 研究者 | 在同一篇论文上切换「对话」与「伴读」 | 先听课再针对性 Chat,或 Chat 后再系统听课 | P1 |
|
||||
| US-X02 | 研究者 | 只配置一次 LLM API Key | 两种模式共用,降低设置成本 | P0 |
|
||||
| US-X03 | 研究者 | Chat 笔记与伴读报告都挂在同一 Item 下 | 写论文时在 Zotero 内统一搜索 | P0 |
|
||||
|
||||
### 3.2 伴读模式用户故事(P0)
|
||||
|
||||
| ID | 作为… | 我想要… | 以便… |
|
||||
|----|-------|---------|-------|
|
||||
| US-B01 | 研究者 | 选中论文后一键「开始备课」 | AI 先读完整篇再讲解 |
|
||||
| US-B02 | 研究者 | 选择「整体讲解」收听 | 20 分钟内把握核心 |
|
||||
| US-B03 | 研究者 | 选择「逐段精读」按章节听 | 深入理解方法与实验 |
|
||||
| US-B04 | 研究者 | 播放中随时文字提问 | 不打断思路又能澄清疑问 |
|
||||
| US-B05 | 研究者 | 选中难句点「解释这句」 | 快速理解长难句 |
|
||||
| US-B06 | 研究者 | 听完后生成伴读报告到 Zotero | 写论文时搜索调出素材 |
|
||||
| US-B07 | 研究者 | 二次打开同一篇秒进、离线重听 | 不重复花 API 费用 |
|
||||
| US-B08 | 研究者 | 标记重点段落 | 报告汇总关心部分 |
|
||||
| US-B09 | 研究者 | 配置 TTS API Key | 控制成本与音质 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 界面与交互需求
|
||||
|
||||
### 4.1 Item Pane 双标签布局
|
||||
|
||||
```
|
||||
Zotero Item Pane(同一 PDF 条目)
|
||||
├── [ChatPapers] ← 现有:对话 / 总结 / 存笔记
|
||||
└── [伴读 BanDu] ← 新增:备课 / 播放 / 报告
|
||||
```
|
||||
|
||||
| ID | 需求 | 优先级 | 验收标准 |
|
||||
|----|------|--------|----------|
|
||||
| UI-001 | 两个 pane section 独立注册、独立生命周期 | P0 | 切换 tab 不互相 destroy 对方状态 |
|
||||
| UI-002 | 伴读 pane 在无 PDF 时禁用并提示 | P0 | 与 Chat pane 行为一致 |
|
||||
| UI-003 | 伴读 pane 头部展示论文标题 + 备课状态 | P0 | 见 §4.3 状态文案 |
|
||||
| UI-004 | 设置页分「通用 LLM」「ChatPapers」「伴读」三个分组 | P0 | LLM 配置只填一次 |
|
||||
| UI-005 | 伴读库窗口(全库状态列表) | P1 | MVP 可简版或后置 |
|
||||
|
||||
### 4.2 伴读页布局(BanDu Pane)
|
||||
|
||||
布局沿用 [PLAN.md §3.6](./PLAN.md) wireframe,关键区域:
|
||||
|
||||
- 模式切换:整体讲解 | 逐段精读
|
||||
- 播放控制:▶ ⏸ ⏮ ⏭ 1x / 1.5x / 2x
|
||||
- 左侧:单位列表(beats 或段落),含已听 / 未听 / ★ 标记
|
||||
- 右侧:当前讲解文本 + 原文对照 + 提问区
|
||||
- 底部/侧栏:「生成报告」「看原文(跳 PDF 页)」
|
||||
|
||||
### 4.3 状态展示
|
||||
|
||||
| 状态 | 用户可见文案 |
|
||||
|------|--------------|
|
||||
| 未备课 | 「点击开始备课」 |
|
||||
| 解析中 | 「正在读取论文… {percent}%」 |
|
||||
| 备课中(可部分播放) | 「整体讲解已就绪,逐段讲解生成中 {n}/{total}」 |
|
||||
| 已备课 | 「老师已备课 ✓」 |
|
||||
| 播放中 | 当前单位高亮 + 进度条 |
|
||||
| 离线可听 | 「已缓存,离线可播放」 |
|
||||
|
||||
### 4.4 模式间交互原则
|
||||
|
||||
- **不强制串联**:用户可只用 Chat、只用伴读,或组合使用
|
||||
- **不自动同步上下文**:Chat 会话历史不自动注入伴读问答(v1.1 可评估「从 Chat 导入问题」)
|
||||
- **笔记类型区分**:Chat 存笔记标题前缀 `ChatPapers:`;伴读报告 `BanDu 伴读报告:`,避免覆盖
|
||||
- **读者发起,系统不打扰**:伴读播完不弹「有疑问吗」(与 BanDu 策划一致)
|
||||
|
||||
---
|
||||
|
||||
## 5. 功能需求
|
||||
|
||||
### 5.1 插件集成(双模式)
|
||||
|
||||
| ID | 需求描述 | 优先级 | 验收标准 |
|
||||
|----|----------|--------|----------|
|
||||
| DM-001 | 伴读作为 ChatPapers 插件内独立模块加载,不新增 addon ID | P0 | 单一 `.xpi`,manifest 不变 |
|
||||
| DM-002 | `hooks.ts` 启动时同时注册 Chat pane 与 Bandu pane | P0 | 两 pane 均可用 |
|
||||
| DM-003 | 模块代码物理隔离:`src/modules/bandu/` 与 `src/modules/ui/chatView.ts` 分离 | P0 | 无循环依赖 |
|
||||
| DM-004 | 伴读不得修改 Chat 模式现有行为与 API | P0 | Chat 回归测试通过 |
|
||||
|
||||
### 5.2 PDF 解析与段落树(伴读专属)
|
||||
|
||||
| ID | 需求描述 | 优先级 | 验收标准 |
|
||||
|----|----------|--------|----------|
|
||||
| FR-B010 | 本地解析 PDF,输出章节 / 段落 / 要点三层结构 | P0 | 段落含唯一 ID、原文、页码、章节路径 |
|
||||
| FR-B011 | 支持双栏、公式、表格的常见学术论文版式 | P0 | Dogfooding 3 类典型论文可用 |
|
||||
| FR-B012 | 解析结果持久化至 SQLite,key = attachment ID + 文件哈希 | P0 | PDF 未变时跳过重复解析 |
|
||||
| FR-B013 | 解析进度与错误对用户可见 | P0 | 扫描版等给出可行动提示 |
|
||||
| FR-B014 | 文字面板展示解析原文供核对 | P1 | 用户可发现 bad case |
|
||||
|
||||
> **与 Chat 模式关系**:Chat 继续使用 `PDFWorker` 纯文本提取;伴读使用 MinerU 结构化解析。两路径并存,互不替换。
|
||||
|
||||
### 5.3 备课(Lecture 生成)
|
||||
|
||||
| ID | 需求描述 | 优先级 | 验收标准 |
|
||||
|----|----------|--------|----------|
|
||||
| FR-B020 | 生成全文摘要:问题 / 方法 / 结果 / 局限 | P0 | 四字段非空、中文 |
|
||||
| FR-B021 | 生成 8–15 个整体讲解 beat,按教学逻辑排序 | P0 | 每 beat 含讲解文本、类型、refs[] |
|
||||
| FR-B022 | beat 的 refs[] 生成后校验,非法引用剔除 | P0 | 无效 refs 的 beat 降级 |
|
||||
| FR-B023 | 每个 beat 含三要素:为什么重要 / 例子或数字 / 易混淆点 | P0 | Prompt 约束 |
|
||||
| FR-B024 | 每个段落生成三层讲解 | P0 | 讲了什么 / 怎么理解 / 全文定位 |
|
||||
| FR-B025 | 增量备课:摘要 + beats 完成后即可播放整体讲解 | P0 | 无需等逐段全部完成 |
|
||||
| FR-B026 | 逐段讲解后台继续生成,UI 显示进度 | P0 | 新段落就绪后可播放 |
|
||||
| FR-B027 | Lecture 本地缓存,二次打开不重复生成 | P0 | 除非哈希变化或强制重备 |
|
||||
|
||||
### 5.4 TTS 与音频
|
||||
|
||||
| ID | 需求描述 | 优先级 | 验收标准 |
|
||||
|----|----------|--------|----------|
|
||||
| FR-B030 | 讲解文本按 beat / 段落分片合成 mp3 并本地缓存 | P0 | 路径与 Lecture 单元绑定 |
|
||||
| FR-B031 | 英文论文音频为中文讲解 | P0 | 自动检测论文语言 |
|
||||
| FR-B032 | 保存 word timestamps(若 TTS 提供) | P0 | 用于文字高亮 |
|
||||
| FR-B033 | 无 timestamps 时降级句子级 / 段落级高亮 | P0 | UI 不崩溃 |
|
||||
| FR-B034 | 问答回答支持 TTS 播放 | P0 | 每轮 Q&A 可重播 |
|
||||
| FR-B035 | TTS 配置在设置页「伴读」分组,与 LLM 配置分离 | P0 | Chat 模式不依赖 TTS |
|
||||
|
||||
### 5.5 播放器
|
||||
|
||||
| ID | 需求描述 | 优先级 | 验收标准 |
|
||||
|----|----------|--------|----------|
|
||||
| FR-B040 | 模式切换:整体讲解 / 逐段精读 | P0 | 列表与播放单位正确 |
|
||||
| FR-B041 | 播放 / 暂停 / 上一单位 / 下一单位 / 1x·1.5x·2x | P0 | 与 `<audio>` 同步 |
|
||||
| FR-B042 | 单位列表展示已听 / 未听;断点续播 | P0 | 恢复正确单位 |
|
||||
| FR-B043 | 整体模式:beat 文本 + refs 来源段落高亮 | P0 | — |
|
||||
| FR-B044 | 逐段模式:原文 + 三层讲解,高亮跟随音频 | P0 | 至少句子级 |
|
||||
| FR-B045 | 「看原文」跳转 PDF 对应页 | P0 | 页码与段落 page 一致 |
|
||||
| FR-B046 | 点击列表任意行跳播 | P0 | Seek 到该单位起始 |
|
||||
| FR-B047 | 段落「重点标记」★ | P0 | 写入进度并进入报告 |
|
||||
|
||||
### 5.6 问答(伴读上下文)
|
||||
|
||||
| ID | 需求描述 | 优先级 | 验收标准 |
|
||||
|----|----------|--------|----------|
|
||||
| FR-B050 | 文字提问,锚定 beat / 段落 / 句子 / 全局 | P0 | 上下文含锚点原文与讲解 |
|
||||
| FR-B051 | 选中句子触发「解释这句」 | P0 | 翻译 + 句法 + 上下文作用 |
|
||||
| FR-B052 | 回答基于 RAG:当前单位 + 摘要 + 检索相关段 | P0 | 长论文不整篇塞入 Prompt |
|
||||
| FR-B053 | 回答文字流式显示,随后 TTS | P0 | 首 token ≤ 3 s |
|
||||
| FR-B054 | Q&A 写入本地记录并纳入伴读报告 | P0 | 可按段落分组 |
|
||||
|
||||
> **与 Chat 模式关系**:伴读问答使用独立 `qa_record` 表与锚点模型;不复用 Chat 的 `sessions/*.json`,避免数据结构冲突。
|
||||
|
||||
### 5.7 伴读报告与参考文献
|
||||
|
||||
| ID | 需求描述 | 优先级 | 验收标准 |
|
||||
|----|----------|--------|----------|
|
||||
| FR-B060 | 手动或条件触发「生成报告」 | P0 | 更新策略:同名 Note 覆盖 |
|
||||
| FR-B061 | 报告结构:速览 / 收听 / 问答 / 重点 / 参考文献卡片 | P0 | Markdown 结构固定 |
|
||||
| FR-B062 | 参考文献卡片含 BibTeX | P0 | 复用 `citeExport` |
|
||||
| FR-B063 | Note 标题格式 `BanDu 伴读报告: {title}` | P0 | 与 Chat 笔记可区分 |
|
||||
|
||||
### 5.8 设置(共享 + 专属)
|
||||
|
||||
| ID | 需求描述 | 优先级 | 验收标准 |
|
||||
|----|----------|--------|----------|
|
||||
| FR-S010 | LLM Provider / Base URL / API Key / Model | P0 | **共享**,两模式只读同一 prefs |
|
||||
| FR-S011 | Chat 专属:system prompt、summary prompt、answer language | P0 | 已有,不变 |
|
||||
| FR-S012 | 伴读专属:TTS Provider / API Key / 音色 | P0 | 新增 prefs 前缀或分组 |
|
||||
| FR-S013 | 伴读专属:教师风格预设(简洁 / 详细) | P1 | 仅影响新生成内容 |
|
||||
| FR-S014 | 数据流向说明(哪些文本发往外网) | P0 | 设置页固定文案 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 非功能需求
|
||||
|
||||
| ID | 类别 | 需求描述 | 指标 |
|
||||
|----|------|----------|------|
|
||||
| NFR-D001 | 隔离性 | 伴读模块崩溃不影响 Chat pane 可用 | 异常捕获 + 独立 try/catch 边界 |
|
||||
| NFR-D002 | 性能 | 8 页论文本地解析 | ≤ 3 min |
|
||||
| NFR-D003 | 性能 | 缓存命中后打开伴读页 | ≤ 2 s 可播放 |
|
||||
| NFR-D004 | 成本 | 单篇 8 页首次备课总 API 成本 | ≤ ¥10 |
|
||||
| NFR-D005 | 隐私 | PDF 不上传;段落文本按请求发送 | Local-first |
|
||||
| NFR-D006 | 离线 | 已缓存 Lecture + mp3 可离线播放 | 除问答外不依赖网络 |
|
||||
| NFR-D007 | 可维护性 | UI / 领域 / Zotero 适配三层分离 | 见 ARCHITECTURE.md |
|
||||
| NFR-D008 | 存储 | 伴读数据目录 `{Profile}/chatpapers/bandu/` | 与 `sessions/` 并列,不混用 |
|
||||
| NFR-D009 | 兼容性 | Zotero 9.x | 与现有 manifest 一致 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 数据与存储策略
|
||||
|
||||
### 7.1 目录布局
|
||||
|
||||
```
|
||||
{ZoteroProfile}/chatpapers/
|
||||
├── sessions/ # Chat 模式(已有):*.json
|
||||
└── bandu/ # 伴读模式(新增)
|
||||
├── bandu.db # SQLite
|
||||
└── audio/
|
||||
└── {cacheKey}/
|
||||
└── {unitId}.mp3
|
||||
```
|
||||
|
||||
### 7.2 缓存键
|
||||
|
||||
```
|
||||
cacheKey = SHA256(attachmentId + "|" + fileHash)
|
||||
```
|
||||
|
||||
Chat 模式继续使用 `itemKey + attachmentKey` 会话文件;伴读使用 `cacheKey`,**互不干扰**。
|
||||
|
||||
### 7.3 笔记命名约定
|
||||
|
||||
| 来源 | Note 标题前缀 | 覆盖策略 |
|
||||
|------|---------------|----------|
|
||||
| ChatPapers | `ChatPapers:` 或用户自定义 | 每次新建或用户选择 |
|
||||
| BanDu | `BanDu 伴读报告:` | 同名更新,避免笔记爆炸 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 技术集成约束
|
||||
|
||||
### 8.1 建议目录结构
|
||||
|
||||
```
|
||||
src/
|
||||
├── hooks.ts # 注册双 pane
|
||||
├── modules/
|
||||
│ ├── ui/
|
||||
│ │ ├── readerPane.ts # 注册 Chat + Bandu section
|
||||
│ │ ├── chatView.ts # Chat 模式(不动)
|
||||
│ │ └── prefs.ts # 扩展伴读设置分组
|
||||
│ ├── llm/ # 共享
|
||||
│ ├── pdf/
|
||||
│ │ ├── extractor.ts # 共享:附件定位
|
||||
│ │ └── selection.ts # 共享:选区
|
||||
│ ├── zotero/ # 共享:notes, citeExport
|
||||
│ ├── storage/
|
||||
│ │ └── sessions.ts # Chat 专属(不动)
|
||||
│ └── bandu/ # 伴读专属(新建)
|
||||
│ ├── ui/
|
||||
│ │ ├── banduPane.ts
|
||||
│ │ └── player/
|
||||
│ ├── application/
|
||||
│ ├── domain/
|
||||
│ └── infrastructure/
|
||||
│ ├── pdf/ # MinerU 封装
|
||||
│ ├── tts/
|
||||
│ ├── storage/ # SQLite
|
||||
│ └── rag/
|
||||
```
|
||||
|
||||
### 8.2 依赖规则
|
||||
|
||||
```
|
||||
bandu/* ──可引用──► llm/, pdf/extractor, pdf/selection, zotero/, utils/
|
||||
chatView ──不可引用──► bandu/* (Chat 保持独立,避免耦合)
|
||||
bandu/* ──不可引用──► chatView, storage/sessions
|
||||
```
|
||||
|
||||
### 8.3 Phase 0 验证项(开工前 Go / No-Go)
|
||||
|
||||
| # | 验证项 | 通过标准 | 阻塞 MVP |
|
||||
|---|--------|----------|----------|
|
||||
| P0-1 | 第二 Item pane section 注册 | 与 Chat pane 并存无冲突 | 是 |
|
||||
| P0-2 | MinerU 解析 3 篇论文 | 段落可用率 ≥ 80% | 是 |
|
||||
| P0-3 | Zotero 9 插件内 SQLite 读写 | CRUD 正常 | 是 |
|
||||
| P0-4 | `<audio>` 长音频播放 | 10 min+ 无泄漏 | 是 |
|
||||
| P0-5 | TTS 中文样本 + timestamps | 自然度可接受;无则确认降级 | 否 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 版本与路线图
|
||||
|
||||
### 9.1 与现有 ChatPapers 版本关系
|
||||
|
||||
| 插件版本 | Chat 模式 | 伴读模式 |
|
||||
|----------|-----------|----------|
|
||||
| v0.2.x(当前) | 对话 / 总结 / 多 PDF | — |
|
||||
| v0.3.0 | 维护 | Phase 0 POC |
|
||||
| v0.4.0 – v0.5.0 | 维护 | M1–M2:解析 + 备课 + TTS + 播放器 |
|
||||
| v0.6.0 | 维护 | M3–M4:问答 RAG + 报告 + dogfooding |
|
||||
| v1.0.0 | 稳定 | 伴读 MVP 功能完整 |
|
||||
|
||||
### 9.2 伴读模式开发里程碑
|
||||
|
||||
| 阶段 | 时间 | 交付 |
|
||||
|------|------|------|
|
||||
| Phase 0 | 第 0 周 | 三项 POC + 双 pane 骨架占位 |
|
||||
| M1 | 第 1–2 周 | MinerU 解析 + 段落树 + 备课流水线(纯文本) |
|
||||
| M2 | 第 3–4 周 | TTS + 播放器双模式 + 高亮 |
|
||||
| M3 | 第 5–6 周 | 锚定问答 RAG + 伴读报告回写 |
|
||||
| M4 | 第 7–8 周 | 增量缓存 / 断点续播 / 设置 / dogfooding 10 篇 |
|
||||
|
||||
---
|
||||
|
||||
## 10. MVP 验收清单(伴读模式)
|
||||
|
||||
- [ ] 安装单一 ChatPapers `.xpi`,Item pane 同时出现 ChatPapers 与伴读两个标签
|
||||
- [ ] LLM 配置一次,两模式均可调用
|
||||
- [ ] 对 1 篇 8 页英文 PDF 完成:解析 → 备课 → 整体讲解播放
|
||||
- [ ] 切换到逐段精读并完成至少 1 个章节播放
|
||||
- [ ] 提问 3 次(含 1 次全局 + 1 次逐句),文字与语音均有
|
||||
- [ ] 标记 2 个重点段落
|
||||
- [ ] 生成伴读报告至 Zotero 子笔记,含 BibTeX;与 Chat 笔记共存不覆盖
|
||||
- [ ] 关闭 Zotero 再打开,缓存命中、断点续播
|
||||
- [ ] 断网重听已缓存内容
|
||||
- [ ] Chat 模式原有功能回归通过(对话 / 总结 / 存笔记)
|
||||
|
||||
---
|
||||
|
||||
## 11. 术语表
|
||||
|
||||
| 术语 | 定义 |
|
||||
|------|------|
|
||||
| Chat 模式 | ChatPapers 现有对话 / 总结工作流 |
|
||||
| 伴读模式 / BanDu | 本插件内第二种工作流:备课 → 听课 → 报告 |
|
||||
| 双模式 | 同一插件、两个 Item pane、共享 LLM 与 Zotero 集成 |
|
||||
| Lecture | 一次备课产物:摘要 + beats + 逐段讲解 |
|
||||
| Beat | 整体讲解播放单位,含 refs[] |
|
||||
| 共享层 | llm / zotero / pdf 附件 / utils,两模式复用 |
|
||||
|
||||
---
|
||||
|
||||
## 12. 文档关系
|
||||
|
||||
```
|
||||
docs/requirements.md ChatPapers 对话模式需求(不变)
|
||||
docs/newidea/
|
||||
├── PLAN.md BanDu 产品策划(参考)
|
||||
├── PRD.md BanDu 独立 PRD(参考)
|
||||
├── ARCHITECTURE.md BanDu 技术架构(参考)
|
||||
└── DUAL_MODE_PRD.md ★ 本文:双模式集成需求(实施依据)
|
||||
```
|
||||
|
||||
**实施优先级**:开发伴读功能时,以**本文**为集成需求主文档;细节 Prompt、数据表、状态机见 ARCHITECTURE.md;产品动机与竞品见 PLAN.md。
|
||||
|
||||
---
|
||||
|
||||
## 13. 待决事项
|
||||
|
||||
| 事项 | 负责人 | 备注 |
|
||||
|------|--------|------|
|
||||
| TTS 提供商选型 | 技术 | Phase 0 |
|
||||
| SQLite 实现方案(better-sqlite3 vs sql.js) | 技术 | Phase 0 |
|
||||
| 伴读 pane 图标与 l10n 命名(BanDu / 伴读) | 产品 | M1 前 |
|
||||
| Chat → 伴读上下文导入是否做 v1.1 | 产品 | MVP 不做 |
|
||||
| 插件对外宣传:单品牌 vs 双品牌露出 | 产品 | MVP 后 |
|
||||
|
||||
---
|
||||
|
||||
## 14. 文档修订记录
|
||||
|
||||
| 版本 | 日期 | 说明 |
|
||||
|------|------|------|
|
||||
| v0.1 | 2026-08-29 | 初稿:方案 A 双模式集成需求 |
|
||||
@@ -0,0 +1,350 @@
|
||||
# 产品策划书:BanDu(伴读)— Zotero 插件
|
||||
|
||||
| 字段 | 内容 |
|
||||
|------|------|
|
||||
| 中文名 | 伴读 |
|
||||
| 英文名 | BanDu |
|
||||
| 插件全名 | BanDu for Zotero |
|
||||
|
||||
> 一句话定位:**BanDu(伴读)——在 Zotero 里给每篇论文配一位 AI 老师**——先读完论文备好课,
|
||||
> 再按你选的模式授课(整体讲解 / 逐段精读 / 逐句按需),随时提问、系统不打扰,
|
||||
> 听完后把伴读笔记和参考文献卡片留在 Zotero,写论文时一键引用。
|
||||
|
||||
## 0. 一期范围声明(重要)
|
||||
|
||||
- **一期 = PC 端 + Zotero 插件**,这是产品本体,不是独立 App 的附属渠道;
|
||||
- 论文库 = Zotero 现有库(零迁移),插件直接读 Zotero 里的 PDF;
|
||||
- 移动端是**未来的独立产品**(v2+),通过云后端与 Zotero 端同步,不在一期考虑;
|
||||
- 一期核心用户 = 自己(dogfooding)→ 再面向 Zotero 学术用户开放。
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与机会
|
||||
|
||||
### 1.1 核心工作流(一期要服务的完整闭环)
|
||||
|
||||
```
|
||||
Zotero 论文库(已有)
|
||||
│ 打开某篇 PDF
|
||||
▼
|
||||
BanDu 插件:AI 老师读完整篇 → 备课 → 按模式授课 ── 随时提问 ── 标记重点
|
||||
│
|
||||
▼
|
||||
伴读产出留在 Zotero 里:
|
||||
• 伴读报告(问答记录 + 重点标记 + 收听情况)→ Zotero 子笔记,可搜索
|
||||
• 参考文献卡片(中文速览 + 推荐引用句 + BibTeX)
|
||||
│
|
||||
▼
|
||||
写论文时:从 Zotero 直接导入引用 + 复用伴读总结(Related Work 素材)
|
||||
```
|
||||
|
||||
### 1.2 机会点
|
||||
|
||||
- 在 Zotero 生态里,**文字 AI 已是红海**(Paper Copilot、Owl 等:摘要/问答/翻译),
|
||||
**"AI 老师式音频授课 + 段落/句子级交互"仍是空白**——即使桌面端,"边干活边听论文"也是真实场景;
|
||||
- EndNote / Mendeley 无开放插件生态,**插件路线 = Zotero 路线**,护城河是生态卡位;
|
||||
- Paper Copilot 的流行证明 Zotero 用户对论文 AI 的需求强且愿意装插件。
|
||||
|
||||
> 立项前需实测确认:Paper Copilot / Owl 最新版是否已加 TTS 朗读(目前判断没有,需录屏存档)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 竞品分析
|
||||
|
||||
| 产品 | 形态 | 段落级锚定 | 语音伴读 | 听完提问 | 与 Zotero 集成 | 差距 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| Paper Copilot(Zotero 插件) | 插件 | ◐ | ✗ | ✓(文字) | ✓ | 无音频、无老师式授课、无伴读报告闭环 |
|
||||
| Owl(Zotero 插件) | 插件 | ✗ | ✗ | ✓(文字) | ✓ | 同上 |
|
||||
| SciSpace | 网页 | ✓ | ✗ | ✓(文字) | ✗ | 不在 Zotero 工作流里 |
|
||||
| NotebookLM Audio | 网页 | ✗ | ◐(预生成播客,不可交互) | ✗ | ✗ | 不可交互、不可锚定、不在 Zotero |
|
||||
| ChatGPT 语音 + PDF | App | ✗ | ✓ | ✓ | ✗ | 对话不锚定段落、无播放器、无笔记回写 |
|
||||
| Speechify 类 | 多端 | ✗ | ✓(裸读) | ✗ | ✗ | 机器朗读,无 AI 讲解 |
|
||||
|
||||
**空白确认:Zotero 内 × 老师式授课(整体+逐段+逐句)× 随时提问 × 伴读报告回写,同时满足的产品不存在。**
|
||||
|
||||
---
|
||||
|
||||
## 3. 核心设计决策
|
||||
|
||||
### 3.0 心智模型:AI = 论文老师(不是 TTS 播放器)
|
||||
|
||||
产品的概念基础是一位"**先读完整篇论文、再给读者讲解的老师**",四个阶段:
|
||||
|
||||
```
|
||||
① 读 读完整篇论文(解析 + 段落索引 + 全文摘要)
|
||||
② 备课 基于全文理解生成"讲义"(Lecture 对象,缓存复用):
|
||||
- 整体讲解脚本(按教学逻辑组织,不按论文物理顺序)
|
||||
- 逐段三层讲解(按论文结构组织)
|
||||
- (按需)逐句讲解
|
||||
③ 授课 读者自选模式收听;随时可提问(读者发起,系统不打扰)
|
||||
④ 复盘 听完后生成伴读报告/笔记 → 回写 Zotero,为写论文提供素材
|
||||
```
|
||||
|
||||
**关键架构含义:②"备课"是一次性、可缓存的产物。**
|
||||
老师备一次课,读者听 N 次、换模式重听、隔周再听,都不再花 LLM 成本;
|
||||
整体讲解与逐段精读**共享同一份备课**(同一个全文摘要上下文),边际成本只多一次生成。
|
||||
|
||||
### 3.1 多种授课模式(粒度 = 模式,不是全局开关)
|
||||
|
||||
| 模式 | 组织逻辑 | 播放单位 | 场景 |
|
||||
|---|---|---|---|
|
||||
| **整体讲解** | **教学逻辑**:问题→动机→核心方法→关键证据→结论与局限(不是论文物理顺序) | beat(30s–2min 讲解片段,全篇 8–15 个) | 第一次过论文,听"具体讲了什么、核心是什么" |
|
||||
| **逐段精读** | 论文物理顺序:章节→段落 | 段落(>150 词的长段拆 1–3 个"要点") | 第二次精读,吃透方法/实验 |
|
||||
| **逐句讲解** | 按需:选中任意句子,问"解释这句" | 句 | 硬句子手术 |
|
||||
|
||||
**逐段与逐句并存的理由**:老师的粒度应随内容难度走——
|
||||
简单章节整体带过、核心章节逐段精读、难句子逐句解剖。
|
||||
**"整篇逐句自动播放"不做**(成本高、收益低),但逐句**按需解释**永远可用;
|
||||
v1.1 加"**难点句清单**":备课时 AI 主动标出全篇 5–10 个最难读的句子,逐个听讲解
|
||||
(比整篇逐句性价比高得多的折中)。
|
||||
|
||||
**整体讲解的 beat ≠ 段落(关键数据结构差异)**:老师会重组材料
|
||||
("这个 motivation 其实在 p12,我们回去看一下")。
|
||||
每个 beat 携带 `refs[]` 指回来源段落,用于文字面板同步高亮对应原文、点击"看原文"跳 PDF 页。
|
||||
|
||||
v2:**检验模式**——老师反考读者("你觉得作者为什么这么设计?",答完给反馈),
|
||||
直接服务"提高理解能力"的终极目标。
|
||||
|
||||
### 3.2 老师讲什么:三种内容模型
|
||||
|
||||
**① 整体讲解 beat**(全篇 10–20 分钟 ≈ 一杯咖啡过完论文核心):
|
||||
- 讲解文本(中文口语)
|
||||
- `refs[]`:支撑该 beat 的来源段落 ID
|
||||
- 类型标签:问题引入 / 核心方法 / 关键实验 / 结论局限 / 延伸思考
|
||||
- 质量要求(防"像 Wikipedia 摘要"):每个 beat 必须含 **为什么重要 / 一个具体例子或数字 / 一个易混淆点** 三要素
|
||||
|
||||
**② 逐段三层讲解**(对应核心诉求"什么意思 / 怎么理解 / 整体研究思路"):
|
||||
1. **讲了什么**(白话翻译/转述;公式转口语不念 LaTeX;专有名词保留英文 + 中文解释)
|
||||
2. **怎么理解**(关键概念、重要数字、和常规做法的差异)
|
||||
3. **全文定位**("这是 motivation 收尾,为下一节 method 引出问题")
|
||||
|
||||
**③ 逐句讲解**(按需触发):句翻译 + 句法拆解(长句怎么断)+ 这句话在上下文的作用。
|
||||
|
||||
**教师风格(prompt 层可配置)**:主动"点"、不主动"问"——
|
||||
提示性内容(易混淆点、作者隐含假设、"注意这里做了一个强假设")**写进生成的讲义里**,
|
||||
运行时不弹任何提问提示(见 3.3)。
|
||||
|
||||
**语言处理**:
|
||||
- 英文论文:音频 = 中文讲解(不是英文裸读);文字面板 = 英文原文 + 中文讲解对照;
|
||||
- 中文论文:音频 = 中文"白话简化 + 方法分析 + 全文定位"(价值不在翻译,在降维讲解);
|
||||
- 自动检测论文语言,切换 prompt 模板;v1.1 可选"原文朗读模式"(英文 TTS 练听力)。
|
||||
|
||||
### 3.3 交互:读者发起,系统不打扰
|
||||
|
||||
- **不做**每段/每句播完的"这段有什么疑问吗?"提示(已确认砍掉);
|
||||
- 交互入口(任意时刻):
|
||||
1. **随时提问**:锚定当前位置(当前 beat / 段落),也可问全局
|
||||
("这篇和 Transformer 什么关系?");
|
||||
2. **暂停即问**:暂停是天然交互点,暂停时提问入口高亮;
|
||||
3. **选中句子问**:"解释这句"(逐句讲解的入口)。
|
||||
- 问答上下文:当前单位原文 + 对应讲解 + 全文摘要 + 全文索引(RAG);
|
||||
回答**文字先行显示 + TTS 流式播放**;所有问答沉淀进伴读记录(进报告)。
|
||||
- v1.1:章节边界软提示(**默认关闭**:"方法部分讲完了,要问点什么吗?")。
|
||||
|
||||
**系统必须答好的问题类型**(prompt 与 RAG 设计依据):
|
||||
翻译类("这句什么意思")/ 理解类("为什么不用 X")/ 逻辑类("结论怎么从实验得出")/
|
||||
关联类("这和前面 Y 什么关系")/ 元认知类("这段在全文什么作用")。
|
||||
|
||||
### 3.4 伴读报告(听后沉淀,存成 Zotero 笔记)
|
||||
|
||||
听完(或随时点"生成报告")自动汇总:
|
||||
|
||||
```
|
||||
# 伴读报告:<论文标题>
|
||||
## 论文速览 问题/方法/结果/局限(中文四句)
|
||||
## 收听情况 模式、已听 x%、已听段落列表
|
||||
## 问答记录 按段落:Q + AI 回答(可重播)
|
||||
## 重点标记 读者标记的段落 + AI 一句话理由
|
||||
## 参考文献卡片 一句话总结 + 推荐引用句 + BibTeX(BBT citation key)
|
||||
```
|
||||
|
||||
- 存为 Zotero 子笔记(markdown,挂 item 下、可搜索),可导出 .md;
|
||||
- **为写论文服务**:推荐引用句 + 速览可直接进 related work,BibTeX 走 Zotero 原生管道;
|
||||
后续写相关论文时,Zotero 里搜论文标题即可调出全部伴读素材;
|
||||
- v1.1+:理解度评估(按提问分布:"你在实验部分提问最多,建议重听该章节")。
|
||||
|
||||
### 3.5 与 Zotero 的深度集成(一期核心卖点)
|
||||
|
||||
| 集成点 | 做法 |
|
||||
|---|---|
|
||||
| 读 PDF | 插件取 item 的 PDF attachment 本地路径,直接读,**论文不出 Zotero** |
|
||||
| 触发 | 选中带 PDF 的 item → Item pane 出现"伴读"标签页 |
|
||||
| 缓存 | 本地 SQLite:Lecture 对象(beats/三层讲解)+ mp3 + 问答记录,key = attachment ID + 文件哈希;二次打开秒进、离线可重听 |
|
||||
| 伴读报告回写 | 报告 + 每段 Q&A + 重点标记 → `Zotero.Note` 子笔记 |
|
||||
| 参考文献卡片 | 中文速览(问题/方法/结果/局限)+ 推荐引用句 + BibTeX 条目(有 Better BibTeX 时取 citation key) |
|
||||
| 写论文导入 | 写 related work 时:卡片摘要可直接粘贴,BibTeX 经 Zotero 原生/BBT 进 LaTeX;重点段落笔记提供引用上下文 |
|
||||
| 伴读库视图 | 插件主窗口列全部论文:未备课 / 已备课 / 已听 x% / 有报告,快速进入 |
|
||||
|
||||
### 3.6 播放器 UI(Zotero Item pane 内的"伴读"页)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ 论文标题 [英] 老师已备课 ✓ [备课进度(首次)] │
|
||||
├──────────────────────────────────────────────┤
|
||||
│ 模式: [整体讲解 | 逐段精读] │
|
||||
│ ▶ ⏸ ⏮ ⏭ 1x/1.5x/2x │
|
||||
├────────────────────────┬─────────────────────┤
|
||||
│ 单位列表: │ 当前内容: │
|
||||
│ 整体讲解: │ 整体模式: beat 文本 │
|
||||
│ b1 问题引入 ●已讲 │ + refs 来源段落高亮 │
|
||||
│ b2 核心方法 ○ │ 逐段模式: 原文(英)+ │
|
||||
│ b3 关键实验 ○ │ 三层中文讲解(高亮跟随) │
|
||||
│ 逐段精读: │ ── 提问(随时)── │
|
||||
│ 2 Method │ Q: ... [重播] │
|
||||
│ p17 ●已听 [问2] [★] │ A: ... [重播] │
|
||||
│ p18 ○ │ [输入框: 问当前段/全局] │
|
||||
│ ... │ [选中句子→解释这句] │
|
||||
└────────────────────────┴─────────────────────┘
|
||||
```
|
||||
|
||||
- 文字面板逐句/逐词高亮跟随音频(TTS word timestamps);
|
||||
- 整体讲解模式:面板高亮当前 beat 的 `refs[]` 来源段落原文,可点"看原文"跳 PDF 页;
|
||||
- 点列表任意行跳播;播放中列表自动滚动跟随;断点续播。
|
||||
|
||||
---
|
||||
|
||||
## 4. 技术架构(Local-first)
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ Zotero 7 (PC, macOS/Win) │
|
||||
│ ┌────────────────────────────────────────────────────┐ │
|
||||
│ │ 插件 (TypeScript, 官方 zotero-plugin boilerplate) │ │
|
||||
│ │ • UI 层:伴读 pane / 伴读库窗口 (HTML5 + <audio>) │ │
|
||||
│ │ • 本地引擎:PDF 解析 (MinerU/Marker, 本地 CPU 跑) │ │
|
||||
│ │ • 本地存储:SQLite(Lecture 对象/问答/进度) + mp3 文件│ │
|
||||
│ │ • 设置:LLM 提供商+key / TTS 提供商 / 教师风格 │ │
|
||||
│ │ • 回写:Zotero.Note / 附件路径 / BBT citation key │ │
|
||||
│ └──────────────┬─────────────────────────────────────┘ │
|
||||
└─────────────────┼──────────────────────────────────────┘
|
||||
│ (仅发送段落文本/提问,论文库本身不上云)
|
||||
┌─────────┴──────────┐
|
||||
│ LLM API │ TTS API
|
||||
│ (用户自带 key; │ (ElevenLabs/Azure/
|
||||
│ 或 Ollama 本地) │ 火山/MiniMax; mp3 落盘)
|
||||
└────────────────────┘
|
||||
```
|
||||
|
||||
### 4.1 关键流水线
|
||||
|
||||
1. **解析**:attachment 路径 → 本地 layout 切段(双栏/公式/表格)→ 段落树(章节/段落/要点三层)→ SQLite;
|
||||
2. **备课**(Lecture 对象,一次生成、缓存复用):
|
||||
a. 全文摘要(问题/方法/结果/局限)→ 作为后续所有生成的全局上下文;
|
||||
b. **整体讲解 beats**(8–15 个,教学逻辑序,每个 beat 带 `refs[]` 来源段落 ID;
|
||||
prompt 只允许引用已分配的段落 ID,**生成后校验,非法引用剔除**);
|
||||
c. **逐段三层讲解**(按 5 段一批,带章节标题 + 全文摘要上下文);
|
||||
d. v1.1:难点句清单(5–10 句,带句内定位);
|
||||
→ **增量备课**:全文摘要 + beats 先生成(约 1–2 min),即可开听整体讲解;逐段内容后台继续生成("边备课边开讲")。
|
||||
3. **合成**:讲解文本 → TTS → mp3 + word timestamps → 落盘(按 beat / 段落分片);
|
||||
4. **播放**:三种模式共用同一播放器引擎(beat 序 / 段落序 / 单句);
|
||||
word timestamps 驱动文字面板高亮;整体模式按 beat 的 `refs[]` 高亮来源段落;
|
||||
5. **问答**(随时发起):锚定当前单位(beat/段落/句子)→ 组装 prompt
|
||||
(当前单位原文 + 讲解 + 全文摘要 + RAG 相关段)→ LLM 流式 → 文字即时显示 → TTS 流式播放;问答写入伴读记录;
|
||||
6. **报告**:收听完成或手动触发 → 伴读报告 + 参考文献卡片 → Zotero Note / 导出 .md。
|
||||
|
||||
### 4.2 关键技术难点与对策
|
||||
|
||||
| 难点 | 对策 |
|
||||
|---|---|
|
||||
| 双栏/公式/表格切分质量 | 首选 MinerU(CPU 可跑、公式识别好),Marker 备选;解析结果在文字面板可见,bad case 可兜底 |
|
||||
| beat 的 refs 幻觉(引用不存在的段落) | prompt 约束只引用已分配 ID + 生成后校验剔除;refs 缺失的 beat 降级为纯讲解(不高亮原文) |
|
||||
| 整体讲解"太泛"(像 Wikipedia 摘要,不像老师) | beat 模板强制三要素(为什么重要/具体例子或数字/易混淆点);dogfooding 迭代 prompt |
|
||||
| PDF viewer 内段落高亮 | 依赖 PDF viewer 内部 API,稳定性未验证 → **MVP 不做**,用"文字面板高亮 + PDF 页跳转";v1.1 调研 |
|
||||
| 音文同步 | 优先 TTS 提供商返回的 word boundary;缺失则 WhisperX 强制对齐(离线跑一次) |
|
||||
| 问答延迟 | LLM 流式 + TTS 流式,首句 < 2–3s;文字答案先行显示,音频随后 |
|
||||
| LLM 幻觉(讲解偏离原文) | prompt 严格 grounding 到段落原文;温度低;原文始终并排可见;"这段讲解"可一键重生成 |
|
||||
| 长论文成本 | 备课一次、终身缓存(本地);单篇总成本 < ¥10,一次性;二次打开零成本、可离线重听 |
|
||||
| 隐私 | local-first:PDF 不出机器、只发段落文本给 LLM API;设置支持 Ollama 纯本地模型(质量换隐私) |
|
||||
| Zotero 7 API 较新 | 官方 boilerplate、pin 版本;UI 只依赖稳定 API(pane/notes/attachments) |
|
||||
|
||||
### 4.3 单篇成本/耗时估算(8 页英文论文)
|
||||
|
||||
- 解析:本地 CPU 1–3 min,免费
|
||||
- 备课 LLM:全文摘要 + 12 beats + 50–70 段三层讲解 ≈ 30k token ≈ $0.05–0.3(一次性)
|
||||
- TTS:整体讲解 ≈ 3k–5k 中文字 + 逐段 ≈ 8k–15k 字 ≈ ¥1–5(火山/Azure)或 $0.2–0.6(ElevenLabs flash)
|
||||
- 问答:按需,每轮 ≈ $0.002 + 几分钱 TTS
|
||||
- **二次打开 / 换模式重听:零成本**(全部本地缓存,离线可听)
|
||||
|
||||
---
|
||||
|
||||
## 5. MVP 范围(6–8 周,PC + Zotero 插件)
|
||||
|
||||
**MVP 一句话:在 Zotero 里打开一篇英文论文,点"备课",AI 读完整篇后按你选的模式授课
|
||||
(整体讲解 / 逐段精读),可随时提问并听到语音回答,听完后伴读报告与参考文献卡片留在 Zotero 里。**
|
||||
|
||||
✅ 包含:
|
||||
1. Zotero 7 插件骨架 + Item pane "伴读"标签页 + 伴读库主窗口(简版)
|
||||
2. 读 Zotero 附件 PDF → 本地解析 → 段落树(章节/段落/要点)
|
||||
3. **备课流水线**:全文摘要 + 整体讲解 beats(含 refs 校验)+ 逐段三层讲解;增量备课(边备边开讲)
|
||||
4. TTS 逐单元合成 + 本地 mp3 缓存
|
||||
5. 播放器:**模式切换(整体讲解 / 逐段精读)**、播放/暂停/跳单位/语速 + 列表 + 文字面板高亮 + PDF 页跳转
|
||||
6. 随时提问(文字输入,锚定当前位置或全局)→ 文字先行 + 语音回答 + 追问;选中句子问"解释这句"(逐句按需)
|
||||
7. 伴读报告(问答记录/重点标记/收听情况)→ Zotero 子笔记;参考文献卡片 + BibTeX
|
||||
8. 设置页(LLM/TTS 提供商与 key、教师风格)、断点续播
|
||||
9. Dogfooding:自己读 10 篇论文,迭代 prompt 与节奏
|
||||
|
||||
❌ 明确砍到 v1.1+:
|
||||
- 语音输入提问(ASR)、barge-in 打断(MVP 用打字提问)
|
||||
- 章节边界软提示(默认关闭)、难点句清单、PDF 内段落级高亮、原文朗读模式
|
||||
- 检验模式(老师考读者,v2)
|
||||
- 移动端 App、云同步、多用户/订阅
|
||||
|
||||
**MVP 验证的假设:**
|
||||
1. 整体讲解 10–20 分钟的信息密度是否合适 → dogfooding 调 beat 数与单 beat 长度;
|
||||
2. "无提示、随时问"的交互是否够用 → 记录自己"想问但没问"的时刻,决定 v1.1 软提示的默认值;
|
||||
3. 伴读报告回 Zotero 后,写论文时是否真的被复用(**关键闭环验证**)。
|
||||
|
||||
---
|
||||
|
||||
## 6. 路线图
|
||||
|
||||
| 阶段 | 时间 | 内容 |
|
||||
|---|---|---|
|
||||
| M1 | 第 1–2 周 | 插件骨架 + PDF 解析(MinerU 本地)+ 段落树 + 备课流水线(摘要/beats/三层讲解,纯文本跑通) |
|
||||
| M2 | 第 3–4 周 | TTS 流水线 + 播放器(双模式切换)+ 文字面板高亮 + refs 高亮 |
|
||||
| M3 | 第 5–6 周 | 随时问答(RAG + 流式)+ 伴读报告/卡片回写 + BibTeX |
|
||||
| M4 | 第 7–8 周 | 增量备课/缓存/断点续播 + 设置页 + dogfooding 10 篇 + 打磨 |
|
||||
| v1.1 | +4–6 周 | 语音输入(ASR)、barge-in、难点句清单、软提示(默认关)、PDF 内高亮、原文朗读模式、理解度评估、伴读库完善、开源核心 |
|
||||
| v2 | 后续 | **独立移动端 App**(云后端同步论文与伴读进度)、**检验模式**(老师考读者)、多论文对比听、团队版/订阅 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 商业模式(分期)
|
||||
|
||||
- **一期**:个人工具,插件免费 + 用户自带 LLM/TTS key(零边际成本,dogfooding + 口碑);
|
||||
- **v1.1**:核心开源,Pro 云服务(TTS 代付、跨设备同步、热门 arXiv 论文讲义共享缓存)$9.9–19.9/月;
|
||||
- **长期**:学术团队版(共享伴读库、写作引用协作);移动端 App 跟随 v2。
|
||||
- 成本结构:local-first 下服务器成本极低,主要成本在 TTS 代付(共享缓存可摊薄)。
|
||||
|
||||
---
|
||||
|
||||
## 8. 风险与对策
|
||||
|
||||
| 风险 | 等级 | 对策 |
|
||||
|---|---|---|
|
||||
| 整体讲解"太泛"不像老师(核心体验风险) | 中 | beat 三要素模板 + dogfooding 迭代;教师风格可配置 |
|
||||
| Zotero 7 插件 API 不稳定 / 未来版本变动 | 中 | 官方 boilerplate、pin 版本、只依赖稳定 API;UI 层与 API 层隔离 |
|
||||
| PDF viewer 高亮依赖内部 API | 中 | MVP 绕开(文字面板高亮 + 页跳转);v1.1 专项调研 |
|
||||
| LLM 讲解幻觉 / 质量不稳定 | 中 | grounding prompt + 原文并排 + 一键重生成 + dogfooding 迭代 prompt |
|
||||
| 无提示交互下用户忘了提问(交互率过低) | 低 | dogfooding 记录数据;v1.1 软提示(默认关)兜底 |
|
||||
| 大厂在 Zotero 生态做音频伴读(或 Zotero 官方 AI 化) | 中 | 生态卡位 + "备课-授课-报告"闭环是差异化;持续盯 Paper Copilot 与 Zotero roadmap |
|
||||
| 隐私顾虑(论文文本发给 LLM API) | 低 | local-first + 可选 Ollama 纯本地;设置中明示数据流向 |
|
||||
| 解析 bad case(复杂版面) | 中 | 双引擎备选 + 文字面板兜底 + 用户反馈入口 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 团队与预算(一期)
|
||||
|
||||
- 1 名全栈(插件 + 后端流水线)+ 1 名 AI 工程(解析/prompt/RAG,可同一人);
|
||||
- 云成本 ≈ 0(全 local-first,仅 LLM/TTS API 按量,个人使用 <$10/月);
|
||||
- 8 周 dogfooding 就绪,不含人力的直接成本 <$200。
|
||||
|
||||
---
|
||||
|
||||
## 10. 下一步行动(本周)
|
||||
|
||||
1. **竞品复核**:装 Paper Copilot + Owl,实测是否已有 TTS/音频能力,录屏存档;
|
||||
2. **解析 POC**:取自己 Zotero 里 3 篇典型论文(双栏 + 公式密集)跑 MinerU(本地 CPU),评估段落切分与公式识别质量;
|
||||
3. **插件 POC**:zotero-plugin boilerplate 跑通三件事——取 attachment 路径、Item pane 加标签页、页面里 `<audio>` 播一段 mp3(1–2 天);
|
||||
4. **TTS 选型**:中文自然度 + 价格 + 是否返回 word timestamps,对比 ElevenLabs / Azure / 火山,各合成 30 秒样本试听;
|
||||
5. **Prompt 设计**:写三套 prompt 初版——全文摘要 / 整体讲解 beats(含 refs 约束)/ 逐段三层讲解;用 1 篇论文人工评估"像不像老师"。
|
||||
@@ -0,0 +1,323 @@
|
||||
# 产品需求规格说明书(PRD)
|
||||
|
||||
| 字段 | 内容 |
|
||||
|------|------|
|
||||
| 产品名称 | BanDu(伴读)— Zotero 插件 |
|
||||
| 中文名 | 伴读 |
|
||||
| 英文名 | BanDu |
|
||||
| 文档版本 | v0.1 |
|
||||
| 状态 | 草案 |
|
||||
| 关联文档 | [PLAN.md](./PLAN.md)(产品策划)、[ARCHITECTURE.md](./ARCHITECTURE.md)(架构设计) |
|
||||
| 一期范围 | Zotero 7 桌面插件(macOS / Windows) |
|
||||
|
||||
---
|
||||
|
||||
## 1. 产品概述
|
||||
|
||||
### 1.1 一句话定位
|
||||
|
||||
在 Zotero 里给每篇论文配一位 AI 老师:先读完整篇论文并备好课,再按用户选择的模式授课(整体讲解 / 逐段精读 / 逐句按需),随时提问且系统不主动打扰;听完后将伴读笔记与参考文献卡片写回 Zotero,供写论文时复用。
|
||||
|
||||
**品牌**:中文名 **伴读**,英文名 **BanDu**(「伴读」音译)。
|
||||
|
||||
### 1.2 产品目标
|
||||
|
||||
| 目标 | 说明 |
|
||||
|------|------|
|
||||
| G1 降低读论文门槛 | 用户可在 10–20 分钟内通过「整体讲解」把握论文核心 |
|
||||
| G2 支持深度精读 | 通过「逐段精读」与「逐句按需解释」吃透方法与实验 |
|
||||
| G3 闭环沉淀知识 | 伴读报告、问答、重点标记留在 Zotero,可直接用于 Related Work |
|
||||
| G4 零迁移成本 | 论文库沿用 Zotero,PDF 不出本地机器 |
|
||||
|
||||
### 1.3 成功指标(MVP 验证期)
|
||||
|
||||
| 指标 | 目标 | 测量方式 |
|
||||
|------|------|----------|
|
||||
| 整体讲解完成率 | ≥ 60% 启动后听完 | 播放进度日志 |
|
||||
| 单篇备课可听时间 | 摘要 + beats 生成后 ≤ 2 min 可开听 | 备课流水线耗时 |
|
||||
| 问答首字延迟 | ≤ 3 s(文字) | 问答链路埋点 |
|
||||
| 报告复用率 | Dogfooding 10 篇中 ≥ 3 篇报告内容被粘贴进写作 | 人工记录 |
|
||||
| 解析可用率 | 典型 8 页 NLP 论文 ≥ 90% 段落无需手动修正 | Dogfooding |
|
||||
|
||||
### 1.4 目标用户
|
||||
|
||||
| 角色 | 描述 | 一期优先级 |
|
||||
|------|------|------------|
|
||||
| 研究者(Primary) | 使用 Zotero 管理文献,需快速过大量论文并精读关键篇 | P0 |
|
||||
| 研究生 | 英文论文阅读吃力,需要中文讲解与随时提问 | P0 |
|
||||
| 论文写作者 | 需要 Related Work 素材、引用句与 BibTeX | P0 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 范围定义
|
||||
|
||||
### 2.1 一期包含(MVP)
|
||||
|
||||
- Zotero 7 插件:Item pane「伴读」标签页 + 伴读库主窗口(简版)
|
||||
- 读取 Zotero PDF 附件 → 本地解析 → 段落树
|
||||
- 备课流水线:全文摘要 + 整体讲解 beats + 逐段三层讲解;增量备课
|
||||
- TTS 合成 + 本地 mp3 缓存
|
||||
- 播放器:整体讲解 / 逐段精读双模式、跳播、语速、文字面板高亮、PDF 页跳转
|
||||
- 文字提问(锚定当前位置 / 全局 / 选中句子)→ 文字 + 语音回答
|
||||
- 伴读报告 + 参考文献卡片 → Zotero 子笔记;BibTeX
|
||||
- 设置页(LLM / TTS / 教师风格)、断点续播、本地缓存
|
||||
|
||||
### 2.2 一期明确不包含
|
||||
|
||||
| 功能 | 计划版本 |
|
||||
|------|----------|
|
||||
| 语音输入提问(ASR)、barge-in 打断 | v1.1 |
|
||||
| 章节边界软提示 | v1.1 |
|
||||
| 难点句清单(备课预标) | v1.1 |
|
||||
| PDF viewer 内段落级高亮 | v1.1 |
|
||||
| 英文原文朗读模式 | v1.1 |
|
||||
| 检验模式(老师考读者) | v2 |
|
||||
| 移动端 App、云同步、多用户订阅 | v2 |
|
||||
|
||||
### 2.3 不支持场景(需在 UI 明示)
|
||||
|
||||
- 扫描版 PDF(无 OCR 文本层)
|
||||
- Item 无 PDF 附件或非 PDF 附件
|
||||
- 加密且无法本地读取的 PDF
|
||||
- 极长论文(> 80 页)默认仅推荐整体讲解模式(逐段按需生成)
|
||||
|
||||
---
|
||||
|
||||
## 3. 用户故事与用例
|
||||
|
||||
### 3.1 核心用户故事
|
||||
|
||||
| ID | 作为… | 我想要… | 以便… | 优先级 |
|
||||
|----|-------|---------|-------|--------|
|
||||
| US-01 | 研究者 | 选中 Zotero 论文后一键「备课」 | AI 先读完整篇再给我讲解 | P0 |
|
||||
| US-02 | 研究者 | 选择「整体讲解」模式收听 | 20 分钟内把握论文核心 | P0 |
|
||||
| US-03 | 研究者 | 选择「逐段精读」按章节听 | 深入理解方法与实验 | P0 |
|
||||
| US-04 | 研究者 | 播放中随时文字提问 | 不打断思路又能澄清疑问 | P0 |
|
||||
| US-05 | 研究者 | 选中难句点「解释这句」 | 快速理解长难句 | P0 |
|
||||
| US-06 | 研究者 | 听完后自动生成伴读报告到 Zotero | 写论文时搜索即可调出素材 | P0 |
|
||||
| US-07 | 研究者 | 二次打开同一篇论文秒进、离线重听 | 不重复花 API 费用 | P0 |
|
||||
| US-08 | 研究者 | 在伴读库看到全部论文的备课/收听状态 | 快速找到未读论文 | P1 |
|
||||
| US-09 | 研究者 | 标记重点段落 | 报告里汇总我关心的部分 | P0 |
|
||||
| US-10 | 研究者 | 配置自己的 LLM / TTS API Key | 控制成本与隐私 | P0 |
|
||||
|
||||
### 3.2 用例:首次伴读一篇论文
|
||||
|
||||
**前置条件**:Zotero 7 已安装插件;Item 有 PDF 附件;用户已配置 LLM Key。
|
||||
|
||||
**主流程**:
|
||||
|
||||
1. 用户在 Zotero 选中带 PDF 的 Item
|
||||
2. 打开 Item pane「伴读」标签页
|
||||
3. 点击「开始备课」
|
||||
4. 系统解析 PDF,展示进度(解析 → 摘要 → beats → 逐段讲解 → TTS)
|
||||
5. 摘要 + beats 就绪后(约 1–2 min),整体讲解模式可用,用户开始收听
|
||||
6. 用户可随时暂停、提问、切换逐段精读、标记重点
|
||||
7. 用户点击「生成报告」或收听达阈值后,系统创建 Zotero 子笔记
|
||||
|
||||
**后置条件**:本地 SQLite + mp3 缓存就绪;Zotero 子笔记(若生成)可搜索。
|
||||
|
||||
### 3.3 用例:缓存命中二次打开
|
||||
|
||||
**前置条件**:该 attachment 已备课且文件哈希未变。
|
||||
|
||||
**主流程**:
|
||||
|
||||
1. 用户打开同一 Item 的伴读页
|
||||
2. 系统校验 attachment ID + 文件哈希,命中缓存
|
||||
3. 立即展示播放器,恢复上次播放进度
|
||||
4. 不调用 LLM / TTS(除非用户触发重生成或问答)
|
||||
|
||||
---
|
||||
|
||||
## 4. 功能需求
|
||||
|
||||
### 4.1 插件与 Zotero 集成
|
||||
|
||||
| ID | 需求描述 | 优先级 | 验收标准 |
|
||||
|----|----------|--------|----------|
|
||||
| FR-001 | 插件基于 Zotero 7 官方 boilerplate,支持 macOS 与 Windows | P0 | 两平台可安装、启用 |
|
||||
| FR-002 | 选中含 PDF 附件的 Item 时,Item pane 显示「伴读」标签页 | P0 | 无 PDF 时标签页禁用并提示原因 |
|
||||
| FR-003 | 通过 Zotero API 读取 PDF attachment 本地路径 | P0 | 路径可读、文件哈希可计算 |
|
||||
| FR-004 | 伴读报告写入 Item 下 `Zotero.Note` 子笔记(Markdown) | P0 | 笔记在 Zotero 内可搜索、可导出 .md |
|
||||
| FR-005 | 有 Better BibTeX 时读取 citation key;无则降级 | P1 | 卡片中 BibTeX 字段正确或标注缺失 |
|
||||
| FR-006 | 伴读库窗口列出库内论文及状态:未备课 / 已备课 / 已听 x% / 有报告 | P1 | 点击条目进入对应伴读页 |
|
||||
|
||||
### 4.2 PDF 解析与段落树
|
||||
|
||||
| ID | 需求描述 | 优先级 | 验收标准 |
|
||||
|----|----------|--------|----------|
|
||||
| FR-010 | 本地解析 PDF,输出章节 / 段落 / 要点三层结构 | P0 | 段落含唯一 ID、原文、页码、章节路径 |
|
||||
| FR-011 | 支持双栏、公式、表格的常见学术论文版式 | P0 | Dogfooding 3 类典型论文可用 |
|
||||
| FR-012 | 解析结果持久化至 SQLite,key = attachment ID + 文件哈希 | P0 | PDF 未变时跳过重复解析 |
|
||||
| FR-013 | 解析进度与错误对用户可见 | P0 | 失败时给出可行动提示(如扫描版) |
|
||||
| FR-014 | 文字面板展示解析原文,供用户核对 | P1 | 用户可发现 bad case |
|
||||
|
||||
### 4.3 备课(Lecture 生成)
|
||||
|
||||
| ID | 需求描述 | 优先级 | 验收标准 |
|
||||
|----|----------|--------|----------|
|
||||
| FR-020 | 生成全文摘要:问题 / 方法 / 结果 / 局限 | P0 | 四字段非空、中文 |
|
||||
| FR-021 | 生成 8–15 个整体讲解 beat,按教学逻辑排序 | P0 | 每 beat 含讲解文本、类型标签、refs[] |
|
||||
| FR-022 | beat 的 refs[] 仅允许引用已存在段落 ID;生成后校验,非法引用剔除 | P0 | 校验日志可审计;无效 refs 的 beat 降级 |
|
||||
| FR-023 | 每个 beat 含三要素:为什么重要 / 具体例子或数字 / 易混淆点 | P0 | Prompt 模板约束 + 抽检 |
|
||||
| FR-024 | 为每个段落生成三层讲解:讲了什么 / 怎么理解 / 全文定位 | P0 | 三层均非空 |
|
||||
| FR-025 | 增量备课:摘要 + beats 完成后即可播放整体讲解 | P0 | 用户无需等逐段全部完成 |
|
||||
| FR-026 | 逐段讲解后台继续生成,UI 显示进度 | P0 | 新段落就绪后可播放 |
|
||||
| FR-027 | Lecture 对象本地缓存,二次打开不重复生成 | P0 | 除非文件哈希变化或用户强制重备 |
|
||||
| FR-028 | 支持一键重生成单个 beat 或单段讲解 | P1 | 重生成后更新缓存与音频 |
|
||||
|
||||
### 4.4 TTS 与音频
|
||||
|
||||
| ID | 需求描述 | 优先级 | 验收标准 |
|
||||
|----|----------|--------|----------|
|
||||
| FR-030 | 讲解文本按 beat / 段落分片合成 mp3 并本地缓存 | P0 | 文件路径与 Lecture 单元绑定 |
|
||||
| FR-031 | 英文论文音频为中文讲解;自动检测论文语言切换 Prompt | P0 | 英/中论文各测 1 篇 |
|
||||
| FR-032 | 保存 word / character timestamps(若 TTS 提供) | P0 | 用于文字面板高亮 |
|
||||
| FR-033 | TTS 不支持 timestamps 时,降级为句子级或段落级高亮 | P0 | UI 不崩溃,高亮粒度下降可接受 |
|
||||
| FR-034 | 问答回答支持 TTS 播放(可先整段合成,非必须流式) | P0 | 每轮 Q&A 可重播语音 |
|
||||
|
||||
### 4.5 播放器
|
||||
|
||||
| ID | 需求描述 | 优先级 | 验收标准 |
|
||||
|----|----------|--------|----------|
|
||||
| FR-040 | 支持模式切换:整体讲解 / 逐段精读 | P0 | 切换后列表与播放单位正确 |
|
||||
| FR-041 | 播放控制:播放 / 暂停 / 上一单位 / 下一单位 / 1x·1.5x·2x | P0 | 状态与 `<audio>` 同步 |
|
||||
| FR-042 | 单位列表展示进度:已听 / 未听;播放中自动滚动 | P0 | 断点续播恢复正确单位 |
|
||||
| FR-043 | 整体模式:文字面板展示 beat 文本 + refs 来源段落高亮 | P0 | 高亮与当前 beat 一致 |
|
||||
| FR-044 | 逐段模式:左侧原文 + 右侧三层讲解,高亮跟随音频 | P0 | 至少句子级跟随 |
|
||||
| FR-045 | 「看原文」跳转 PDF 对应页 | P0 | 页码与段落 page 字段一致 |
|
||||
| FR-046 | 点击列表任意行跳播 | P0 | Seek 到该单位起始 |
|
||||
| FR-047 | 段落支持「重点标记」★ | P0 | 标记写入进度并进入报告 |
|
||||
|
||||
### 4.6 问答
|
||||
|
||||
| ID | 需求描述 | 优先级 | 验收标准 |
|
||||
|----|----------|--------|----------|
|
||||
| FR-050 | 任意时刻文字提问,锚定类型:beat / 段落 / 句子 / 全局 | P0 | 上下文含锚点原文与讲解 |
|
||||
| FR-051 | 暂停时提问入口视觉强化 | P1 | 暂停态输入框高亮 |
|
||||
| FR-052 | 选中句子触发「解释这句」(逐句讲解) | P0 | 输出:翻译 + 句法拆解 + 上下文作用 |
|
||||
| FR-053 | 回答基于 RAG:当前单位 + 全文摘要 + 检索相关段 | P0 | 长论文不整篇塞入 Prompt |
|
||||
| FR-054 | 回答文字流式显示,随后 TTS 播放 | P0 | 首 token ≤ 3 s(网络正常) |
|
||||
| FR-055 | 所有 Q&A 写入本地记录并纳入伴读报告 | P0 | 可按段落分组展示 |
|
||||
| FR-056 | 不支持语音输入与 barge-in(MVP) | — | 仅文字输入 |
|
||||
|
||||
### 4.7 伴读报告与参考文献卡片
|
||||
|
||||
| ID | 需求描述 | 优先级 | 验收标准 |
|
||||
|----|----------|--------|----------|
|
||||
| FR-060 | 手动或条件触发「生成报告」 | P0 | 不覆盖已有报告时需确认(或版本策略见架构) |
|
||||
| FR-061 | 报告结构:论文速览 / 收听情况 / 问答记录 / 重点标记 / 参考文献卡片 | P0 | Markdown 结构固定 |
|
||||
| FR-062 | 参考文献卡片:中文速览 + 推荐引用句 + BibTeX | P0 | 可直接粘贴进 LaTeX 写作流 |
|
||||
| FR-063 | 支持导出 .md 文件 | P1 | 与 Note 内容一致 |
|
||||
|
||||
### 4.8 设置
|
||||
|
||||
| ID | 需求描述 | 优先级 | 验收标准 |
|
||||
|----|----------|--------|----------|
|
||||
| FR-070 | 配置 LLM 提供商、Endpoint、API Key | P0 | 支持 OpenAI 兼容 API + Ollama |
|
||||
| FR-071 | 配置 TTS 提供商与 Key | P0 | 至少支持一种中文 TTS |
|
||||
| FR-072 | 教师风格 Prompt 预设(如:简洁 / 详细) | P1 | 变更后对**新生成**内容生效 |
|
||||
| FR-073 | 明示数据流向:哪些文本会发往外部 API | P0 | 设置页固定文案 |
|
||||
| FR-074 | API Key 安全存储(Zotero 偏好或 OS Keychain) | P0 | 不以明文写入日志 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 非功能需求
|
||||
|
||||
| ID | 类别 | 需求描述 | 指标 |
|
||||
|----|------|----------|------|
|
||||
| NFR-001 | 性能 | 8 页论文本地解析 | ≤ 3 min(CPU) |
|
||||
| NFR-002 | 性能 | 缓存命中后打开伴读页 | ≤ 2 s 可播放 |
|
||||
| NFR-003 | 性能 | 问答首字延迟 | ≤ 3 s |
|
||||
| NFR-004 | 成本 | 单篇 8 页论文首次备课总 API 成本 | ≤ ¥10 |
|
||||
| NFR-005 | 可用性 | 二次打开 / 换模式重听 | 零 LLM/TTS 调用 |
|
||||
| NFR-006 | 隐私 | PDF 文件不上传;仅段落文本按请求发送 | Local-first |
|
||||
| NFR-007 | 离线 | 已缓存 Lecture + mp3 可离线播放 | 除问答外不依赖网络 |
|
||||
| NFR-008 | 可靠性 | 备课/Task 中断后可恢复 | 状态机支持断点续跑 |
|
||||
| NFR-009 | 兼容性 | Zotero 7.x 指定 minor 版本范围 | 文档 pin 版本 |
|
||||
| NFR-010 | 可维护性 | UI 层与 Zotero API 适配层分离 | 代码模块边界清晰 |
|
||||
| NFR-011 | 无障碍 | 播放器键盘可操作(播放/暂停/跳段) | P1 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 界面需求摘要
|
||||
|
||||
伴读页布局见 [PLAN.md §3.6](./PLAN.md);关键交互原则:
|
||||
|
||||
- **读者发起,系统不打扰**:播完不弹「有疑问吗」
|
||||
- **原文始终可见**:讲解与原文并排,降低幻觉信任成本
|
||||
- **进度可感知**:首次备课、增量生成、TTS 合成均有进度反馈
|
||||
- **错误可恢复**:解析失败、API 失败提供重试与降级路径
|
||||
|
||||
### 6.1 状态展示
|
||||
|
||||
| 状态 | 用户可见文案示例 |
|
||||
|------|------------------|
|
||||
| 未备课 | 「点击开始备课」 |
|
||||
| 解析中 | 「正在读取论文… 45%」 |
|
||||
| 备课中(可部分播放) | 「整体讲解已就绪,逐段讲解生成中 12/58」 |
|
||||
| 已备课 | 「老师已备课 ✓」 |
|
||||
| 播放中 | 当前单位高亮 + 进度条 |
|
||||
| 离线可听 | 「已缓存,离线可播放」 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 内容质量需求(Prompt / 讲解)
|
||||
|
||||
| ID | 需求 |
|
||||
|----|------|
|
||||
| CQ-001 | 讲解必须 grounding 到段落原文,温度偏低 |
|
||||
| CQ-002 | 公式转口语,不朗读 LaTeX 符号 |
|
||||
| CQ-003 | 专有名词:英文保留 + 中文解释 |
|
||||
| CQ-004 | beat 类型标签枚举:问题引入 / 核心方法 / 关键实验 / 结论局限 / 延伸思考 |
|
||||
| CQ-005 | 问答需覆盖:翻译 / 理解 / 逻辑 / 关联 / 元认知五类问题 |
|
||||
|
||||
---
|
||||
|
||||
## 8. MVP 验收清单
|
||||
|
||||
- [ ] 在 Zotero 7 安装插件,Item pane 出现伴读页
|
||||
- [ ] 对 1 篇 8 页英文 PDF 完成:解析 → 备课 → 整体讲解播放
|
||||
- [ ] 切换到逐段精读并完成至少 1 个章节播放
|
||||
- [ ] 提问 3 次(含 1 次全局 + 1 次逐句),文字与语音均有
|
||||
- [ ] 标记 2 个重点段落
|
||||
- [ ] 生成伴读报告至 Zotero 子笔记,含 BibTeX
|
||||
- [ ] 关闭 Zotero 再打开,缓存命中、断点续播
|
||||
- [ ] airplane 模式(或断网)下重听已缓存内容
|
||||
- [ ] Dogfooding 累计 10 篇,记录指标表
|
||||
|
||||
---
|
||||
|
||||
## 9. 版本规划(需求视角)
|
||||
|
||||
| 版本 | 核心交付 |
|
||||
|------|----------|
|
||||
| Phase 0 | 三项 POC 通过:Zotero pane、MinerU 解析、TTS timestamps |
|
||||
| MVP(M1–M4) | 本文 §2.1 全部 P0 需求 |
|
||||
| v1.1 | ASR、barge-in、难点句清单、PDF 内高亮、理解度评估 |
|
||||
| v2 | 移动端、云同步、检验模式 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 术语表
|
||||
|
||||
| 术语 | 定义 |
|
||||
|------|------|
|
||||
| BanDu / 伴读 | 产品品牌名;英文 BanDu 为「伴读」音译,Zotero 插件全名 BanDu for Zotero |
|
||||
| Lecture | 一次备课的完整产物:摘要 + beats + 逐段讲解 + 元数据 |
|
||||
| Beat | 整体讲解模式下的播放单位,30 s–2 min,含 refs[] |
|
||||
| 三层讲解 | 讲了什么 / 怎么理解 / 全文定位 |
|
||||
| 增量备课 | 摘要与 beats 优先生成,逐段与 TTS 后台继续 |
|
||||
| refs[] | Beat 引用的来源段落 ID 列表 |
|
||||
| 伴读报告 | 听后汇总写入 Zotero 的 Markdown 笔记 |
|
||||
|
||||
---
|
||||
|
||||
## 11. 待决事项
|
||||
|
||||
| 事项 | 负责人 | 截止 |
|
||||
|------|--------|------|
|
||||
| TTS 提供商最终选型 | 技术 | Phase 0 |
|
||||
| 报告生成:覆盖 vs 追加策略 | 产品 | M3 前 |
|
||||
| 极长论文逐段生成策略(全量 vs 按章 lazy) | 产品 + 技术 | M1 前 |
|
||||
| Better BibTeX 缺失时的 BibTeX 生成规则 | 技术 | M3 前 |
|
||||
@@ -0,0 +1,29 @@
|
||||
# 文档索引
|
||||
|
||||
**BanDu(伴读)** — Zotero AI 论文伴读插件项目文档。
|
||||
|
||||
| 字段 | 内容 |
|
||||
|------|------|
|
||||
| 中文名 | 伴读 |
|
||||
| 英文名 | BanDu |
|
||||
| 插件全名 | BanDu for Zotero |
|
||||
|
||||
| 文档 | 说明 | 读者 |
|
||||
|------|------|------|
|
||||
| [PLAN.md](./PLAN.md) | 产品策划书:背景、竞品、设计决策、路线图 | 产品 / 全员 |
|
||||
| [PRD.md](./PRD.md) | 产品需求规格说明书:功能需求、非功能需求、验收标准 | 产品 / 开发 / 测试 |
|
||||
| [ARCHITECTURE.md](./ARCHITECTURE.md) | 软件架构设计:分层、数据模型、流水线、状态机 | 开发 / 架构 |
|
||||
|
||||
## 阅读顺序建议
|
||||
|
||||
1. **PLAN.md** — 理解产品定位与为什么做
|
||||
2. **PRD.md** — 明确做什么、验收标准
|
||||
3. **ARCHITECTURE.md** — 明确怎么做、模块与数据如何组织
|
||||
|
||||
## 文档关系
|
||||
|
||||
```
|
||||
PLAN.md(策划)
|
||||
├── PRD.md(需求:What)
|
||||
└── ARCHITECTURE.md(架构:How)
|
||||
```
|
||||
Reference in New Issue
Block a user