SmallThinker-3B-Preview快速上手JavaScript交互式AI应用开发教程最近有不少前端的朋友问我现在大模型这么火能不能用我们最熟悉的JavaScript来玩一玩答案是肯定的。今天我就带大家用最基础的JavaScript快速上手调用部署在星图GPU平台上的SmallThinker-3B-Preview模型做一个简单的浏览器端聊天机器人。整个过程不需要你懂Python也不需要复杂的后端知识只要会写点HTML、CSS和JavaScript就能让AI在你的网页里“活”起来。咱们的目标很简单一小时内让你看到自己的网页能和AI对话。1. 准备工作你需要知道的基础知识在开始敲代码之前我们先花几分钟把几个关键概念理清楚这样后面操作起来会更顺畅。1.1 什么是SmallThinker-3B-PreviewSmallThinker-3B-Preview是一个轻量级的开源大语言模型你可以把它理解成一个“小号”的智能助手。虽然参数规模不大但它在日常对话、文本生成、简单推理这些任务上表现还不错特别适合我们这种想快速上手、在浏览器端做点小应用的场景。它的优势就是“快”和“轻”。部署在星图GPU平台上后我们可以通过标准的网络接口来调用它就像调用一个普通的在线服务一样。1.2 核心概念RESTful API你可能听过这个词但别被它吓到。简单来说API就是一个“菜单”而RESTful是一种点菜的方式。想象一下你去餐厅API地址就是餐厅的地址。请求方法比如POST就像你告诉服务员“我要点菜”。请求头就像你告诉服务员“我要用中文菜单”。请求体就是你具体点的菜名和口味要求。我们调用SmallThinker模型本质上就是向星图平台提供的这个“餐厅地址”发一个“点菜请求”然后等着“厨房”也就是GPU服务器做好“菜”生成文本给我们送回来。1.3 你需要准备的工具工欲善其事必先利其器。下面这几样东西我猜你电脑上应该都有一个现代浏览器Chrome、Edge、Firefox都行版本别太老。一个代码编辑器VS Code、Sublime Text、甚至记事本都可以。我用的是VS Code因为它的插件和提示功能很友好。一个本地Web服务器可选但推荐因为后面我们会用JavaScript直接发网络请求有些浏览器出于安全考虑不允许从本地文件直接发请求。最简单的办法是如果你用VS Code可以安装“Live Server”插件一键启动本地服务器。或者在项目文件夹里打开终端运行npx serve或python -m http.server 8000也可以。好了理论部分就这么多。我知道你可能已经等不及要写代码了那咱们现在就动手。2. 第一步获取API访问凭证调用任何在线服务第一步都是“敲门”告诉对方你是谁。对于星图平台的API我们需要两样东西API地址和API密钥。2.1 找到你的API地址和密钥首先你需要在星图GPU平台上成功部署SmallThinker-3B-Preview模型。部署成功后平台会提供一个访问地址通常长这样https://your-instance-id.csdn.net/v1。这个地址就是我们的“餐厅地址”请把它记下来。在平台的控制台找到API密钥管理的地方创建一个新的密钥。这个密钥就像你的“会员卡”或“密码”每次点菜都要出示。请务必妥善保管这个密钥不要把它直接硬编码在前端代码里并上传到公开仓库这会有安全风险。我们稍后会讲到更安全的做法。2.2 创建一个安全的配置文件为了避免密钥泄露我们创建一个单独的配置文件并把它加入.gitignore。在项目根目录下创建一个名为config.js的文件// config.js - 这个文件不要提交到Git仓库 const API_CONFIG { // 替换成你从星图平台获取的实际地址 BASE_URL: https://your-actual-instance-id.csdn.net/v1, // 替换成你从星图平台获取的实际密钥 API_KEY: sk-your-actual-api-key-here }; // 导出配置以便在其他文件中使用 if (typeof module ! undefined module.exports) { module.exports API_CONFIG; // 用于Node.js环境测试 }重要提示config.js这个文件只存在于你的本地开发环境。在实际部署时你应该使用环境变量或后端服务来管理这些敏感信息。对于纯前端演示我们可以暂时这样处理但务必确保它不会被公开访问。3. 第二步编写核心API调用函数有了地址和钥匙我们就可以编写“点餐员”了。这个“点餐员”就是一个JavaScript函数负责把我们的问题打包、发送给服务器并处理返回的答案。在项目根目录下创建一个api.js文件// api.js - 负责与SmallThinker API通信的核心模块 import { API_CONFIG } from ./config.js; class SmallThinkerClient { constructor() { this.baseUrl API_CONFIG.BASE_URL; this.apiKey API_CONFIG.API_KEY; } /** * 向SmallThinker模型发送对话请求 * param {Array} messages - 对话历史数组格式见下方示例 * param {Object} options - 可选参数如温度、最大生成长度等 * returns {Promisestring} - 模型生成的回复内容 */ async chat(messages, options {}) { // 1. 准备请求的URL const url ${this.baseUrl}/chat/completions; // 2. 准备请求的“菜单”请求体 const requestBody { model: smallthinker-3b-preview, // 指定模型 messages: messages, // 对话历史 stream: false, // 我们先使用非流式响应更简单 ...options // 合并用户传入的额外参数 }; // 3. 准备“点餐方式”请求头 const headers { Content-Type: application/json, Authorization: Bearer ${this.apiKey} // 携带API密钥 }; try { // 4. 发送“点餐请求” const response await fetch(url, { method: POST, headers: headers, body: JSON.stringify(requestBody) }); // 5. 检查“餐厅”是否正常接单 if (!response.ok) { const errorData await response.json(); throw new Error(API请求失败: ${response.status} - ${errorData.error?.message || 未知错误}); } // 6. 接收“做好的菜”响应数据 const data await response.json(); // 7. 从响应中提取模型的回复文本 // 通常回复在 choices[0].message.content 里 const reply data.choices?.[0]?.message?.content; if (!reply) { throw new Error(API响应格式异常未找到回复内容); } return reply; } catch (error) { // 8. 处理“送餐失败”等异常情况 console.error(调用SmallThinker API时出错:, error); throw error; // 将错误抛给上层调用者处理 } } /** * 一个简化版的单轮对话函数不需要维护历史 * param {string} userInput - 用户输入的问题 * returns {Promisestring} - 模型的回复 */ async ask(userInput) { const messages [ { role: user, content: userInput } ]; return await this.chat(messages); } } // 导出这个客户端类方便在其他地方使用 export default SmallThinkerClient;我来解释一下这个函数的关键部分fetch这是浏览器内置的、用于发起网络请求的函数我们用它来和服务器通信。async/await这是处理异步操作比如等网络回复的现代语法让代码看起来像同步的一样直观。messages格式这是与大模型对话的标准格式。它是一个数组里面每个对象都有role角色是user用户还是assistant助手和content内容。通过这个数组我们可以实现多轮对话。错误处理我们用try...catch包裹了网络请求这样即使网络出错或API返回错误我们的程序也不会直接崩溃而是能给出友好的提示。4. 第三步构建一个简单的聊天界面光有后台“点餐员”还不够我们得给用户一个能输入问题、看到回答的“餐厅前台”。这就是我们的网页界面。创建一个index.html文件!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleSmallThinker 迷你聊天助手/title style * { box-sizing: border-box; margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif; } body { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; display: flex; justify-content: center; align-items: center; padding: 20px; } .chat-container { width: 100%; max-width: 800px; height: 80vh; background: white; border-radius: 20px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.1); display: flex; flex-direction: column; overflow: hidden; } .chat-header { background: linear-gradient(90deg, #4776E6 0%, #8E54E9 100%); color: white; padding: 20px 30px; text-align: center; } .chat-header h1 { font-size: 1.8rem; margin-bottom: 5px; } .chat-header p { opacity: 0.9; font-size: 0.9rem; } .chat-messages { flex: 1; padding: 25px; overflow-y: auto; display: flex; flex-direction: column; gap: 20px; } .message { max-width: 80%; padding: 15px 20px; border-radius: 18px; line-height: 1.5; animation: fadeIn 0.3s ease-out; } .user-message { align-self: flex-end; background: #4776E6; color: white; border-bottom-right-radius: 5px; } .bot-message { align-self: flex-start; background: #f0f2f5; color: #333; border-bottom-left-radius: 5px; } .thinking { align-self: flex-start; background: #f0f2f5; color: #888; font-style: italic; } .chat-input-area { border-top: 1px solid #eee; padding: 20px 30px; display: flex; gap: 15px; } #userInput { flex: 1; padding: 15px 20px; border: 2px solid #e1e5eb; border-radius: 12px; font-size: 1rem; outline: none; transition: border 0.3s; } #userInput:focus { border-color: #4776E6; } #sendButton { background: linear-gradient(90deg, #4776E6 0%, #8E54E9 100%); color: white; border: none; border-radius: 12px; padding: 0 30px; font-size: 1rem; font-weight: 600; cursor: pointer; transition: transform 0.2s, box-shadow 0.2s; } #sendButton:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(71, 118, 230, 0.3); } #sendButton:disabled { background: #ccc; cursor: not-allowed; transform: none; box-shadow: none; } keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } /* 滚动条样式 */ .chat-messages::-webkit-scrollbar { width: 8px; } .chat-messages::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 4px; } .chat-messages::-webkit-scrollbar-thumb { background: #c1c1c1; border-radius: 4px; } .chat-messages::-webkit-scrollbar-thumb:hover { background: #a8a8a8; } /style /head body div classchat-container div classchat-header h1 SmallThinker 迷你助手/h1 p基于 SmallThinker-3B-Preview 模型的浏览器端对话演示/p /div div classchat-messages idchatMessages !-- 初始欢迎消息 -- div classmessage bot-message 你好我是基于 SmallThinker-3B 模型驱动的聊天助手。我已经准备好为你解答问题或进行对话了。试试问我点什么吧 /div /div div classchat-input-area input typetext iduserInput placeholder输入你的问题按回车发送... autocompleteoff button idsendButton发送/button /div /div !-- 引入我们的JavaScript模块 -- script typemodule srcapp.js/script /body /html这个界面虽然简单但该有的都有了一个显示对话历史的区域、一个输入框和一个发送按钮。样式也稍微美化了一下看起来不那么“简陋”。5. 第四步用JavaScript让界面“活”起来现在我们需要把前面写的API调用函数和这个漂亮的界面连接起来。创建app.js文件// app.js - 前端应用的主逻辑连接界面与API import SmallThinkerClient from ./api.js; // 等待页面完全加载后再执行我们的代码 document.addEventListener(DOMContentLoaded, () { // 1. 获取页面上的关键元素 const chatMessages document.getElementById(chatMessages); const userInput document.getElementById(userInput); const sendButton document.getElementById(sendButton); // 2. 创建我们的AI客户端实例 const aiClient new SmallThinkerClient(); // 3. 用一个数组来保存完整的对话历史 // 格式: [{ role: user, content: ... }, { role: assistant, content: ... }] let conversationHistory [ { role: assistant, content: 你好我是基于 SmallThinker-3B 模型驱动的聊天助手。我已经准备好为你解答问题或进行对话了。试试问我点什么吧 } ]; /** * 向聊天区域添加一条新消息 * param {string} text - 消息文本 * param {string} sender - user 或 bot */ function addMessageToChat(text, sender) { const messageDiv document.createElement(div); messageDiv.className message ${sender}-message; messageDiv.textContent text; chatMessages.appendChild(messageDiv); // 自动滚动到最新消息 chatMessages.scrollTop chatMessages.scrollHeight; } /** * 显示“思考中...”的提示 */ function showThinkingIndicator() { const thinkingDiv document.createElement(div); thinkingDiv.className message thinking; thinkingDiv.id thinkingIndicator; thinkingDiv.textContent 思考中...; chatMessages.appendChild(thinkingDiv); chatMessages.scrollTop chatMessages.scrollHeight; } /** * 移除“思考中...”的提示 */ function removeThinkingIndicator() { const indicator document.getElementById(thinkingIndicator); if (indicator) { indicator.remove(); } } /** * 处理用户发送消息 */ async function handleSendMessage() { const userText userInput.value.trim(); // 如果输入框是空的什么都不做 if (!userText) return; // 1. 清空输入框并暂时禁用发送按钮 userInput.value ; sendButton.disabled true; userInput.disabled true; // 2. 在界面上显示用户的消息 addMessageToChat(userText, user); // 3. 将用户消息添加到历史记录 conversationHistory.push({ role: user, content: userText }); // 4. 显示“思考中...”提示 showThinkingIndicator(); try { // 5. 调用AI API获取回复 // 注意这里我们把整个对话历史都传过去模型就能知道上下文 const botReply await aiClient.chat(conversationHistory); // 6. 移除“思考中...”提示 removeThinkingIndicator(); // 7. 在界面上显示AI的回复 addMessageToChat(botReply, bot); // 8. 将AI回复也添加到历史记录 conversationHistory.push({ role: assistant, content: botReply }); } catch (error) { // 9. 如果出错了移除“思考中...”提示并显示错误信息 removeThinkingIndicator(); addMessageToChat(抱歉出错了: ${error.message}。请检查网络或API配置。, bot); console.error(对话出错:, error); } finally { // 10. 无论成功失败都重新启用输入框和按钮 sendButton.disabled false; userInput.disabled false; userInput.focus(); // 让输入框重新获得焦点方便继续输入 } } // 绑定发送按钮的点击事件 sendButton.addEventListener(click, handleSendMessage); // 绑定输入框的回车键事件 userInput.addEventListener(keypress, (event) { if (event.key Enter !event.shiftKey) { event.preventDefault(); // 防止在输入框中换行 handleSendMessage(); } }); // 页面加载后让输入框自动获得焦点 userInput.focus(); });这段代码做了几件关键的事情事件监听监听了发送按钮的点击和输入框的回车键这样用户就能方便地发送消息。状态管理用conversationHistory数组保存了完整的对话记录。每次发送新消息时都把整个历史传给API这样AI就能记住之前的对话内容实现真正的多轮对话。用户体验添加了“思考中...”的提示、发送时禁用按钮、错误处理等细节让交互更友好。模块化通过import引入了我们之前写的SmallThinkerClient类代码结构清晰。6. 运行你的第一个AI聊天应用所有文件都准备好了现在让我们看看成果。确保你的项目文件夹结构是这样的your-project-folder/ ├── index.html ├── config.js (记得填入你的真实API地址和密钥) ├── api.js └── app.js启动一个本地Web服务器。如果你用VS Code的Live Server直接右键index.html选择“Open with Live Server”。或者用前面提到的命令行方式。在浏览器中打开http://localhost:5500端口号可能不同。你应该能看到我们设计的聊天界面。在输入框里试着问点问题比如“你好介绍一下你自己”或者“用JavaScript写一个简单的计算器函数”。如果一切配置正确几秒钟后你就能看到SmallThinker模型的回复出现在聊天窗口里了恭喜你你已经用纯前端技术实现了一个能对话的AI应用。7. 遇到问题怎么办第一次运行很可能不会一帆风顺。别担心这是学习的一部分。下面是一些常见问题和排查思路问题页面空白控制台报错“Failed to fetch”或跨域错误CORS。原因浏览器出于安全限制阻止了从本地文件或不同域发起的请求。解决确保你通过本地服务器如Live Server访问页面而不是直接双击打开index.html文件。API服务端也需要正确配置CORS头星图平台的镜像通常已配置好。问题发送消息后一直显示“思考中...”没有回复。排查打开浏览器的开发者工具F12切换到“网络”Network标签页查看发送的请求。可能原因1config.js中的BASE_URL或API_KEY填错了。检查地址末尾是否有/v1密钥是否正确。可能原因2API服务器响应慢或暂时不可用。查看网络请求的响应状态码和返回信息。问题能收到回复但回复是乱码或格式不对。排查检查api.js中解析响应数据的部分data.choices?.[0]?.message.content。有时候API返回的格式可能有细微差别根据实际返回结构调整代码。问题对话没有上下文AI不记得之前说过的话。检查确保app.js中的conversationHistory数组正确地将每轮对话的user和assistant消息都push进去了并且在调用aiClient.chat()时传递的是整个历史数组。调试的关键就是多用浏览器的开发者工具看看控制台Console的报错信息观察网络Network请求的发送和接收情况。8. 下一步可以玩什么现在你的基础聊天机器人已经跑起来了但这只是个开始。你可以基于这个“骨架”添加更多有趣的功能美化界面给不同的消息添加头像、时间戳让AI的回复像打字机一样逐字出现流式输出。探索流式响应修改api.js中的stream: false为stream: true然后处理服务器返回的数据流实现打字机效果。这需要处理fetch返回的ReadableStream。添加对话管理增加“新话题”按钮来清空历史把对话历史保存到浏览器的localStorage刷新页面也不会丢失。尝试模型参数在调用chat()方法时传入options对象比如{ temperature: 0.7, max_tokens: 500 }看看“温度”参数如何影响回答的随机性“最大生成长度”如何控制回复长短。集成到你的项目把这个聊天组件打包成一个独立的模块嵌入到你现有的网站或应用中。动手去改代码去试错这才是学习最快的方式。这个小小的项目已经包含了现代Web应用与AI交互的核心流程前端界面、用户交互、网络请求、状态管理。理解了这些你再去看那些复杂的AI应用就会发现它们的基本原理都是相通的。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。