Skip to content

这是什么

ai-profile 是一个 Rust crate,为桌面应用提供「让用户配置 AI 模型服务」所需的那一层: 服务商预置清单、跨应用配置互通协议、端点拼接、连通性验证与结构化错误。

不是 LLM SDK —— 它不替你发起对话请求,不做流式解析,不管上下文。 它管的是在用户点下「开始对话」之前的所有事:选哪家、地址怎么拼、密钥对不对、模型名存不存在。

解决的问题

假设你有四个桌面应用,每个都要一个「模型服务」设置页。每个页面都需要:

  • 一份服务商下拉(DeepSeek、智谱、通义、Kimi、OpenAI、Claude……)
  • 每家的 base_url 预填值 —— 而各家的版本段并不统一
  • 每家的候选模型 id —— 而这些 id 每月都在变
  • 一个「测试连接」按钮,以及失败时说得清楚的提示

于是同一套逻辑被写了四遍。问题不在于重复本身,而在于它们会各自漂移

DeepSeek 在 2026-07-24 下线了 deepseek-chat / deepseek-reasoner 两个老别名 (V4 发布时公告的三个月过渡期到期)。四个应用里只有一个及时改了预置, 另外三个的用户打开下拉、选中默认值、点击测试 —— 报错。 这类问题没有任何编译期信号,只能等用户报障。

把这部分收进一个库,漏更新至少变成全漏而不是「有的应用对、有的错」, 而修一次就全部生效。

边界:什么进、什么不进

内容归属
预置清单(base_url / 模型 id / 协议 / 专有字段 / 申请页)本 crate
ai.profile 跨应用配置交换协议本 crate
端点拼接、模型清单清洗本 crate
连通性验证与结构化错误本 crate
密钥存储与加密留给应用
数据库、CRUD、激活态管理留给应用
对话 / 生图 / 语音的实际调用留给应用或专门的 SDK

为什么密钥存储不进来

四个应用的密钥存储方式差异极大:有的落系统密钥环,有的进 SQLCipher 加密金库, 有的就放在用户数据目录的明文配置里(本地自建服务的场景)。

这些差异不是历史包袱,而是各自合理的取舍 —— 强行统一只会让每个应用都得绕过它。 所以本 crate 一行存储代码都没有:验证时你把密钥传进来,用完即弃。

能力(kind)

kind状态执行模型
chat第一版可用同步 / 流式
image规划中同步 提交+轮询
video规划中必然提交+轮询(各家轮询协议不同)
tts规划中同步,返回字节流

按 feature 开启,不用的不编进二进制 —— 只做对话的应用不会为生图能力付出任何体积。 详见安装与 feature

三条设计约定

这三条贯穿全库,理解它们能省下很多「为什么是这样」的疑问。

1. base_url 原样使用,不推断版本段

各家的版本段并不统一:多数是 /v1,智谱是 /v4,Gemini 是 /v1beta/openai(还不在末尾)。

自动补全需要引入转义机制才能表达例外,而推断错的代价是隐性的: 用户照着官方文档填对了地址,库悄悄给他加了一段,他只看到 404, 而且会先怀疑是自己填错了。

所以本 crate 把 base_url 原样当作前缀,只负责拼上端点后缀。 详见端点与模型清单

2. 默认模型选「够用档」而非「最强档」

用户点开预置是奔着"能用"来的,不是奔着"最贵"。把旗舰型号塞进默认值, 等于替他做了一个他没同意的花钱决定。最强档留在候选清单里随时可选。

3. 错误必须结构化

Err(String) 会让调用方只能显示一行红字。而 NotFound { suggested_url } 才能让界面给出「一键改用这个地址」的按钮。

六个错误变体各自对应一个具体的界面动作,详见错误码对照

下一步

MIT 协议开源 · 文档同样欢迎 PR