AI 对话脚手架
Workers AI 流式聊天 + credits 按条计费——一条「AI SaaS 变现」的完整闭环。
/app/ai 是一个可直接售卖的 AI 聊天最小闭环:Workers AI 流式输出 + 每条消息扣
credits。它的价值不在聊天本身,而在把「AI 功能怎么收钱」这条链路完整示范了一遍
——扣费、失败退款、余额不足拦截、未配置降级,全部有测试兜着。
请求链路
POST /api/ai/chat(src/routes/api/ai/chat.ts):
- 会话鉴权:cookie session + org 成员校验(活跃 org,回退第一个 org)。
- 输入校验:
validateMessages限制条数(≤40)与单条长度(≤4000),且必须以 用户消息收尾——防止把纯 assistant 上下文塞给模型刷长回复。 - 先扣后调(
ai.server.ts#chatWithCredits):先spendCredits(每条AI_CHAT_COST,默认 1,在credits.config.ts),余额不足直接 402,一个 token 都不会发给上游;扣费成功后才调ai.run(..., { stream: true })。 - SSE 透传:Workers AI 返回的流原样作为
text/event-stream响应体转发, Worker 不缓冲不改写,首 token 延迟就是上游延迟。
上游失败原路退款:ai.run 抛错时按 <ref>:refund 幂等退回本次扣费,返回 502。
用户视角「没得到回复就没花钱」。扣费用请求级 ref(ai:<uuid>),同一次调用绝不双扣
——幂等与防超扣的机制细节见 Credits 用量计费。
优雅降级
AI 是可选绑定:wrangler.jsonc 里没有 ai block 时,/app/ai 页面显示未启用、
接口返回 503,其他一切照常。计费视角同理——没配 Stripe 只是不能充值,已有余额照扣。
本地 dev 的坑:Workers AI 没有本地模拟,pnpm dev 会为 ai 绑定向 Cloudflare 开一个
远程代理会话(需要已登录 wrangler 的账号)。网络不好时这个握手会超时并拖垮整个
dev 启动。临时解法:删掉本地 wrangler.jsonc 里的 ai block(别动 example 文件),
AI 功能整体隐藏、其余开发不受影响;用完 cp wrangler.example.jsonc wrangler.jsonc
恢复。
换模型 / 调成本
- 模型:
ai.shared.ts的AI_MODEL(默认@cf/meta/llama-3.1-8b-instruct), 换成任意 Workers AI 模型即可, 流式接口不变。 - 单价:
credits.config.ts的AI_CHAT_COST。按条计费是最简单的口径;要按 token 计费,把扣费挪到流结束后按实际用量结算即可——但那要接受「先服务后收费」 的坏账风险,按条预扣是更稳的默认。 - 系统提示词 / 多模型路由:都在
chatWithCredits的aiRun注入点上做, 计费编排不用动。
测试
计费编排(先扣、退款、余额不足)在 workers 池里用注入的假 aiRun 全覆盖
(ai.workers.test.ts),不需要真实 AI 绑定;输入校验是纯函数 node 测试。改动链路时
先跑 pnpm test 再上手工冒烟。