feat(provider): 完成阶段 E 多协议模型接入、国内预设与能力路由 #15

Merged
Kronecker merged 3 commits from feat/provider-routing into main 2026-09-04 07:35:50 +08:00
Owner

概述

本 PR 完成阶段 E 的模型提供商与能力路由开发。用户可以在设置页选择带 Logo 的国内常用提供商预设,填写独立 API Key,并分别配置聊天、Embedding、音频转写和声纹匹配模型。

同时完善流式协议处理、提供商配置持久化、远程向量索引隔离,以及 Benchmark 的实际模型记录。阶段 F 的本地音频模型保持接口预留。

提供商预设与配置界面

新增或完善以下预设:

  • OpenAI、OpenAI Responses、Anthropic
  • DeepSeek、Kimi、阿里云百炼、智谱 GLM
  • 火山方舟、硅基流动、百度千帆、腾讯混元
  • MiniMax、阶跃星辰、Ollama

前端采用带 Logo 的预设选择网格,支持预设与自定义连接配置:

  • 可编辑协议、名称、Base URL、默认聊天模型及启用状态。
  • 支持模型列表发现,也允许手动填写模型 ID。
  • API Key 使用密码输入框,由本地 AI Core 加密保存;提供商配置仅保存凭据引用。
  • 新增提供商或更新密钥时使用独立凭据引用,避免多个配置意外共用密钥。
  • 切换预设、协议或连接地址时解除原凭据关联,防止将旧密钥发送到新的服务地址。
  • 提供商配置持久化至 SQLite,重启后恢复。
  • Logo 资源随应用打包,包含来源说明与许可证。

模型协议与流式处理

协议 本次支持
OpenAI Chat / Compatible 普通响应、流式文本、工具调用、Usage
OpenAI Responses 原生请求与事件解析、工具历史转换、错误处理
Anthropic Messages 原生消息与内容块解析、工具结果转换、Usage
Ollama 普通响应、JSONL 流式响应、工具调用

协议层统一处理工具调用事件、可用的推理内容事件、Token 用量和结束状态。

内部带命名空间或超长的工具名会转换为协议允许的名称,并在响应中还原。对于工具名分片返回的情况,等待名称完整后再发送工具调用事件;文本内容仍逐片发送。

此外,完善超时、鉴权失败、限流、无效响应及截断流的处理。公开错误不直接回显上游响应正文,流式请求取消时会关闭底层迭代器和连接。

独立能力路由与本地回退

Embedding、音频转写和声纹匹配分别配置提供商、模型与相对端点,独立于默认聊天模型。

处理顺序:

  1. 未配置 API 时使用本地路径。
  2. 已配置 API 时优先请求远程服务。
  3. API 不可用或返回无效结果时回退到本地路径。
  4. 本地能力尚未安装时明确返回未安装状态及远程失败原因。

当前本地能力状态

能力 本地实现状态
Embedding 保留 hash-v1 确定性占位向量,尚未集成真实本地语义模型
音频转写 保留可注入接口,真实本地 ASR 在阶段 F 接入
声纹匹配 保留可注入接口,真实本地模型在阶段 F 接入

远程能力路由当前支持 OpenAI Chat / Compatible HTTP 配置。

声纹匹配采用本应用自定义 multipart 契约,服务端需实现相应接口,不能直接视为通用厂商标准端点。

接口与配置校验

接口 用途
GET /api/model-routing 获取能力绑定及本地后端状态
PUT /api/model-routing 保存能力绑定,使用版本号检测并发修改
POST /api/models/embeddings 获取 API 或本地 Embedding 结果
POST /api/media/speaker-matches 执行声纹匹配
POST /api/media/transcriptions 接入转写路由并返回来源及回退信息

配置和响应增加以下校验:

  • Base URL 必须使用 HTTP(S),不能内嵌用户名、密码、查询参数或片段。
  • 能力端点必须是所选提供商下的相对路径。
  • 模型路由版本冲突返回 409,避免覆盖其他窗口已保存的配置。
  • 被能力路由引用的提供商不能直接删除。
  • Embedding 响应检查数量、索引、维度、有限数值及非零范数。
  • 转写响应必须包含非空文本;声纹分数必须是有限的 0–1 数值。
  • 音频请求限制为 1 byte–25 MiB,远程响应读取设置大小上限。

远程向量索引与重建

