大家好,我是《.NET骚操作》的作者周杰。今天和大家聊一个有趣的话题。

Sdcb Chats 是一款我开发的、功能强大且易于部署的开源 ChatGPT 及大语言模型前端项目,旨在帮助用户轻松接入、管理和使用各类主流大语言模型。

在我们深入技术细节之前,先快速了解一下 Sdcb Chats 的核心特性:

  • 🔍 「广泛的模型支持」:兼容 18+ 主流大语言模型(含 Azure OpenAI/Ollama 等)。

  • 🗄️ 「灵活的数据库选项」:支持 SQLite、SQL Server 和 PostgreSQL。

  • 🚢 「多样的部署方式」:提供 Docker 镜像和二进制文件,实现秒级部署。

  • ⚙️ 「完善的管理功能」:内置多用户管理、Token 消耗统计与成本控制模块。

有朋友可能会问,Sdcb Chats 支持这么多模型提供商,那么它在技术上是如何实现对这些大语言模型的调用的呢?今天我们就来揭秘一下这背后的“骚操作”。

Chats 支持的模型提供商列表

首先,我们来看看 Sdcb Chats 目前已经支持的 18 个模型提供商。这个列表还在不断壮大中(注,不是写了这18个就代表只支持这18个,所有兼容协议的都可以调用,只是这18个经过了我的测试):

Id

Name

Host

0

Test

null

1

Azure OpenAI

https://<resource-name>.openai.azure.com/

2

腾讯混元

https://api.hunyuan.cloud.tencent.com/v1

3

01.ai

https://api.lingyiwanwu.com/v1

4

Moonshot

https://api.moonshot.cn/v1

5

OpenAI

https://api.openai.com/v1

6

文心千帆

https://qianfan.baidubce.com/v2

7

阿里通义

https://dashscope.aliyuncs.com/compatible-mode/v1

8

讯飞星火

https://spark-api-open.xf-yun.com/v1

9

智谱AI

https://open.bigmodel.cn/api/paas/v4/

10

DeepSeek

https://api.deepseek.com/v1

11

x.ai

https://api.x.ai/v1

12

Github Models

https://models.inference.ai.azure.com

13

Google AI

https://generativelanguage.googleapis.com/v1beta/openai/

14

Ollama

http://localhost:11434/v1

15

MiniMax

https://api.minimax.chat/v1

16

火山方舟

https://ark.cn-beijing.volces.com/api/v3/

17

硅基流动

https://api.siliconflow.cn/v1

18

OpenRouter

https://openrouter.ai/api/v1

看到这个列表,你可能会觉得要为每个提供商都写一套独立的调用逻辑,那将是多么繁重的工作。

但事实可能出乎你的意料。在 Sdcb Chats 中,上述列表里有 「17 个」 模型提供商,都是通过同一个 .NET SDK——「OpenAI .NET SDK」 来调用的。

现在,你可以猜猜,哪个是唯一的例外?

没错,答案就是 「Google Gemini AI」。对于它,我使用的是 Mscc.GenerativeAI(当前版本 2.6.5)这个库。而其他所有模型,都统一使用了 OpenAI-DotNet(当前版本 2.2.0)。

为什么一个 SDK 能通吃天下?

你可能会好奇,为什么 OpenAI 的 .NET SDK 能够调用腾讯混元、阿里通义、智谱 AI 等这么多家公司的模型?

答案很简单:「OpenAI API 已成为事实上的行业标准」

在大模型技术浪潮的初期,各大厂商纷纷推出自家的 API,格式五花八门。但随着 OpenAI 的影响力日益扩大,其简洁、强大的 API 设计逐渐赢得了开发者的青睐。为了降低开发者的接入成本、吸引更广泛的用户群体,国内外许多模型提供商都主动选择兼容 OpenAI 的 API 规范。

这就意味着,开发者可以使用与调用 OpenAI gpt-4 完全相同的代码结构,只需更换 API Key 和 Endpoint 地址,就能无缝切换到其他厂商的模型。这极大地简化了多模型应用的开发和维护。

