1. 从「每天手动刷一眼」到小程序巡检:这个原型到底解决什么问题
先说清楚这个东西是什么。它是一个跑在微信里的小程序原型,核心动作只有一个:定时去查一次 Codex 的重置状态信号,把结果落到页面上,状态有变化时给你推一条订阅消息。适合谁?适合像我这样每天要开好几次 Codex、又总惦记「今天重置了没」的个人开发者。它不判断你账号的额度,也不替你做任何决策,只负责把「查一次」这件事自动化,让你打开小程序一眼就能看到。
我自己的痛点很具体。写代码写到一半,突然想确认一下当前的重置节奏,于是切浏览器、找页面、刷新、再切回来,一次十几秒,一天来回好几次,节奏全断了。后来我想,这种「定时看一眼、有变化提醒我」的活,天生就该交给小程序:微信常驻,订阅消息能推送,开发成本又低。于是就有了这个巡检原型。
整篇我会按「需求拆解 → 页面结构 → 定时查询 → 消息提醒 → 本地验证」的顺序讲,每一步都给可复制的配置和代码。接口通道这块,我用 TaoToken 统一 Key 来收口,好处是后面不管换模型还是加通道,只改一处配置,小程序端不用动。目标很明确:一天之内,你能跑通一个自己用的巡检小程序。
需要提前说明边界:这个原型查的是公开的重置信号,不涉及任何账号私有数据;小程序本身也不做任何绕过平台规则的事。我们要的是「把公开信息聚合起来定时看」,不是「替你操作账号」。
先看整体结构,心里有个图:
| 模块 | 职责 | 技术点 |
|---|---|---|
| 首页状态卡 | 展示最近一次查询结果 | 云函数返回值渲染 |
| 历史列表 | 看是不是连续变化 | 云数据库分页查询 |
| 定时任务 | 每隔一段时间查一次 | 云函数定时触发器 |
| 消息提醒 | 状态变化时推送 | 订阅消息 |
| 接口通道 | 统一调用入口 | TaoToken 统一 Key |
这张表就是接下来所有步骤的骨架。你可以先把项目建起来,再一块一块往里填。别一上来就想着「做个完整产品」,先把首页那张状态卡跑通,比什么都强。
2. TaoToken 前置准备:统一 Key 与 API 通道怎么配
在写小程序代码之前,先把接口通道准备好。这一步不做,后面云函数里全是空的。我用 TaoToken 做统一入口,原因是它把 Key 和 API 地址收敛成一套,小程序端只认一个 Base URL 和一个 Key,换通道时不用改客户端。
第一步,拿到你的 Key。打开控制台,进 API Keys 页面创建一个:
https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite创建完把 Key 复制出来,形如sk-xxxxxxxx。这个 Key 只存在云函数的环境变量里,绝对不要写进小程序前端代码——前端代码是可以被反编译看到的,Key 泄露等于把通道白送人。
第二步,确认 API 地址。统一入口是:
https://taotoken.net/api注意这个地址不带任何查询参数,就是干净的 Base URL。后面所有请求都基于它拼路径。
第三步,在小程序云开发里配置环境变量。打开微信开发者工具,进「云开发」面板,找到你的云环境,在「云函数配置」里给需要用到的云函数加两个环境变量:
TAOTOKEN_API_KEY = sk-你的Key TAOTOKEN_BASE_URL = https://taotoken.net/api这样云函数里用process.env.TAOTOKEN_API_KEY就能读到,代码里不出现明文。
第四步,验证通道通不通。在本地先用 curl 打一发,确认 Key 和地址都对:
curl -s https://taotoken.net/api/v1/models \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json"返回里能看到模型列表,就说明通道没问题。这一步很关键,很多人后面云函数报 401,其实是 Key 或地址在本地就没通,白白在云函数里排查半天。
关于模型 ID,如果你后面要接对话类能力,常用的写法是claude-sonnet-4-5这类标识,具体以控制台里列出的为准。记住三件套:Base URL、Key、Model ID,缺一个都调不通。这三样配齐,前置就算完成了。
提示:环境变量配好后,云函数需要重新部署一次才会生效。改完配置不重新部署,读到的还是旧值,这是新手最容易踩的坑之一。
3. 可复制配置:云函数、定时触发器与页面结构
这一节全是能直接抄的东西。我按「云函数 → 定时触发器 → 页面结构」的顺序给,你照着建文件就行。
先建云函数目录,名字叫checkReset。它的职责是:调用 TaoToken 通道查询重置信号,把结果写进云数据库,并判断是否需要推送。
云函数的config.json,用来声明定时触发器:
{ "permissions": { "openapi": [] }, "triggers": [ { "name": "resetCheckTimer", "type": "timer", "config": "0 0 9,21 * * * *" } ] }这个 cron 表达式是每天 9 点和 21 点各跑一次。微信云函数的 cron 是七位,最后一位是秒,别写成五位,否则不生效。
云函数主逻辑index.js:
const cloud = require('wx-server-sdk') const axios = require('axios') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db = cloud.database() const BASE_URL = process.env.TAOTOKEN_BASE_URL const API_KEY = process.env.TAOTOKEN_API_KEY exports.main = async () => { const now = new Date() let signal = null let ok = false try { const resp = await axios.post( `${BASE_URL}/v1/chat/completions`, { model: 'claude-sonnet-4-5', messages: [ { role: 'user', content: '请用一句话概括当前公开的重置信号状态,不要编造。' } ], max_tokens: 200 }, { headers: { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, timeout: 15000 } ) signal = resp.data.choices[0].message.content ok = true } catch (err) { signal = `查询失败: ${err.message}` } const record = { time: now, ok, signal, createdAt: db.serverDate() } await db.collection('reset_records').add({ data: record }) if (ok) { await pushNotice(signal) } return record } async function pushNotice(text) { // 订阅消息推送,模板 ID 换成你自己的 try { await cloud.openapi.subscribeMessage.send({ touser: '你的openid', templateId: '你的模板ID', page: 'pages/index/index', data: { thing1: { value: text.slice(0, 20) }, time2: { value: new Date().toLocaleString() } } }) } catch (e) { console.log('推送失败', e) } }注意pushNotice里的touser和templateId要换成你自己的。订阅消息需要用户先在小程序里点一次授权,这个后面页面里会加。
页面结构我建议就三个页面,别贪多:
pages/ index/ 首页,状态卡 + 最近一条记录 history/ 历史列表,分页看连续变化 detail/ 详情,看单条记录的来源和时间首页index.wxml的核心结构:
<view class="card"> <view class="title">当前重置信号</view> <view class="status {{latest.ok ? 'ok' : 'fail'}}"> {{latest.signal || '暂无数据'}} </view> <view class="time">更新于 {{latest.timeText}}</view> </view> <button bindtap="subscribe">开启状态提醒</button> <button bindtap="goHistory">查看历史</button>首页只回答一个问题:现在有什么公开信号。别把历史、说明、入口全堆上来,那样第一眼反而不知道看哪。这是我踩过的坑,页面越满,重点越糊。
数据库集合建一个reset_records,字段就是上面 record 里那几个。权限设成「仅创建者可读写」或者「所有用户可读」,按你的使用场景定。
4. 验证请求与成功结果:本地跑通再上云
配置写完,别急着部署,先在本地把请求验证一遍。这一步能帮你把 90% 的问题挡在云函数之外。
本地验证分两层。第一层,直接验证 TaoToken 通道。用第 2 节那条 curl,确认能返回模型列表。第二层,验证云函数逻辑。在微信开发者工具里,右键checkReset云函数,选「本地调试」,它会用你配置的环境变量跑一次。
如果本地调试不方便,也可以写个独立的 Node 脚本先跑通请求逻辑:
const axios = require('axios') async function test() { const resp = await axios.post( 'https://taotoken.net/api/v1/chat/completions', { model: 'claude-sonnet-4-5', messages: [{ role: 'user', content: '返回一句话即可' }], max_tokens: 100 }, { headers: { Authorization: `Bearer ${process.env.TAOTOKEN_API_KEY}`, 'Content-Type': 'application/json' } } ) console.log(resp.data.choices[0].message.content) } test().catch(console.error)跑通后你会看到类似这样的输出:
当前公开信号显示重置节奏平稳,暂无异常变化。看到这句话,说明通道、Key、模型 ID 三件套全对。这时候再部署云函数,成功率就高很多。
部署后,手动触发一次云函数(开发者工具里点「云端测试」),然后去云开发数据库看reset_records集合,应该多了一条记录。再打开小程序首页,状态卡应该显示刚才那条信号。
验证成功的标志有三个:数据库有新记录、首页状态卡有内容、订阅消息能收到。三个都满足,巡检链路就通了。
注意:定时触发器不会立刻执行,它按 cron 时间点跑。想马上验证,就手动触发一次,别干等。
如果你在首页看到的是「暂无数据」,先别改代码,去数据库确认记录到底写没写进去。数据在、页面空,那是前端读取的问题;数据也没有,那是云函数没跑通。分清楚这两层,排查会快很多。
5. 本篇常见错排查:401、local proxy failed 与 reading choices
这一节我把真实会遇到的报错列出来,对照着查。
报错一:401 Unauthorized。这是最常见的。原因通常是三个:Key 写错、Key 没配进环境变量、环境变量改了没重新部署。排查顺序是:先在本地 curl 用同一个 Key 打一次,本地通、云端不通,那就是环境变量或部署问题;本地也不通,那就是 Key 本身的问题,回控制台重新复制一次。注意复制时别带空格,很多人从网页复制会多一个换行。
报错二:local proxy failed。这个报错一般出现在本地调试阶段,意思是本地请求没发出去。检查你的网络能不能正常访问https://taotoken.net/api,以及 Base URL 是不是写成了带路径的形式。Base URL 就是干净的https://taotoken.net/api,后面拼/v1/chat/completions,别自己多加斜杠或者少写。
报错三:Cannot read properties of undefined (reading 'choices')。这个报错说明请求发出去了,但返回结构里没有choices。原因通常是返回了一个错误对象,而你的代码直接去取resp.data.choices[0]。正确做法是先判断:
if (resp.data && resp.data.choices && resp.data.choices.length) { signal = resp.data.choices[0].message.content } else { signal = `返回异常: ${JSON.stringify(resp.data)}` }把原始返回打出来,你就能看到到底是限流、参数错还是模型 ID 不对。
报错四:OAuth 相关报错。如果你在配置订阅消息或某些开放接口时看到 OAuth 字样,多半是openid或模板 ID 不对。订阅消息的touser必须是真实用户openid,模板 ID 必须是你自己小程序后台申请的那个。用别人的模板 ID 一定失败。
报错五:定时任务不触发。检查config.json里的 cron 是不是七位,以及云函数有没有重新部署。触发器配置改了不部署,等于没改。
排查时记住一个原则:先分层,再定位。通道层(curl 能不能通)、云函数层(数据库有没有记录)、页面层(首页有没有渲染),一层一层往下查,别一上来就改代码。
6. 把巡检跑成日常:接入文档与后续扩展
链路跑通之后,剩下的就是让它稳定跑起来。定时任务每天两次,你打开小程序就能看到最新状态,有变化时订阅消息会推给你。这套东西的价值不在于多复杂,而在于它替你省掉了「每天手动刷一眼」这个动作。
如果你想把通道能力用得更顺,接入文档在这里,里面有完整的参数说明和示例:
https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite想直接在网页里试模型返回,可以用模型对话页:
https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite如果你后面不只是做巡检,还想把 Codex 相关的编码任务、Agent 流程也接进来,可以看看 Coding Plan:
https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite后续扩展我给两个方向。一是把历史页做成趋势图,用云数据库聚合出「最近七天信号变化」,这样一眼能看出是偶发还是连续。二是把查询频率做成可配置,比如工作日查得勤一点、周末少一点,避免无意义的请求。
最后说个实用技巧:云函数的日志一定要看。开发者工具里点开云函数日志,每次执行的返回、报错、耗时都在里面。我调这个原型时,大部分问题都是靠日志定位的,比盲改代码快得多。把日志当第一手信息,你的排查效率会高一个档次。