Qwen Chat UI vs DeepSeek Proxy 对比分析
一、项目定位对比
| 维度 | DeepSeek Proxy | Qwen Chat UI |
|------|---------------|--------------|
| 目标服务 | chat.deepseek.com | chat.qwen.ai |
| 认证方式 | Bearer Token + PoW + HMAC 签名 | Cookie + bx-ua + bx-umidtoken |
| 反爬难度 | 高(PoW 求解 + 签名计算) | 中(TLS 指纹 + WAF 滑块) |
| 多账号 | ✅ 主备池 + 故障切换 | ❌ 单账号 |
| 会话管理 | 服务端维护 session_id 链 | 依赖客户端传入 chat_id |
| 代码量 | ~2322 行 Go + 746 行 HTML | ~1451 行 Go |
| 外部依赖 | 零(纯标准库) | fsnotify + uTLS(2个) |
核心差异: DeepSeek 的逆向难度显著更高(需要破解 PoW 算法 + HMAC 签名),而 Qwen 的难点在于 TLS 指纹伪装和 WAF 绕过。
二、架构设计对比
DeepSeek:单包扁平结构
package main
├── main.go (1265行 - 路由+业务+配置+OpenAI适配)
├── account.go (189行 - 账号池)
├── pow.go (294行 - PoW算法)
├── sse.go (302行 - SSE解析)
└── wechat_push.go (228行 - 告警)
所有代码在 package main 中,通过文件名划分职责。简单直接,但 main.go 承载过重。
Qwen Chat UI:多包分层结构
main.go (107行 - 入口+优雅关闭)
├── config/
│ ├── config.go (355行 - 配置+热加载+Cookie解析)
│ └── config_test.go (142行)
├── handlers/
│ ├── handlers.go (538行 - HTTP处理器)
│ ├── handlers_test.go (53行)
│ └── middleware.go (73行 - Recovery/Logging/CORS)
└── proxy/
├── client.go (142行 - HTTP客户端+uTLS)
└── keepalive.go (41行 - 心跳保活)
评价: Qwen 的分包明显更工程化。config、handlers、proxy 三层各司其职,main.go 只有 107 行——这正是我之前建议 DeepSeek 做的拆分。Qwen 项目看起来是吸取了 DeepSeek 的经验后重新设计的。
三、关键技术差异
3.1 反爬对抗
DeepSeek — 纯算法破解:
// 自实现 Keccak-f[1600] 的 23 轮变体
func keccakF23(s *[25]uint64) { ... }
// 暴力搜索 nonce
func SolvePow(ctx, challengeHex, salt, expireAt, difficulty) (int64, error) { ... }
// HMAC 签名
func generateHifLeim(token string, answer int) string { ... }
Qwen — TLS 指纹伪装:
// 使用 uTLS 模拟 Chrome TLS 握手
conn := utls.UClient(tcpConn, &utls.Config{
ServerName: host,
}, utls.HelloChrome_Auto)
DeepSeek 需要"算出正确答案",Qwen 需要"看起来像真浏览器"。两种路线,两种难度。
3.2 配置热加载
DeepSeek — mtime 轮询:
func reloadConfigIfNeeded() {
fi, _ := os.Stat("config.json")
if fi.ModTime().Equal(lastMod) { return }
// 重新加载...
}
每个请求入口检查一次,简单但有最大 1 个请求的延迟。
Qwen — fsnotify 文件监听 + SIGHUP:
func WatchEnvFiles() {
watcher, _ := fsnotify.NewWatcher()
watcher.Add(dir)
// 300ms 防抖
// 支持 kill -HUP <pid> 强制重载
}
Qwen 的方案更优雅:实时监听 + 防抖 + 信号触发,三重保障。但引入了 fsnotify 依赖。
3.3 多轮对话
DeepSeek — 服务端会话缓存(复杂但完整):
-
双层指纹(全量 SHA256 + 尾部锚点)
-
每账号独立 64 条 LRU 缓存
-
自动创建 session、维护 parent_message_id 链
-
客户端完全无感知
Qwen — 客户端传入 chat_id(简单但有局限):
chatID := r.URL.Query().Get("chat_id")
if chatID == "" {
chatID = r.Header.Get("X-Chat-Id")
}
if chatID == "" {
// 报错:需要先在 chat.qwen.ai 创建对话
}
Qwen 要求用户先在网页端创建对话,然后把 chat_id 传给代理。这意味着:
-
无法自动创建新对话
-
多轮对话依赖 Qwen 服务端的 session 管理
-
代理本身是无状态的(更简单,但功能受限)
3.4 消息处理
DeepSeek — 完整的多轮重放:
// 把 OpenAI messages 拆成 system + prefix + newTurn
// 缓存命中 → 只发 newTurn
// 缓存未命中 → 拼接完整历史重放
func buildPrompt(reuse bool, systemPrompt string, prefix []openAIMsg, newTurn string) string
Qwen — 扁平化拼接:
func FlattenMessages(msgs []openAIMessage) string {
// "用户: xxx\n助手: xxx\n用户: xxx"
}
Qwen 直接把所有消息拼成一段文本发给 Qwen 的 t2t 接口。这丢失了多轮对话的结构化语义——Qwen 服务端无法区分哪条是历史、哪条是新消息。对于单轮问答够用,多轮对话质量会下降。
四、工程质量对比
4.1 中间件模式
DeepSeek: 只有一个简单的 corsMiddleware,无 panic 恢复、无请求日志。
Qwen: 完整的中间件链:
h = handlers.RecoveryMiddleware(h) // panic → 500 + 堆栈日志
h = handlers.LoggingMiddleware(h) // 每请求记录 method/path/status/duration
h = handlers.CORSMiddleware(h) // CORS + OPTIONS 预检
还有 responseWriter 包装器捕获状态码。这是生产级 HTTP 服务的标准做法。
4.2 优雅关闭
DeepSeek:
log.Fatal(http.ListenAndServe(port, nil)) // 硬退出
Qwen:
srv := &http.Server{
ReadTimeout: 30 * time.Second,
WriteTimeout: 300 * time.Second,
IdleTimeout: 120 * time.Second,
}
// SIGINT/SIGTERM → 10s 优雅关闭
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
// srv.Shutdown(ctx)
Qwen 有完整的超时配置和优雅关闭,DeepSeek 没有。
4.3 保活机制
DeepSeek: 无(依赖请求触发)
Qwen:
func StartKeepalive() {
// 每 2-5 分钟随机间隔
// GET /api/v2/config(保持 Cookie 活跃)
// POST /api/analytics(模拟前端心跳)
}
这很关键——Qwen 的 Cookie 可能因为长时间不活跃而过期,keepalive 模拟浏览器心跳延长有效期。DeepSeek 的 Token 过期机制不同(固定有效期),所以不需要。
4.4 配置持久化
DeepSeek: 通过 API 写入 config.json(原子写入)
Qwen: 通过 API 修改 .env 文件(逐行查找替换)
func UpdateEnvFile(key, value string) {
// 找到 KEY= 开头的行,替换值
// 找不到则追加
}
两者都支持运行时修改配置,但 Qwen 的 .env 格式更通用(兼容 Docker、systemd EnvironmentFile)。
五、测试对比
| | DeepSeek | Qwen Chat UI |
|--|--|--|
| 测试行数 | 186 行 | 195 行 (142+53) |
| 覆盖模块 | 账号池状态机 | 配置解析 + 消息处理 |
| 测试质量 | 高(含边界条件、去重逻辑) | 中(主要是 happy path) |
| 缺失 | PoW、SSE 解析 | HTTP handler 集成测试、uTLS |
两者测试覆盖度相当,都覆盖了核心逻辑但缺少端到端测试。
六、安全性对比
| 风险点 | DeepSeek | Qwen Chat UI |
|--------|----------|--------------|
| 管理接口认证 | 硬编码 deepseek | 无认证(/api/config 裸奔) |
| CORS | * 全开放 | * 全开放 |
| 凭据存储 | config.json (0600) | .env 文件 (0644 ⚠️) |
| Token 暴露 | /admin/config 返回明文 | /api/config 不返回 Cookie(只返回 cookie_configured: bool)✅ |
| WAF 检测 | 无(DeepSeek 无 WAF) | 有检测(aliyun_waf 识别)✅ |
Qwen 做得更好的地方: /api/config GET 不返回 Cookie 原文,只返回 cookie_configured: true/false。DeepSeek 的 /admin/config 会返回完整 Token。
Qwen 做得更差的地方: .env 文件权限是 0644(所有用户可读),DeepSeek 的 config.json 是 0600。
七、Qwen 项目的独特亮点
7.1 Cookie 多格式兼容
func ParseCookie(input string) string {
// 支持三种格式:
// 1. JSON 数组: [{"name":"cna","value":"xxx"},...]
// 2. JSON 对象: {"cna":"xxx","token":"eyJ..."}
// 3. 原始字符串: "cna=xxx; token=eyJ..."
}
这非常实用——不同浏览器扩展导出的 Cookie 格式不同,一次性兼容避免了用户手动转换。
7.2 WAF 识别
waf := strings.Contains(snippet, "aliyun_waf") || strings.Contains(snippet, "_waf_")
if waf {
errMsg = "blocked by Qwen WAF (slider challenge) -- refresh Cookie/bx-ua or check IP geo"
}
当 Qwen 返回 WAF 滑块验证时,代理能识别并给出明确提示,而不是返回一个莫名其妙的 HTML 页面。
7.3 随机抖动
func RandomJitter() time.Duration {
return time.Duration(rand.Intn(JitterMax-JitterMin)+JitterMin) * time.Millisecond
}
// 代理请求前 sleep 300-2000ms
time.Sleep(config.RandomJitter())
模拟人类操作间隔,降低被频控检测的概率。DeepSeek 没有这个(因为 PoW 本身就是"证明我是人")。
7.4 运行时配置 API
GET /api/config → 查看当前模型、温度等
POST /api/config → 修改模型、温度、max_tokens、top_p
无需重启、无需编辑文件,HTTP 调用即可切换模型。DeepSeek 只有 Token 管理 API,没有推理参数调整。
八、Qwen 项目的不足
8.1 多轮对话能力弱
FlattenMessages 把所有历史拼成一段文本,Qwen 服务端无法正确理解对话结构。对比 DeepSeek 的 session 缓存 + parent_message_id 链,Qwen 的多轮体验会明显差。
8.2 无故障恢复
单账号设计意味着 Cookie 过期 = 服务完全不可用。没有告警、没有自动恢复、没有备用账号。
8.3 无流式解析
Qwen 的流式路径直接透传上游 SSE:
proxy.CopyWithFlush(w, resp.Body, make([]byte, 4096))
不做任何解析、不提取 citation、不处理 thinking 标签。非流式路径才做 SSE 解析拼装。DeepSeek 对流式/非流式都有完整的解析和 citation 处理。
8.4 管理接口无认证
/api/config POST 可以修改模型、base_url 等关键配置,但没有任何认证。如果暴露在内网以外,任何人都能篡改配置。
九、综合评分对比
| 维度 | DeepSeek | Qwen Chat UI |
|------|:---:|:---:|
| 逆向难度 | ★★★★★ | ★★★☆☆ |
| 架构设计 | ★★★★☆ | ★★★★★ |
| 代码质量 | ★★★★☆ | ★★★★☆ |
| 多轮对话 | ★★★★★ | ★★☆☆☆ |
| 容错/高可用 | ★★★★★ | ★★☆☆☆ |
| 安全性 | ★★★☆☆ | ★★★☆☆ |
| 可运维性 | ★★★★★ | ★★★★☆ |
| 测试 | ★★★☆☆ | ★★★☆☆ |
| 文档 | ★★★★★ | ★★☆☆☆(无 README) |
| 实用性 | ★★★★★ | ★★★★☆ |
十、总结
这两个项目是同一作者、同一思路、不同阶段的产物:
DeepSeek Proxy 是"先跑起来再说"的实战派;Qwen Chat UI 是"想清楚再动手"的工程派。
从 Qwen 项目中能看到明显的"吸取教训"痕迹:
-
DeepSeek 的
main.go1265 行 → Qwen 拆成 4 个包,main 只有 107 行 -
DeepSeek 无优雅关闭 → Qwen 有完整的 signal handling + timeout
-
DeepSeek 无中间件 → Qwen 有 Recovery + Logging + CORS 三件套
-
DeepSeek 无 keepalive → Qwen 有随机间隔心跳
-
DeepSeek 配置用 mtime 轮询 → Qwen 用 fsnotify + SIGHUP
但 DeepSeek 在业务复杂度上远超 Qwen:多账号池、PoW 求解、会话指纹缓存、citation 解析、微信告警——这些都是 Qwen 没有的。Qwen 目前更像一个"轻量透传代理",而 DeepSeek 是一个"完整的 AI 网关"。
如果要用一句话概括:
DeepSeek 是"功能驱动"的极致,Qwen 是"架构驱动"的起点。 理想状态是把 Qwen 的架构 + DeepSeek 的功能合在一起。🎯