Files
NotesAgentic/docs/development/模型提供商与模型发现开发说明.md
T

10 KiB
Raw Permalink Blame History

模型提供商、协议适配与模型路由开发说明

更新日期:2026-09-04。阶段 E 实现记录。本地小模型的实际安装与多模态队列属于阶段 F;本阶段保留并测试可注入的本地后端接口。

1. 设置与凭据

设置 → 模型提供商 → 新增 Provider 提供可搜索的 logo 预设网格,包含 DeepSeek、Kimi、阿里云百炼、智谱 GLM、火山方舟、硅基流动、百度千帆、腾讯混元、MiniMax、阶跃星辰,以及 OpenAI Chat / Responses、Anthropic 和 Ollama。图标打包到前端,使用时不请求第三方图片服务;来源和许可见前端 assets/providers 目录。

预设返回 preset_idlogo_idnameprovider_typebase_urlrequires_credentialdescriptioncapabilities。能力标签表示预设接入范围,不保证该账号的每个模型支持全部能力。厂商专用媒体协议、Coding Plan 和海外地域需要使用对应地址,不能仅凭厂商名称推断协议兼容。

预设和自定义服务都可以直接输入 API Key。每个新配置分配独立 Credential ID,避免同厂商多账号相互覆盖。明文只留在密码输入框和专用请求中,提交、失败、切换预设及关闭时清空;密钥不进入 Pinia、localStorage、Provider 配置响应或模型路由。

凭据继续使用独立的 PUT /api/credentials/{credential_id} 和 Fernet 开发存储。plugin.*mcp.* 是保留命名空间。桌面端阶段仍需要把主密钥管理迁移到 Stronghold。保存密钥与保存 Provider 是两个请求,Provider 保存失败时可能留下未引用的加密凭据,可通过凭据删除接口清理。

Provider 配置和 Credential ID 写入 SQLite provider_configs,重启后恢复。Mock 为内置 Provider,不能编辑或删除。PATCH 已支持变更 provider_type 并重新创建 Adapter;Base URL 限制为不带用户信息、查询或 fragment 的 HTTP(S) 地址。

2. 协议适配

支持的协议是 OpenAI Chat Completions、OpenAI-Compatible、OpenAI Responses、Anthropic Messages 和 Ollama。Agent、Chat、Skill 仍只依赖内部 ModelRequest / ModelEvent / ProviderTurn,不直接解释厂商协议。

Adapter 负责消息及 Tool 历史转换、增量文本、可用的 reasoning delta、工具参数片段、usage、终止与统一错误。外部错误正文不原样返回;HTTP 鉴权、限流、超时、无效数据、流中断分别映射为内部错误。取消继续传播并关闭上游连接,不触发第二次本地推理。

GET /api/providers/{provider_id}/models 用于发现模型。模型列表不等于每个模型的能力承诺;部分厂商或代理不提供 /models 时,允许直接手动输入模型 ID。连接测试验证模型发现接口,不代表每一种媒体模型已完成真实推理验收。

3. 三类模型路由

接口:

方法 路径 用途
GET /api/model-routing 读取配置和本地后端状态
PUT /api/model-routing 带版本更新三类模型绑定
POST /api/models/embeddings 文本向量,返回来源和回退原因
POST /api/media/transcriptions 附件转写作业
GET /api/media/transcriptions/{job_id} 获取转写作业
POST /api/media/speaker-matches 两个音频附件的声纹相似度

设置 → 索引与模型分别选择 Embedding、音频转文本和声纹匹配。三种绑定互相独立,可使用不同提供商、模型、密钥和 API 路径。

GET / PUT 响应:

{
  "config": {
    "version": 1,
    "embedding": {
      "provider_id": "provider_example",
      "model": "your-embedding-model",
      "endpoint": "/embeddings",
      "dimensions": null
    },
    "transcription": null,
    "speaker_matching": null
  },
  "local_backends": [
    {"capability": "embedding", "status": "placeholder", "message": "当前为 hash-v1 占位向量"},
    {"capability": "transcription", "status": "not_installed", "message": "阶段 F 接入"},
    {"capability": "speaker_matching", "status": "not_installed", "message": "阶段 F 接入"}
  ]
}

PUT body 只提交 config 的内容。version 为读取时的版本,成功递增;并发更新返回 MODEL_ROUTING_VERSION_CONFLICT。绑定为空表示使用本地后端。删除仍被路由引用的 Provider 返回 PROVIDER_IN_USE,须先解除绑定。

本阶段三类远程路由使用 openai_chat / openai_compatible 的 Bearer HTTP 配置,endpoint 只能是该提供商下的路径。Responses、Anthropic 和 Ollama 原生协议不冒充上述媒体协议;Ollama 用户需要另建兼容 HTTP 配置才能用于当前远程 Embedding 接口。

