返回更新日志

开发记录

Syzygy 1.2.0 开发周期:语音听写、本地大模型与提示词版本管理

1.2.0 开发周期的 70 个提交:从零建成的实时语音输入链路(Silero VAD 分段、多引擎回退、重试预算)、进程内 mistral.rs 本地模型、gitoxide 提示词版本管理与技能注册表,以及 macOS 公证与 Linux AppImage 签名的发布工程。

Syzygy 团队发布于 阅读约 12 分钟更新于
  • #Syzygy 1.2.0
  • #语音输入
  • #语音转文字离线
  • #本地大模型
  • #提示词管理
  • #技能管理
本文目录

本文是 1.2.0 开发周期整理,更新于 2026 年 10 月 4 日,不作为 1.2.0 已发布或发布日期的证明。可用安装包与实际发布版本以下载页为准。下文提交数量统计截至 9 月 26 日。

1.1.0 在 2026 年 8 月 31 日发布。此后到 9 月 26 日,主干合入 70 个提交,其中 32 个 feat、17 个 refactor、9 个 add、5 个 fix。这一版的三块能力全部是在这 70 个提交里从零长出来的:语音听写的第一行代码出现在 9 月 5 日,本地大模型 crate 建于 9 月 14 日,技能注册表建于 9 月 23 日。加上贯穿整月的发布工程(macOS 公证与更新签名、Linux AppImage 签名),1.2.0 的主题可以概括为:让数据进入工作台的方式从复制扩展到说话,让 AI 的算力从云端搬回本机,让用户积累的提示词和技能像代码一样有版本。

语音听写台:录音、分段与转写在同一界面完成
语音听写台:录音、分段与转写在同一界面完成

语音听写:从第一行代码到完整链路

动工之前先做了一个会决定整个形态的决策(ADR 0001):转写结果物化为剪贴板条目,进入 Syzygy 既有的预览、搜索、组织、粘贴流程,恢复焦点后自动注入外部应用的方案被否决。自动注入要跨平台处理全局按键、焦点恢复和辅助功能权限,误投递的风险由用户承担;而工作台内粘贴的路径已经存在,语音结果放进这个模型里,用户保留最终落点的控制权。

完整链路如下:

flowchart LR
  A[麦克风采集] --> B[VAD 流式分段]
  B --> C[分段转写与引擎回退]
  C --> D[整段重转写]
  D --> E[标点润色与热词改写]
  E --> F[物化为剪贴板条目]

不看图也可以这样理解:录音被切成段送识别,识别失败按预算换引擎重试,说完之后整段录音还有一次利用全上下文重新识别的机会,最后经过确定性的文本清理进入剪贴板。

分段。 长录音必须在用户停止之前决定何处分段送识别。这一版引入 Silero VAD 作为默认分段器:模型锁定在目录里(来源、版本、SHA-256 固定,不接受任何路径下直接放一个 onnx 文件),推理跑在 sherpa-onnx 的阻塞线程上,窗口与采样率对齐 Silero 的训练设定,每段裁掉尾部再切下一段。能量检测没有删除,它作为回退分支保留:模型未安装或加载失败时自动接管,两套分段共享同一套窗口上限和最小段长。用户按下停止时,未完成的检测强制 flush,不会丢音频。要诚实交代的边界:策略与接口已有回归测试覆盖,但跨设备真实麦克风的长录音验收(Windows、Linux 的设备差异)还在计划中。

回退链与重试预算。 识别引擎分四类,顺序由用户配置:系统识别(macOS Speech)、本地 Whisper、本地 Paraformer(sherpa-onnx 运行时),以及 AI 账号远程引擎。远程引擎走三种音频协议:OpenAI 兼容的 chat 补全(音频作为 content part 发送)、multipart 转写端点、DashScope 原生多模态端点(qwen-audio-3.0-asr-flash 使用原生协议,价格与区域可用性以厂商当前说明为准),另有腾讯云 ASR 模块。

这一版的重点是把重试从"失败一次就降级"改成三重预算:一段识别总共最多尝试 12 次,单个引擎降级前最多 3 次,墙钟预算 60 秒。预算只在尝试启动时检查,已经开始的请求会跑到自己的结论,没有硬超时把在途请求拦腰取消。失败分类决定去向:超时、传输错误、5xx 这类瞬态失败原地退避重试(500ms 起步、每档加 500ms、封顶 1.5s),引擎不可用和"确定没说话"直接换下一个引擎,对确定性结论反复提问没有意义。旧实现里 max_attempts 会在计划期静默截断用户配置的引擎清单,这个语义已经删除。远程引擎的请求与响应形状用 wiremock 契约测试锁定,凭据只存系统钥匙串,语音配置文件里保留厂商的非敏感字段。

