news 2026/9/29 5:15:52

微信公众号每日天气推送:测试号+天气API完成定时消息自动化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
微信公众号每日天气推送:测试号+天气API完成定时消息自动化

简介:近期短视频平台带火了‘给对象做天气推送’的公众号玩法,这套教程专门面向想亲手为伴侣制作每日推送的读者,只要有最基础的代码阅读能力,就能按步骤完成部署。资源包共5个文件,压缩后仅5KB,内容非常紧凑:Python主程序负责抓取天气和纪念日信息;配置文件用来填入公众号Token与API密钥;工作流配置让GitHub在每天指定时间自动运行;依赖清单确保环境完整;说明文档则从申请API到部署上线给出了全程指引。这套教程发布至今已有7688人学习使用,热度在同类型资源中表现亮眼,可见这种兼具实用与浪漫的小项目很受欢迎。借助包内的源码与消息模板,读者只需要将密钥替换成自己的信息,就能生成一个免本地运行的每日推送服务;即使之前没接触过GitHub Actions,也可以参照说明文档的逐步讲解完成配置。整个方案轻量、直接,既能定时发送天气提醒,也能加入纪念日等个性化内容,很适合作为情人节、生日等时机的用心惊喜,同时为后续二次开发保留了清晰入口。

1. 微信公众号每日天气推送:把一次 API 调用变成每天早上的问候

做这个项目前,我一直以为公众号推送要折腾域名回调、消息验签那一套。真正跑通后发现核心就一句话:每天早上用天气 API 拿数据,塞进微信模板消息,推给指定的人。不需要认证服务号,不需要备案域名,一台能跑 Python 的服务器或者树莓派就够,核心脚本不到 80 行,剩下的大头全在配置。

这套方案用的是微信测试号加免费天气接口,我实际跑了大半年没断过。它解决的是「每天定时给女朋友或家人推一条真实天气」的具体诉求,顺带把公众号模板消息的完整调用链演示了一遍。适合刚开始接触公众号开发的新手,也适合想抄一份能改参数的定时推送脚本的从业者。下面从选型开始,一步步拆到可以照抄的代码和避坑经验。

2. 推送链路怎么搭:测试号、天气 API 和模板消息三件套

2.1 为什么是测试号而不是认证服务号

公众号做消息推送,最大的门槛在账号类型。个人主体能注册的只有订阅号,而订阅号的接口权限表里没有模板消息;模板消息是服务号的主场,但服务号注册需要企业主体,个人拿不到,认证还要每年 300 元。微信官方给开发者留了一条免费通道:公众平台测试号。用个人微信扫码登录,立刻就有独立的 appID 和 appsecret,user/get、template/send这些接口全部能调,不需要绑定域名,不需要走认证流程。

测试号和正式号的区别主要有三点:粉丝必须通过页面上的测试二维码扫码关注,关注量有上限;模板消息只能从测试模板里选,不能申请正式模板库里的行业模板;没有自定义菜单、客服消息这些外围能力。对「每天给一个人推一条天气」的场景,前两点完全无所谓。我把它当生产环境用了一年,推送成功率和正式接口没有区别,速度也一样。

一个容易忽略的点:测试号页面是用个人微信扫码登录的,上面显示 appID 和 appsecret。别以为记住了路径就永远能回来——微信账号状态异常、换绑手机号,都可能让你进不去调试页,密钥取不回来就得重新生成,所有配置跟着重来一遍。所以第一步就把两个密钥抄到本地,或者写进服务器的环境变量文件里备份。这一条在后面避坑章还会再提。

2.2 天气数据源:和风还是心知

推送内容要真实、定时、还不能断,天气数据源的稳定性比字段丰富程度更重要。国内免费接口里最常见的是和风天气和心知天气,我两家都注册过,最终固定在和风的/v7/weather/3d接口上。先看一张对比表再解释选择理由:

数据源免费额度定位方式常见坑
和风天气开发版每日约 1000 次城市 ID 或经纬度返回字段名多,textDay/textNight 容易记混
心知天气免费版每日次数较少城市拼音(beijing)免费版偶有数据延迟,字段较精简