保留完整的本地 Hash / sqlite-vec 索引,同时将远程向量写入独立的 routed_block_vectors 表。

远程向量空间由接口地址、端点、模型和实际维度共同区分。查询仅使用匹配空间,并要求覆盖全部当前 Block。

远程请求失败、索引缺失、不完整或损坏时,使用完整本地索引,不混合不同空间的向量评分。

全量重建调整为:

  1. 先完成笔记解析和全部向量计算。
  2. 网络请求结束后开启 SQLite 写事务。
  3. 在同一事务内更新元数据、FTS、本地与远程向量以及任务关联。
  4. 写入失败时由 SQLite 回滚,不再恢复整个数据库文件。

这解决了重建取消或失败时覆盖并发保存配置的问题。向量准备期间,旧索引仍可查询。

Benchmark 模型归属记录

此前 Benchmark 可能实际使用远程 Embedding,却在报告中记录本地 hash-v1 的模型和维度。本 PR 改为逐样本记录实际执行路径:

  • source:API、本地、未使用向量或未完成向量检索。
  • 实际模型空间 model_id、维度及本地模型版本。
  • API 或索引回退原因。
  • 请求时的路由版本、提供商引用、模型、相对端点和配置维度。
  • 已生成远程查询向量时尝试使用的模型空间。

记录同时出现在报告 casesCaseCompleted SSE 事件中,不包含 API Key 或凭据引用。

使用任务局部上下文隔离并发检索,避免不同评测相互覆盖记录。

运行快照中的 local_embedding 仅描述本地基线;实际模型归属以逐样本记录为准。

审阅问题与修复结果

问题 修复结果
取消索引重建导致已保存配置回退 移除整库文件恢复,改用索引单事务提交
流式工具名分片被当作完整名称发送 完整组装名称后发送工具调用事件,并验证名称映射还原
Benchmark 模型快照与实际检索不一致 按样本记录实际模型空间、维度和回退路径
与主分支存在文档冲突 已同步主分支并解决冲突

验证结果

  • 后端全量 452 项测试通过
  • 前端 76 项测试通过
  • 前端 TypeScript 类型检查通过
  • 前端生产构建通过
  • 最终复审相关 300 项测试通过
  • git diff --check 通过
  • 预设页面浅色、深色显示及模型路由保存完成浏览器检查

新增回归覆盖:

  • 重建取消与事务中途失败
  • 重建期间并发保存配置
  • 工具名称分片及名称映射还原
  • 远程模型实际归属
  • API 超时回退
  • 远程索引空间缺失
  • 并发评测记录隔离
  • 请求期间配置变化

测试使用模拟 API 和隔离数据目录,未使用真实 API Key 进行厂商接口联调。

当前限制与后续工作

  • 本地真实 Embedding、转写和声纹模型尚未集成,继续按阶段规划推进。
  • 说话人分离与声纹匹配是不同能力;当前不支持 diarization
  • 远程向量采用 SQLite JSON 存储和精确余弦扫描,适用于当前小型 Vault;大规模数据需要后续索引优化。
  • 向量计算仍增加保存和重建延迟;全量重建会在内存中准备全部向量,异步索引队列尚未接入。
  • 更换 Embedding 模型、接口或维度后需要重建全部索引。
  • Benchmark 允许样本间配置变化,汇总指标可能包含不同模型空间,比较实验时需检查逐样本记录。
  • 保留现有 Starlette 测试客户端弃用提示和前端大体积 bundle 提示。

合并建议

  • 分支:feat/provider-routing
  • 最新提交:a75d81a

已同步审阅时的最新主分支,本地与远程一致,工作区干净。历次审阅发现的问题均已修复,最终复审未发现新的合并阻塞问题,建议合并。

