ds2api 实现原理解析:让 DeepSeek 网页学会说 OpenAI 普通话

ds2api 实现原理解析:让 DeepSeek 网页学会说 OpenAI 普通话

要一句话说清 ds2api 是干什么的,其实很简单:

它是一个“万能翻译官”,让只会刷网页的 DeepSeek,学会了说 OpenAI、Claude、Gemini 三家的普通话。

你手里各种 AI 软件,比如 Cherry Studio、NextChat、各种编程助手、Agent 框架,它们只认 OpenAI 那套接口。但 DeepSeek 最强、最便宜、更新最快的模型,偏偏藏在 chat.deepseek.com 这个网页后面。你不能直接插上去用。

ds2api 就在中间开了家“翻译餐厅”:前面迎接所有客人,后厨只跟 DeepSeek 网页打交道。

1. 它解决了一个很现实的别扭事

想象一下,你有个特别聪明的朋友,什么都会,但他有个怪癖:只愿意在微信里用语音跟你聊天,不接电话,不回邮件。

而你公司所有的办公系统,都只会发邮件。你想让他帮你干活,怎么办?你只能雇一个人,专门坐在中间:把邮件念成大白话发微信给他,再把他的微信语音整理成正式邮件发回公司。

ds2api 就是这个中间人。

DeepSeek 官方 API 和网页版,其实是两套东西。网页版往往模型更新、能力更全,还有思考过程、联网搜索。但它没有给开发者用的标准插口。而全世界的 AI 工具,默认插口都是 OpenAI 定的:POST /v1/chat/completions 发过去,SSE 一行行流回来。

ds2api 把这两头接起来了。你在代码里写 model: gpt-4omodel: claude-sonnet,它都笑着收下,转头翻译成 deepseek-v4-flash / pro,然后去网页端帮你问完,再包装成你想要的样子还给你。

这就是为什么它 config/models.go 里有一张巨大的“别名表”:gpt-5 对应 flash,o1gpt-5-pro 对应 pro,gemini-pro-vision 对应 vision。就像万能充电头,不管你拿的是美标、欧标插头,到它这儿都能插上。

2. 整家店的结构:前厅、翻译官、后厨

你可以把 ds2api 想象成一家餐厅。

前厅是 internal/server/router.go 基于 Go 的 chi 路由。迎宾员站在门口,不管你是 OpenAI 的客人、Claude 的客人还是 Gemini 的客人,都领进来。还顺手管 CORS 跨域、RequestID、日志、panic 恢复。健康检查 /healthz 就像门口挂的“正常营业”牌子。

翻译官是 internal/promptcompat,全店灵魂。 所有点菜单到了这里,都被压成一张后厨能看懂的小纸条。

后厨是 internal/deepseek/client 真正去跟 DeepSeek 网页后厨打交道的地方:登录、占个桌子(建会话)、买门票(过 PoW 验证)、下单(发 completion)、端菜(收 SSE)。

装盘工是 internal/format + internal/assistantturn 后厨端出来是一大盆乱炖,装盘工负责分成:这是思考过程,这是正文,这是工具调用,这是引用链接,再按你点的菜系摆盘。

这家店有个铁规矩:翻译官只做一次,所有菜系共享。 不允许 OpenAI 后厨和 Claude 后厨各炒一锅。先归一,再渲染。这保证了不管你从哪个门进来,吃到的味道是一致的。

3. 门口保安:两种客人两种进法

internal/auth/request.go 就像小区保安,认两种人。

第一种,自带钥匙的散客:直通模式。 你请求头里带的 Bearer Token 本来就是 DeepSeek 的 token,保安直接放你过去,不占小区资源。适合你自己有号,只想借个翻译。

第二种,拿会员卡的住户:托管模式。 你带的 Token 是 ds2api 自己发的 API Key。保安就会去后面的“司机池”给你叫个司机,帮你全程代驾。你甚至可以用 X-Ds2-Target-Account 指定司机。用完司机要归还。

个人玩家和工作室多账号玩法,一套代码全兼容。

4. 司机池:滴滴代驾那套玩法

internal/account 就是一个滴滴调度中心。

它手里攥着几十个 DeepSeek 账号。每个账号默认最多同时接 2 单,全局还有总并发上限。新请求来了,就从队列里轮着派单,有 token 的优先派。

Acquire 是叫车,Release 是结单,waiters 是排队的人。如果司机全忙,就让客人在门口排队等,而不是直接赶走。

