☰
1小时用AI模型手搓塔罗牌占卜H5小游戏:TaoToken统一Key接入实战
2026/10/11 20:38:02 网站建设 项目流程

1. 从零手搓塔罗牌占卜 H5:为什么我选统一 Key 接入 AI 模型

塔罗牌占卜 H5 小游戏,说白了就是一个能在手机浏览器里打开、点击抽牌、然后由 AI 模型生成一段占卜解读文案的轻量网页应用。它不需要后端数据库,不需要用户登录,核心逻辑只有三块:牌面随机抽取、占卜文案生成、移动端适配。适合谁做?适合想练手 AI 应用接入的个人开发者,适合想给公众号或社群做个互动小工具的运营同学,也适合刚学完前端基础、想找个真实项目跑通 API 调用流程的初学者。

我自己的场景是这样的:手头有几个不同的模型服务,每次写 Demo 都要翻不同平台的文档、配不同的 Key、记不同的 Base URL,光是环境变量就搞得头大。后来我改用 TaoToken 的统一 Key 通道,一个 API Key 就能切换不同模型,Base URL 统一成https://taotoken.net/api,省掉了大量重复配置的时间。这篇文章就按这个思路,带你从零跑通一个塔罗牌占卜 H5,目标是一小时内看到可交互的 Demo。

整个流程分六步:先明确问题和场景,再配置 TaoToken 的前置环境,然后写可复制的配置文件,接着验证请求是否跑通,再排查常见报错,最后给出接入文档和 API Keys 的入口。你跟着做就行,代码都是完整的,复制粘贴能直接跑。

先说技术选型。H5 页面我用最轻的方式:一个 HTML 文件加原生 JavaScript,不引入框架,避免 npm 安装和构建的等待时间。AI 调用部分用fetch直接请求 TaoToken 的 API 端点,模型 ID 选一个适合中文文案生成的即可。牌面数据用本地 JSON 数组,78 张牌(22 张大阿卡纳加 56 张小阿卡纳)可以先用 22 张大阿卡纳做 Demo,够用且数据量小。移动端适配用 viewport meta 加 CSS flex 布局,保证在手机浏览器里点击区域够大、文字不溢出。

你可能会问,为什么不直接调某个模型的官方接口?因为统一 Key 的好处在于:今天用这个模型生成占卜文案,明天想换另一个模型对比效果,只需要改一个 Model ID 参数,Base URL 和 Key 都不用动。这对做 Demo 和快速验证特别友好。

2. TaoToken 前置准备:统一 Key 与 API 通道配置

在开始写代码之前,你需要先把 TaoToken 的接入信息准备好。这一步不复杂,但漏了后面请求会直接报 401。

首先打开 TaoToken 官网https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=,注册并登录。登录后进入 Console 页面https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=,在 API Keys 管理页https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=创建一个新的 API Key。创建时给它起个名字,比如tarot-demo,方便后面区分。Key 生成后只显示一次,复制下来存到安全的地方。

接下来确认 API 端点。TaoToken 的 API Base URL 是https://taotoken.net/api,注意这个地址不加 UTM 参数,直接用于代码里的请求地址。对话补全的完整路径是https://taotoken.net/api/v1/chat/completions,兼容 OpenAI 的请求格式。这意味着你之前用 OpenAI SDK 写的代码,只需要把base_url改成这个地址、把api_key换成 TaoToken 的 Key,就能直接跑。

模型 ID 怎么选?如果你只是生成塔罗牌解读文案,选一个中文能力好的对话模型就行。在 TaoToken 的模型列表页可以看到当前支持的模型 ID,复制你想要的那个。我实测下来,生成占卜文案这种任务,响应速度比模型参数量更重要,因为用户点完抽牌后等太久会失去耐心。

环境变量建议这样设:在项目根目录创建一个.env文件,写入TAOTOKEN_API_KEY=你的Key。如果你用 Node.js 做本地代理,可以用dotenv加载;如果纯前端直接请求,注意不要把 Key 硬编码在 HTML 里,Demo 阶段可以用一个简单的本地代理转发。后面第三节我会给出两种配置方式。

