运行、定制、扩展
这一页面向运维者和开发者:从源码运行 JargonSlayer、搭建本地 sidecar、完整设置参考、主题和词典包的内部机制、webhook payload,以及应用自己的架构和测试套件。如果只是想用这个应用,使用指南已经够用。
自己部署网页版
网页版是一个普通的 Next.js 项目,克隆、安装、运行:
git clone https://github.com/mianaz/jargonslayer.git
cd jargonslayer
npm install
npm run dev
# open http://localhost:3000
想要更接近生产环境的本地运行方式,用 build 和 start,而不是 dev server:
npm run build
npm start
提交改动前,跑一遍 CI 会跑的同一套检查:
npm run typecheck
npm test
npm run build
服务端的提供商 Key 放在 .env.local 里,永远不会发到浏览器:
ANTHROPIC_API_KEY=sk-ant-...
部署层级
默认构建是完整本地优先构建——所有功能都可用,只受访问者自己的配置门控。设置 NEXT_PUBLIC_DEPLOY_TIER=preview 会切换到在线体验层:依赖 sidecar 的功能(本地 Whisper、经 sidecar 的标签页音频、视频 URL 导入、订阅直连)会显示为禁用/灰色并附带说明,而不是直接失败;零 Key 的 AI 使用则由预览服务器自己配置好的模型路径支撑——这对隐私意味着什么,见使用指南的AI 模式一节。
PWA 安装
网页版直接用已有的 manifest 和图标安装成 PWA,不需要单独构建。桌面端 Chrome/Edge 会把它装成一个独立窗口的应用;iOS/iPadOS 上通过 Safari 添加到主屏幕。
本地 Whisper Sidecar
网页版的本地 Whisper、标签页音频,以及基于 sidecar 的录音/URL 导入,全部走同一个 Python 进程——sidecar/whisper_server.py。桌面版由首次启动向导自己安装、自己管理这个 sidecar——下面这些手动步骤在桌面版上都不需要,除非你要把它指向一个外部 sidecar(见下方 sidecarMode)。网页版则要手动搭:
cd sidecar
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python whisper_server.py --model small
它通过 ws://127.0.0.1:8765 提供实时转录,并用一个本地 HTTP job API 处理录音和视频 URL 导入,底层基于 faster-whisper、基于能量的 VAD、可选的本地音频保存,以及配置好后用于说话人分离的 pyannote。
模型选择
--model 可选 tiny、base、small、medium、large-v3、large-v3-turbo,以及 parakeet-tdt-0.6b-v3(MLX 加速、英文优先——是桌面版英文加速引擎在手动 sidecar 上的对应版本)。这条手动路径日常默认推荐 small。桌面版向导自己的模型清单更窄、且是 macOS 专属的精选清单:small / medium / large-v3 / large-v3-turbo,外加一个可用时才出现的 Parakeet 条目,预选 medium。
说话人分离
配置一个 Hugging Face token(设置里的 hfToken,或桌面版首次启动引导时粘贴——见使用指南的桌面版),前提是已经在 Hugging Face 上接受了 pyannote 的 segmentation 和 speaker-diarization 模型使用条款。批量导入会对整段录音做说话人分离。实时说话人分离仍是 beta:sidecar 会在最近一段音频窗口上反复运行 pyannote,把说话人匹配到一个稳定的 registry,再把标签更新推回界面,不阻塞转录。
移动目录的坑
Python 虚拟环境里固化了绝对路径。移动或重命名仓库后,venv 会悄悄坏掉;重建它就好(rm -rf sidecar/.venv,再重复上面的安装步骤)。
sidecarMode(桌面版)
桌面版的 sidecarMode 设置可以是 managed(默认——应用自己安装、启动、重启内置 sidecar)或 external(指向你自己起的 sidecar,跟网页版一样)。网页版实质上永远等价于 external。
订阅直连 Agent Sidecar(实验性)
sidecar/agent_server.py 是实验性的、只在本地跑的功能。它驱动你自己已经登录的 claude 或 codex CLI,仅用于 detect/define——翻译和总结/报告在所有构建里始终走正常的应用 route。
- 默认 loopback 地址:
http://127.0.0.1:8767。 - 端点:
GET /agent/health、POST /agent/detect、POST /agent/define。 - detect/define 调用需要 loopback Origin,加上 sidecar 启动时打印的一次性连接码,手动粘贴进设置。
- 功能开关:构建时设置
NEXT_PUBLIC_ENABLE_SUBSCRIPTION_DIRECT=1——不设置的话,整个 UI 区块和调用分支在构建产物里根本不存在。 - sidecar 不可达、未登录,或额度用尽时,应用会回退到词典检测,而不是直接失败。
ANTHROPIC_API_KEY,Claude 调用可能会优先用这个 Key,而不是你的订阅登录。sidecar 会给出警告,但不会替你 unset——想让订阅登录生效,就自己从 sidecar 的 shell 环境里清掉它。设置参考
一个设置对象控制转录、模型路由、检测、翻译、显示和各种集成。v0.7.3 起,LLM 提供商的 Key 按提供商分别存储、分别发送,不再共用一个 Key;v0.7.4 起,设置 → 密钥 是下表所有 Key 和 token 的统一 UI 入口。
apiKey 字段了。每个提供商预设都有自己的字段——apiKeyAnthropic、apiKeyOpenai、apiKeyDeepseek、apiKeyQwen、apiKeyOpenrouter、apiKeyPoe、apiKeyOllama、apiKeyCustom(加上给自定义端点用的 llmCustomHost)——所以切换提供商永远不会把上一个提供商的 Key 带到新提供商那边。裸的 apiKey 字段现在只存在于分任务的 taskLlm 覆盖项里。转录
| 字段 | 作用 | 说明 |
|---|---|---|
engine | 实时捕获引擎选择。 | import 和 browser-whisper 会存在导入会话上,但不是可选的实时引擎。 |
mode | 音频来源:mic / tab / system-audio / import / url。 | v0.7.4 起是独立控件——会议进行中也能改,不只是开会前。 |
language | 传给 Web Speech 的 BCP-47 语言代码。 | 例如 en-US。 |
whisperUrl | 本地 Whisper 的 websocket URL。 | 默认 ws://localhost:8765。 |
whisperModel | sidecar 模型名称。 | 默认 small——完整可选列表见Whisper Sidecar。 |
proxyUrl | 仅桌面版:手动代理覆盖。 | ""(默认)跟随系统代理;否则可以是 http:///https:///socks5:///socks5h://,支持在 URL 里带用户信息做 basic auth——用于系统自动检测看不到的、只靠环境变量或只靠 PAC 的网络。 |
partials | sidecar 是否推送中间(未定稿)文本。 | 默认开启。 |
preferOnDeviceSpeech | 优先使用浏览器的设备端识别,而不是云端兜底。 | 默认开启。 |
sidecarMode | 仅桌面版:managed 或 external。 | 见Whisper Sidecar。 |
tabAudioCloudProvider | 标签页音频使用哪个 BYOK 云端引擎。 | soniox 或 deepgram。 |
sonioxKey / deepgramKey / elevenLabsKey | 三个第三方云端 STT 引擎各自的 BYOK Key。 | 存在 设置 → 密钥 里。 |
AI 路由
| 字段 | 作用 | 说明 |
|---|---|---|
provider | 主 LLM 路由。 | anthropic 或 openai-compat。 |
baseUrl | openai-compat 提供商的 base URL。 | 例如 https://api.deepseek.com/v1。 |
apiKeyAnthropic / apiKeyOpenai / apiKeyDeepseek / apiKeyQwen / apiKeyOpenrouter / apiKeyPoe / apiKeyOllama / apiKeyCustom | 每个提供商各自的 Key。 | 自 v0.7.3 起彼此隔离——见上面的提示框。 |
llmCustomHost | 把 apiKeyCustom 绑定到「自定义」预设指向的那个 base URL。 | |
taskLlm | 可选的分任务 provider/model 覆盖,支持 translate、detect、summary。 | 划词解释(define)始终跟随 detect 的配置;没配置的任务(或 enabled:false)完全继承主路由。 |
agentUrl | 订阅直连 agent sidecar 的 base URL。 | 默认 http://127.0.0.1:8767——见Agent Sidecar。 |
检测
| 字段 | 作用 | 说明 |
|---|---|---|
autoDetect | 实时检测总开关。 | |
aiDetect | 只控制 LLM 那一层。 | 不管这个开关状态如何,词典底座始终即时、离线。 |
minConfidence | 合并检测结果时用的置信度阈值。 | |
enabledPacks | 哪些内置/已安装词典包处于启用状态。 | null 表示全部启用;core 始终启用。 |
detectIdiomMaxWords / detectIdiomMaxChars | 超过这个长度就会把习语/黑话片段当成「选中了整句」而丢弃。 | 默认 12 词 / 90 字符。 |
packAutoUpdate | 已安装的远程词典包自动检查新版本。 | 每天一次。 |
翻译
| 字段 | 作用 | 说明 |
|---|---|---|
bilingualTranscript | 实时逐片段翻译开关。 | 目标语言由 explainLanguage 决定。 |
explainLanguage | 解释/翻译的目标语言。 | zh(默认)或 en。 |
translateEngine | 使用哪个翻译引擎。 | "system"(本机翻译,默认)| "deepl" | "youdao" | "llm"。 |
deeplKey | 自备的 DeepL Key。 | |
youdaoAppKey / youdaoAppSecret | 自备的有道(Youdao)应用ID/应用密钥。 | 仅原生 App 可用(桌面版/iOS)——有道的 API 不支持网页版所需的跨域请求。 |
显示
| 字段 | 作用 | 说明 |
|---|---|---|
uiMode | 设置的详细程度。 | simple 或 advanced。 |
themeId / customThemes | 当前主题 id 和用户自建的主题定义。 | 见主题。 |
uiFont / monoFont | 界面字体和等宽字体选择。 | 见主题。 |
overlayGlass | 毛玻璃浮层开关。 | 默认关闭。 |
fontSize / transcriptScale / transcriptLeading | 全局字号、转录专属字号,以及转录行距控制。 | |
bitCostume | Bit 装扮的固定选项。 | "auto"(跟随主题,默认)、"none",或指定的装扮 id。 |
集成
| 字段 | 作用 | 说明 |
|---|---|---|
hfToken / realtimeDiarize | 说话人分离用的 token,和实时分离 beta 开关。 | 见Whisper Sidecar。 |
ankiConnect | { enabled, deckName, port }——AnkiConnect 同步目标。 | 端口默认 8765,可单独配置,因为它和 whisperUrl 的默认端口撞了。见自动化。 |
usageTracking | 本地、设备端的用量账本(按天+提供商记录 STT 秒数、LLM 调用/token 数)。 | 不上传遥测——这里的数据永远不会离开浏览器/桌面应用。 |
autoExport / webhookUrl / exportFrontmatter | 文件夹自动导出、webhook URL("" 表示关闭),以及 Markdown frontmatter。 | payload 格式和文件命名见自动化。 |
模型路由
分任务模型
高级设置可以通过 taskLlm(见上面的设置参考)把检测、翻译、报告分别指向不同的提供商或模型。常见搭配是实时检测用又快又便宜的模型,会后报告用更强的模型;划词解释(define)始终跟随检测的配置。
提供商预设
设置 → AI 检测内置八个预设:
- Anthropic 官方——api.anthropic.com,原生提供商。
- OpenAI——api.openai.com/v1。
- DeepSeek——api.deepseek.com。
- 通义千问——dashscope.aliyuncs.com/compatible-mode/v1。
- OpenRouter——openrouter.ai/api/v1,也能一键 OAuth 接入。
- Poe 订阅——api.poe.com/v1,用 Poe 订阅驱动任意 OpenAI 兼容客户端。
- Ollama 本地——localhost:11434/v1,完全本地推理。
- 自定义——任意其他 OpenAI 兼容端点。
除 Anthropic 外的每个预设都对应同一个 openai-compat provider 类型,只是 base URL 不同。每个预设的 Key 都独立存储、独立发送——这是 v0.7.3 的修复,让切换提供商永远不会把上一个提供商的 Key 发到新提供商那边。
BYOK Key 处理路径
| 路径 | Key 存在哪里 | 说明 |
|---|---|---|
| 浏览器存储 | 本地浏览器存储 | 在 设置 → 密钥 里填入;下面每条路径都从这里开始。 |
| 自托管网页版,中转 | 经你自己服务器的 API route 内存中转 | 服务端从不落盘。 |
| 在线体验,BYOK | 浏览器直连提供商 | 完全不经过项目服务器;服务端会直接拒绝任何带 Key 的转发请求。 |
| 环境变量 Key | .env.local 服务端变量,例如 ANTHROPIC_API_KEY | Key 完全不进入浏览器。 |
| 桌面版,OAuth | macOS 钥匙串,经 RFC 8252 loopback OAuth | 系统默认浏览器打开做 OpenRouter 登录,不是应用内弹窗,和首次引导是同一套流程。 |
proxyUrl(仅桌面版)是系统级自动检测看不到路由时,手动触达 BYOK 端点的应急出口——比如只靠环境变量配置的代理,或者应用没法执行脚本的纯 PAC 网络。
主题
每套主题——不管内置还是自定义——都是一张扁平的 token 到 hex 的映射表,加一个 scheme 标记。每个值必须是严格的 hex,#RGB 或 #RRGGBB——rgb()、hsl()、颜色名和 CSS 变量全部会被直接拒绝——值会作为 CSS 自定义属性注入,而不是拼字符串。每套主题都会做 WCAG AA 对比度检查。scheme(dark 或 light)是一个独立的结构字段,不是第 18 个 token——它决定原生表单控件/滚动条外观和界面内图标的切换。
17 个 token
ink、panel、panel2、panel3、edge、edge2、fg、mut、mut2、lab-red、lab-orange、lab-yellow、lab-green、lab-purple、lab-cyan、act、warn-soft。
编辑器行为
- 主题网格里的
新建格子可以从零建一套主题,也可以复制任意一套(内置或自定义)。 - 编辑每个 token 时会实时预览,取消编辑会恢复到已保存的那套。
- 对比度提示会随时标出掉到 AA/控件对比度门槛以下的 token——只警告,从不阻止保存。
- 可以导出成 JSON 文件,也能通过粘贴或选择文件导入;导入的主题会重新校验,id 也会重新分配,所以导入文件永远盖不掉、也覆盖不了内置主题。
- 删除主题需要二次确认;删掉当前正在使用的那套会回退到终端默认主题。
自定义主题示例
{
"id": "custom-1737421200000",
"label": "深海蓝调",
"scheme": "dark",
"tokens": {
"ink": "#050b14",
"panel": "#0d1620",
"panel2": "#14202c",
"panel3": "#1b2c3a",
"edge": "#25384a",
"edge2": "#37516a",
"fg": "#e8f1fb",
"mut": "#93a9bd",
"mut2": "#5c7186",
"lab-red": "#ff6b6b",
"lab-orange": "#ffa552",
"lab-yellow": "#f4d35e",
"lab-green": "#4ade80",
"lab-purple": "#b895ff",
"lab-cyan": "#41d1e0",
"act": "#2fd0e8",
"warn-soft": "#ffcf7a"
}
}
界面字体(UI font)和等宽字体(monospace),各自提供零下载的系统预设(界面:默认 / 衬线 / 圆体;等宽:内置的 JetBrains Mono 默认 / 你的系统等宽),或者你自己输入一个系统字体名。uiFont/monoFont 接受 "default"、某个预设 id,或 "custom:<family>";自定义名字使用前会做净化处理。词典包制作
远程词典包是 JSON manifest,会被校验、归一化后载入内存 registry,再和内置词典包一起显示在设置里。发布到 GitHub raw URL 或 jsDelivr URL,粘贴该 URL 即可安装;也可以直接分享文件,走文件导入。
Manifest 格式
{
"id": "biotech-terms",
"name": "Biotech Terms",
"version": "1.2.0",
"terms": [{
"term": "IND",
"type": "acronym",
"gloss_en": "Investigational New Drug application",
"gloss_zh": "美国 FDA 新药临床试验申请"
}],
"expressions": [{
"expression": "go/no-go",
"category": "phrase",
"meaning": "a decision point to proceed or stop a program",
"chinese_explanation": "继续或终止(项目决策点)",
"plain_english": "decide whether to continue or stop",
"tone": "neutral, common in R&D governance"
}]
}
manifest 根层必须有 id、name、version;terms 和 expressions 都是可选数组。缩写就是 terms 里 "type": "acronym" 的条目——没有单独的缩写结构。
校验行为
- 坏条目会被单独丢弃并打印 warning;只有 manifest 本身不可用时整个包才会安装失败。
- 条目级
pack字段会被忽略,并强制改成 manifest 自己的id——单个条目没法冒充另一个包的 id。 - 只要
version改变,就足以触发一次更新;已安装的包每天自动检查一次新版本(packAutoUpdate)。
官方目录
设置里有一个一键浏览安装的官方目录——医学、生物医药、AI/ML、法律、金融、工程计算机六个专业包,共 4,300+ 词条,发布在 jargonslayer-dicts——此外还支持从文件或 GitHub URL 导入任意第三方包。
优先级
个人词典 > 已安装词典包 > 内置词典。个人词典条目会盖过任何词典命中。已安装包的释义优先于内置词典(而不是内置词典默默获胜)是 v0.7.2 上线的。
自动化与集成
自动导出文件夹
设置里可以把每场会议保存时同步写入一个文件夹,格式是 Markdown 加 JSON,命名为 YYYY-MM-DD-HHmm-jargonslayer.md 和同名的 .json。这个功能用的是 File System Access API,所以偏 Chromium(Chrome/Edge)——是为 Obsidian vault 和 agent 工作流设计的,不是一个通用的跨浏览器功能。
Webhook
配置好 webhook URL 后,每次会议保存都会 POST 一个 meeting.saved payload:
{
"schemaVersion": 1,
"event": "meeting.saved",
"exportedAt": 1735689600000,
"session": { /* full MeetingSession */ }
}
同一个 URL 也会收到后台任务生命周期事件——task.started、task.done、task.error——payload 结构和上面完全一致,只是把完整的 session 换成了一个 task 对象(id、kind、label、stage、progress、status、error、sessionId、timestamps);这些事件对应的正是桌面版后台任务的任务 registry(模型下载、说话人分离扩展安装、导入任务)。每次调用超时 8 秒,不重试——接收端慢或者挂了,只会错过那一次事件,不会阻塞或者重新排队。
接收端应该尽快返回 200,把真正的处理逻辑放到异步里做:
app.post("/jargonslayer-webhook", (req, res) => {
res.sendStatus(200); // ack immediately, before the 8s timeout
queue.add("process-event", req.body); // do the real work off-request
});
AnkiConnect
开启 ankiConnect 后,每次保存会议都会向本地 AnkiConnect 实例发起一次 addNotes 调用,用的是和手动导出 Anki TSV 一样的卡片映射规则,并做了去重,重复保存同一场会议不会产生重复卡片。它的默认端口 8765 和 Whisper sidecar 自己的 websocket 默认端口撞了,所以是可以独立配置而不是写死的——两者同机运行时检查一下 ankiConnect.port。
Obsidian
Markdown 导出可以带上给 Obsidian/Dataview 用的 YAML frontmatter(exportFrontmatter)——配合文件夹自动导出,就能搭出一个每次开完会自动更新的 vault。
备份与恢复
完整备份会把会议、词典和设置一起导出。导出对话框里的 不包含 API Key 选项默认勾选,下载前会从内嵌的设置里剥离所有 API Key、Hugging Face token、订阅直连 agent token、webhook URL,以及分任务 API Key;取消勾选就会把它们留在文件里。恢复备份时会先预览内容再写入,不管备份导出时是怎么处理的,都会重新对这些字段做一遍脱敏,并清空订阅直连的连接状态,避免恢复后静默重连到旧的本地 agent sidecar。
架构
仓库是一个 npm workspace:apps/web(Next.js 15、TypeScript、Tailwind、zustand、IndexedDB、服务端 LLM route proxy)加上 packages/core(纯 TS 共享核心——词典、类型定义)、apps/desktop(Tauri 外壳)、apps/extension(Chrome MV3 侧边栏)。apps/web 里的 zustand store 是转录片段、中间结果、卡片、术语、纪要、设置和会议历史的共享总线。
apps/web(浏览器客户端)
transcription engines
demo | Web Speech | local Whisper | tab audio | appaudio(桌面版)
zustand store
segments | cards | terms | summaries | settings | sessions
detection
dictionary instant floor
LLM scheduler and dedupe
UI
transcript | cards | history | summary | review
Server routes
/api/detect
/api/define
/api/translate
/api/summarize
Sidecars(Python)
whisper_server.py
agent_server.py
apps/desktop(Tauri v2,macOS)
Rust 核心:用固定版本的 uv 装独立 Python、
管理 sidecar 进程、CoreAudio process-tap 管线
jargonslayer-audiocap:签名的 Swift 辅助进程(CoreAudio process tap)
apps/extension(Chrome MV3)
侧边栏:Web Speech 实时捕获 + 内置词典 + 设备端 Translator API
自己的 IndexedDB 数据库 + chrome.storage.local,跟 apps/web 的存储互不相通
packages/core
共享类型 + 内置词典,网页版和插件都消费它
apps/web/src/lib/types.ts(转出自@jargonslayer/core)维护跨模块契约。apps/web/src/lib/store.ts是应用总线;STT 和检测通过 store 通信,而不是直接互相依赖。apps/web/src/lib/detect/dictionary.ts是同步即时词典层。apps/web/src/lib/detect/scheduler.ts负责 LLM 批处理和词典模式 fallback。apps/web/src/lib/detect/dedupe.ts合并重复命中,并用 LLM 结果原地升级词典卡片。apps/web/src/app/api/summarize/route.ts编排总结、分块翻译和漏检补扫。apps/desktop/src-tauri/src/audiocap.rs监管 Swift 捕获辅助进程,把它的输出重采样后接入既有转录管线。
数据与存储
所有应用数据都在 IndexedDB 里,通过 idb-keyval 访问——不是 localStorage。历史遗留的 meetlingo:* key 会在启动时复制迁移到新命名空间,并保留原数据,方便回退。
| 位置 | 存储 key | 说明 |
|---|---|---|
| 设置 | jargonslayer:settings | 本地配置。 |
| 会议 | jargonslayer:sessions:index + jargonslayer:session:<id> | 一个索引数组,加上每个 key 一条完整的 MeetingSession 记录。 |
| 个人词典 | jargonslayer:glossary | 个人条目和掌握状态。 |
| 自动保存文件夹 | jargonslayer:export-dir | 浏览器文件 handle;偏 Chromium。 |
| 远程词典包 | jargonslayer:remote-packs | 已安装包的来源 URL 和校验过的 manifest。 |
Schema version 1 覆盖会议 JSON、Markdown frontmatter、webhook payload、全量备份和远程词典包。
应用里真正用到 localStorage 的只有两处:一个是主题 FOUC(无样式内容闪烁)镜像 js-display,在 IndexedDB 解析完成前就同步读取,让首次渲染就用上正确的主题/字号;另一个是更新检查的 ETag 缓存 js-update-etag-cache,让重复的 GitHub release 检查更省资源。
chrome.storage.local 存收藏的查词记录——从不和网页版的存储同步或共享。行为规格
这些是排查配置问题、或者想扩展这个工具时最值得了解的行为。
检测规格
- 启用检测时,每个最终转录片段都会被词典同步扫描。
- LLM 层只批处理新增文本,之前的 context tail 仅用于消歧。
- LLM 结果可以原地升级词典卡片,但不会撤回词典命中。
- 重复表达按归一化 key 合并,更新 count 和 last-seen 元数据。
- 个人词典优先于内置词典,且不会被 AI 覆盖。
- 全大写缩写使用更严格匹配,降低误报;普通混合大小写术语匹配更宽松。
翻译规格
- 实时双语转录按 segment id 分小批翻译最终片段。
- 会议中打开双语转录时,会补翻最近几个尚未翻译的最终片段。
- 手动修改某个片段文本时,旧翻译会失效;会议仍在进行时,会重新入队。
- 无 Key 暂停是可自愈的:填入 Key 后,新翻译工作可以恢复,不必重启会议。
- 连续 429 会暂停,并最终跳过最老的卡住批次,避免新片段一直被阻塞。
- 会后报告翻译是另一条路径:按 segment index 分块翻译,并尽量修复缺失的 index。
导入规格
| 导入路径 | 处理方式 | 限制 / 说明 |
|---|---|---|
文稿粘贴 / .txt / .srt / .vtt | 解析、构建合成会话、检测、可选翻译、保存。 | 有时间戳的 cue 会归一到导入时间;纯文本会使用合成时间间隔。 |
| 浏览器音频导入 | 浏览器 AudioContext 解码,重采样到 16 kHz mono,交给浏览器 Whisper worker。 | 45 分钟时长上限;200 MB 压缩音频上限。 |
| 浏览器视频导入 | 用 ffmpeg.wasm 提取音轨,再复用浏览器音频路径。 | 视频提取有单独的大小限制;首次运行可能下载浏览器侧模型资源。 |
| Sidecar 录音导入 | 本地 job API 处理转录和可选说话人分离。 | 需要本地 sidecar;进度显示是 UI 本地状态,刷新会丢失显示但任务会继续。 |
| 视频 URL 导入 | 本地 sidecar 使用 yt-dlp,再走同一转录 job 管线。 | 仅本地版;用户自行负责版权和平台条款。 |
失败与恢复规格
- 无 API Key:检测回退到词典;实时翻译暂停;生成报告会提示需要 Key。
- 限流:检测对 429 带抖动重试一次;翻译会暂停,并避免老批次永久阻塞新片段。
- Sidecar 不可达:本地 Whisper、sidecar 导入、URL 导入、说话人分离、订阅直连都不可用;浏览器本地导入仍可处理支持的文件。
- 会议已切换:异步结果携带
meetingGen;旧会议的过期结果会被丢弃。 - 后台标签页:浏览器会节流 timer,所以检测也会在片段到达和可见性变化时触发 flush。
- 停止后的晚到结果:检测和翻译可能在停止后到达;store 会安排补保存,避免历史记录缺尾部结果。
词典与学习规格
- 今天个人词典是持久学习主场;会议记录保持为不可变归档。
- 条目可以手动创建、由划词 AI 定义创建,也可以从本场卡片收藏。
- 每个条目保存 headword、variants、解释、例句、原始上下文、用户笔记、来源、时间戳和复习状态。
- 表达条目可保存分类、英文含义、plain-English 改写和语气。术语条目可保存术语类型和英文 gloss。
- 复习状态包括 mastered、review count、last reviewed time——v0.3.0 起已经是真正的 learn-set 和到期复习循环(SM-2 简化排期)。
异步委派设计
产品体验应该像一个稳定的前台进程,背后有多个专业后台 worker。前台负责监听、文本编辑、用户焦点和已保存会议状态;昂贵任务交给异步委派者,结果准备好后再自然合并回来。
translate | detect | search | diarize 这几种 kind。前台
追加最终转录片段,保持用户焦点稳定,允许编辑和复盘,渲染当前最好的状态,不等待 AI 或搜索。
委派者
翻译、LLM 检测、实时搜索、说话人分离、会后报告各自排队运行,有优先级和重试策略。
任务账本
按片段记录覆盖情况:词典已完成、LLM 检测待处理/完成/失败、翻译待处理/完成/跳过、搜索待处理/完成/过期。
{
"id": "job_x",
"kind": "translate | detect | search | diarize",
"meetingGen": 12,
"segmentIds": ["seg_1", "seg_2"],
"textHash": "...",
"priority": 2,
"status": "queued | running | done | failed | stale",
"attempts": 0
}
结果只有在 meeting generation 仍匹配、目标片段仍存在、源文本 hash 仍是当前版本时才应用。结果应该以 patch 方式合并到相关字段,不能替换前台转录,也不能抢走用户焦点。
未来的实时搜索天然适合这个模型:卡片可以用当前表达加最近上下文发起一次后台搜索,同时立刻显示本地解释,等搜索委派者返回后再附加参考来源或消歧说明。搜索应该由歧义、用户请求,或显式的词典包/卡片操作触发,而不是默认给每张卡都搜一遍;结果应该作为引用或上下文说明附加到卡片上,而不是替换掉本地解释;搜索任务的优先级应该低于实时转录、词典扫描和可见卡片检测;隐私设置也应该清楚区分本地词典、模型提供商 AI 和联网搜索这三类调用。
测试
Web App
npm run typecheck
npm test
npm run build
Sidecar
sidecar/.venv/bin/python sidecar/test_agent_json.py
sidecar/.venv/bin/python sidecar/test_agent_postfilter.py
sidecar/.venv/bin/python sidecar/test_agent_server.py
sidecar/.venv/bin/python sidecar/test_download.py
sidecar/.venv/bin/python sidecar/test_ingest_url.py
sidecar/.venv/bin/python sidecar/test_lazy_load.py
sidecar/.venv/bin/python sidecar/test_model_registry.py
sidecar/.venv/bin/python sidecar/test_parakeet_backend.py
sidecar/.venv/bin/python sidecar/test_realtime_diar.py
sidecar/.venv/bin/python sidecar/test_whisper_protocol.py
sidecar 测试是 plain-assert 脚本,依赖 sidecar/requirements.txt。本地 Whisper 和 agent sidecar 是独立进程,改哪个测哪个。
桌面版(Rust + Swift)
cd apps/desktop/src-tauri && cargo test
cd apps/desktop/src-tauri/audiocap-helper && swift test
Rust 核心(进程监管、重采样、CoreAudio framing 协议)和签名的 Swift 捕获辅助进程各有自己的测试套件;CI 环境没有真实 CoreAudio session,所以音频捕获相关的测试都是围绕 fake/seam 设计的,不依赖真实 tap。
插件
npm test -w apps/extension