Address reliability and consistency gaps across Agent Runtimes, persistence, settings, Knowledge, Magic Notes, Smart Heartbeat, and the download site. Runtime processes now have bounded lifecycle cleanup and atomic configuration rollback, while model packages and persisted mutations recover safely. Add a configurable global shortcut, protect unsaved work, improve modal and keyboard behavior, localize the built-in project without rewriting stored data, and lazy-load heavy renderer routes under enforced bundle budgets. Align project forms and disabled controls with shared typography and interaction states, and strengthen website release metadata validation and navigation accessibility. Release note: 修复 Runtime、设置、知识库、魔法笔记与智能心跳中的可靠性和交互一致性问题;新增可配置全局快捷键,改进无障碍与加载性能,并强化官网下载校验。
34 KiB
GoodBuddy 平台功能页签与模型下载源设计
文档信息
| 项目 | 内容 |
|---|---|
| 文档类型 | 跨功能技术与产品架构 |
| 状态 | 已实现(语音输入与 OCR) |
| 版本 | 1.0 |
| 日期 | 2026-08-19 |
| 适用产品 | GoodBuddy 桌面端 |
| 目标平台 | Windows、macOS、Linux,x64 与 arm64 |
| 相关基线 | 统一界面设计系统、文档解析与本地 OCR、本地文本向量模型与连接设计、全双工实时语音交互设计 |
本文定义“设置 → 平台功能”的二级页签结构,以及所有 GoodBuddy 托管本地模型共同使用的 “模型下载源”设置。首期下载源为 ModelScope 和 Hugging Face,默认使用 ModelScope。
模型下载源是用户明确选择的供应链路径。当前来源不可用或没有所选模型时,GoodBuddy 必须明确失败并提供设置入口,不能静默切换到另一个下载源。
1. 摘要与核心决策
- “平台功能”保留现有一级设置分类,在分类内部增加共享
PageTabs:- 通用设置
- 魔法笔记
- “通用设置”首个功能为“模型下载源”。
- 首期提供两个互斥选项:
modelscope,用户文案为 ModelScope,默认值。hugging-face,用户文案为 Hugging Face。
- 设置影响 GoodBuddy 管理的所有本地模型下载:
- 当前本地语音输入模型。
- 当前本地 OCR 模型。
- 规划中的应用托管文本向量模型。
- 后续接入统一模型目录的本地 ASR、TTS、VAD 或其他本地模型。
- 设置不影响:
- 已安装模型及其当前选择。
- ZIP 导入和导出。
- Ollama 自行安装或拉取的模型。
- 云端模型调用。
- LLM Provider、Runtime、Skills、MCP 或扩展下载。
- GoodBuddy 应用自身的“检查更新源”。
- 每个模型文件继续固定 Revision、字节数和 SHA-256。两个来源只有提供完全相同的预期 文件时,才表示同一个模型工件。
- 一个模型在所选来源缺少任何必需文件时,整个模型在该来源标记“暂不可下载”。不能把 同一模型包的部分文件从 ModelScope 下载、部分文件从 Hugging Face 下载。
- 下载任务启动时冻结来源。任务运行期间切换全局设置,不改变、重启或迁移该任务;新的 下载使用新来源。
- 当前来源失败时,不自动重试另一个来源。错误可以提供“前往通用设置更换下载源”,由 用户明确选择。
- 现有魔法笔记设置只移动到“魔法笔记”页签,不改变保存、启用或评论行为。
2. 实现基线
2.1 平台功能界面
“平台功能”现已使用两个二级页签:
- “通用设置”承载全局模型下载源。
- “魔法笔记”承载显示入口、AI 评论方式和 AI 评论形式。
页签复用共享 PageTabs,默认打开“通用设置”,不会把当前页签写入应用设置。
2.2 受管模型下载
SpeechModelManager管理语音输入模型。DocumentOcrModelManager管理 OCR 模型。
两者均已接入全局模型下载源,并具备:
- Main 进程下载。
- 来源无关的固定文件名、字节数和 SHA-256。
- ModelScope 或 Hugging Face 的固定 Revision 与下载 Target。
- 随机暂存目录。
- 下载进度和取消。
- 完整校验后原子安装。
- ZIP 导入、导出和删除。
- 受管模型目录。
- 来源冻结、无静默回退和无跨来源文件混合。
- 只向 Renderer 暴露来源可用性,不暴露下载 URL 或重定向 Host。
2.3 当前应用设置
平台功能与检查更新共用版本化 ApplicationSettingsStore。modelDownloadSource 已加入
版本 7 设置,通过共享 Zod Schema、可信 IPC sender 校验和原子 JSON 写入持久化。版本
1 至 6 惰性迁移为 ModelScope;未知未来版本继续拒绝读取且不会覆盖用户文件。
3. 目标
3.1 用户目标
- 在一个固定入口选择后续本地模型从 ModelScope 还是 Hugging Face 下载。
- 清楚知道当前选择、默认值和影响范围。
- 切换来源时不影响已安装、已选择或正在使用的模型。
- 下载失败时知道实际使用了哪个来源。
- 所选来源没有模型时得到明确说明,并可以主动前往设置切换。
- 在“平台功能”中快速区分通用设置与魔法笔记。
3.2 产品目标
- 为语音、OCR、向量和后续本地模型建立一个共同的下载来源契约。
- 保持模型目录中固定 Revision、大小、SHA-256 和许可信息不变。
- 禁止模型管理器各自实现不同的来源回退和设置读取。
- 为未来平台功能保留可扩展的二级页签结构。
- 保持现有安装、ZIP、取消、原子替换和安全边界。
3.3 质量目标
- 同一模型从两个来源下载后,最终安装文件的名称、大小和 SHA-256 完全一致。
- 来源切换不会产生混合来源模型包。
- 活动下载可以准确显示其冻结来源。
- 设置迁移后所有现有用户默认获得 ModelScope,不丢失其他应用设置。
- ModelScope 或 Hugging Face 单独故障时,测试能证明应用不会请求另一个来源。
- 页签和来源选择均可通过键盘、屏幕阅读器、浅色、深色和窄窗口使用。
4. 非目标
首期不包含:
- 自定义模型下载源 URL。
- 根据网络、地域、速度、HTTP 状态或模型可用性自动选择来源。
- ModelScope 与 Hugging Face 的自动测速或自动排序。
- 同一下载任务内跨来源续传。
- 在来源失败后弹窗默认勾选另一个来源并继续。
- 修改 GoodBuddy 应用更新源。
- 安装、配置或管理 Ollama。
- 为云端 Provider 下载模型。
- 把模型权重打入 GoodBuddy 安装包。
- 将“通用设置”做成所有设置的重复入口。
- 创建空的“未来功能”页签。
- 改变现有魔法笔记的产品行为。
5. 术语
| 术语 | 定义 |
|---|---|
| 模型下载源 | 用户选择的 ModelScope 或 Hugging Face |
| 模型目录 | GoodBuddy 源码中经过验证的模型元数据集合 |
| 模型包 | 一个模型运行所需的全部必需文件和安装清单 |
| 下载目标 | 某个来源中一个固定模型文件的 URL 和仓库信息 |
| 规范工件 | 由文件名、字节数和 SHA-256 定义的来源无关文件 |
| 来源覆盖 | 一个来源是否为模型包的全部必需文件提供下载目标 |
| 冻结来源 | 下载操作启动时记录且在操作期间不变化的来源 |
| 来源回退 | 一个来源失败后由应用自动请求另一个来源 |
“模型来源”容易被理解为模型作者或 License 来源,因此用户界面统一使用“模型下载源”。 模型卡中的 License 和上游模型作者不随下载源改变。
6. 平台功能信息架构
6.1 页面结构
设置
└─ 平台功能
├─ 通用设置
│ └─ 模型下载源
└─ 魔法笔记
├─ 显示魔法笔记入口
├─ AI 评论方式
└─ AI 评论形式
分类页只有一个一级标题“平台功能”。页签标题不重复渲染为第二个页面标题;每个面板内部 使用区块标题说明设置内容。
6.2 PageTabs
使用共享 PageTabs,建议使用 segmented 视觉变体:
<PageTabs
ariaLabel="平台功能设置"
idPrefix="platform-features"
onChange={setActiveSection}
tabs={[
{ id: 'general', label: '通用设置' },
{ id: 'magic-notes', label: '魔法笔记' }
]}
value={activeSection}
variant="segmented"
/>
语义要求:
- 容器使用
tablist。 - 每项使用
tab和aria-selected。 - 面板使用
tabpanel,并由对应 Tab 控制。 - 左、右方向键切换,支持 Home、End。
- Tab 键离开页签组进入当前面板。
- 切换后焦点和可见面板保持一致。
- 不使用普通按钮组或
SegmentedControl替代页签语义。
6.3 默认页签和持久化
- 每次打开“平台功能”默认进入“通用设置”。
- 当前页签只在本次设置页面生命周期内保留。
- 不把当前页签写入
ApplicationSettingsStore。 - 后续支持设置深链接时,可以通过明确的导航参数打开某个页签,不改变默认设置值。
6.4 未来扩展
未来新增页签时:
- 必须与“通用设置”和“魔法笔记”处于同级。
- 页签表示完整、独立的功能设置域。
- 通用设置只放跨功能的全局行为,不成为任意设置的杂物区。
- 页签超过可读数量时重新组织信息架构,不允许多行堆叠。
- 尚未提供的功能不显示空页签或“敬请期待”占位。
7. 通用设置界面
7.1 布局
平台功能
管理通用平台行为与可选工作区能力
[ 通用设置 ] [ 魔法笔记 ]
┌ 本地模型 ──────────────────────────────────────────────┐
│ 模型下载源 │
│ 选择 GoodBuddy 托管本地模型后续下载使用的平台。 │
│ 已安装模型和 ZIP 导入不受影响。 │
│ │
│ ◉ ModelScope │
│ 默认,适合优先访问 ModelScope 的网络环境 │
│ │
│ ○ Hugging Face │
│ 适合可以稳定访问 Hugging Face 的网络环境 │
│ │
│ 当前选择:ModelScope │
└────────────────────────────────────────────────────────┘
页面只保留一个视觉上最突出的任务。模型下载源保存属于即时设置,不增加与来源选项竞争的 大型“保存全部”按钮。
7.2 控件
来源选项包含说明和未来可能的不可用原因,因此使用语义化 Radio Group,而不是原生
select 或 SegmentedControl:
fieldset+legend表达“模型下载源”。- 每个选项使用 Radio 和整行可点击卡片。
- 当前项同时显示选中 Radio、强调边框和选中背景。
- 不能只用站点 Logo 或颜色表达选择。
- Logo 可作为辅助图形,但站点名称必须始终显示为文字。
用户文案固定为:
ModelScopeHugging Face
内部标识不直接显示。
7.3 说明文案
区块说明:
选择 GoodBuddy 托管本地模型后续下载使用的平台。已安装模型、ZIP 导入、Ollama 模型和应用更新不受影响。
来源下方说明:
- ModelScope:
默认,适合优先访问 ModelScope 的网络环境。 - Hugging Face:
适合可以稳定访问 Hugging Face 的网络环境。
不使用“国内源”“国外源”等绝对地理描述,也不保证任一来源在用户网络中一定更快。
7.4 保存交互
- 用户选择另一个 Radio 后立即调用应用设置更新。
- 保存期间禁用两个选项,保留最后确认值。
- 成功后更新当前选择,并通过应用通知提示:
模型下载源已切换为 Hugging Face。 - 失败时恢复最后确认值,在 Radio Group 附近显示可重试错误。
- 同一个失败不再同时显示页面横幅和全局错误通知。
来源变化不触发:
- 模型下载。
- 已安装模型验证。
- 模型删除。
- 当前模型切换。
- 活动下载取消。
7.5 活动下载说明
来源选项下方持续说明:
正在进行的模型下载会继续使用启动时的来源;新的下载使用当前选择。
首期不为这段说明新增跨语音、OCR 和向量管理器的活动任务聚合 IPC。具体活动操作继续在 对应模型卡显示冻结来源、进度和取消入口。
8. 魔法笔记页签
现有魔法笔记卡完整移动到“魔法笔记”面板:
- 显示魔法笔记入口。
- AI 评论方式。
- AI 评论形式。
保持以下行为不变:
- 开关继续使用共享 Switch 和
role="switch"。 - 评论方式和形式继续使用
SegmentedControl。 - 每项继续即时保存。
- 保存失败保留最后确认设置。
onMagicNotesEnabledChange继续更新应用导航入口。
页签重构不能:
- 重置现有设置。
- 在切换页签时保存或改变值。
- 因魔法笔记入口关闭而隐藏“魔法笔记”设置页签。
- 把页签选择误当成启用开关。
9. 设置契约与迁移
9.1 共享契约
扩展 src/shared/application-settings-contracts.ts:
export const modelDownloadSourceSchema = z.enum([
'modelscope',
'hugging-face'
])
export type ModelDownloadSource = z.infer<
typeof modelDownloadSourceSchema
>
type ApplicationPreferences = {
checkUpdatesOnStartup: boolean
updateSource: 'github' | 'mirror'
modelDownloadSource: ModelDownloadSource
magicNotesEnabled: boolean
magicNoteCommentMode: MagicNoteCommentMode
magicNoteCommentFormat: MagicNoteCommentFormat
}
applicationSettingsUpdateSchema 继续允许有界的 Partial 更新,不允许未知字段或空更新。
9.2 默认值
modelDownloadSource: 'modelscope'
默认值适用于:
- 首次安装。
- 没有应用设置文件。
- 从旧设置版本迁移。
- 设置文件损坏并完成现有隔离恢复流程。
恢复损坏设置时继续显示现有应用设置恢复警告,不把来源恢复伪装成用户选择。
9.3 设置版本
ApplicationSettingsStore 增加新版本,例如从当前版本 6 升级到 7:
type StoredApplicationSettingsV7 = {
version: 7
checkUpdatesOnStartup: boolean
updateSource: 'github' | 'mirror'
modelDownloadSource: 'modelscope' | 'hugging-face'
magicNotesEnabled: boolean
magicNoteCommentMode: MagicNoteCommentMode
magicNoteCommentFormat: MagicNoteCommentFormat
lastSeenReleaseNotesVersion: string | null
}
迁移要求:
- 版本 1 至 6 全部迁移为
modelDownloadSource: 'modelscope'。 - 保留更新源、魔法笔记和已读发布说明版本。
- 读取不立即写盘,继续沿用设置存储现有的惰性迁移语义。
- 下一次设置更新或发布说明确认时按新版本原子写入。
- 更高未知版本继续拒绝读取,不覆盖用户文件。
9.4 设置服务
首期继续复用现有:
settings:application:getsettings:application:updatewindow.goodbuddy.updates.getSettings()window.goodbuddy.updates.updateSettings()
虽然 Preload Namespace 名称为 updates,底层契约已经承载应用设置。此功能不要求为了一个
字段进行无关的桥接重命名。未来如果拆分 applicationSettings Namespace,必须保持迁移期
兼容并避免两套设置源。
10. 模型目录契约
10.1 来源无关的规范工件
当前每个文件把 URL、大小和 SHA-256 放在同一个 download 对象中。新契约把工件身份与
下载目标拆开:
type ModelArtifactTarget = {
url: string
repositoryUrl: string
revision: string
redirectHosts?: string[]
}
type ModelArtifactFile = {
name: string
role: string
size: number
sha256: string
targets: Partial<
Record<ModelDownloadSource, ModelArtifactTarget>
>
}
type ManagedModelCatalogEntry = {
id: string
displayName: string
files: ModelArtifactFile[]
}
size 和 sha256 位于来源外层,表示两个来源必须提供同一规范工件。
如果两个站点提供的文件不是完全相同的字节:
- 不能把它们放入同一文件的两个 Target。
- 不能为了“兼容”使用两个 SHA-256。
- 应创建不同模型工件版本、Variant 或不同模型 ID。
- 每个 Variant 分别经过运行时和质量验证。
Renderer 使用的目录快照不暴露下载 URL、重定向 Host 或文件存储地址,只返回来源可用性:
type ModelDownloadAvailability = {
source: ModelDownloadSource
available: boolean
totalBytes?: number
unavailableReason?: string
}
type ManagedModelCatalogView = {
id: string
displayName: string
downloadAvailability: ModelDownloadAvailability[]
}
type ManagedModelSnapshot = {
selectedDownloadSource: ModelDownloadSource
catalog: ManagedModelCatalogView[]
installed: InstalledManagedModel[]
operations: ManagedModelOperation[]
}
“打开模型仓库”继续通过 Main 中按模型 ID 和当前来源解析的专用 IPC 完成,不让 Renderer 提交或接收任意仓库 URL。
10.2 模型来源覆盖
一个模型在某个来源可下载,当且仅当:
- 每个必需文件都有该来源的 Target。
- 每个 Target 使用固定 Revision,不使用
main、latest或可变 Tag。 - URL、仓库 URL 和重定向 Host 通过 Schema 和来源策略校验。
- 文件大小和 SHA-256 已独立验证。
只要缺少一个文件,该模型在该来源整体不可下载。
禁止:
detection.onnx ← ModelScope
recognition.onnx ← Hugging Face
dictionary.yml ← ModelScope
即使最终 SHA-256 正确,也不能在一次任务中混合来源,因为用户选择和审计语义将不再准确。
10.3 仓库入口
“打开模型仓库”使用当前所选下载源对应的 repositoryUrl:
- 当前来源有完整覆盖时,打开对应来源仓库。
- 当前来源无覆盖但另一个来源有覆盖时,按钮保持可读但禁用,并说明原因。
- 不自动打开另一个来源的仓库。
- 已安装模型可以显示“安装来源”,但打开仓库仍遵守当前选择,避免把历史来源当成全局值。
10.4 目录校验
启动时对整个模型目录执行静态校验:
- 模型 ID 唯一。
- 文件名和角色唯一。
- 每个来源 Target 的 URL 和 Revision 有效。
- 至少一个来源完整覆盖可下载模型。
- 非手动模型不得在两个来源都缺失。
repositoryUrl与下载 Target 属于同一声明来源。- 重定向 Host Allowlist 有界且不包含通配公网域名。
目录错误应在开发和测试阶段阻止启动或测试,不在运行时猜测修复。
10.5 当前已验证覆盖
当前目录只声明已经逐文件核对字节数和 SHA-256 的来源:
| 模型 | ModelScope | Hugging Face |
|---|---|---|
| PP-OCRv6 Tiny / Small / Medium | 可下载 | 可下载 |
| SenseVoiceSmall INT8 | 可下载 | 可下载 |
| Whisper Tiny | 可下载 | 可下载 |
| Paraformer 中英双语 / 中英日三语 | 暂不可下载 | 可下载 |
| Whisper Small / Medium | 暂不可下载 | 可下载 |
OCR 模型包的检测模型与识别模型来自同一下载源中的不同上游仓库。因此每个 Target 都必须 符合所声明来源的 Host 策略,而模型卡的主仓库入口必须对应到该来源至少一个实际文件 Target。语音模型当前每个模型包的 Target 与主仓库入口保持一致。
未找到可独立验证且字节完全一致的 Target 时,目录保留部分覆盖,不通过替换模型、 改写摘要或请求另一个来源来伪造双来源支持。
11. 下载任务
11.1 启动
Renderer 发起安装时提交:
type ManagedModelInstallInput = {
modelId: string
expectedDownloadSource: ModelDownloadSource
}
Main 必须:
- 从
ApplicationSettingsStore重新读取已保存来源。 - 验证其与
expectedDownloadSource一致。 - 在模型目录中解析该来源的完整模型包。
- 创建记录冻结来源的操作。
- 只使用该解析结果完成全部文件下载。
expectedDownloadSource 只用于检测 Renderer 快照过期,不能覆盖 Main 中的已保存设置。
如果两个值不一致:
- 不开始下载。
- 返回“模型下载源已变化,请刷新后重试”。
- 不使用 Renderer 提交的来源。
11.2 操作快照
扩展模型操作:
type ManagedModelOperation = {
modelId: string
kind: 'download' | 'import'
downloadSource?: ModelDownloadSource
phase: 'preparing' | 'transferring' | 'installing'
currentFile: string | null
completedBytes: number
totalBytes: number | null
}
- 下载操作必须包含
downloadSource。 - ZIP 导入不包含
downloadSource。 - UI 使用操作快照显示“正在从 ModelScope 下载”。
- 全局设置变化不修改现有操作对象的来源。
11.3 来源解析
语音与 OCR 管理器复用共享的 resolveModelDownloadPackage 契约函数:
resolveModelDownloadPackage(files, source)
解析结果是不可变快照,包含:
- 来源。
- 全部文件的 URL、大小、SHA-256。
- 允许的重定向 Host。
模型管理器不直接拼接 ModelScope 或 Hugging Face URL,也不自行尝试第二来源。
11.4 设置切换期间
来源设置更新和模型下载任务相互独立:
10:00 语音模型下载从 ModelScope 启动
10:01 用户把全局来源切换为 Hugging Face
10:01 当前语音下载继续使用 ModelScope
10:02 新 OCR 下载使用 Hugging Face
不能:
- 取消旧任务后从新来源重新开始。
- 让当前文件继续从旧来源、下一文件切到新来源。
- 把进度归零但不告诉用户。
- 在设置更新完成前启动使用草稿来源的下载。
12. 模型管理界面
12.1 来源状态
语音、OCR、向量和后续模型卡统一显示:
- 当前全局下载源。
- 当前模型在该来源是否可下载。
- 活动操作的冻结来源。
- 已安装模型的安装和校验状态。
未安装且来源可用:
可从 Hugging Face 下载 · 约 91 MB
[下载]
未安装且来源不可用:
Hugging Face 暂不提供此模型的完整已验证文件。
[前往通用设置]
不能显示可点击“下载”后才告诉用户缺少某个文件。
12.2 已安装模型
已安装模型不依赖当前下载源:
- 切换来源后继续可用。
- 不重新验证网络地址。
- 继续按安装 Manifest 中的大小和 SHA-256 验证。
- 不自动重新下载。
- 不改变当前模型选择。
当前安装 Manifest 保持来源无关,只记录运行和离线迁移需要的模型 ID、文件名、大小与 SHA-256。下载来源记录在活动操作快照中,尚未持久化到安装 Manifest。未来如需持久化 审计来源,可以新增:
type InstalledModelProvenance = {
installKind: 'download' | 'archive' | 'local-directory'
downloadSource?: ModelDownloadSource
catalogDigest: string
}
该扩展必须向后兼容;不能根据文件路径、模型 ID 或当前全局设置猜测旧安装的来源。
12.3 ZIP 导入与导出
ZIP 是来源无关的离线迁移方式:
- 导入始终按模型 ID、文件名、大小和 SHA-256 验证。
- 当前下载源不参与导入兼容判断。
- 从 ModelScope 下载的模型可以在选择 Hugging Face 的设备上导入。
- 导出可以保留原安装来源作为审计元数据,但不能限制另一设备导入。
- 导入失败不触发网络下载。
12.4 文案
所有硬编码文案改为来源感知:
正在从 {{source}} 下载打开 {{source}} 模型仓库当前下载源暂不提供此模型前往通用设置更换下载源
不再写死:
请从 ModelScope 下载打开 ModelScope正在从 ModelScope 下载
13. 不静默切换契约
13.1 禁止行为
以下行为全部禁止:
- ModelScope 网络失败后请求 Hugging Face。
- Hugging Face 返回 404 后请求 ModelScope。
- 当前来源缺少一个文件时从另一个来源补齐。
- 当前来源速度慢时自动测速并切换。
- 使用环境变量覆盖 UI 已保存来源。
- 为某一种模型单独保留隐藏的“优先来源”。
- 同一模型管理器使用与全局设置不同的默认值。
- 把 CDN 重定向误表示为切换到另一个模型下载源。
13.2 允许的同来源恢复
以下恢复可以自动执行:
- 同一固定 Target 的有界网络重试。
- 同一来源声明的固定镜像 Endpoint。
- 同一来源明确允许的 CDN 重定向。
.partial暂存文件内的安全续传,前提是服务支持且完整文件最终通过 SHA-256。
这些恢复必须保持:
- 同一用户选择来源。
- 同一模型 ID 和 Revision。
- 同一预期大小和 SHA-256。
- 同一冻结下载任务。
13.3 用户主动切换
失败状态提供:
- 重试当前来源。
- 前往“平台功能 → 通用设置”。
- 取消。
用户切换来源后需要重新点击下载。不能在设置保存后自动恢复上一失败任务。
14. 网络与供应链安全
14.1 URL
- 只允许 HTTPS 下载;开发测试 Fixture 可以注入受控 Transport。
- URL 必须来自源码内置目录,不接受 Renderer 自由输入。
- 禁止 URL 中的用户名和密码。
- Fragment 在目录校验时拒绝。
- 查询参数不写入日志和错误文案。
- 模型卡只显示来源名称和仓库,不显示带签名的最终 CDN URL。
14.2 重定向
ModelScope 和 Hugging Face 都可能跳转到文件存储或 CDN。每个来源 Adapter 维护明确的
允许 Host,或者由固定 Target 提供有界 redirectHosts:
- 重定向次数有上限。
- 每一步重新校验 HTTPS 和 Host。
- 不允许跳转到另一下载源的站点。
- 不允许通配任意公网 Host。
- 最终内容仍按固定大小和 SHA-256 验证。
重定向到已声明 CDN 属于同一下载源内部传输,不构成下载源切换。UI 继续显示用户选择的 逻辑来源。
14.3 凭据和隐私
- 两个公开模型源首期不要求用户凭据。
- 不把 LLM、Embedding、Runtime 或渠道 API Key 附加到模型下载请求。
- 不继承浏览器 Cookie。
- 不上传已安装模型清单、知识库、对话或设备文件。
- 常规 User-Agent 可以包含应用名称和版本,不包含用户 ID 或机器标识。
- 下载错误只记录来源、模型 ID、文件角色、HTTP 状态和有界网络分类。
14.4 校验
无论来源如何,安装成功必须满足:
- Content-Length 未超过上限。
- 实际完整字节数匹配目录。
- SHA-256 匹配目录。
- 所有必需文件存在且角色匹配。
- 安装 Manifest 完整。
- 正式目录通过原子替换创建。
来源可信不替代文件校验,HTTPS 成功也不证明模型工件正确。
15. 与其他设置和能力的关系
15.1 检查更新源
“模型下载源”与“检查更新源”是两个独立设置:
| 设置 | 位置 | 作用 |
|---|---|---|
| 模型下载源 | 平台功能 → 通用设置 | GoodBuddy 托管本地模型 |
| 检查更新源 | 关于与更新 | GoodBuddy 版本检查和下载页 |
切换任一设置不得修改另一个设置。用户选择 ModelScope 不表示应用更新使用镜像节点;选择 Hugging Face 也不表示应用更新使用 GitHub。
15.2 Ollama
Ollama 的模型下载由用户和 Ollama 管理。GoodBuddy 的模型下载源不:
- 改写 Ollama Registry。
- 影响
ollama pull。 - 安装或更新 Ollama 模型。
- 改变 Ollama Endpoint。
15.3 云端模型
云端 LLM、Embedding、Rerank、语音和图像 Provider 不下载本地权重,因此不受该设置 影响。
15.4 模型运行
模型下载源只决定获取规范工件的位置,不进入:
- 推理配置。
- 向量 Provider Fingerprint。
- 语音识别模型选择。
- OCR 模型选择。
- 模型质量或速度标签。
只要最终模型文件 SHA-256 相同,从 ModelScope 或 Hugging Face 安装后运行语义必须完全 一致。
16. 失败与恢复
| 场景 | 行为 |
|---|---|
| 读取设置失败 | 显示平台功能阻塞错误,不猜测或写入来源 |
| 保存来源失败 | 保留上一个确认值,显示就地错误 |
| 当前来源无完整模型包 | 下载按钮禁用,显示前往通用设置 |
| 当前来源网络不可用 | 当前下载失败或允许同来源重试,不请求另一来源 |
| 当前来源返回 404 | 显示来源缺少固定文件,不请求另一来源 |
| 重定向 Host 不受信任 | 立即失败并删除本次暂存 |
| 文件大小不匹配 | 立即失败并删除本次暂存 |
| SHA-256 不匹配 | 立即失败并删除本次暂存 |
| 设置在下载期间变化 | 当前任务继续冻结来源,新任务使用新来源 |
| Renderer 来源快照过期 | 拒绝启动,要求刷新 |
| ZIP 导入期间切换来源 | 导入不受影响 |
| 已安装模型缺少来源元数据 | 继续可用,显示安装来源未知 |
| 应用关闭 | 取消下载并清理本次暂存 |
错误必须包含可执行下一步,但不能默认执行来源切换。
17. 测试
17.1 设置契约
- 接受
modelscope和hugging-face。 - 拒绝未知来源、空值和多余字段。
- Partial 更新不覆盖魔法笔记和更新源。
- 新安装默认 ModelScope。
- 版本 1 至 6 全部迁移为 ModelScope。
- 更高未知版本拒绝,不覆盖文件。
- 并发设置更新保持完整 JSON。
17.2 平台功能界面
- 默认选中“通用设置”Tab。
- Tab 使用
tablist、tab、tabpanel和aria-selected。 - 方向键、Home、End 和焦点恢复正确。
- 切换到魔法笔记不改变任何设置。
- Radio Group 具有持久 Label。
- 选择来源时保存一次,保存期间防止重复提交。
- 保存失败恢复原值并保留可重试错误。
- 窄窗口不隐藏页签或来源名称。
17.3 目录
对语音、OCR 和向量目录分别验证:
- 两个来源解析到相同文件名、大小和 SHA-256。
- 缺少任一 Target 时模型在该来源不可下载。
- 不允许一次任务混合来源。
- 可变 Revision 被拒绝。
- 仓库 URL、下载 URL 和重定向 Host 符合来源策略。
- 重复模型、文件和角色被拒绝。
17.4 下载管理器
使用注入的受控 Fetch 验证:
- ModelScope 选择只请求 ModelScope Target。
- Hugging Face 选择只请求 Hugging Face Target。
- 一个来源失败时另一个来源零请求。
- 设置中途变化不改变活动任务。
- 新任务使用最新已保存来源。
- Renderer 提交过期来源时零网络请求。
- 两个来源的安装 Manifest 文件摘要相同。
- 取消、关闭、错误和摘要不匹配不留下正式模型目录。
- 快照准备会清理名称同时匹配受管模型 ID 与安装 UUID 的陈旧
.install-*目录,以及受管文件名对应的孤立.partial文件;已安装模型、 非目录条目和用户自建文件不参与清理。
17.5 回归
- 已安装语音模型继续可选择和转写。
- 已安装 OCR 模型继续可解析。
- ZIP 导入导出在任一来源设置下可用。
- 魔法笔记入口、评论方式和评论形式保持一致。
- 检查更新源行为不变。
- 文档、向量和语音推理不因下载源变化而改变。
18. 实施阶段
阶段 1:应用设置和页签(已完成)
- 新增
ModelDownloadSourceSchema 和默认值。 - 将应用设置版本升级并迁移旧版本。
- 在“平台功能”中增加
PageTabs。 - 增加“通用设置”与模型下载源 Radio Group。
- 把现有魔法笔记卡移动到“魔法笔记”面板。
阶段 2:共享目录和来源解析(已完成)
- 定义来源无关的规范工件与分来源 Target。
- 实现共享
ModelDownloadTargetResolver。 - 为语音和 OCR 目录补充 ModelScope 与 Hugging Face 固定 Target。
- 静态验证两个来源的文件大小和 SHA-256。
- 更正来源感知文案和仓库入口。
阶段 3:模型管理器(已完成)
- 扩展安装输入和操作快照。
- 让语音与 OCR 下载任务冻结应用设置来源。
- 保持 ZIP、删除、校验和模型选择行为不变。
- 接入来源 Host 和重定向策略。
阶段 4:后续模型
- 应用托管文本向量模型接入同一解析器。
- 实时语音的 ASR、TTS、VAD 模型目录接入同一解析器。
- 其他本地模型只有满足统一目录契约后才能使用全局下载源。
19. 验收标准
19.1 产品
- “平台功能”显示“通用设置”和“魔法笔记”两个可访问页签。
- 默认打开“通用设置”。
- “通用设置”提供 ModelScope 和 Hugging Face,默认 ModelScope。
- 来源选择说明影响范围,并明确已安装模型、ZIP、Ollama 和应用更新不受影响。
- 切换来源不会下载、删除、切换或重新验证任何已安装模型。
- 当前来源没有模型时,在点击下载前即可看到不可用原因。
- 下载失败不会自动请求另一来源。
- 魔法笔记的现有设置和入口行为保持不变。
19.2 数据与下载
- 旧应用设置完整迁移,并得到 ModelScope 默认值。
- 每个双来源模型的规范文件名、大小和 SHA-256 相同。
- 单次下载任务只使用一个冻结来源。
- 活动下载显示实际冻结来源。
- 已安装 Manifest 和 ZIP 兼容性不依赖来源。
- ModelScope 和 Hugging Face 分别通过固定 Target、重定向、取消和摘要校验测试。
19.3 安全和无障碍
- Renderer 不能提交任意下载 URL。
- Main 重新读取设置并校验 Renderer 的预期来源。
- 重定向不能跨到未声明来源或任意公网 Host。
- 下载请求不携带模型 Provider、Runtime 或渠道密钥。
- 页签、Radio、错误和进度可由键盘及屏幕阅读器操作。
- 浅色、深色和窄窗口均持续显示当前来源和活动下载来源。
19.4 工程验证
源代码实施后必须通过:
npm test
npm run typecheck
npm run lint
npm run build
模型文件真实性和双来源一致性使用门控目录验证命令,不在普通测试中下载模型权重。只有 显式授权的维护流程可以访问外部模型仓库并更新固定字节数和 SHA-256。