还有一个细节:TaoToken 的 API 支持流式输出(stream),对于占卜文案这种需要逐字显示的效果,开流式体验更好。请求体里加"stream": true,前端用ReadableStream读取即可。不过为了 Demo 简单,我先用非流式跑通,再给流式的改法。

3. 可复制配置:JSON 与 settings 片段

这一节给你可以直接复制的配置文件。分三种场景:纯前端 fetch 调用、Node.js 本地代理、以及如果你用 Claude Code 或 Cline 这类工具时的 settings 配置。

先看纯前端 fetch 的请求体 JSON。这是最核心的片段,路径和参数都按 TaoToken 的格式来:

{ "model": "你的模型ID", "messages": [ { "role": "system", "content": "你是一位塔罗牌解读师,用温和、鼓励的语气为用户解读牌面。解读控制在150字以内,分三段:牌面含义、当前处境、行动建议。" }, { "role": "user", "content": "我抽到了「愚者」正位,问题是关于职业发展。" } ], "temperature": 0.8, "max_tokens": 500, "stream": false }

对应的 fetch 调用代码:

const response = await fetch("https://taotoken.net/api/v1/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + apiKey }, body: JSON.stringify(requestBody) }); const data = await response.json(); const text = data.choices[0].message.content;

如果你用 Node.js 做本地代理,避免前端暴露 Key,可以建一个server.js:

import express from "express"; import dotenv from "dotenv"; dotenv.config(); const app = express(); app.use(express.json()); app.use(express.static("public")); app.post("/api/tarot", async (req, res) => { const response = await fetch("https://taotoken.net/api/v1/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${process.env.TAOTOKEN_API_KEY}` }, body: JSON.stringify({ model: "你的模型ID", messages: req.body.messages, temperature: 0.8, max_tokens: 500 }) }); const data = await response.json(); res.json(data); }); app.listen(3000, () => console.log("Server running on http://localhost:3000"));

.env文件内容:

TAOTOKEN_API_KEY=sk-你的实际Key

如果你用 Claude Code 接入 TaoToken,配置文件在~/.claude/settings.json,写入:

{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-你的实际Key", "ANTHROPIC_MODEL": "你的模型ID" } }

这三件套——Base URL、Key、Model ID——在 Claude Code、Cline、Codex 的auth.json里都是同样的逻辑。Cline 的 MCP 配置里如果涉及模型调用,也是填这三个值。Codex 的auth.json格式类似,把base_url指向https://taotoken.net/api即可。

注意:配置文件里的路径和字段名要和你实际使用的工具版本一致。Claude Code 的 settings 文件如果不存在就手动创建,JSON 格式不能有注释。

4. 验证请求:从抽牌到文案生成的完整链路

配置写好后,先别急着写完整页面,用一条 curl 命令验证 API 是否通。这是最快确认 Key 和端点正确的方式:

curl -X POST https://taotoken.net/api/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-你的实际Key" \ -d '{ "model": "你的模型ID", "messages": [{"role": "user", "content": "用一句话解释塔罗牌愚者的含义"}], "max_tokens": 100 }'

如果返回 JSON 里有choices[0].message.content字段且内容正常,说明通道没问题。如果返回 401,检查 Key 是否复制完整;如果返回 404,检查 URL 是否写成了https://taotoken.net/api而不是完整的/v1/chat/completions。

API 通了之后,写 H5 页面。完整 HTML 骨架如下:

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>塔罗牌占卜</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, "PingFang SC", sans-serif; background: linear-gradient(160deg, #1a1a2e 0%, #16213e 100%); color: #e8e8e8; min-height: 100vh; display: flex; flex-direction: column; align-items: center; padding: 24px 16px; } h1 { font-size: 22px; margin-bottom: 8px; letter-spacing: 2px; } .subtitle { font-size: 13px; color: #8888aa; margin-bottom: 32px; } .card-area { width: 100%; max-width: 360px; aspect-ratio: 3/5; border-radius: 16px; background: linear-gradient(145deg, #2a2a4a, #1e1e3a); border: 1px solid #3a3a5a; display: flex; align-items: center; justify-content: center; font-size: 48px; margin-bottom: 24px; transition: transform 0.3s; } .card-area.flipped { transform: rotateY(180deg); } button { width: 100%; max-width: 360px; padding: 16px; border: none; border-radius: 12px; background: linear-gradient(90deg, #6a5acd, #8a7aed); color: #fff; font-size: 16px; font-weight: 600; cursor: pointer; transition: opacity 0.2s; } button:disabled { opacity: 0.5; } .result { width: 100%; max-width: 360px; margin-top: 24px; padding: 20px; border-radius: 12px; background: rgba(255,255,255,0.05); border: 1px solid #3a3a5a; font-size: 15px; line-height: 1.8; display: none; } .result.show { display: block; } </style> </head> <body> <h1>塔罗牌占卜</h1> <p class="subtitle">静心默念你的问题,然后抽牌</p> <div class="card-area" id="cardArea">?</div> <button id="drawBtn">抽一张牌</button> <div class="result" id="result"></div> <script> const cards = [ { name: "愚者", meaning: "新的开始、冒险、纯真" }, { name: "魔术师", meaning: "创造力、行动力、资源整合" }, { name: "女祭司", meaning: "直觉、潜意识、内在智慧" }, { name: "皇后", meaning: "丰盛、滋养、创造力" }, { name: "皇帝", meaning: "秩序、权威、稳定" }, { name: "教皇", meaning: "传统、指引、精神导师" }, { name: "恋人", meaning: "选择、关系、价值观" }, { name: "战车", meaning: "意志力、前进、胜利" }, { name: "力量", meaning: "勇气、耐心、内在力量" }, { name: "隐士", meaning: "内省、独处、寻找答案" }, { name: "命运之轮", meaning: "转折、机遇、周期" }, { name: "正义", meaning: "公平、因果、平衡" }, { name: "倒吊人", meaning: "换位思考、等待、牺牲" }, { name: "死神", meaning: "结束、转变、重生" }, { name: "节制", meaning: "平衡、调和、耐心" }, { name: "恶魔", meaning: "束缚、欲望、执念" }, { name: "高塔", meaning: "突变、觉醒、打破旧有" }, { name: "星星", meaning: "希望、灵感、疗愈" }, { name: "月亮", meaning: "潜意识、不安、幻觉" }, { name: "太阳", meaning: "成功、喜悦、生命力" }, { name: "审判", meaning: "觉醒、召唤、重生" }, { name: "世界", meaning: "完成、圆满、整合" } ]; const cardArea = document.getElementById("cardArea"); const drawBtn = document.getElementById("drawBtn"); const resultDiv = document.getElementById("result"); drawBtn.addEventListener("click", async () => { drawBtn.disabled = true; drawBtn.textContent = "解读中..."; resultDiv.classList.remove("show"); const card = cards[Math.floor(Math.random() * cards.length)]; const isReversed = Math.random() < 0.3; const position = isReversed ? "逆位" : "正位"; cardArea.textContent = card.name; cardArea.classList.add("flipped"); const prompt = `我抽到了「${card.name}」${position},牌面关键词是${card.meaning}。请为我解读这张牌,分三段:牌面含义、当前处境、行动建议。控制在150字以内。`; try { const response = await fetch("/api/tarot", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ messages: [ { role: "system", content: "你是一位温和的塔罗牌解读师,用鼓励的语气给出解读。" }, { role: "user", content: prompt } ] }) }); const data = await response.json(); resultDiv.textContent = data.choices[0].message.content; resultDiv.classList.add("show"); } catch (err) { resultDiv.textContent = "解读失败,请检查网络或 API 配置。"; resultDiv.classList.add("show"); } drawBtn.disabled = false; drawBtn.textContent = "再抽一张"; }); </script> </body> </html>

把这段 HTML 保存为public/index.html,配合前面的server.js运行。启动命令:

npm install express dotenv node server.js

浏览器打开http://localhost:3000,点击抽牌按钮,应该能看到牌面翻转并显示 AI 生成的解读文案。在 Chrome 开发者工具里按Ctrl+Shift+M切换到手机预览模式,选一个 iPhone 或 Android 机型,检查布局是否正常、按钮是否好点。

实测下来,从点击到文案显示大约 2 到 4 秒,取决于模型响应速度。如果想让体验更流畅,把stream改成true,前端用response.body.getReader()逐块读取,实现打字机效果。改法是在 server.js 里把stream: true传给 TaoToken,然后把响应流直接 pipe 给前端。

5. 常见报错排查:401、local proxy failed、reading choices

这一节列出我踩过的坑和对应的解法。你遇到报错时先对照这里,大部分问题能直接定位。

401 Unauthorized:最常见。原因通常是 Key 没填对、Key 前面多了空格、或者.env文件没被正确加载。检查process.env.TAOTOKEN_API_KEY是否有值,可以在 server.js 里加一行console.log(process.env.TAOTOKEN_API_KEY ? "Key loaded" : "Key missing")。如果用的是 Claude Code 的 settings.json,检查ANTHROPIC_API_KEY字段名是否写对,JSON 里不能有尾逗号。

local proxy failed / connection refused:这个报错通常出现在你用了本地代理工具但代理没启动,或者代理端口配错。如果你没有用代理,检查 server.js 是否真的在 3000 端口监听,curl http://localhost:3000/api/tarot能不能通。如果用了 Cline 或 Claude Code 的 MCP 配置,检查 MCP server 的启动命令路径是否正确。

Cannot read properties of undefined (reading 'choices'):说明data.choices是 undefined,即 API 返回的结构和你预期的不一样。打印完整的data看看,通常是返回了错误信息,比如{"error": {"message": "model not found"}}。检查 Model ID 是否拼写正确,是否在 TaoToken 支持的模型列表里。另一个可能是请求体里messages格式不对,必须是数组且每个元素有role和content。

OAuth 相关报错:如果你在 Claude Code 里看到 OAuth 错误,说明它还在尝试用 Anthropic 官方登录态。检查settings.json里是否同时设置了ANTHROPIC_BASE_URL和ANTHROPIC_API_KEY,两个都要有。如果之前登录过官方账号,可能需要清除~/.claude下的缓存文件再重启。

CORS 跨域报错:如果你跳过 server.js 直接用前端 fetch 请求 TaoToken,浏览器会拦截跨域请求。解法就是用本地代理转发,或者用cors中间件在 server.js 里加app.use(cors())。Demo 阶段建议走代理,顺便把 Key 藏在服务端。

流式输出乱码:如果用stream: true,前端读取时要注意按\n\n分割事件,每个事件以data:开头。遇到data: [DONE]表示结束。不要直接JSON.parse整个响应体。

移动端点击延迟:在 iOS Safari 上,按钮点击可能有 300ms 延迟。加<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">可以缓解。另外按钮的touch-action: manipulation也能减少延迟。

排查时记住一个原则:先确认 API 通道通不通(用 curl),再确认前端请求发没发出去(看 Network 面板),最后确认返回数据格式对不对(打印 data)。三步定位,基本不会卡住。

6. 接入文档与 API Keys 入口

如果你想把 Demo 继续完善,比如加更多牌面、加正逆位动画、加分享卡片生成,核心的 API 调用逻辑不用变,只需要扩展前端交互和牌面数据。TaoToken 的统一 Key 通道在这里的优势是:你换模型对比文案风格时,只改一个 Model ID,其他配置不动。

需要查看完整的 API 参数说明和模型列表,可以访问接入文档https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=。如果你还没有 API Key,去 API Keys 页面https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=创建一个。想先在网页里直接测试模型对话效果,可以用模型对话入口https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=快速验证。

如果你打算长期做 AI 编码或 Agent 类项目,Coding Plan 页面https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=有更详细的套餐说明。Claude Code 接入的专门说明在https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=,里面有三件套配置的完整示例。

最后给一个实用技巧:把server.js里的模型 ID 和 system prompt 抽成环境变量,这样你可以在不重启服务的情况下,通过改.env文件切换模型或调整解读风格。对于做 Demo 和 A/B 对比特别方便。另外,牌面数据建议存成独立的cards.json,前端用fetch加载,这样加牌不用改 HTML。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询