ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

trae solo开发实战:用Electron+Vue3打造AI情绪盲盒治愈桌面应用,TaoToken统一Key接入

trae solo开发实战:用Electron+Vue3打造AI情绪盲盒治愈桌面应用,TaoToken统一Key接入 1. 从零跑通一个 AI 情绪盲盒桌面应用到底难在哪先说清楚这篇要做什么用 Electron Vue3 搭一个「情绪盲盒」治愈系桌面应用用户选一种情绪点开盲盒AI 生成一句治愈语录同时把 AI 调用统一收敛到 TaoToken 的 Key 上避免在项目里散落七八套不同厂商的鉴权逻辑。适合谁看会一点 Vue3、想入门 Electron 桌面端、又希望顺手把 AI 对话闭环跑通的前端同学。我试过在渲染层直接写死各家 API 的 endpoint 和 Key结果就是设置页越写越像配置中心加一个模型要改三处代码。情绪盲盒这类应用的核心交互其实很轻——选情绪、抽盲盒、拿一句回复——真正拖慢进度的是「AI 接入」这块的重复劳动。所以这篇的路线是Electron 主进程管窗口和本地文件Vue3 渲染层管界面和状态AI 请求统一走 TaoToken 的 OpenAI 兼容接口一个 Key、一个 base_url 就能切换模型。读完你能拿到三样东西一份能直接抄的 Electron Vue3 目录骨架一份settings.json/config.toml的 TaoToken 配置模板以及一次从「点开盲盒」到「AI 返回语录」的端到端验证动作。全程不涉及任何网络工具只用一个标准 HTTPS 接口。2. TaoToken 前置一个 Key 收口所有模型调用2.1 为什么盲盒应用适合统一 Key情绪盲盒的 AI 调用场景很碎抽语录、写日记分析、生成小故事、AI 对话每个场景对模型的要求不一样。语录要快、要便宜日记分析要稳对话要上下文能力好。如果每个场景接一家厂商设置页会变成一张巨大的表单用户填 Key 填到崩溃。TaoToken 在这里的角色是「统一入口」它提供 OpenAI 兼容的/v1/chat/completions接口你只维护一个base_url和一个 Key模型名通过参数切换。对桌面应用来说这意味着设置页只需要两个输入框本地存储也只需要存一份凭证。官网地址https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址https://taotoken.net/api注意 API 地址不带任何查询参数配置时直接填https://taotoken.net/api作为 base具体路径由 SDK 或手写请求拼接。2.2 拿 Key 与选模型进入控制台创建 API Key建议给桌面应用单独建一个 Key方便后续按项目撤销。模型对话可以在模型对话页先试跑一句确认返回正常再写进代码。控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewriteAPI Keyshttps://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite模型对话https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite如果你后面要长期跑编码类 Agent 或批量生成可以看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite2.3 桌面应用里的凭证存放原则桌面应用和 Web 不一样Key 存在本地文件里用户自己能看见。所以原则是Key 只存本地、不上传、不写进代码仓库。Electron 里推荐存到用户数据目录的settings.json而不是打包进 asar。下面第三节会给完整模板。3. 可复制配置Electron Vue3 骨架与 TaoToken 接入3.1 目录结构先给一份最小可跑的骨架和情绪盲盒的功能模块对应emotion-box/ ├── electron/ │ ├── main.js # 主进程窗口、文件读写、IPC │ └── preload.js # contextBridge 安全桥接 ├── src/ │ ├── components/ │ │ ├── Home.vue # 情绪选择 │ │ ├── BoxOpen.vue # 盲盒动画 触发 AI │ │ └── Result.vue # 语录展示 │ ├── services/ │ │ └── aiService.js # 统一 AI 调用走 TaoToken │ ├── stores/ │ │ └── app.js # Pinia 全局状态 │ ├── App.vue │ └── main.js ├── index.html ├── package.json └── vite.config.js主进程和渲染层的分工要一开始就定死主进程只做窗口管理、文件系统读写、IPC 转发渲染层只做 UI 和状态。AI 请求放在渲染层发因为它是标准 HTTPS不需要 Node 能力这样也避免了把 Key 传进主进程再传回来。3.2 主进程与 preload 桥接electron/main.js里关掉 nodeIntegration、打开 contextIsolation这是桌面应用的底线配置// electron/main.js const { app, BrowserWindow, ipcMain } require(electron) const path require(path) const fs require(fs) const SETTINGS_PATH path.join(app.getPath(userData), settings.json) function createWindow() { const win new BrowserWindow({ width: 1000, height: 720, webPreferences: { preload: path.join(__dirname, preload.js), nodeIntegration: false, contextIsolation: true } }) win.loadURL(http://localhost:5173) } ipcMain.handle(settings:read, async () { if (!fs.existsSync(SETTINGS_PATH)) return null return JSON.parse(fs.readFileSync(SETTINGS_PATH, utf-8)) }) ipcMain.handle(settings:write, async (_e, data) { fs.writeFileSync(SETTINGS_PATH, JSON.stringify(data, null, 2), utf-8) return true }) app.whenReady().then(createWindow)electron/preload.js只暴露必要方法// electron/preload.js const { contextBridge, ipcRenderer } require(electron) contextBridge.exposeInMainWorld(electronAPI, { readSettings: () ipcRenderer.invoke(settings:read), writeSettings: (data) ipcRenderer.invoke(settings:write, data) })3.3 TaoToken 配置模板settings.json设置页保存后落到用户数据目录结构如下。baseUrl固定为 TaoToken 的 API 地址model按场景可切换{ theme: light, ai: { provider: taotoken, baseUrl: https://taotoken.net/api, apiKey: sk-你的Key, model: gpt-4o-mini, timeout: 20000 }, fallbackToLocal: true }如果你更习惯 TOML比如后续接 CLI 工具等价写法[ai] provider taotoken base_url https://taotoken.net/api api_key sk-你的Key model gpt-4o-mini timeout 20000 [app] fallback_to_local true注意apiKey只写进本地settings.json不要提交到 Git。可以在.gitignore里加上settings.json仓库里只保留settings.example.json。3.4 统一 AI 调用服务src/services/aiService.js是核心所有场景都走这一个函数// src/services/aiService.js export async function chatWithAI({ settings, systemPrompt, userPrompt }) { const { baseUrl, apiKey, model, timeout } settings.ai const controller new AbortController() const timer setTimeout(() controller.abort(), timeout) try { const res await fetch(${baseUrl}/v1/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${apiKey} }, body: JSON.stringify({ model, messages: [ { role: system, content: systemPrompt }, { role: user, content: userPrompt } ], temperature: 0.9 }), signal: controller.signal }) if (!res.ok) throw new Error(HTTP ${res.status}) const data await res.json() return data.choices?.[0]?.message?.content?.trim() || } finally { clearTimeout(timer) } }盲盒抽语录时这样调用失败就降级到本地语录库// src/components/BoxOpen.vue 片段 import { chatWithAI } from ../services/aiService import { localQuotes } from ../assets/quotes async function drawQuote(emotion, settings) { try { const text await chatWithAI({ settings, systemPrompt: 你是一位温柔的治愈系陪伴者回复不超过40字。, userPrompt: 我现在感到${emotion}请给我一句治愈的话。 }) if (text) return { text, source: ai } } catch (e) { console.warn(AI 不可用降级本地语录, e) } const pool localQuotes[emotion] || localQuotes.default return { text: pool[Math.floor(Math.random() * pool.length)], source: local } }这段降级逻辑是桌面应用的关键AI 挂了、Key 没填、断网了盲盒照样能开只是没有光晕特效。4. 验证请求从点开盲盒到 AI 返回语录4.1 先单独验证接口在写进 Vue 之前先用一条命令确认 Key 和 base_url 是对的。把下面存成test.mjs替换你的 Key 后运行node test.mjs// test.mjs const res await fetch(https://taotoken.net/api/v1/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer sk-你的Key }, body: JSON.stringify({ model: gpt-4o-mini, messages: [{ role: user, content: 用一句话安慰今天很累的人。 }] }) }) const data await res.json() console.log(data.choices?.[0]?.message?.content)返回一段中文安慰语说明链路通了。如果返回 401是 Key 问题返回 404检查 base 后面有没有多写/v1。4.2 端到端验证动作启动开发环境npm install npm run electron:dev预期结果分三步首页出现 14 种情绪按钮点击「焦虑」后盲盒播放摇晃动画约 1.5 秒动画结束展示一句 AI 语录且语录卡片带光晕边框source ai时加 class。如果展示的是本地语录、没有光晕说明 AI 调用失败走了降级去控制台看console.warn的内容。4.3 用设置页做连接测试在设置页加一个「测试连接」按钮复用同一个chatWithAI只是 prompt 换成固定短句async function testConnection(settings) { const text await chatWithAI({ settings, systemPrompt: 你是连接测试助手。, userPrompt: 回复ok两个字母即可。 }) return text.includes(ok) }这样用户填完 Key 能立刻知道对不对不用等抽盲盒才发现。5. 本篇常见错排查5.1 渲染层拿不到 electronAPI现象是window.electronAPI为 undefined。原因通常是 preload 路径写错或者contextIsolation被关掉了。检查main.js里preload是否用了path.join(__dirname, preload.js)以及webPreferences里contextIsolation: true。开发模式下 Vite 端口变了不影响 preload但打包后路径要用app.getAppPath()拼。5.2 请求报 CORS 或 403Electron 渲染层发 fetch 默认不受浏览器同源限制但如果你在webSecurity上做了奇怪配置或者请求头里带了多余字段可能触发 403。标准做法是只带Content-Type和Authorization两个头不要手动加Origin。TaoToken 的接口是标准 HTTPS正常配置下不会出现 CORS 问题。5.3 盲盒动画和 AI 请求打架盲盒摇晃 1.5 秒如果等动画结束才发请求用户会多等一个网络往返。正确做法是动画开始的同时就发请求动画结束时用Promise.race或直接 await 结果谁慢等谁。这样感知延迟从「1.5s 网络」降到「max(1.5s, 网络)」。5.4 打包后 Key 丢失开发时settings.json在项目目录打包后app.getPath(userData)指向%APPDATA%/emotion-box/两个路径不一样。所以读写设置必须统一走 IPC不要在渲染层用localStorage存 Key否则换台机器或重装就没了。5.5 模型名写错返回 400不同模型名对不上会返回 400 或 model not found。先在模型对话页确认可用模型名再写进settings.json。切换模型只改这一个字段不用动代码这正是统一 Key 的价值。6. 把闭环收在桌面端情绪盲盒这类应用的技术难点不在 UI而在「AI 调用要稳、要能降级、要能换模型」。把 TaoToken 作为统一入口后你的设置页只需要维护baseUrl、apiKey、model三个字段其余全是业务逻辑。Electron 主进程负责把这份配置安全落到本地渲染层负责在动画和请求之间做并发编排本地语录库负责兜底。下一步可以做的把 AI 对话历史也存进用户数据目录给盲盒加一个「再抽一次」的节流避免用户连点打爆接口。如果你要长期跑编码类任务或 Agent可以了解 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite接入文档在这里https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewriteKey 管理在 API Keys 页https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite想先试模型效果就去模型对话https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite官网入口https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content
返回列表