## 概述 本 PR 完成阶段 E 的模型提供商与能力路由开发。用户可以在设置页选择带 Logo 的国内常用提供商预设,填写独立 API Key,并分别配置聊天、Embedding、音频转写和声纹匹配模型。 同时完善流式协议处理、提供商配置持久化、远程向量索引隔离,以及 Benchmark 的实际模型记录。阶段 F 的本地音频模型保持接口预留。 ## 提供商预设与配置界面 新增或完善以下预设: - OpenAI、OpenAI Responses、Anthropic - DeepSeek、Kimi、阿里云百炼、智谱 GLM - 火山方舟、硅基流动、百度千帆、腾讯混元 - MiniMax、阶跃星辰、Ollama 前端采用带 Logo 的预设选择网格,支持预设与自定义连接配置: - 可编辑协议、名称、Base URL、默认聊天模型及启用状态。 - 支持模型列表发现,也允许手动填写模型 ID。 - API Key 使用密码输入框,由本地 AI Core 加密保存;提供商配置仅保存凭据引用。 - 新增提供商或更新密钥时使用独立凭据引用,避免多个配置意外共用密钥。 - 切换预设、协议或连接地址时解除原凭据关联,防止将旧密钥发送到新的服务地址。 - 提供商配置持久化至 SQLite,重启后恢复。 - Logo 资源随应用打包,包含来源说明与许可证。 ## 模型协议与流式处理 | 协议 | 本次支持 | | --- | --- | | OpenAI Chat / Compatible | 普通响应、流式文本、工具调用、Usage | | OpenAI Responses | 原生请求与事件解析、工具历史转换、错误处理 | | Anthropic Messages | 原生消息与内容块解析、工具结果转换、Usage | | Ollama | 普通响应、JSONL 流式响应、工具调用 | 协议层统一处理工具调用事件、可用的推理内容事件、Token 用量和结束状态。 内部带命名空间或超长的工具名会转换为协议允许的名称,并在响应中还原。对于工具名分片返回的情况,等待名称完整后再发送工具调用事件;文本内容仍逐片发送。 此外,完善超时、鉴权失败、限流、无效响应及截断流的处理。公开错误不直接回显上游响应正文,流式请求取消时会关闭底层迭代器和连接。 ## 独立能力路由与本地回退 Embedding、音频转写和声纹匹配分别配置提供商、模型与相对端点,独立于默认聊天模型。 处理顺序: 1. 未配置 API 时使用本地路径。 2. 已配置 API 时优先请求远程服务。 3. API 不可用或返回无效结果时回退到本地路径。 4. 本地能力尚未安装时明确返回未安装状态及远程失败原因。 ### 当前本地能力状态 | 能力 | 本地实现状态 | | --- | --- | | Embedding | 保留 `hash-v1` 确定性占位向量,尚未集成真实本地语义模型 | | 音频转写 | 保留可注入接口,真实本地 ASR 在阶段 F 接入 | | 声纹匹配 | 保留可注入接口,真实本地模型在阶段 F 接入 | 远程能力路由当前支持 OpenAI Chat / Compatible HTTP 配置。 声纹匹配采用本应用自定义 multipart 契约,服务端需实现相应接口,不能直接视为通用厂商标准端点。 ## 接口与配置校验 | 接口 | 用途 | | --- | --- | | `GET /api/model-routing` | 获取能力绑定及本地后端状态 | | `PUT /api/model-routing` | 保存能力绑定,使用版本号检测并发修改 | | `POST /api/models/embeddings` | 获取 API 或本地 Embedding 结果 | | `POST /api/media/speaker-matches` | 执行声纹匹配 | | `POST /api/media/transcriptions` | 接入转写路由并返回来源及回退信息 | 配置和响应增加以下校验: - Base URL 必须使用 HTTP(S),不能内嵌用户名、密码、查询参数或片段。 - 能力端点必须是所选提供商下的相对路径。 - 模型路由版本冲突返回 `409`,避免覆盖其他窗口已保存的配置。 - 被能力路由引用的提供商不能直接删除。 - Embedding 响应检查数量、索引、维度、有限数值及非零范数。 - 转写响应必须包含非空文本;声纹分数必须是有限的 `0–1` 数值。 - 音频请求限制为 `1 byte–25 MiB`,远程响应读取设置大小上限。 ## 远程向量索引与重建 保留完整的本地 Hash / sqlite-vec 索引,同时将远程向量写入独立的 `routed_block_vectors` 表。 远程向量空间由接口地址、端点、模型和实际维度共同区分。查询仅使用匹配空间,并要求覆盖全部当前 Block。 远程请求失败、索引缺失、不完整或损坏时,使用完整本地索引,不混合不同空间的向量评分。 全量重建调整为: 1. 先完成笔记解析和全部向量计算。 2. 网络请求结束后开启 SQLite 写事务。 3. 在同一事务内更新元数据、FTS、本地与远程向量以及任务关联。 4. 写入失败时由 SQLite 回滚,不再恢复整个数据库文件。 这解决了重建取消或失败时覆盖并发保存配置的问题。向量准备期间,旧索引仍可查询。 ## Benchmark 模型归属记录 此前 Benchmark 可能实际使用远程 Embedding,却在报告中记录本地 `hash-v1` 的模型和维度。本 PR 改为逐样本记录实际执行路径: - `source`:API、本地、未使用向量或未完成向量检索。 - 实际模型空间 `model_id`、维度及本地模型版本。 - API 或索引回退原因。 - 请求时的路由版本、提供商引用、模型、相对端点和配置维度。 - 已生成远程查询向量时尝试使用的模型空间。 记录同时出现在报告 `cases` 和 `CaseCompleted` SSE 事件中,不包含 API Key 或凭据引用。 使用任务局部上下文隔离并发检索,避免不同评测相互覆盖记录。 运行快照中的 `local_embedding` 仅描述本地基线;实际模型归属以逐样本记录为准。 ## 审阅问题与修复结果 | 问题 | 修复结果 | | --- | --- | | 取消索引重建导致已保存配置回退 | 移除整库文件恢复,改用索引单事务提交 | | 流式工具名分片被当作完整名称发送 | 完整组装名称后发送工具调用事件,并验证名称映射还原 | | Benchmark 模型快照与实际检索不一致 | 按样本记录实际模型空间、维度和回退路径 | | 与主分支存在文档冲突 | 已同步主分支并解决冲突 | ## 验证结果 - [x] 后端全量 **452 项测试通过** - [x] 前端 **76 项测试通过** - [x] 前端 TypeScript 类型检查通过 - [x] 前端生产构建通过 - [x] 最终复审相关 **300 项测试通过** - [x] `git diff --check` 通过 - [x] 预设页面浅色、深色显示及模型路由保存完成浏览器检查 新增回归覆盖: - 重建取消与事务中途失败 - 重建期间并发保存配置 - 工具名称分片及名称映射还原 - 远程模型实际归属 - API 超时回退 - 远程索引空间缺失 - 并发评测记录隔离 - 请求期间配置变化 测试使用模拟 API 和隔离数据目录,未使用真实 API Key 进行厂商接口联调。 ## 当前限制与后续工作 - 本地真实 Embedding、转写和声纹模型尚未集成,继续按阶段规划推进。 - 说话人分离与声纹匹配是不同能力;当前不支持 `diarization`。 - 远程向量采用 SQLite JSON 存储和精确余弦扫描,适用于当前小型 Vault;大规模数据需要后续索引优化。 - 向量计算仍增加保存和重建延迟;全量重建会在内存中准备全部向量,异步索引队列尚未接入。 - 更换 Embedding 模型、接口或维度后需要重建全部索引。 - Benchmark 允许样本间配置变化,汇总指标可能包含不同模型空间,比较实验时需检查逐样本记录。 - 保留现有 Starlette 测试客户端弃用提示和前端大体积 bundle 提示。 ## 合并建议 - 分支:`feat/provider-routing` - 最新提交:`a75d81a` 已同步审阅时的最新主分支,本地与远程一致,工作区干净。历次审阅发现的问题均已修复,最终复审未发现新的合并阻塞问题,建议合并。
Kronecker added 3 commits 2026-09-04 07:34:38 +08:00
Author
Owner

审阅通过,同意合并。

此前发现的索引重建回滚覆盖配置、流式工具名称分片及 Benchmark 模型归属记录问题均已修复。最终复审未发现新的合并阻塞问题。

验证结果:

  • 后端全量 452 项测试通过,最终复审相关 300 项测试通过。
  • 前端 76 项测试、类型检查及生产构建通过。
  • 已同步审阅时的最新主分支,无合并冲突。

本次审阅基于提交 a75d81a。真实厂商 API 联调及本地小模型集成按后续阶段继续推进。

审阅通过,同意合并。 此前发现的索引重建回滚覆盖配置、流式工具名称分片及 Benchmark 模型归属记录问题均已修复。最终复审未发现新的合并阻塞问题。 验证结果: - 后端全量 452 项测试通过,最终复审相关 300 项测试通过。 - 前端 76 项测试、类型检查及生产构建通过。 - 已同步审阅时的最新主分支,无合并冲突。 本次审阅基于提交 `a75d81a`。真实厂商 API 联调及本地小模型集成按后续阶段继续推进。
Kronecker merged commit 2e496462a9 into main 2026-09-04 07:35:50 +08:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Kronecker/NotesAgentic#15