选和风有三个理由。第一,3 天预报接口一次请求就把今天、明天、后天都拉回来了,早上推送时能顺手预告「明天降温」,比单日报更有用。第二,定位参数用城市 ID 而不是拼音,拼音方案在多音字城市上容易翻车,比如重庆有人写 chongqing 有人写 zhongqing,而城市 ID 是纯数字,没有这种歧义。第三,免费版额度 1000 次/天,每天跑一次脚本只消耗一次配额,余量足够测试和误操作。

注册和风账号是标准套路:进控制台创建项目,选「免费开发版」,生成一串 32 位左右的 API Key;项目详情页里能看到 Key,复制后放到配置区。定位用的城市 ID 在和风官网「城市列表」里搜城市名获取,北京是 101010100,上海是 101020100,直接查自己的城市就行。注意城市 ID 不是行政区划代码,别拿 110000 那种六位行政区划去填 location 参数,接口不认。

2.3 手动拉通接口:curl 验证和 openid 定位

写脚本之前,我习惯先把两个接口用 curl 手动调通。这一步能把「接口配置错」和「代码写错」隔离开,后面写代码时所有报错都能直接归因到代码层。第一个命令验证天气接口:

curl "https://devapi.qweather.com/v7/weather/3d?location=101010100&key=你的APIKey"

正常返回的 JSON 里code是200,daily数组里有三条记录,分别对应今天、明天、后天。如果返回401或403,多半是 Key 复制少了字符,或者项目刚创建还没生效,等一分钟再试。这里注意看daily[0].fxDate是不是今天的日期,如果对不上,说明这个城市 ID 对应的不是你想推的城市,换 ID 重试。

第二个命令验证微信接口,拿一个临时 access_token:

curl "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=你的appID&secret=你的appsecret"

返回 JSON 里出现access_token和expires_in: 7200就说明 appID、appsecret 正确;报errcode: 40013说明 appID 写错了,报errcode: 40125说明 appsecret 写错了,逐个核对粘贴即可。token 拉到手后,顺手把关注者列表拉出来,因为测试号后台「体验者」列表只显示微信号,openid 要通过接口取:

curl "https://api.weixin.qq.com/cgi-bin/user/get?access_token=刚拿到的token"

返回的data.openid数组里就是所有关注者的 openid,一条一个。测试号关注者少,逐条比对微信显示名就能确认哪条是女朋友的,复制进配置里。

最后一步在测试号页面往下拉,找到「模板消息接口」区域,点「新增测试模板」,从模板库选一个天气模板,也可以直接自定义内容。字段名自己起,但保存后脚本传参必须和它一致;模板 ID 那串字符要抄下来,它就是后面脚本里TEMPLATE_ID的值。到这里,账号凭证、天气 Key、openid、模板 ID 四样东西齐全,手动链路已经打通,下一章把它们拼成真正的推送消息。

3. 把天气数据装进模板消息:字段映射与拼装

3.1 模板消息结构:字段名必须逐字对应

微信公众号模板消息,本质上是一段带占位符的文本。在测试号后台点「新增测试模板」,编辑框里写的就是将来用户收到的样式,占位符写法是{{字段名.DATA}}。发消息时脚本往data里传值,微信把占位符替换成对应内容。比如我一直用的模板:

城市:{{city.DATA}} 日期:{{date.DATA}} 天气:{{weather.DATA}} 气温:{{temp.DATA}},风力:{{wind.DATA}} 温馨提示:{{note.DATA}}

字段名(city、date、weather、temp、wind、note)是创建模板时自己起的,但一旦保存,脚本传参就必须和它一字不差,大小写敏感。{{city.DATA}}和脚本里的City都不匹配。这是全项目翻车率最高的地方,避坑章第一条就是它。字段名建议全小写英文单词,复合词用下划线连接,比如temp_max,别用中文或缩写,后期维护省心。

推送接口是message/template/send,一条完整请求的 JSON 结构如下:

