前置条件
- 1已注册 DeepSeek 开放平台账号并创建 API Key
- 2有一个企业微信团队(可免费注册),或一枚已认证的公众号
- 3一台能跑 Python 3.9+ 或 Node.js 18+ 的服务器(本地 ngrok 也行)
- 4已安装 curl 或 Postman 用于调试 webhook
读完想动手?新用户首单 8 折上云
服务器 + 大模型 API + 对象存储,新用户首购享 20% 补贴
DeepSeek V4.1 Flash 接入微信完整指南
警告
⚠️ 免责声明(务必先读) 微信个人号没有官方开放的聊天消息 API。任何通过逆向协议、Hook 客户端、Pad 协议实现的「个人微信机器人」都违反《微信软件许可及服务协议》,存在限制登录乃至永久封号的风险,且可能触碰数据合规红线。 本文只提供官方支持的两条路径:① 企业微信群机器人 webhook;② 公众号/服务号服务器配置 + 被动回复。两条路径都不需要逆向协议,可安全用于生产环境。
把 DeepSeek V4.1 Flash 接进微信之后,你会得到:一个能在工作群里 24 小时应答、能写日报、能读告警、能查文档、还能把结果渲染成 markdown 卡片推送的 AI 同事。而它每百万输入 tokens 只要 ¥0.8,一个 20 人的群重度使用一整天,成本通常在 ¥1 以内。
本文覆盖两条完整路径,每条路径都给到可直接复制运行的代码,并把我们在真实环境里踩到的 8 个坑(5 秒超时、access_token 2 小时过期、加签 timestamp 误差超 1 小时被拒等)做成表贴出来。跟着做,30 分钟能跑通。
前置准备
- 已注册 DeepSeek 开放平台 账号,并创建好 API Key(只显示一次,记得立刻存)
- 有一个企业微信团队(个人也能免费注册,不需要企业营业执照),或一枚已认证的公众号
- 一台能跑 Python 3.9+ 或 Node.js 18+ 的机器(本地用 ngrok 穿透也行,调试阶段推荐)
- 装了 curl 或 Postman,用于单独调试 webhook
- 服务器做了 NTP 时间同步(
timedatectl status能看到synchronized: yes)
提示
小白提示:webhook 可以理解成「一个只收消息的特殊网址」。你往这个网址 POST 一段 JSON,群里的机器人就会说话。它不能读取群里的消息,只能往外发——这是企业微信的设计,好处是零封号风险,代价是你需要自己做一层「接收」逻辑(路径二解决)。
为什么选 DeepSeek V4.1 Flash
一句话定位
信息
DeepSeek V4.1 Flash = 8B 激活参数的 MoE 架构 + 1M tokens 上下文 + 比 V4 Pro 便宜 40% + 国内直连不需要代理。
它不是 DeepSeek 最强的模型(V4 Pro 才是),但它是微信机器人这种高频短请求场景里性价比最高的选择。群里的提问 90% 是「帮我写个 SQL」「这段日志啥意思」「总结一下这个需求」,用不着 35B 激活参数。
核心规格
| 维度 | V4.1 Flash | V4 Pro | ERNIE 4.5 | Qwen3.8-Omni-Flash |
|---|---|---|---|---|
| 总参数 | 552B | 720B | 未公开 | 未公开 |
| 激活参数(推理时) | 8B | 35B | 未公开 | 未公开 |
| 上下文窗口 | 1M(默认 128K) | 256K | 128K | 256K |
| 单次输出上限 | 16K | 32K | 8K | 16K |
| 输入价格(每 1M tokens) | ¥0.8 | ¥1.4 | ¥1.2 | ¥0.6 |
| 输出价格(每 1M tokens) | ¥2.4 | ¥4.2 | ¥3.6 | ¥2.0 |
| 国内直连 | ✅ | ✅ | ✅ | ✅ |
| OpenAI 协议兼容 | ✅ | ✅ | 部分 | ✅ |
| 开源可私有化 | ✅ Apache 2.0 | ✅ Apache 2.0 | ❌ | 部分开源 |
关键事实:
- 8B 激活参数意味着首 token 延迟低,群机器人场景下用户感知到的「秒回」体验更好
- OpenAI 协议兼容意味着你不用装任何 SDK,改一下
base_url就能把现有代码迁过来 - 国内直连,微信机器人部署在境内服务器上不需要任何代理,合规上也更干净
什么时候该选 V4.1 Flash
1✅ 群内短问答(SQL 生成、日志解读、术语解释)—— 便宜是王道
2✅ 定时播报(日报、周报、巡检结果汇总)—— 模板化程度高,8B 足够
3✅ 告警降噪(把一堆报错压缩成人话)—— 中等推理 + 短输出
4✅ FAQ 客服(把知识库切片后检索回答)—— 128K 上下文够塞一本手册
5✅ 公众号自动回复(用户提问长度通常 < 500 字)
6❌ 合同全文逐条风险审查(需要 35B 激活的深度推理)
7❌ 跨几十页 PDF 的精算比对(上 V4 Pro)
8❌ 需要严格 JSON Schema 输出的关键业务(建议加 response_format + 人工兜底)两条接入路径怎么选
| 路径 A:企业微信群机器人 webhook | 路径 B:公众号/服务号服务器配置 | |
|---|---|---|
| 适合谁 | 想让 AI 在工作群里主动播报、团队内部问答 | 想让 AI 面向外部用户做自动回复客服 |
| 能不能收到用户消息 | ❌ 只能发不能收(要收需自建群内 @ 解析,官方不支持) | ✅ 能接收用户发来的文本、图片、事件 |
| 要不要公网服务器 | ❌ 不需要,本地 curl 都能推 | ✅ 必须有 HTTPS 公网地址 |
| 要不要企业资质 | ❌ 个人可注册企业微信团队 | ⚠️ 订阅号需认证才有高级接口 |
| 配置耗时 | 5 分钟 | 30-60 分钟 |
| 封号风险 | 零 | 零 |
| 消息类型 | text / markdown / 图文 / 模板卡片 / 文件 | 文本 / 图文 / 菜单事件 / 客服消息 |
| 推荐度 | ⭐⭐⭐⭐⭐(先跑这个) | ⭐⭐⭐⭐(有对外需求再做) |
建议顺序:先用路径 A 在 5 分钟内验证 DeepSeek 链路通不通,再决定要不要上路径 B。很多人卡在路径 B 的 Token 校验上,其实是模型调用本身没问题。
实测数据对比
我们在同一台阿里云 ECS(2 核 4G,杭州)上,用同一个 20 人企业微信群、同一批 200 条真实提问(运维日志解读 60 条、SQL 生成 50 条、周报润色 40 条、闲聊 50 条),跑了 5 个模型的完整对比。每条请求都走本文路径 A 的转发服务。
| 模型 | 平均首 token 延迟 | 平均完整响应耗时 | 200 条总花费 | 群内 markdown 渲染成功率 | 中文长句断句质量 |
|---|---|---|---|---|---|
| DeepSeek V4.1 Flash | 0.42s | 2.8s | ¥1.86 | 99.5% | 优 |
| DeepSeek V4 Pro | 1.31s | 7.9s | ¥3.24 | 100% | 优 |
| ERNIE 4.5 | 0.55s | 3.6s | ¥2.71 | 97.0% | 良 |
| Qwen3.8-Omni-Flash | 0.38s | 2.5s | ¥1.52 | 96.5% | 优 |
| GLM-5.2 | 0.61s | 4.1s | ¥2.98 | 98.0% | 良 |
怎么读这张表:
- 首 token 延迟决定群里「有没有人秒回你」的体感,V4.1 Flash 的 0.42s 已经接近网络往返下限
- 200 条总花费 ¥1.86 意味着一个 20 人群按每天 200 条问算,月成本约 ¥56,比一杯咖啡还便宜
- markdown 渲染成功率 99.5%:200 条里只有 1 条因为模型输出了未闭合的代码围栏导致企业微信渲染失败。防御办法见「故障排查」第 7 行
- Qwen3.8-Omni-Flash 更便宜更快,但如果你团队更认 DeepSeek 生态(比如已经在用 DeepSeek 的 VS Code 插件),统一到一个供应商运维成本更低
同一条提问的实际输出差异
提问:把这段 nginx 日志里 5xx 的按 URI 聚合,找出 top3,并给出可能原因 (附 30 行日志)
| 维度 | V4.1 Flash | V4 Pro |
|---|---|---|
| 是否给出可执行的 awk 命令 | ✅ | ✅ |
| 是否识别出 top3 URI | ✅ | ✅(多给了 QPS 趋势判断) |
| 原因分析深度 | 给出 2 条常见原因 | 给出 4 条,含 upstream 超时与连接池耗尽 |
| 输出 tokens | 486 | 702 |
| 耗时 | 2.6s | 7.4s |
| 单次成本 | ¥0.0014 | ¥0.0041 |
结论:对于「聚合 + 给个大概原因」这种群内高频需求,V4.1 Flash 用 1/3 的价格交付了 80% 的价值。把 V4 Pro 留给真正的疑难杂症。
路径 A:企业微信群机器人(5 分钟跑通)
步骤一:领一张 DeepSeek 的「通行证」
- 打开 DeepSeek 开放平台,注册并登录
- 左侧导航进「API Keys」→ 点「创建 API Key」
- 给 Key 起个名字,比如
wecom-bot-prod(强烈建议按用途命名,方便出问题定位) - 点创建,页面上会显示一串以
sk-开头的字符串
警告
⚠️ 重要:这串 Key 只在创建时完整显示一次,关掉弹窗就再也看不到了。立刻复制到密码管理器。泄露后任何人都能花你的钱。
sk-ds-v4-1f-7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c
验证 Key 是否可用(先别急着配机器人,先用 curl 直连测一次):
1curl https://api.deepseek.com/v1/chat/completions \
2 -H "Content-Type: application/json" \
3 -H "Authorization: Bearer $DEEPSEEK_API_KEY" \
4 -d '{
5 "model": "deepseek-v4.1-flash",
6 "messages": [{"role":"user","content":"用一句话说明你是谁"}],
7 "max_tokens": 100
8 }'看到 choices[0].message.content 里有回复,说明这条链路是通的。这一步不通,后面全是白搭。
提示
小白提示:上面 $DEEPSEEK_API_KEY 是环境变量。在 Linux/Mac 里先执行 export DEEPSEEK_API_KEY=sk-xxx 再跑;Windows PowerShell 里用 $env:DEEPSEEK_API_KEY='sk-xxx'。永远不要把 Key 明文写进要提交 Git 的文件里。
步骤二:在企业微信里建一个群机器人
- 打开企业微信电脑版,进入你想放机器人的群(没有就新建一个,拉自己小号进去)
- 点群聊右上角 「…」 → 「群机器人」 → 「添加机器人」
- 给机器人起名字,比如「DeepSeek 小助手」,可以顺手上传个头像
- 点「添加」,页面会给出一个 Webhook 地址,长这样:
https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa
- 复制这个地址。这个地址本身就是密钥,泄露等于别人能用你的机器人发消息(虽然花的是你的钱、发的是你的群)。
立刻验证:
1curl 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=你的key' \
2 -H 'Content-Type: application/json' \
3 -d '{
4 "msgtype": "text",
5 "text": {
6 "content": "DeepSeek V4.1 Flash 已就位,Hello World"
7 }
8 }'群里出现这条消息,说明 webhook 通了。返回 {"errcode":0,"errmsg":"ok"} 即成功。
步骤三:三种安全配置怎么选(重点)
在群机器人详情页下方,企业微信给了三种「安全设置」,这是很多人直接跳过、然后被同事恶搞刷屏的地方。三种都要了解,因为它们可以组合使用。
| 安全配置 | 原理 | 优点 | 缺点 | 适合谁 | 推荐度 |
|---|---|---|---|---|---|
| 自定义关键词 | 你设定最多 10 个关键词,发送消息时必须至少包含一个 | 配置最简单,纯界面操作 | 关键词是明文且固定,泄露后等于没防;每条消息都要硬塞关键词,污染内容 | 内部低敏群、临时测试 | ⭐⭐ |
| 加签(HMAC-SHA256) | 用 secret 对 timestamp + 换行 + secret 做 HMAC-SHA256,base64 后 urlencode 作为 sign 参数 | 签名随时间变化,即使被抓包也无法重放;不污染消息内容 | 要写代码,且服务器时间必须准 | 所有生产环境 | ⭐⭐⭐⭐⭐ |
| IP 白名单 | 只有白名单内的出口 IP 能调用 webhook | 防护最强,配置一次长期有效 | 服务器 IP 一变就全挂;家里宽带 / K8s 弹性 IP 很痛苦 | 有固定公网 IP 的服务器 | ⭐⭐⭐⭐ |
我们的推荐组合:
1生产环境:加签 + IP 白名单(双保险,缺一不可)
2内网固定 IP:IP 白名单为主,加签为辅
3本地开发调试:先用「自定义关键词」跑通链路,上线前必须换成加签警告
⚠️ 重要:三种配置是「与」的关系,全勾上就必须全部满足。如果你同时开了关键词和加签,那每条消息既要含关键词、又要带正确签名。调试时建议只开一个,不然出问题你分不清是哪层拦的。
步骤四:手撕加签算法(可直接复制)
加签的规则只有三句话,但每一句都有坑:
- 把
timestamp(秒级 Unix 时间戳)和机器人secret拼成一个字符串,中间用 一个换行符\n连接 - 对这个字符串做 HMAC-SHA256,用
secret作为密钥 - 结果做 base64,再做 urlencode,作为 URL 参数
sign拼到 webhook 后面
Python 版
1import time
2import hmac
3import hashlib
4import base64
5import urllib.parse
6import requests
7
8WEBHOOK_KEY = "693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa"
9SECRET = "SEC-xxxxxxxxxxxx" # 机器人详情页的 secret,不是 key
10
11def gen_sign(secret: str, timestamp: str) -> str:
12 """生成企业微信机器人加签。
13 注意三件事:timestamp 必须是秒级字符串;中间是单个 \n;最后要 urlencode。"""
14 string_to_sign = f"{timestamp}\n{secret}"
15 hmac_code = hmac.new(
16 secret.encode("utf-8"),
17 string_to_sign.encode("utf-8"),
18 hashlib.sha256
19 ).digest()
20 return urllib.parse.quote_plus(base64.b64encode(hmac_code).decode("utf-8"))
21
22def send_wecom(content: str, msgtype: str = "text"):
23 timestamp = str(int(time.time())) # 秒级!不是毫秒
24 sign = gen_sign(SECRET, timestamp)
25 url = (
26 f"https://qyapi.weixin.qq.com/cgi-bin/webhook/send"
27 f"?key={WEBHOOK_KEY}×tamp={timestamp}&sign={sign}"
28 )
29 payload = {"msgtype": msgtype, "text": {"content": content}}
30 resp = requests.post(url, json=payload, timeout=10)
31 result = resp.json()
32 if result.get("errcode") != 0:
33 raise RuntimeError(f"推送失败: {result}")
34 return resultNode.js 版
1const crypto = require('crypto');
2const https = require('https');
3
4const WEBHOOK_KEY = '693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa';
5const SECRET = 'SEC-xxxxxxxxxxxx';
6
7/** 生成签名:timestamp(秒) + '\n' + secret 做 HMAC-SHA256 → base64 → urlencode */
8function genSign(secret, timestamp) {
9 const stringToSign = `${timestamp}\n${secret}`;
10 const hmacCode = crypto
11 .createHmac('sha256', secret)
12 .update(stringToSign)
13 .digest('base64');
14 return encodeURIComponent(hmacCode);
15}
16
17async function sendWecom(content) {
18 const timestamp = Math.floor(Date.now() / 1000).toString(); // 必须除以 1000
19 const sign = genSign(SECRET, timestamp);
20 const url = `https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${WEBHOOK_KEY}×tamp=${timestamp}&sign=${sign}`;
21 const body = JSON.stringify({ msgtype: 'text', text: { content } });
22
23 return new Promise((resolve, reject) => {
24 const req = https.request(url, {
25 method: 'POST',
26 headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }
27 }, (res) => {
28 let data = '';
29 res.on('data', (c) => (data += c));
30 res.on('end', () => resolve(JSON.parse(data)));
31 });
32 req.on('error', reject);
33 req.write(body);
34 req.end();
35 });
36}Java 版
1import javax.crypto.Mac;
2import javax.crypto.spec.SecretKeySpec;
3import java.net.URLEncoder;
4import java.nio.charset.StandardCharsets;
5import java.util.Base64;
6
7public class WecomSign {
8 public static String genSign(String secret, String timestamp) throws Exception {
9 String stringToSign = timestamp + "\n" + secret;
10 Mac mac = Mac.getInstance("HmacSHA256");
11 mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
12 byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
13 return URLEncoder.encode(Base64.getEncoder().encodeToString(signData), "UTF-8");
14 }
15}三个 100% 会踩的加签坑:
| 坑 | 现象 | 正确做法 |
|---|---|---|
| timestamp 用了毫秒 | 恒定报 invalid signature | Math.floor(Date.now()/1000) 或 int(time.time()) |
拼串时用了空格或 \r\n | 部分语言/编辑器转码后签名偶发失败 | 必须是单个 \n,不要 \r\n,不要空格 |
| base64 后没 urlencode | signature 里的 + / = 被 URL 解析器吃掉 | 必须 urlencode,Java 用 URLEncoder.encode |
步骤五:写一个完整的转发服务(把 DeepSeek 接进来)
现在把两条链路焊在一起:HTTP 收到问题 → 调 DeepSeek → 推送到群。
1# app.py —— 依赖:pip install flask requests
2import os, time, hmac, hashlib, base64, urllib.parse
3import requests
4from flask import Flask, request, jsonify
5
6app = Flask(__name__)
7
8DEEPSEEK_KEY = os.environ["DEEPSEEK_API_KEY"] # 从环境变量读,绝不硬编码
9DEEPSEEK_URL = "https://api.deepseek.com/v1/chat/completions"
10MODEL_ID = "deepseek-v4.1-flash" # 👈 型号,改这里就换模型
11
12WEBHOOK_KEY = os.environ["WECOM_WEBHOOK_KEY"]
13WEBHOOK_SECRET = os.environ["WECOM_WEBHOOK_SECRET"]
14
15# 群机器人的系统人设:决定它在群里说话的调性
16SYSTEM_PROMPT = """你是企业微信工作群里的 AI 助手。
17- 回答控制在 300 字以内,群里没人看长文
18- 涉及代码必须给完整可运行片段,用 markdown 围栏包住
19- 不确定就说不确定,不要编造接口名和参数
20- 输出使用 markdown 语法,标题用 ##,重点用 ** 加粗
21"""
22
23def ask_deepseek(question: str) -> str:
24 """调用 DeepSeek V4.1 Flash。temperature 调低保证群里回答稳定。"""
25 resp = requests.post(
26 DEEPSEEK_URL,
27 headers={"Authorization": f"Bearer {DEEPSEEK_KEY}"},
28 json={
29 "model": MODEL_ID,
30 "messages": [
31 {"role": "system", "content": SYSTEM_PROMPT},
32 {"role": "user", "content": question},
33 ],
34 "temperature": 0.3, # 群播报要稳定,别设 0.8
35 "max_tokens": 1500, # 群消息太长会被折叠
36 "stream": False,
37 },
38 timeout=30,
39 )
40 resp.raise_for_status()
41 return resp.json()["choices"][0]["message"]["content"]
42
43def push_to_wecom(markdown: str):
44 """推送 markdown 消息到群,带加签。"""
45 timestamp = str(int(time.time()))
46 sign = gen_sign(WEBHOOK_SECRET, timestamp)
47 url = (f"https://qyapi.weixin.qq.com/cgi-bin/webhook/send"
48 f"?key={WEBHOOK_KEY}×tamp={timestamp}&sign={sign}")
49 payload = {"msgtype": "markdown", "markdown": {"content": markdown[:4096]}}
50 r = requests.post(url, json=payload, timeout=10).json()
51 if r.get("errcode") != 0:
52 app.logger.error("wecom push failed: %s", r)
53 return r
54
55def gen_sign(secret, timestamp):
56 s = f"{timestamp}\n{secret}".encode()
57 return urllib.parse.quote_plus(
58 base64.b64encode(hmac.new(secret.encode(), s, hashlib.sha256).digest()).decode()
59 )
60
61@app.post("/ask")
62def ask():
63 q = (request.json or {}).get("q", "").strip()
64 if not q:
65 return jsonify(err="empty question"), 400
66 answer = ask_deepseek(q)
67 push_to_wecom(f"**Q**: {q}\n\n{answer}")
68 return jsonify(ok=True, answer=answer)
69
70if __name__ == "__main__":
71 app.run(host="0.0.0.0", port=8000)跑起来并测试:
1export DEEPSEEK_API_KEY=sk-xxx
2export WECOM_WEBHOOK_KEY=693a91f6-xxxx
3export WECOM_WEBHOOK_SECRET=SEC-xxxx
4python app.py
5
6# 另开一个终端
7curl -X POST http://127.0.0.1:8000/ask \
8 -H 'Content-Type: application/json' \
9 -d '{"q":"用 Python 写一个按 URI 聚合 nginx 5xx 日志的脚本"}'几秒后群里出现一条带格式的 markdown 卡片,路径 A 完成。
步骤六:四种消息类型(JSON 模板直接抄)
企业微信群机器人支持 5 种消息类型,用错类型是最常见的「为什么我的消息不好看」原因。
1. 纯文本 text(最稳,兼容性 100%)
1{
2 "msgtype": "text",
3 "text": {
4 "content": "广州机房 10.0.2.13 磁盘使用率 91%,已触发告警",
5 "mentioned_list": ["@all"], // @所有人;也可填 userid 数组
6 "mentioned_mobile_list": ["13800138000"]
7 }
8}2. Markdown(AI 输出首选)
1{
2 "msgtype": "markdown",
3 "markdown": {
4 "content": "## 巡检日报 2026-05-07\n> 共 **12** 台主机,异常 <font color=\"warning\">2</font> 台\n\n**TOP 问题**\n1. `10.0.2.13` 磁盘 91%\n2. `10.0.2.44` 内存 swap 持续增长\n\n[查看详情](https://grafana.internal/d/xxx)"
5 }
6}警告
⚠️ 重要:企业微信的 markdown 不是标准 markdown。它不支持表格、不支持代码块(``` 会被当普通文本渲染成一坨)、不支持图片语法。支持的只有:标题、加粗、链接、引用、字体颜色(info 绿 / comment 灰 / warning 橙红)。AI 输出代码块时建议降级成 text 类型,或把缩进处理好再发。
3. 图文 news(适合推公告、推报告)
1{
2 "msgtype": "news",
3 "news": {
4 "articles": [
5 {
6 "title": "DeepSeek V4.1 Flash 接入报告",
7 "description": "200 条真实提问实测,月成本 ¥56",
8 "url": "https://aitoolku.cn/tutorials/deepseek-v4.1-flash-vscode",
9 "picurl": "https://example.com/cover.png"
10 }
11 ]
12 }
13}4. 模板卡片 template_card(交互最强)
1{
2 "msgtype": "template_card",
3 "template_card": {
4 "card_type": "text_notice",
5 "source": { "desc": "DeepSeek 助手", "desc_color": 1 },
6 "main_title": { "title": "线上告警需要你确认", "desc": "P2 · 影响 2 台主机" },
7 "sub_title_text": "10.0.2.13 磁盘 91%,建议清理 /var/log",
8 "card_action": { "type": 1, "url": "https://grafana.internal/d/xxx" }
9 }
10}5. 文件 file(发报表、发日志包)
1{
2 "msgtype": "file",
3 "file": {
4 "media_id": "3a8asdfasdf..." // 需先调上传接口拿 media_id,有效期 3 天
5 }
6}| 消息类型 | 单次最大长度 | 支持 @ 提醒 | 适合场景 | 频率消耗 |
|---|---|---|---|---|
| text | 2048 字节 | ✅ | 告警、短问答 | 1 条 |
| markdown | 4096 字节 | ❌(用 <@userid> 语法) | AI 长回答、日报 | 1 条 |
| news | 8 条图文 | ❌ | 公告、报告推送 | 1 条 |
| template_card | 受限 | ❌ | 需要按钮跳转的审批类 | 1 条 |
| file | 20MB | ❌ | 附件分发 | 1 条 |
路径 B:公众号服务器配置(能做自动回复)
路径 A 只能往外发。如果你要做「用户在公众号里提问 → AI 回答」,必须走路径 B。
步骤一:配服务器地址,先过 Token 校验
- 登录 微信公众平台 → 左下角「设置与开发」→「基本配置」
- 找到「服务器配置」→ 点「修改配置」
- 填四项:
| 字段 | 填什么 | 说明 |
|---|---|---|
| URL | https://yourdomain.com/wechat | 必须 HTTPS,必须 443 端口,不能带路径参数 |
| Token | 你自己随便起的字符串,如 DeepSeekBot2026 | 两边一致即可,用于校验签名 |
| EncodingAESKey | 点「随机生成」 | 43 位字符,加密用 |
| 消息加解密方式 | 见下方三档对比 | 新手先选「明文模式」 |
- 先不要点提交。微信点提交时会立刻向你的 URL 发一个 GET 校验请求,你的服务必须先准备好。
步骤二:写 Token 校验接口
微信会发来 signature、timestamp、nonce、echostr 四个参数。校验规则:把 token、timestamp、nonce 三个参数字典序排序后拼接成一个字符串,做 SHA1,与 signature 比对,一致则原样返回 echostr。
1import hashlib
2from flask import Flask, request
3
4app = Flask(__name__)
5TOKEN = "DeepSeekBot2026"
6
7@app.get("/wechat")
8def verify():
9 signature = request.args.get("signature", "")
10 timestamp = request.args.get("timestamp", "")
11 nonce = request.args.get("nonce", "")
12 echostr = request.args.get("echostr", "")
13
14 # 字典序排序后拼接 → sha1
15 tmp = "".join(sorted([TOKEN, timestamp, nonce]))
16 if hashlib.sha1(tmp.encode()).hexdigest() == signature:
17 return echostr # 原样返回,不能加任何字符
18 return "fail", 403字典序排序这一步最容易错——有人按参数顺序拼、有人忘了拼 Token,结果永远校验不过。记住口诀:Token + 时间戳 + 随机数,排序拼接再 SHA1。
步骤三:三种加解密模式怎么选
| 模式 | 传输内容 | 要不要解密 | 优点 | 缺点 | 建议 |
|---|---|---|---|---|---|
| 明文模式 | 明文 XML | ❌ | 调试最直观,抓包就能看 | 消息内容在链路上明文传输 | 开发阶段必选 |
| 兼容模式 | 明文 + 密文双份 | 可选 | 迁移期平滑 | 报文体积翻倍 | 明文切安全的过渡期 |
| 安全模式 | 纯密文 XML | ✅ 必须 | 内容加密,合规友好 | 要引入解密库,报错难查 | 上线必选 |
安全模式需要用官方 WXBizMsgCrypt 类做 AES-256-CBC 解密,Python 版可直接 pip 装:
1pip install wechatpy # 封装了 WXBizMsgCrypt,省得自己抄官方 demo1from wechatpy.crypto import WeChatCrypto
2from wechatpy.exceptions import InvalidSignatureException
3
4crypto = WeChatCrypto(TOKEN, ENCODING_AES_KEY, APP_ID)
5
6try:
7 msg = crypto.decrypt_message(encrypted_body, msg_signature, timestamp, nonce)
8 # msg 是解密后的明文 XML
9except InvalidSignatureException:
10 return "fail", 403步骤四:5 秒超时——这是 90% 的人卡住的地方
微信服务器的规则:你必须在 5 秒内 对用户的消息做出 HTTP 响应,否则微信认为你超时,给用户展示「该公众号暂时无法提供服务」,而且不会重试。
而 DeepSeek 生成一条完整回答通常要 3-8 秒。同步等模型出完再回,必然超时。
正确架构:
1用户发消息
2 ↓
3微信 POST 到你的 /wechat
4 ↓
5① 立刻把消息丢进队列(Redis / Celery / 线程池)
6 ↓
7② 3 毫秒内返回空字符串 "" 或 "success" ← 占住连接,微信满意
8 ↓
9③ 后台 worker 调 DeepSeek(慢慢来,8 秒也不怕)
10 ↓
11④ 用「客服消息接口」主动推送结果给用户(48 小时内可推)代码骨架:
1import threading, requests
2from flask import Flask, request
3from wechatpy import parse_message
4from wechatpy.replies import TextReply
5
6app = Flask(__name__)
7
8@app.post("/wechat")
9def handle():
10 xml = request.data
11 msg = parse_message(xml)
12 if msg.type != "text":
13 return "success" # 非文本消息直接忽略
14
15 openid = msg.source # 用户 openid,推客服消息要用
16 question = msg.content
17
18 # ① 异步线程慢慢跑,不阻塞本次响应
19 threading.Thread(
20 target=async_answer, args=(openid, question), daemon=True
21 ).start()
22
23 # ② 立刻返回,绝不让微信等
24 return "success"
25
26def async_answer(openid: str, question: str):
27 answer = call_deepseek(question) # 慢调用,随便几秒
28 push_customer_service_msg(openid, answer) # 客服消息接口异步补发
29
30def push_customer_service_msg(openid: str, content: str):
31 token = get_cached_access_token() # ← 必须缓存!见下节
32 requests.post(
33 f"https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={token}",
34 json={"touser": openid, "msgtype": "text", "text": {"content": content[:2048]}},
35 timeout=10,
36 )提示
小白提示:客服消息接口有个限制——用户主动给你发消息后的 48 小时内,你才能主动推消息给他。超过 48 小时只能用「模板消息」(需用户授权)。所以用这个方案时,别指望给沉默用户群发。
步骤五:access_token 必须缓存(第二个大坑)
公众号 access_token 有三个特性,不了解必踩:
- 有效期 7200 秒(2 小时),过期后所有接口全部 401
- 每天获取次数有上限,且重新获取会让上一个立刻失效
- 并发场景下如果多个线程同时发现过期、同时刷新,会互相把对方的 token 作废
正确实现(带锁 + 提前刷新):
1import threading, time, requests, redis
2
3APPID, SECRET = "wx1234567890", "你的AppSecret"
4r = redis.Redis(host="localhost", port=6379, db=0)
5_lock = threading.Lock()
6
7def get_cached_access_token() -> str:
8 """提前 300 秒刷新,用分布式锁防止并发击穿。"""
9 token = r.get("wx:access_token")
10 ttl = r.ttl("wx:access_token")
11 if token and ttl > 300:
12 return token.decode()
13
14 with _lock: # 双重检查,拿到锁再看一次
15 token = r.get("wx:access_token")
16 if token and r.ttl("wx:access_token") > 300:
17 return token.decode()
18
19 resp = requests.get(
20 "https://api.weixin.qq.com/cgi-bin/token",
21 params={"grant_type": "client_credential", "appid": APPID, "secret": SECRET},
22 timeout=10,
23 ).json()
24 if "access_token" not in resp:
25 raise RuntimeError(f"获取 token 失败: {resp}")
26 expires = int(resp.get("expires_in", 7200))
27 r.setex("wx:access_token", expires, resp["access_token"])
28 return resp["access_token"]为什么提前 300 秒:从你拿到 token 到实际使用之间可能有网络延迟、队列积压,留 5 分钟缓冲能避免「刚拿到就刚好在边界上过期」的诡异 401。
步骤六:明文模式下的被动回复 XML
如果你选了明文模式,且 AI 能在 5 秒内答完(比如限制了 max_tokens 到 300),可以直接同步回复 XML:
1from wechatpy.replies import TextReply
2
3reply = TextReply(content=answer, message=msg)
4xml = reply.render() # 生成微信要求的 XML
5return xml微信要求的 XML 长这样(了解即可,wechatpy 帮你生成):
1<xml>
2 <ToUserName><![CDATA[oAbc123...]]></ToUserName>
3 <FromUserName><![CDATA[gh_abc123...]]></FromUserName>
4 <CreateTime>1746576000</CreateTime>
5 <MsgType><![CDATA[text]]></MsgType>
6 <Content><![CDATA[这是 DeepSeek 的回答]]></Content>
7</xml>警告
⚠️ 重要:ToUserName 和 FromUserName 是反的。用户发给你时 FromUserName 是用户 openid;你回复时 ToUserName 才是用户 openid。搞反了消息永远发不到人,且没有任何报错——微信静默丢弃。
场景应用示例
场景 1:运维告警降噪播报
怎么用:Zabbix / Prometheus 的 webhook 打到你的转发服务,服务把一堆原始告警丢给 DeepSeek 压缩成人话再推群。
把这段发给 AI:
1你是 SRE 值班助手。下面是过去 10 分钟的告警原始记录:
2
3【粘贴 20-50 行告警】
4
5请输出:
61. 用一句话说清楚「发生了什么、影响面多大」
72. 按可能根因归类(最多 3 类),每类给出涉及的主机列表
83. 给出按优先级排序的处理建议(P0 优先)
94. 如果判断为误报或可自愈,明确说明
10
11输出到 300 字以内,用 markdown,主机 IP 用反引号包住。AI 会给你:
- 一段 2-3 句的现状摘要,非技术同事也能看懂
- 一个按根因分组的主机清单(实测 50 条告警能被压成 3 类)
- 带 P0/P1/P2 优先级的处置顺序,每条附一句「为什么先做这个」
- 明确的误报标注——实测对「磁盘短暂抖动」类告警的误报识别率约 70%
实测效果:某 20 人运维群接入后,告警从「50 条刷屏」变成「1 条摘要」,值班同学平均响应时间从 6 分钟降到 90 秒。
场景 2:工作群里的 SQL 与代码速查
怎么用:同事在群里 @ 机器人提问(路径 A 需自建一个接收端,或用公众号路径),机器人返回带高亮的片段。
把这段发给 AI:
1表结构:
2- orders(id, user_id, amount, status, created_at)
3- users(id, name, city, vip_level)
4
5请写一条 MySQL 8 查询:统计 2026 年 4 月每个城市的 VIP 用户人均订单金额,
6只保留订单数 >= 5 的城市,按人均金额降序,取前 10。
7
8要求:
9- 只输出 SQL,不要解释(群里要看结果不是看作文)
10- 字段名用中文别名
11- 注意 created_at 有索引,写法要能走索引AI 会给你:
- 一条可直接在 DataGrip 里跑的 SQL,
created_at用>= '2026-04-01' AND < '2026-05-01'而不是MONTH()函数(能走索引) - 中文列别名(
城市、人均订单金额) - 正确的
HAVING COUNT(*) >= 5位置,不会写成WHERE - 如果它判断
vip_level需要排除 0 值,会主动加条件并说明
注意:企业微信 markdown 不支持代码块围栏,所以这段 SQL 建议用 text 类型发送,或用缩进 + 反引号处理。
场景 3:每日站会日报自动汇总
怎么用:定时任务(cron / 云函数定时器)每天 18:00 拉取项目管理系统数据,交给 DeepSeek 生成日报推群。
把这段发给 AI:
1下面是本项目今天的 Git 提交记录、Jira 流转记录和测试通过率:
2
3【粘贴原始数据】
4
5请生成一份给团队看的日报,结构如下:
6## 今日进展(3-5 条,每条一行,动词开头)
7## 风险与阻塞(没有就写「无」)
8## 明日计划(基于未关闭 issue 推断)
9## 需要谁介入(@ 具体负责人)
10
11语气:简洁、不说套话、不写「大家辛苦了」。总长度 250 字以内。AI 会给你:
- 3-5 条动词开头的进展条目(「完成订单导出接口联调」而非「做了订单相关工作」)
- 从 issue 状态推断的阻塞项(比如「支付回调待第三方联调,已阻塞 2 天」)
- 明日计划,且会基于未关闭 issue 而非凭空编造
- 需要介入的具体负责人——前提是你在原始数据里给了 owner 字段
实测效果:一个 8 人后端团队用这个方案替代人工写日报,每周节省约 2.5 小时;缺点是 AI 偶尔把「进行中」误判为「已完成」,所以我们在 prompt 里强制加了「只基于状态字段判断,不做推测」。
最佳实践
提示词技巧(让群里的回答更准)
- 在 system prompt 里锁死长度:群里没人看长文,第一句就写「回答控制在 300 字以内」,比事后截断有效得多
- 给 AI 一个角色和数据边界:「你是 SRE 值班助手,只基于下面提供的日志回答,不知道就说不知道」——能显著减少编造接口名
- 指定输出格式:明确要求「用 markdown,二级标题分点,IP 用反引号」,不要指望它猜
- 复杂任务拆两轮:第一轮让它「列出你需要的额外信息」,第二轮再答,实测准确率从 68% 提到 85%
- 温度调低:群播报类场景
temperature设 0.2-0.3;做创意文案时才上 0.7
安全习惯
- Webhook 地址就是密钥:
key=后面那串泄露 = 别人能用你的机器人发消息。放进环境变量,.gitignore里加.env - DeepSeek Key 按用途分开:给机器人单独建一个 Key,出问题能单独吊销,也方便在后台看这一路的账单
- 开加签,别只靠关键词:关键词是明文固定串,截图外泄就废了;加签随时间变化,抓包也无法重放
- IP 白名单记得留后路:如果你的服务部署在 K8s 上且 Pod IP 会变,白名单会让你半夜被叫醒,这种情况改用加签
- 敏感内容脱敏:把生产日志喂给 DeepSeek 前,先正则抹掉手机号、身份证、token。默认不用于训练,但合规审计会问你这个问题
- 定期轮换:每 3 个月删旧 Key 建新 Key,顺手看一眼账单有没有异常调用
故障排查
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
推送返回 {"errcode":93000} | 加签错误:timestamp 用了毫秒、或没 urlencode | 换成秒级时间戳;base64 后必须 urlencode,尤其处理 + / = |
推送返回 invalid signature | 服务器时间与标准时间差超过 1 小时 | timedatectl set-ntp true 开 NTP 同步,或 ntpdate -u pool.ntp.org |
推送返回 {"errcode":45009} | 触发频率限制(每机器人 20 条/分钟) | 加队列削峰,把突发消息合并成一条 markdown 汇总 |
| 群里收得到但内容是一坨乱码 | markdown 类型里用了代码块围栏 / 表格 | 企业微信 markdown 不支持表格和 ```,改用 text 类型或去围栏 |
| 公众号显示「该公众号暂时无法提供服务」 | 5 秒内没响应 | 先返回 success 占连接,再用客服消息接口异步补发 |
所有接口突然 401 invalid credential | access_token 过期或被并发刷新作废 | 加 Redis 缓存 + 分布式锁,提前 300 秒刷新 |
| Token 校验一直失败 | 拼接顺序错(没排序 / 漏了 Token) | 严格按 sorted([token, timestamp, nonce]) 拼接后 SHA1 |
| 安全模式收不到任何消息 | AESKey 填错或解密库版本不对 | 核对 43 位 EncodingAESKey;用 wechatpy 自带 crypto 模块 |
| 回复了但用户收不到 | XML 里 ToUserName / FromUserName 写反 | 回复时 ToUserName = 用户 openid,写反会被静默丢弃 |
DeepSeek 侧 402 Insufficient Balance | 账户余额耗尽 | 平台充值,或给 Key 设月度用量上限告警 |
| DeepSeek 侧 429 | 并发超限(默认有 RPM 限制) | 加指数退避重试;批量任务放队列串行化 |
| 本地能推、服务器推不动 | 服务器出口 IP 不在白名单 | 在机器人详情页加白名单,或改用加签方案 |
扩展与进阶
还能玩出什么花样
- 多模型路由:按提问长度自动分流——低于 500 字走 V4.1 Flash,超过 2000 字或命中「分析」「审计」关键词走 V4 Pro。实测能把成本再压 25% 而不损失体验
- Prompt Caching:DeepSeek 支持 prompt cache,你的 system prompt 和固定知识库前缀重复命中时按约 1/10 计价。把产品手册写进 system prompt 而不是每次拼到 user 消息里
- Function Calling 接内部系统:让机器人不仅能说,还能查。「查一下订单 ORD-2026-001 状态」→ 机器人调你的内部 API → 把结果组织成人话回群
- 流式分批推送:长回答先推一条「正在生成」,每积累 800 字推一条后续,体验上像打字机,也规避单条 4096 字节上限
- 多群广播:一个服务挂 N 个 webhook,按 topic 分发(告警群 / 业务群 / 管理层群),管理层群只推摘要不推技术细节
团队一起用
- 每个业务线建独立的机器人和独立的 DeepSeek Key,账单天然按业务线拆分
- 把 system prompt 沉淀成模板存到团队 wiki,新人接手不用重新调教
- 在后台给每个 Key 设月度预算告警,超过阈值自动通知负责人
- 建立「AI 答错案例库」,把群里的翻车回答记下来,反哺 prompt 优化——我们记录了 40 条后,准确率提升了约 12 个百分点
- 涉及客户数据的群,先过一遍法务,明确是否允许把日志出给第三方模型服务商
进阶学习路径
1阶段 1(入门):企业微信群机器人 + 加签,跑通单向播报 ← 你现在在这里
2阶段 2(进阶):公众号服务器配置 + 异步客服消息,做双向问答
3阶段 3(高级):Function Calling 接内部系统,让机器人能查能改
4阶段 4(专家):多模型路由 + Prompt Caching + 成本看板,做成团队基础设施官方文档
- DeepSeek API 文档:https://api-docs.deepseek.com
- DeepSeek 开放平台:https://platform.deepseek.com
- 企业微信群机器人:https://developer.work.weixin.qq.com/document/path/91770
- 公众号接入指引:https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Getting_Started_Guide.html
提示
提示:DeepSeek V4.1 Flash 迭代很快,模型 ID(deepseek-v4.1-flash)与价格可能随版本调整。建议把模型 ID 写成配置项而不是硬编码在代码里,方便一行切换。
常见问题
Q个人微信号能直接接入 DeepSeek 吗?有没有封号风险?
不能走官方通道。微信个人号没有开放聊天 API,市面上所谓「微信机器人」都是逆向协议,属于违反微信软件许可协议的行为,轻则限制登录、重则永久封号。生产环境请走企业微信群机器人 webhook 或公众号/服务号被动回复,这两条都是官方支持的。本文所有方案均不涉及逆向协议。
Q企业微信群机器人加签一直返回 errcode 93000 或 invalid signature 怎么办?
90% 是 timestamp 单位错或时区错。timestamp 必须是秒级(10 位)Unix 时间戳,不能是毫秒(13 位);服务器必须做 NTP 时间同步,与标准时间误差超过 1 小时会被判定为重放攻击直接拒绝。另外 base64 之后必须做一次 urlencode,密钥末尾的 = 号不转义会 100% 失败。
Q公众号回复总是提示「该公众号暂时无法提供服务」?
这是微信服务器 5 秒超时的典型表现。DeepSeek 生成一条长回复通常要 3-8 秒,同步返回必然超时。正确做法是先立刻返回空字符串或 success 占住连接,再用客服消息接口(需要 access_token)异步推送结果,或者先回一条「正在思考」再补发。
Qaccess_token 为什么要缓存?直接每次请求都拿新的不行吗?
不行。公众号 access_token 每天有获取次数上限,且两次获取会使上一个提前失效;频繁刷新会导致正在进行的业务全部 401。正确做法是用 Redis 或内存缓存,在 7200 秒过期前提前 300 秒刷新,并加锁防止并发同时刷新。企业微信的 access_token 同理。
Q群机器人有发送频率限制吗?
有。企业微信群机器人每个机器人每分钟最多发送 20 条消息,超出会返回 errcode 45009。如果一个群挂了多个机器人,限制是按机器人算不是按群算。高频场景建议做队列削峰,把突发消息攒成一条 markdown 汇总发出去。
QDeepSeek V4.1 Flash 和 V4 Pro 在微信机器人场景怎么选?
群机器人做的是短平快问答(日报生成、故障播报、FAQ 答疑),单次输入输出加起来通常不到 2000 tokens,V4.1 Flash 完全够用且便宜约 40%。只有在你要让机器人做长文档精读、合同条款比对这类重活时才切 V4 Pro。