☰
纯HTML+JavaScript调用蓝耘MaaS平台,实战接入DeepSeek-V3.1-Terminus
2026/10/8 4:09:26 网站建设 项目流程

1. 整体设计与思路拆解

1.1 为什么选蓝耘元生代MaaS平台来调DeepSeek-V3.1-Terminus

先讲清楚一个现实:现在大模型能力确实强,但普通人想在自己网页里稳定跑起来,不是填个API地址那么简单。DeepSeek-V3.1-Terminus这个模型本身的权重和推理部署门槛不低,显存、算力、并发调度、负载均衡,每一项都能劝退一批刚入门的人。蓝耘元生代MaaS平台做的事情,就是把模型部署、推理调度、API网关、用量计费这些底层细节全包了,你只需要拿一个API Key,像调普通HTTP接口一样发请求就行。

我当时选蓝耘元生代MaaS,主要是对比了几个平台之后,发现它在兼容OpenAI接口规范这块做得比较省心。也就是说,你用惯了ChatGPT的API格式,切过来几乎不用改代码逻辑——base_url换掉、model换掉、API Key换成蓝耘的,原先的项目直接就能跑。对于只想快速验证模型效果、或者做前端Demo演示的人来说,这是最省时间的一条路。

另外它还支持流式输出(SSE),这对聊天类应用来说是刚需。因为大模型生成Token是需要时间的,如果没有流式输出,用户就得盯着空白页面等好几秒,体验非常差。有了流式,Token是一个一个蹦出来的,像打字机一样,产品观感完全不一样。我用HTML + JavaScript写Demo的时候,流式解析这块是核心,后面会展开讲。

1.2 为什么用纯HTML做Demo而不是上框架

现在写前端的主流做法是React、Vue加一堆工程化构建工具,但对于“调用大模型API”这种单页演示场景,纯HTML + CSS + JavaScript反而是最优解。

原因有几点。第一,零依赖。你只需要一个文本编辑器,写一个.html文件,双击就能在浏览器里打开,不需要Node.js、不需要npm install、不需要webpack。对很多人来说,可能只是想迅速验证一下蓝耘元生代MaaS平台好不好用、DeepSeek-V3.1-Terminus回复质量怎么样,搞一整套前端工程就太重了。第二,好分享。一个单文件HTML,直接发给同事,或者扔到GitHub Pages、OSS静态托管上,别人打开链接就能用。第三,方便调试。浏览器F12打开控制台,网络请求、响应体、报错信息一目了然,排查问题比在框架里追依赖关系直接得多。

当然,纯HTML方案也有它的天花板,比如API Key暴露在浏览器里会有安全隐患,这个我会在后面的安全边界章节专门说。但作为实战Demo、学习工具、内部验证,纯HTML完全够用,而且上手门槛低到几乎为零。

1.3 整体架构与数据流

这个Demo的架构非常轻,数据流也很清晰:

  1. 用户在页面输入框里写下问题,点击发送按钮。
  2. 浏览器里的JavaScript通过fetch向蓝耘元生代MaaS平台的API地址发起POST请求,请求体里带上模型名称、用户消息、参数配置(温度、最大Token等)。
  3. 平台网关转发请求到DeepSeek-V3.1-Terminus模型服务,模型开始推理,返回结果。
  4. 如果开启了流式输出,响应体是一段持续到达的SSE流,前端通过ReadableStream逐步读取并实时渲染到页面上。
  5. 非流式模式则等完整JSON返回后一次性渲染。

整个链路里,前端只做两件事:发请求、渲染响应。真正的智力活全在平台侧和模型侧。这种“前端薄、平台厚”的架构模式,恰恰是MaaS产品最大的价值——让模型能力触手可及,而调用方只需要关心业务和体验。

2. 准备阶段:账号、API Key与基础配置

2.1 注册账号与获取API Key

在写代码之前,先把平台侧的准备工作做完。