{ "touser": "oXXXX-XXXX-XXXX", "template_id": "模板ID", "data": { "city": {"value": "北京"}, "date": {"value": "2025-06-15"}, "weather": {"value": "晴"}, "temp": {"value": "18~28℃"}, "wind": {"value": "东南风 2级"}, "note": {"value": "今天阳光不错"} } }

touser是接收者的 openid,template_id是后台模板列表里那串 ID,data里每个 key 必须命中模板字段。value 是纯文本,单字段最长约 20 个汉字,超长会截断显示;color 是可选配置,网上教程喜欢把温度标红,实际效果一般,默认黑色更干净。我给所有字段都省掉了 color,微信按默认样式渲染,换行和缩进跟随模板原文。

3.2 天气 JSON 解析的几个字段陷阱

和风 3d 接口返回的daily数组里,每一项包含fxDate(日期)、tempMax(最高温)、tempMin(最低温)、textDay(白天天气)、textNight(夜间天气)、windDirDay(白天风向)、windScaleDay(风力等级)。有几个字段非常容易记混,我列一下取值习惯:

  • 推送用textDay表示当天天气,接口文档里没有text这种字段,别凭直觉写。
  • 温度先转int()再拼字符串。tempMax、tempMin是字符串,直接拼会得到"18"~"28"这种带引号的效果,转成整数后拼成18~28℃最自然。
  • windDirDay和windScaleDay免费版偶尔为空,取到空值就不要塞进模板,否则消息里会出现空白字段,用or "微风"兜底。

写解析函数时,我的习惯是先校验code再取数据,而不是直接写resp["daily"][0]。如果接口限流返回错误结构,直接下标取值会抛 KeyError,日志里只有一行难懂的 traceback;加了 code 校验后,错误信息至少能看懂是接口侧的问题:

import requests def get_weather_today(location_id: str, key: str) -> dict: """拉取今日天气摘要,失败时抛异常而不是静默返回空 dict""" url = "https://devapi.qweather.com/v7/weather/3d" params = {"location": location_id, "key": key} resp = requests.get(url, params=params, timeout=10).json() if resp.get("code") != "200": raise RuntimeError(f"天气接口返回异常: {resp}") today = resp["daily"][0] return { "date": today["fxDate"], "weather": today["textDay"], "temp_min": int(today["tempMin"]), "temp_max": int(today["tempMax"]), "wind": f"{today['windDirDay']} {today['windScaleDay']}级", }

这段代码的逻辑分三层:先判 code 拦截接口异常,再取daily[0]作为今天的数据,最后做类型转换和字段拼接。timeout=10是刻意加的,免费接口偶尔会挂起,没有超时的话整个脚本会被一次慢请求卡死,cron 日志里看起来就像什么都没发生过。

注意:daily数组的下标含义固定,[0] 今天、[1] 明天、[2] 后天。想推「明日天气」,把下标改成 1 就行,其余不用动。

3.3 完整推送脚本:三个函数串起整条链路

把前面几节拼起来就是一个独立脚本,不依赖任何框架。三个函数分别负责取 token、取天气、发消息,任何一步出错,日志里能一眼定位。完整代码如下:

import json import requests from datetime import datetime APPID = "wx..." # 测试号 appID APPSECRET = "..." # 测试号 appsecret TEMPLATE_ID = "模板ID" # 测试模板的 template_id OPENID = "oXXXX-XXXX-XXXX" # 关注者 openid QWEATHER_KEY = "..." # 和风 API Key LOCATION_ID = "101010100" # 北京城市 ID def get_access_token() -> str: url = "https://api.weixin.qq.com/cgi-bin/token" params = { "grant_type": "client_credential", "appid": APPID, "secret": APPSECRET, } resp = requests.get(url, params=params, timeout=10).json() if "access_token" not in resp: raise RuntimeError(f"获取 token 失败: {resp}") return resp["access_token"] def send_template_message(token: str, weather: dict) -> dict: url = "https://api.weixin.qq.com/cgi-bin/message/template/send" payload = { "touser": OPENID, "template_id": TEMPLATE_ID, "data": { "city": {"value": "北京"}, "date": {"value": weather["date"]}, "weather": {"value": weather["weather"]}, "temp": {"value": f"{weather['temp_min']}~{weather['temp_max']}℃"}, "wind": {"value": weather["wind"]}, "note": {"value": "早安,记得看天气再出门"}, }, } resp = requests.post(url, json=payload, timeout=10).json() return resp if __name__ == "__main__": token = get_access_token() weather = get_weather_today(LOCATION_ID, QWEATHER_KEY) result = send_template_message(token, weather) print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), json.dumps(result, ensure_ascii=False))