有人可能会问:“那 Ollama 呢?它也支持吗?” 答案是肯定的。Ollama 从很早就开始兼容 OpenAI API 规范,并且支持得相当完善。因此,Sdcb Chats 同样使用 OpenAI .NET SDK 来与本地运行的 Ollama 模型进行交互。

为什么唯独 Google Gemini 是个例外?

既然 Google Gemini 也提供了 OpenAI 兼容的 API 端点,为什么我还要“特立独行”地选择 Mscc.GenerativeAI 这个库呢?

在 Sdcb Chats 的早期版本中,我确实是通过 OpenAI .NET SDK 来调用 Gemini 的。但转折点发生在今年年初,Google 发布了一个非常酷炫的模型:gemini-2.0-flash-preview-image-generation

这个模型有一个根本性的区别:「它支持模型原生的图像生成能力,而不是通过调用外部工具(Tool Calling)」。更强大的是,它支持在一次请求中,一边生成多段文字,一边创作多张图片,几乎可以一键生成一个图文并茂的插画故事(如图):

这个独特的、原生的图文混合生成功能,在标准的 OpenAI API 规范中并未定义,因此 OpenAI .NET SDK 自然也无法支持。为了解锁 Gemini 的全部潜力,我最终决定引入 Mscc.GenerativeAI 这个专门为 Google AI 定制的库。

踩坑实录:统一接口背后的“骚操作”

虽然 OpenAI API 成为标准,但“理想很丰满,现实很骨感”。在实际对接各家兼容 API 的过程中,依然会遇到各种不一致和需要特殊处理的“坑”。这正是“骚操作”大显身手的地方。

坑一:max_tokens vs max_response_tokens

最大的一个坑来自于 OpenAI .NET SDK 的一次更新。它将限制返回最大 Token 数的参数从 max_tokens 更改为了 max_response_tokens

我理解其设计意图:max_tokens 通常指「输入和输出的总 Token 数」,而 max_response_tokens 更精确,仅限制「模型输出的 Token 数」。这是一个更合理的设计。

然而,问题在于,这个新参数只有最新的 OpenAI 模型支持。绝大多数第三方提供商的兼容 API 尚未跟进,它们仍然只认旧的 max_tokens。如果直接使用新版 SDK,发过去的 max_response_tokens 参数会被它们忽略,导致无法限制输出长度。

我的解决方案是,在发送请求前,通过反射强行给请求体塞回一个 max_tokens 字段。这里我用到了 .NET 8 中新增的 UnsafeAccessor 特性来访问 SDK 内部的私有成员,代码非常简洁:

public static void SetMaxTokens(this ChatCompletionOptions options, int value)
{
    // 获取或创建用于存储额外序列化数据的字典
    IDictionary<string, BinaryData>? rawData = GetSerializedAdditionalRawData(options);
    if (rawData == null)
    {
        rawData = new Dictionary<string, BinaryData>();
        SetSerializedAdditionalRawData(options, rawData);
    }
    
    // 强行添加 max_tokens 字段
    rawData["max_tokens"] = BinaryData.FromObjectAsJson(value);
}

// 使用 UnsafeAccessor 访问内部的私有 get 方法
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_SerializedAdditionalRawData")]
privateexternstatic IDictionary<string, BinaryData>? GetSerializedAdditionalRawData(ChatCompletionOptions @this);

// 使用 UnsafeAccessor 访问内部的私有 set 方法(为了完整性展示,实际可能需要另一个Accessor)
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_SerializedAdditionalRawData")]
private extern static void SetSerializedAdditionalRawData(ChatCompletionOptions @this, IDictionary<string, BinaryData>? rawData);

坑二:五花八门的 Web 搜索开启方式

另一个不一致体现在如何开启模型的联网搜索功能。虽然都是布尔开关,但各家的参数名和结构却不同。

例如,「阿里通义千问」是这样开启的:

protected override void SetWebSearchEnabled(ChatCompletionOptions options, bool enabled)
{
    // 直接在附加数据中添加 enable_search 字段
    options.GetOrCreateSerializedAdditionalRawData()["enable_search"] = BinaryData.FromObjectAsJson(enabled);
}

而 「OpenRouter」 的方式则更复杂一些,需要通过一个 plugins 数组来指定:

protected override void SetWebSearchEnabled(ChatCompletionOptions options, bool enabled)
{
    if (enabled)
    {
        // 需要添加一个名为 "plugins" 的数组,并在其中指定 "web" 插件
        options.GetOrCreateSerializedAdditionalRawData()["plugins"] = BinaryData.FromObjectAsJson(new[]
        {
            new { id = "web" }
        });
    }
}

坑三:推理(Reasoning)字段的“标准之争”

大家知道,现代大模型在思考复杂问题时,通常会输出一步步的思考过程,这个过程一般通过一个特定的字段返回给前端,最典型的就是 reasoning_content。这个标准由 「DeepSeek-R1」 开创,并迅速被许多模型(包括国外的 grok 模型)跟进,俨然成为了一个新的事实标准。

但总有例外。比如 OpenRouter,它就“不走寻常路”,使用的是一个名为 reasoning 的字段来返回思考过程。

坑四:多模态图片输入的差异

在处理多模态模型(图文识别)时,有些模型提供商(比如 Google Gemini)不支持直接传入图片的 URL,而只接受 Base64 编码后的图片数据。这就要求我在发送请求前,需要判断目标模型,并对图片 URL 进行预处理,下载图片并转换为 Base64 字符串。

我之前的开源“三兄弟”呢?

熟悉我的朋友可能知道,我之前针对国内几家主流模型,写了三个独立的 .NET SDK 开源项目:

  • 「Sdcb.DashScope」: https://github.com/Sdcb/Sdcb.DashScope (用于调用阿里通义大模型)

  • 「Sdcb.WenXinQianFan」: https://github.com/Sdcb/Sdcb.WenXinQianFan (用于调用百度文心千帆大模型)

  • 「Sdcb.SparkDesk」: https://github.com/Sdcb/Sdcb.SparkDesk (用于调用讯飞星火大模型)

这些库都是基于它们各自的私有 API 实现的。在那个“百家争鸣”的时代,这是唯一的选择。然而,随着 OpenAI API 标准的确立,这些厂商纷纷提供了兼容接口。私有 API 逐渐被边缘化,甚至废弃。

因此,我现在也几乎不需要再维护这“三兄弟”了。一个统一的 OpenAI .NET SDK,加上一些“骚操作”的适配代码,就能搞定一切。

这件事让我深刻体会到「协议和标准」是多么重要!如果没有 OpenAI API 规范的统一,Sdcb Chats 不可能如此轻松地支持这么多模型。值得一提的是,DeepSeek 发布的 reasoning_content 字段,也让我们看到了一个新的事实标准正在中国厂商中形成。我们会持续关注,看未来会有多少模型提供商支持这个标准。

结语

通过巧妙地利用 OpenAI .NET SDK 和 Mscc.GenerativeAI,并针对各家 API 的细微差异进行适配,Sdcb Chats 成功地实现了对多种大语言模型的统一调用和管理。这种基于“事实标准”进行开发,再辅以“骚操作”解决兼容性问题的策略,不仅极大地提高了开发效率,也为用户提供了前所未有的灵活性。

未来,Sdcb Chats 将继续紧跟行业趋势,扩展其功能和支持的模型提供商,以满足不断变化的市场需求。

希望这篇文章能帮助你更好地理解 Sdcb Chats 的技术实现。如果你对这个项目感兴趣,欢迎访问我们的 GitHub 仓库,给我们一个 Star ⭐!

  • 「Sdcb Chats GitHub 仓库」: https://github.com/sdcb/chats

也欢迎加入我们的 QQ 交流群:498452653,您的反馈是我们持续改进的动力!

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