更狠的是 SwitchAccount:如果这个司机开到一半被交警拦了(上游 429 限流、token 失效),调度中心立刻给你换个司机,重下一单。对用户来说只是慢了一点,完全无感。

5. 口音伪装:TLS 指纹,学得像 Safari

DeepSeek 会看你“长得像不像自己人”。浏览器和 Go 程序发 HTTPS 请求时,握手阶段的“口音”是不一样的,叫 TLS 指纹。你用 Go 原生一开口,人家一听机器人,直接拦掉。

internal/deepseek/transport 干的就是“学口音”。它用 utls 库硬是把握手伪装成 Safari,还强制只说 http/1.1,禁用 HTTP/2。因为真正的 DeepSeek 客户端就是这个腔调。

这就像去批发市场拿货,你必须穿得像本地餐馆老板,说本地话,老板才给你批发价。

6. 买门票:PoW,那道变态数学题

DeepSeek 为了防机器人,下单前还要你做数学题,叫 PoW,pow/ 目录就是解题的。

你先去问要啥题,对方给你 challenge 哈希、saltdifficulty(约 14 万)、过期时间。你要在 0 到 14 万之间一个个试,找到 hash(salt_expire_ + 数字) == challenge 的幸运数字。

更变态的是哈希是魔改版 DeepSeekHashV1:Keccak 大转盘本来转 24 轮,它偏偏跳过第 0 轮,只转 1~23 轮。拿现成库算永远对不上。

所以作者用纯 Go 手搓了 Keccak,还做了优化:公共前缀提前揉进状态,循环内零分配,几十毫秒出答案。然后把答案 base64(json) 塞进 x-ds-pow-response 头,这才算买到门票。

7. 把西餐菜单翻译成大白话:PromptCompat

OpenAI 的请求是结构化的乐高:system、user、assistant、tool 分得清清楚楚。DeepSeek 网页版只认一根面条:prompt: string 大文本框。

promptcompat 就是把乐高压成面条还不丢味道的大厨:先归一成 StandardRequest,再把工具说明书贴进 system,把聊天记录熬成一锅粥,太长就拆成附件 ref_file_ids,最后看模型名定思考和搜索开关。

8. 听懂碎碎念:SSE 电报碎片

DeepSeek 回来的不是完整句子,而是一堆电报碎片。internal/sse 就是电报员:按路径分拣思考与正文,扔掉 token_usage 等噪音,看到 </think> 强行切换,还要收集搜索引用、处理风控截断。internal/stream/engine.go 则是传送带,带心跳和超时,保证长思考也不断线。

9. 脑内小剧场 vs 嘴上说的话:AssistantTurn

internal/assistantturn/turn.go 像心理医生:RawThinking 是脑内小剧场,RawText 是嘴上说的话。还要查工具调用、用 tiktoken 本地估算 token 用量、判定空输出原因(限流 429 / 风控 400 / 不可用 503),直接驱动重试策略。

10. 假装会用工具:最像魔术的一招

网页本身不会调函数,只能演。翻译官把调用格式写进 prompt,模型配合输出 XML,internal/toolcall 在后台盯着,一看到就拉上幕布不让用户看到,转头用 OpenAI 格式喊工具登场。还能容错修 JSON、流式防泄漏,Go 和 Node 各养了一个场务保证一致。

11. 回译三国语言 + 售后重做

internal/format 按客人国籍上菜:OpenAI、Claude、Gemini 各摆各的盘,但后厨同一锅。internal/completionruntime 是大堂经理:空输出就重做,429 就换厨师(切账号),流式断了就续上,保证成功率莫名其妙地高。

12. 前台大屏与摆摊车

admin + webui/(React)是酒店前台大屏:账号管理、测活、热更新、排队查看、抓包回放。支持 Docker、Zeabur、Vercel 部署,api/chat-stream.js 给无服务器环境留了 Node 小桥。

结语:桥很精致,但河是别人的

ds2api 是在别人家河上造的桥:这头是全世界统一的标准,那头是随时会变的私协议。它靠伪装混进去,靠解题买门票,靠 prompt 骗模型,靠拼图还原思考,靠账号池保 SLA。厉害,也脆弱——上游一改就得连夜修。所以仓库里存了大量真实抓包和法医级 SSE 报告。

如果你只是想用,记住三件事:配好账号池,选对模型别名,开好重试。剩下的,交给这位翻译官吧。

项目地址:git@github.com:yuanguangshan/ds2api.git(fork 自 CJackHwang/ds2api)