主流程只有三行:拿 token,取天气,发消息。脚本每次运行都实时获取新 token,刻意不做缓存,就为了避开两小时过期问题。网上很多教程教你先 curl 拿 token 再粘进代码,第二天必报 40001,因为 token 早失效了。每次请求 token 的时间成本是一次普通 HTTP GET,对每天跑一次的任务完全不是负担。

参数替换时重点关注四样:APPID和APPSECRET在测试号页面抄;TEMPLATE_ID在测试模板列表里复制;OPENID用 2.3 节user/get接口查出来的那串;LOCATION_ID换成目标城市 ID。四个都替换后手动执行一次,女朋友微信如果收到消息,手动链路就算闭环了,后面只是让它每天自动跑。

4. 定时触发与部署:cron、日志和失败重试

手动跑通只完成了一半,另一半是让脚本每天自动执行。Linux 服务器上最普适的方案是 cron,不需要 systemd 服务,几行配置解决。

4.1 cron 定时:绝对路径和时区

编辑当前用户的定时任务:

crontab -e

首次执行会让你选编辑器,选 nano 或 vim 都可以,进去后加一行:

0 7 * * * cd /home/user/weather-push && /usr/bin/python3 push_weather.py >> weather.log 2>&1

这一行拆开看:0 7 * * *是时间字段,依次是分钟、小时、日期、月份、星期,含义是每天 7:00 执行;cd /home/user/weather-push先把工作目录切到脚本所在目录,避免脚本里用相对路径读取文件时报错;/usr/bin/python3用绝对路径指定解释器,cron 的环境变量比登录 shell 精简得多,不写绝对路径会出现「手动跑没问题,定时任务就是不执行」的玄学;最后的>> weather.log 2>&1把标准输出和标准错误都追加进日志,出问题时有迹可查。

时区是第一次部署最容易踩的坑。服务器默认时区如果是 UTC,cron 的 7 点就是北京时间的 15 点,女朋友下午才收到「早安天气」,数据没错,时间全错,排查半天找不到原因。两个解决办法,我更推荐第二个:

# 方案一:在 crontab 顶部声明时区,只影响 cron 本身 CRON_TZ=Asia/Shanghai 0 7 * * * cd /home/user/weather-push && /usr/bin/python3 push_weather.py >> weather.log 2>&1 # 方案二:改系统时区,脚本里的 datetime.now() 也跟着变 timedatectl set-timezone Asia/Shanghai date # 确认输出是 CST 而不是 UTC

方案二的优势在于脚本里datetime.now()的时间戳和 cron 的调度时间基于同一个时钟,日志时间看起来更顺。改完时区后,用crontab -l确认任务真的写进去了,再手动执行一次脚本确认日志文件可写、路径都正确,之后就可以等定时点位。

4.2 日志与重试:错误码一眼定位问题

脚本扔进 cron 后,唯一的信息出口就是weather.log。如果日志只 print 成功结果,出问题时排查看不到任何线索。我在主流程里加了一层失败日志和有限重试:

import time from datetime import datetime if __name__ == "__main__": token = get_access_token() weather = get_weather_today(LOCATION_ID, QWEATHER_KEY) result = send_template_message(token, weather) if result.get("errcode", 0) != 0: print(f"推送失败: {result}") if result["errcode"] == 40001: token = get_access_token() time.sleep(1) result = send_template_message(token, weather) print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), json.dumps(result, ensure_ascii=False))

逻辑很简单:微信接口成功时返回errcode: 0,非 0 即失败。这里只对 40001 重试,因为 40001 是 access_token 过期或失效,重新获取后重发一次大概率成功;而 47003(模板字段不匹配)、40003(openid 不合法)这类是代码 bug,重试一百次也没用,反而把日志刷爆。time.sleep(1)是为了避开接口频率限制,属于习惯使然。