去蓝耘官网注册账号,然后进入控制台,找到元生代MaaS平台的入口。首次使用一般需要开通服务或创建API Key,流程跟绝大多数云平台类似,按提示操作即可。创建API Key的时候,建议给Key起一个能看明白的名字,比如“html-demo”,方便后续在用量明细里追踪。

这里有一个实操提醒:API Key的显示机会通常只有一次,关闭弹窗之后就看不到了。一定要当时就复制、粘贴、保存到本地密码管理器里。我见过太多人没保存Key,之后只能重新创建一个,虽然不麻烦,但没必要。

获取到API Key之后,看一下控制台里的模型列表,确认你能调用的模型标识符。蓝耘元生代MaaS平台对DeepSeek-V3.1-Terminus的模型命名一般会直接标注,形如deepseek-v3.1- terminus这样的字符串,但因为版本和区域可能不同,以控制台里的实际显示为准。后面代码里的model字段就填这个值。

2.2 接口文档里的关键信息

蓝耘元生代MaaS平台的接口设计兼容OpenAI格式,所以核心信息就那么几个:

  • 请求地址:base_url加上/chat/completions,一般形如https://api.xxx.com/v1/chat/completions,具体域名以官方文档为准。
  • 请求方法:POST。
  • 请求头:Content-Type: application/json,Authorization: Bearer 你的API Key。
  • 请求体:model、messages、stream、max_tokens、temperature等字段。

messages数组的结构跟OpenAI一致,是对话历史的列表,每条消息包含role和content。role有三种:system(系统设定)、user(用户)、assistant(模型回复)。多轮对话原理上就是把历史消息全部带上一起发给模型,模型才能有上下文连续感。

stream字段决定返回方式。默认false时,服务器一次性返回JSON,里面包含完整的回复内容;设为true时,服务器返回SSE流,数据是一块一块到达的。对于聊天Demo,我强烈建议开stream: true,交互体验是完全不同的。

2.3 前端安全边界,先别踩坑

纯HTML页面直接调API,最大的问题就是API Key暴露。你把这个HTML发给别人,别人按F12就能看到你的Key。你的Key被别人盗用去跑量,产生的高额费用算在你账上——这是实打实的风险。

所以必须分层处理安全问题:

  • 本机学习用:Key写在HTML里无所谓,因为只有你自己用,风险可控。
  • 分享给别人用:不要直接发带Key的HTML。可以临时生成一个服务端代理,前端请求你代理,代理再带Key去请求蓝耘平台。
  • 正式生产项目:Key必须放在服务端环境变量里,前端绝不能接触。

我后面提供的Demo代码,默认是在本机或信任环境里学习使用。如果你要部署到公开网络,务必先部署一个轻量的代理后端,哪怕只是几十行的Node.js或Python代理,也比Key裸奔强得多。

3. 核心代码实现:请求封装与流式解析

3.1 非流式请求:最简版本先跑通

先把最简单的版本写出来,验证链路是否通畅。打开文本编辑器,新建一个index.html,先填一个输入框和一个按钮,JavaScript里用fetch发POST请求。

<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>蓝耘MaaS + DeepSeek-V3.1-Terminus 实战Demo</title> </head> <body> <div> <textarea id="prompt" rows="5" cols="60" placeholder="请输入你的问题..."></textarea> <br> <button id="sendBtn">发送</button> </div> <div id="output" style="margin-top:20px; white-space:pre-wrap;"></div> <script> const API_KEY = '你的API Key'; const BASE_URL = 'https://你的平台域名/v1/chat/completions'; const MODEL = 'deepseek-v3.1-terminus'; // 以控制台实际显示为准 document.getElementById('sendBtn').addEventListener('click', async () => { const prompt = document.getElementById('prompt').value.trim(); if (!prompt) return; const output = document.getElementById('output'); output.textContent = '思考中...'; const response = await fetch(BASE_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${API_KEY}` }, body: JSON.stringify({ model: MODEL, messages: [ { role: 'system', content: '你是一个乐于助人的AI助手。' }, { role: 'user', content: prompt } ], stream: false, temperature: 0.7, max_tokens: 2048 }) }); const data = await response.json(); if (data.choices && data.choices.length > 0) { output.textContent = data.choices[0].message.content; } else { output.textContent = JSON.stringify(data, null, 2); } }); </script> </body> </html>