整段重转写。 分段是为了即时性,代价是丢失跨段上下文。所以停止之后、后处理之前,运行时把该会话自己的分段录音(至少两段、总长不超过 300 秒,这正好对上远程引擎 10MB 的请求上限)按口语序拼接,交回本会话胜出的引擎再做一次整段识别,产出的文本替换分段拼接结果。重转写是升级而非关卡:引擎拒绝、拼接失败或用户取消,都回退到分段拼接文本继续走后处理,说过的字不会丢。会话期间音频只驻留内存,进入终态即清空,不落盘。

收尾不挡下一次录音。 之前的设计里 AI 后处理进行期间录音按钮是灰的。现在 Stop 受理的瞬间麦克风槽就释放了,收尾(后处理、重转写、物化)全部在后台完成并按 Run 归属回填,用户可以立刻开始下一轮听写。

后处理。 文本清理分两层。第一层是确定性标点润色:中文标点补全、重复标点收敛这类规则不经过模型,成本与文本长度线性,结果可复现。第二层是热词词典。识别器不会按用户录入的写法返回术语,中文引擎会按自己的分词把 OpenAI 返回成 open ai,把 GPT 返回成 G P T。1.2.0 之前,词典用 12 条手工短语改写逐个补偿;现在匹配规则改为"去掉行内空白后再匹配、命中回映射到原文",一次覆盖全部 1,153 条已启用词条,那 12 条手工改写随之删除。空白跨度有限制(不超过被中断术语本身的两倍),不会把相隔较远的字符合成一个词,拼音候选不走这条路径。

免费版的 60 秒。 免费版单次连续录音上限 60 秒,这条限制的实现方式经过专门设计:到上限时录音暂停(麦克风关闭、这一段不转写),点击继续在原录音上接着录,停止后所有片段合并转写;计时按音频样本数计算,运行时不持时钟,到上限与麦克风真正关闭之间送达的样本照常保留。Pro 版没有这个上限。

此外有一批小而实在的修复:系统识别在用户口语语言与系统区域不一致时报"未检测到语音"的问题已修;语音输入的运行历史改为有界保留,避免长期使用后无限增长;Quick Panel 获得了与主窗口一致的语音入口,听写台的布局按"说话流为主列"重新排布。

本地大模型:进程内推理,回环端点

本地模型的目标只有一个:让剪贴板理解、翻译、对话这些能力可以完全离线运行,且数据不出本机。

实现选了 mistral.rs 0.9.3 作为进程内引擎,直接加载 GGUF 权重,macOS 构建启用 Metal。模型通过一个仅绑定 127.0.0.1 的 OpenAI 兼容端点对外服务:POST /v1/chat/completions 支持缓冲与 SSE 两种响应,GET /v1/models 提供模型列表。协议适配与推理引擎因此解耦,应用内所有 AI 消费者走同一个调用边界。带推理标签的模型输出经过一个增量过滤器,<think> 内容在流式过程中被剥离,不会泄漏到用户面前。本地服务一次只承载一个模型,应用重启后按上次意图自动恢复。

下载的接纳规则比"文件存在"严格得多:下载在暂存文件中校验长度与 SHA-256,通过后才发布到模型目录;每次实际加载前独立重新校验,文件或父目录是符号链接时直接拒绝。同长度的篡改文件过不了关,"上次校验通过"也构成不了这次的加载许可。

模型从哪来?四份目录(chat、ASR、embedding、OCR)由上游快照可复现生成:同步脚本锁定上游 revision 与 SDK 版本,每个工件声明 role、路径、格式、大小与 SHA-256,policy 决定哪些条目进入产品目录,--check 离线重算并逐字节比较以发现漂移。运行时绑定是一个闭集:mistral.rs GGUF、fastembed ONNX、whisper.cpp、sherpa-onnx Paraformer、PaddleOCR MNN。目录里没有对应引擎绑定的条目不会被宣称可安装,API-only 的模型(比如 DashScope 的 ASR)不会被伪装成可下载权重。

提示词库:模板、标签与版本历史在同一工作台管理
提示词库:模板、标签与版本历史在同一工作台管理

检索是这版的另一条主线。语义向量检索与词法检索融合,融合算法用倒数排名聚合(k=60),两种排名先各自排好再合并,同分按文档 ID 字典序定序,同样的输入永远得到同样的结果。向量是派生缓存:正文指纹与模型身份共同决定其有效性,切换模型时其他模型的向量被清除,不会混用向量空间。索引任务空闲时执行有界回填,每批最多 64 条、间隔至少 15 秒,不与增量更新争抢。本地 embedding 与远程 embedding 共用同一端口,远程走已配置账号的 /embeddings,校验返回的索引、数量、维度与数值有效性。远程模型目录新增 OpenRouter 同步脚本,用量记账随之对齐(含按模型的价格数据)。

搜索限定符:语义与词法检索融合后,仍可按类型、标签、设备过滤
搜索限定符:语义与词法检索融合后,仍可按类型、标签、设备过滤