验证 cron 是否生效,有个很实用的办法:把时间字段临时改成下一分钟,比如当前 14:30,就写29 14 * * *,等一分钟看日志有没有新输出;确认没问题再改回0 7 * * *。这一步比等一个晚上确认快得多,我每次部署新机器都会走一遍。

4.3 部署边界:超时、权限和 API 限额

免费服务器或树莓派上长期跑定时任务,还有三个容易被忽略的边界条件。第一,所有 HTTP 请求都要加超时,前面代码里统一写的timeout=10。公共接口偶尔挂起,没有超时的话脚本会被一次慢请求卡住,cron 认为任务还在跑,第二天日志里什么都没有。第二,日志文件权限。cron 任务以当前用户身份运行,脚本如果放在 root 目录里,日志写不进去,任务静默失败。检查方式:手动执行一次脚本,确认weather.log里真的有内容,文件所有者是你自己的用户。第三,天气 API 的日限额。和风开发版写的是每日 1000 次,每天跑一次用 1 次,理论上用不完,但要小心 Key 泄漏——如果脚本被传到了公开仓库,或日志里无意打印了 Key,被爬虫扫到后额度可能几分钟耗光。怀疑泄漏时,到和风后台重置 Key 即可。

到这一步,整套系统已经从手动进化成每天自动跑:cron 负责叫醒,脚本负责取数发送,日志负责留痕。剩下的问题都在「异常情况怎么处理」上,这也是下一章的主题。

5. 天气推送避坑手册:token 过期、字段不匹配和时区玄学

这一章把我在三台不同机器上复现这个项目时踩过的坑整理成五条,每条都是现象、原因、解决三段,方便对照排查。

5.1 40001:第二天 token 就过期

现象:第一天手动跑通,第二天 cron 日志里出现errcode: 40001,access_token 明明没改过,其他配置也都原样。

原因:access_token 有效期只有 7200 秒,两小时就过期。更隐蔽的是,如果脚本里自己缓存了 token 文件但没有判断过期时间,或者从网上教程复制了「手动获取后硬编码」的写法,第二天必然踩中。微信在同一段时间内多次调用 token 接口,还会让旧 token 提前失效。

解决:脚本每次执行都实时获取新 token,不做缓存。有人担心频率限制把 token 存文件里省请求,最后反而在 token 过期判断上反复出 bug。实时获取是一次普通 HTTP GET,对每天一次的任务毫无压力,是最省心的写法。如果实在要缓存,就同时存下expires_in和获取时间,过期前 5 分钟再刷新,但复杂度完全不值得。

5.2 47003:模板参数不匹配

现象:推送请求返回errcode: 47003,提示 argument invalid,代码语法、接口地址都检查过,就是发不出去。

原因:data里的 key 和测试号后台模板的字段名不一致。最常见的几个场景:模板里写的是{{weather.DATA}},脚本里写成weather_info;字段名大小写不同;模板保存后又改了字段,脚本没有同步。微信端按模板逐字段解析,少一个字段、多一个字段都会报 47003。

解决:把测试号后台的模板原文复制到本地文件,写脚本时照着抄字段名,不要凭记忆打。比对时注意大小写敏感,city和City是两个字段。改完模板字段后,强制手动执行一次脚本确认errcode: 0,再交给 cron。下一章会讲一个用错误值自测字段映射的技巧,能提前暴露这类问题。

5.3 openid 填错:要么发给自己,要么报 40003

现象:消息没有发到女朋友微信,而是发给了自己;或者接口返回errcode: 40003,invalid openid。

原因:openid 是用户在特定公众号下的身份标识,不是微信号,也不是接口返回的昵称。测试号后台「体验者」列表显示的是微信号,有人直接拿微信号填进touser,微信当然不认。另一个隐蔽场景:同一个人关注了测试号和正式号,两边的 openid 是两串完全不同的字符,从别处复制错了就会发给自己或报错。