注意几个点:第一,output那个div加了white-space: pre-wrap,这样模型回复里的换行和缩进能原样显示。第二,temperature控制随机性和创造性,0到2之间取值,值越大回复越多变,值越小越稳定,日常问答用0.7是比较中庸的选择。第三,max_tokens限制回复的最大Token数,256如果不够用就调大,但这个Demo里我给了2048。

如果这一步能在页面上正确显示模型的回复,说明API Key、模型名称、网络通路都没问题,链路是通的。接下来再上流式。

3.2 流式请求:SSE解析完整实现

流式交互才是这个Demo的灵魂。一样是用fetch,但body里的stream要设为true,然后通过response.body拿到一个ReadableStream,用getReader()读取数据块。

服务器通过SSE格式发送数据,每一块数据以data:开头,以两个换行符结束。当所有数据发完后,会有一条data: [DONE]。解析逻辑就是不断读块、按行切分、提取data:后面的JSON字符串、JSON.parse解析出增量内容,然后追加到页面上。

document.getElementById('sendBtn').addEventListener('click', async () => { const prompt = document.getElementById('prompt').value.trim(); if (!prompt) return; const output = document.getElementById('output'); output.textContent = ''; const response = await fetch(BASE_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${API_KEY}` }, body: JSON.stringify({ model: MODEL, messages: [ { role: 'system', content: '你是一个乐于助人的AI助手。' }, { role: 'user', content: prompt } ], stream: true, temperature: 0.7, max_tokens: 2048 }) }); const reader = response.body.getReader(); const decoder = new TextDecoder('utf-8'); let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); // SSE数据块通常以"\n\n"分隔 const lines = buffer.split('\n'); buffer = lines.pop(); // 最后一段可能是不完整的,留到下一轮再处理 for (const line of lines) { const trimmed = line.trim(); if (!trimmed.startsWith('data:')) continue; const dataStr = trimmed.slice(5).trim(); if (dataStr === '[DONE]') continue; try { const json = JSON.parse(dataStr); const delta = json.choices[0].delta.content; if (delta) { output.textContent += delta; // 滚动到页面底部 window.scrollTo(0, document.body.scrollHeight); } } catch (e) { console.error('解析失败:', e, dataStr); } } } });

流式解析的核心技巧就是那个buffer。因为网络包是切片的,一次reader.read()不一定刚好读到一个完整的SSE事件,可能半截、可能几个事件黏在一起。所以惯用做法是把每次读到的数据追加到buffer里,按\n\n切分成若干行,把最后一段不完整的留到下一次循环再拼。这个细节新手很容易忽略,结果就是页面经常丢字、乱码、解析报错。

还有一个细节:decoder.decode(value, { stream: true })这个参数也很关键。如果不用{ stream: true },一个多字节UTF-8字符被拆到两个数据块里时,第二块会解析出乱码。加上{ stream: true }之后,解码器内部会缓存中间结果,跨块字符也能正确拼出来。

3.3 参数调优:提升输出质量的几个旋钮

同一个模型,参数不同,输出效果天差地别。我在调API的时候经常被问“为什么模型回答这么机械”或者“为什么答案太啰嗦”,很多时候问题不在模型,在参数。

  • temperature:日常问答0.6~0.7。写文案、头脑风暴可以拉到0.9~1.2,发挥空间更大。做数据提取、代码生成这种需要确定性的任务,降到0.2~0.3,降低胡说八道的概率。
  • max_tokens:如果默认值是2048,长文本生成会被截断。写代码、写文章这种长输出场景,最好设到4096或更高,具体看平台的额度上限。
  • top_p:另一个控制随机性的参数,跟temperature类似但原理不同。一般只用其中一个做调节,别同时大幅调整两个参数,容易让输出变得奇怪。
  • system消息:很多人小看它,实际上它是控制模型行为最有效的工具。跟模型说“你是一个严谨的技术专家,回答要简洁、准确、分点输出”,比你在问题里夹带一万个要求都管用。

在Demo页面里,我建议做一个参数面板,把temperature做成滑块,max_tokens做成数字输入框,实时点击实时调整,这样你能直观感受到参数对回复的影响。我记得第一次把temperature从一个极端拉到另一个极端时,同一个问题问出来的回答风格差异大到像换了个人,那个瞬间对“随机性参数”的理解一下子通了。

4. 实战演示页面搭建

4.1 HTML结构:布局清晰,交互顺手

既然叫HTML实战Demo,页面结构就不能太糊弄。我按一个极简聊天工具的标准来搭:顶部是标题栏,中间是消息区,底部是输入区和发送按钮。没有花哨的设计,但分工明确。

<div class="chat-container"> <header class="chat-header"> <h1>蓝耘MaaS · DeepSeek-V3.1-Terminus 实战Demo</h1> <span class="status-dot" id="statusDot"></span> </header> <main class="chat-main" id="chatMain"> <!-- 消息气泡动态渲染区 --> </main> <footer class="chat-footer"> <textarea id="prompt" rows="3" placeholder="输入你的问题,Ctrl+Enter 发送"></textarea> <button id="sendBtn">发送</button> </footer> </div>

消息区里的内容结构,我采用最直观的方案——每条消息一个div,根据角色加不同的CSS类名。用户消息靠右,整块蓝色背景;模型消息靠左,浅灰背景。这样一眼就能分清楚是谁在说话,聊天的视觉习惯也符合主流聊天工具。

发送方式做了两个入口:点击发送按钮,以及Ctrl+Enter快捷键。聊天工具输完内容直接按快捷键就能发,效率高很多。实现的时候给textarea加一个keydown事件监听,判断event.ctrlKey && event.key === 'Enter'即可。

4.2 CSS样式:花半小时打磨,观感完全不同

CSS没写很复杂,一个几百行的样式表就够用。但有几个点值得单独说。

  • 消息区要独立滚动,而不是整个页面滚动。这样当消息很长时,浏览器不会因为滚动条乱跳而打断阅读节奏。实现方式是给.chat-main设置flex: 1加overflow-y: auto,外层容器限制高度为视口的90%。
  • 模型回复里的代码块需要一个深色背景。我写了一个简单的.markdown pre样式,用等宽字体加浅色背景区分正文。虽然纯前端没做完整Markdown渲染,但至少让代码片段不会跟普通文字糊在一起。
  • 发送中状态要有视觉反馈。发送期间按钮变成灰色、禁用点击,消息区末尾显示一个“正在输入”的动画,等流式内容到达后自动消失。这个小细节直接影响用户体感,没反馈的页面会让人以为卡死了。

4.3 交互逻辑:多轮对话、清空会话、错误提示

这个Demo不止是单轮问答,我还加了多轮对话支持。核心做法很简单:维护一个messages数组,用户每次发送的新消息push进去,模型返回的assistant消息也push进去,下次请求时把整个数组传给接口。这样模型就能结合上下文回复,聊起来感觉像连续的人。注意历史消息越来越多时请求体也越来越大,Token消耗会增加,所以Demo里我做了个“清空对话”按钮,一键重置,防止越聊越长。

错误提示也做了分类处理。网络超时、HTTP 4xx/5xx、流式中断,分别给用户显示对应的中文提示。调试的时候发现在浏览器里最容易遇到的是跨域问题,这个下面专门讲。

5. 常见问题与排查技巧实录

5.1 CORS跨域:你大概率会撞上的第一面墙

纯HTML文件直接双击打开,用file://协议调API,浏览器会报CORS错误,而且这种场景下平台侧很难通过允许跨域来解决,因为Origin是null。

正确做法是不要用file://打开页面,而是本地起一个HTTP服务。最简单的方式:在页面所在目录运行python3 -m http.server 8000,然后浏览器访问http://localhost:8000。如果你装了Node.js,也可以npx serve。这本质上只是改变页面的加载协议,却能让浏览器正常发送跨域请求。

蓝耘元生代MaaS平台一般会在服务端配置允许特定域名跨域,但http://localhost或具体域名是否被允许,取决于平台策略,要提前确认。如果平台不支持浏览器直接跨域调用,那就必须走代理服务,这是一个硬性约束,不是前端能绕过去的。

5.2 流式输出不生效:先检查响应头

开着stream: true,但页面还是等很久才一次性出现全部内容,问题多半出在服务器响应头或者网络中间层。正常SSE请求,响应头的Content-Type应该是text/event-stream。你可以在浏览器控制台的Network面板里点开这条请求,查看响应头确认。

还有一种情况是网络代理或CDN把流式响应缓冲了,导致数据攒够一定量才发给浏览器,看起来就跟非流式一样。如果是本地调试,可以试着关掉系统代理,直接用直连。我遇到过一次是内网网关做了缓冲,换了网络环境后流式立刻恢复正常。这种情况不在代码层面,惯性怀疑自己写错是很常见的误导。

5.3 响应解析报错和Token超长截断

如果控制台报JSON.parse失败,先看一眼打印出来的dataStr长什么样。SSE格式里可能有注释行(:开头)、空行、event:字段,这些都要跳过。我写的解析逻辑只处理data:前缀的行,其他行直接忽略,这样能规避大部分偶发问题。

如果模型回复突然断在半句,并且结束得很突然,没有任何错误标志,大概率是max_tokens设小了。把max_tokens调大,或者让模型自觉分段输出。调试长文本生成时,我习惯把max_tokens直接拉到上限,排除截断这个变量,确认效果后再调回合理值。

5.4 费用与用量管理:别让Key裸奔后失控

最后聊一个跟代码无关但比代码都重要的点——费用管控。API用了是要计费的,流式和非流式按Token计费,多轮对话和超长文本消耗尤其明显。Demo阶段建议看到效果就收,不要写个死循环疯狂调接口测试。

另外再次提醒:API Key绝对不能提交到公开Git仓库,不能贴在在线演示页面上。你辛苦写的Demo代码开源出去的同时把Key也带出去了,等于免费给陌生人开通了一个自动提款通道。真要分享,用后端代理、用环境变量、用变量替换占位符,怎么都行,别让Key裸奔。

6. 扩展方向:这个Demo还能往哪里走

写到这里,基础实战已经完整跑通了。如果你对这套HTML调用蓝耘元生代MaaS平台的方案玩熟练了,后续有不少自然的扩展方向:

把textarea升级成支持Markdown渲染的编辑器,模型回复可以呈现表格、列表、标题,观感直接上一档。给页面加上语音识别接口,用麦克风输入替代打字,做一个语音版问答助手。加上Prompt模板管理,把常用问题预置成按钮,适合演示场景一键触发。或者做一个上下文历史记录的本地存储,刷新页面后聊天记录不丢。

就我个人的感觉,把这个Demo做熟、做透的意义不只是在“学会了调一个API”,而是理解了云端模型服务的基本工作方式——请求、响应、流式、Token、参数调优,这套逻辑放在任何一家AI平台上都通用。蓝耘元生代MaaS平台帮你省去了部署模型的折腾,但你依然需要懂这些基础知识,才能用好它。

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

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

立即咨询