请求发出之前还有一道预检。模型的能力声明是三态的:未声明、声明为空、明确列出,三者的语义不同。声明明确不含所需输入模态时,请求在调用提供方之前就被显式拒绝并报告原因,附件不会被静默 OCR,也不会被悄悄丢弃。声明未知时允许尝试,但尝试的成功不会反过来改写声明。

提示词与技能:把文本资产当代码管

提示词在这版有了真正的版本管理。应用内置 gitoxide(gix,纯 Rust 的 git 实现),保存更新工作草稿,显式发布才分配版本并写入 Git 提交。历史与差异命令查看发布记录,恢复将所选版本写回工作内容,检查后可再决定是否发布。变量系统做了五级解析(按作用域逐级回退),配套的安全测试保证模板不会把密钥等内容带进渲染结果。提示词模板获得了标签体系,检索与组织对齐剪贴板条目的习惯。

附件机制让提示词可以引用图片或文件:附件有独立的身份、存储与引用语法,请求组装严格按正文渲染顺序排列内容,被引用的附件缺失时请求以失败结束,不会静默跳过。发请求前引用声明与三态模态预检配合,声明了 vision 的模型才会接到图片。

技能注册表是新领域。技能来源是一份用户维护的有序列表(支持添加、开关、移除、排序),扫描以 SKILL.md 文件的 SHA-256 做内容去重,保留首个实例并标记重复来源。移除一个来源不会删除磁盘上的文件,外部目录不会因为被索引而变成应用可回收空间。编辑器里的斜杠菜单按来源限定符筛选技能,技能、提示词、记忆三者的存储所有权分开,共享的只是文件底座与编辑器组件。

发布工程:让更新通道值得信任

发版本身占了这个周期 14 个提交。macOS 侧建起一条可重复的公证流水线:公证脚本负责提交应用与轮询苹果的状态机,签名脚本在不暴露私钥的前提下给 Tauri 更新包签名,公钥由独立脚本投影进桌面构建配置,更新器的公钥进构建、私钥不落构建机。期间修掉一个具体问题:codesign 输出 plist 需要 --xml 参数。

Linux 侧的 AppImage 走容器化构建,脚本支持 dry-run 预览,签名发生在重打包之后,私钥路径先校验再使用。DMG 制作改用 zlib 压缩控制体积。iOS 的上架素材也在这个周期补齐(登录、首页与主要功能界面的商店截图)。随发版脚本一起,内置语音词典的再生成也纳入了流水线:从人工复核过的 CSpell 数据离线复现词典文件,词典内容可溯源、可重建。

此外有一批小而具体的变更:复制路径文本(比如编辑器里的 Copy Path)时,符合条件的纯文本路径在捕获时升级为文件条目,原始文本保留为出处,粘贴回写忠实于所复制的内容;取消条目标签后标签本身保留,孤儿标签关联随迁移清理;翻译多引擎的优先级字段重构为显式的回退顺序;OCR 模型处理与资源同步逻辑重构,模型注册表补齐翻译支持;AI 对话接入了用量记账,聊天链路引入确定性 nonce 加密。

测试面同步扩张:这 70 个提交里有 4 个纯端到端测试提交,覆盖技能工作台、斜杠技能面板、隐私应用选择、语音听写界面、布局回归、工作区路由、AI 模型声明、图片预览与提示词附件。语音运行时这一条线的单元与集成测试在 9 月中旬累计 193 个通过(领域 crate 83、运行时 crate 110),技能注册表落地当周相关 10 个包累计 1,066 个测试通过。多语言文案键位校验维持在 100% 同步。

已知边界与下一步

如实列出这一版没有做完的:VAD 分段与识别链路的跨设备真实麦克风长录音验收待执行;重转写的流式部分结果上屏未做(当前整段替换一次性落地);识别错误分类还没有下探到 429 与 Retry-After 这样的细粒度;当前运行时已经装配 Qwen Realtime 与 DashScope Message、阶跃星辰、讯飞、火山引擎和腾讯云。代码接入不代表所有厂商账号与真实麦克风场景已经完成验收。

下一步的方向在路线图里有完整叙述:移动端应用市场上架、把数据空间与记忆以 MCP 服务器形式暴露、铺设加密中继节点。上一版的变更见 1.1.0 更新说明,语音能力的使用方式见语音输入指南,本地模型与提示词工作台分别有独立的模型与技能文章。

常见问题

Syzygy 的语音输入需要联网吗?

不需要。本地目录内置 Paraformer、Whisper 量化版等可下载模型,录音、分段、转写都在设备上完成;只有显式选择远程引擎并同意音频出设备时,音频才会离开本机。

免费版语音输入有什么限制?

免费版单次连续录音上限 60 秒。到上限时录音自动暂停、已录内容保留,点继续可以接着说,停止后整段一起转写。这是节奏限制,每次重新开始录音都会重新计数。

1.2.0 这个版本号是怎么定的?

1.1.0 于 8 月 31 日发布后进入本版本的持续构建,仓库版本字段会随发布流程更新;本篇按 1.2.0 组织这一周期的全部变更,安装包版本号以下载页为准。