解决:openid 一律通过 2.3 节的user/get接口拉取,不要从后台页面复制。拉取后和脚本里的OPENID逐字符比对,openid 通常是 28 位左右的字符串,以字母和数字混合。确认后手动发一条,收到消息才算数。

5.4 cron 不执行:时区和路径排查

现象:脚本扔进 cron 后完全没有动静,日志文件空着;或者推送时间比设定整整晚了 8 小时,比如设定 7 点,实际 15 点收到。

原因:时间差 8 小时是时区问题,服务器系统时区是 UTC,cron 按系统时区解释时间字段。完全不执行则看三点:cron 行里用了相对路径、python3 没写绝对路径、日志文件所在目录没有写权限。cron 自己不会告诉你任务失败了,所有错误都只在重定向的日志里。

解决:按 4.1 节先timedatectl set-timezone Asia/Shanghai统一时区,再检查 cron 行是否用了绝对路径。验证用「改到下一分钟」的办法,等一分钟看日志。如果还没输出,去/var/log/syslog里搜CRON关键字,系统会记录每次执行命令和退出状态,这是定位 cron 问题的最后一招。

5.5 天气接口限流:非 200 的重试策略

现象:日志里出现天气接口返回异常,手动执行又恢复正常,频率不高但确实发生过。

原因:免费天气接口的限流策略,或者 Key 被泄漏后被人刷接口。我用和风跑了一年,只遇到过一次连续几分钟非 200 的情况;如果频繁出现,先到 GitHub 或 Gitee 搜索自己的 Key 字符串,确认没被公开,被扫到了就重置 Key。

解决:脚本里保留 code 校验和异常抛出,让问题能出现在日志里;同时给天气请求加timeout=10和重试,失败一次后等 2 秒重试两次,大多数临时限流都能扛过去。如果重试仍然失败,宁可当天不发也不要编造天气数据——女朋友收不到消息最多问一句,收到错误天气才是真翻车。

6. 进阶玩法:多城市、纪念日提醒和推送验证三板斧

基础版能跑通后,改造空间主要在消息内容和可靠性上。三个能力叠加成本很低,效果却很明显。

多城市支持解决异地恋场景:把配置改成城市列表,循环发送,每个城市一个 LocationID 和 openid。核心改动是把单发逻辑包进循环:

CITIES = [ {"name": "北京", "location": "101010100", "openid": "oXXXX-1"}, {"name": "上海", "location": "101020100", "openid": "oXXXX-2"}, ] for city in CITIES: w = get_weather_today(city["location"], QWEATHER_KEY) send_template_message(get_access_token(), city, w) time.sleep(1)

纪念日提醒用日期差计算,塞进模板的 note 字段:

from datetime import date start = date(2023, 5, 20) days = (date.today() - start).days note = f"今天是我们在一起的第 {days} 天"

这里再次体现时区统一的重要性:date.today()用的是系统时区,跨 8 小时边界时日期就差一天,纪念日就算错。

最后是验证三板斧,我每次改完脚本都强制走一遍:第一步手动执行,确认日志里errcode: 0;第二步故意把 data 里某个字段改成错误值重发一次,比如 date 填9999-99-99,确认返回 47003,证明字段校验真的在工作;第三步改回原值,看消息是否按模板正确渲染。

从那以后,我每次改模板字段或者换城市 ID,都会强制走一遍这三步验证流程,几分钟的时间能堵住绝大多数低级错误。拆这类小自动化项目最大的体会是:推送本身不难,难的是把 token 过期、时区偏差、字段映射这些边界条件都摸透,让系统每天无声地稳定运行。完整的脚本和配置清单我整理在下载资源里,第一次搭照着改参数就行,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 5:15:52

STM32开发参考资源与实战避坑指南:从入门到整机项目

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 5:15:26

BL350异构双核MCU深度解析:M4F实时核如何保证工业控制确定性

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 5:14:16

PHP在线客服接入AI知识库:RAG召回与流式输出实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 5:13:16

嵌入式内存管理实战:从链接脚本到缓存一致性的避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 5:12:48

嵌入式与芯片方向本科四年怎么学?从STM32到FPGA的实战路径

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华