调用规则:无绑定 → 本地接口;有绑定 → API → 校验结果 → 失败或无效时调用本地接口。Provider 停用、密钥缺失、鉴权失败、限流、网络超时及无效结果均可回退;用户取消不会回退。附件不存在、大小非法等输入错误直接返回,不把用户输入错误当成模型故障。

4. Embedding 与索引一致性

请求使用 modelinputencoding_format: float;只有明确配置维度时才发送 dimensions。按最多 32 条分批请求,全部批次有效才使用 API 结果。校验返回数量、连续唯一 index、维度一致性、有限数值、非零范数,并 L2 归一化。维度可为 1–16384,不截断、补零或混用不同模型的向量。

返回 vectorssourcemodel_iddimensionsfallback_reason。远程空间 ID 由完整 API URL、模型和实际维度生成;即使维度相同,不同模型的空间也不同。

笔记索引始终保留现有 hash/sqlite-vec 本地基线,远程向量写入独立 routed_block_vectors 表。远程查询只搜索对应空间,并要求覆盖全部当前 Block。API 失败、索引缺失、不完整或损坏时使用完整本地索引。切换模型、URL、维度后应在设置中重建全部索引。旧空间与当前文本不会混合打分,删除笔记或重建索引会通过外键清理远程向量。

当前远程侧索引采用 SQLite JSON 向量和精确余弦扫描,复杂度 O(Block 数量 × 维度),适用于当前小型 Vault;后续大规模索引需替换为按空间隔离的 ANN。网络等待发生在数据库写事务之前,当前仍会增加保存或重建延迟,异步索引队列尚未接入。全量重建先在内存中准备全部向量,再使用一个 SQLite 事务更新元数据、FTS、本地与远程向量及任务关联;取消或失败只回滚索引事务,不再覆盖整库文件。准备阶段保留旧索引可查询,代价是内存同时容纳本次重建的向量。

OpenAI Compatible 流中,工具名称可能分片返回。适配器在本轮输出结束后发送完整工具名及已缓冲参数,避免把名称片段当作工具 ID;文本与推理内容仍逐片发送。

无 API 时使用的 HashEmbeddingProvider 是确定性特征哈希占位实现,不是已集成的小型语义模型。真实本地 Embedding 可实现既有 EmbeddingProvider 接口注入。

5. 音频与声纹边界

转写默认请求 /audio/transcriptionsmultipart 字段 model、可选 languagefile,响应必须包含非空字符串 text。已有纯文本附件和 Host 旁路 .txt 导入保留,来源标记 sidecar,不伪称 ASR。转写作业新增 sourcefallback_reason;回退失败的作业记录 LOCAL_MODEL_NOT_INSTALLED 等明确错误。作业目前同步执行、限量保存在内存中,不是持久化异步队列。

声纹匹配使用本项目自定义 HTTP 契约,默认 /audio/speaker-matchesmultipart 字段 modelfilereference_file;响应为 {"score": 0.85},score 必须为有限的 0–1 数值。公共入口只接受 attachment_idreference_attachment_id,不接收任意文件路径。此接口用于一对一声纹比对,不等同于 pyannote 说话人分离,也不声称任意国内厂商原生支持该路径。

媒体文件限制 1 字节至 25 MiB,API 响应限制 16 MiB,单次请求超时 30 秒。文件从后端受控附件目录读取,使用结束或取消时关闭句柄。

LocalSpeechBackend 提供 transcribematch 接口。阶段 E 默认 PendingSpeechBackend 明确报告未安装;阶段 F 接入 faster-whisper、pyannote.audio 及模型资源后替换。当前 diarization=true 明确返回失败作业 DIARIZATION_NOT_IMPLEMENTED,不会静默忽略。视频解码、TTS、视频生成及厂商专用异步媒体协议不在本次交付内。

6. 官方协议依据与验证

国内通用地址核对依据:阿里云百炼兼容接口百度千帆兼容接口腾讯混元兼容接口MiniMax 文本接口阶跃星辰通用与套餐地址区别火山方舟 API智谱开放接口。模型 ID 以账号实际开通列表为准,不写死“最新模型”。

流式事件依据:OpenAI Responses streamingAnthropic streaming。音频请求依据:SiliconFlow transcription

自动化验证使用虚构凭据、本地附件、httpx.MockTransport 和可注入本地模型,覆盖流式 Tool/Usage/取消、错误映射、回退、索引空间隔离、版本冲突、重启恢复和界面凭据行为。没有使用真实 API Key 或向厂商发送推理请求。审阅修复并同步主分支后验证:后端全量 447 项、前端 76 项测试通过,Vue/TypeScript 类型检查和生产构建通过,浅色/深色预设页面与路由保存经过浏览器检查,git diff --check 通过。后端仅保留既有 Starlette 测试客户端弃用提示,前端保留既有大 bundle 提示。

cd backend
uv run pytest -q -p no:cacheprovider
cd ../frontend
pnpm test
pnpm build