如何构建支持 GPT-5.4/Claude 4.6 的流式聊天机器人:4sapi 前端实战

3 阅读9分钟

前言

2026 年,AI 能力已经成为前端应用的标配。从智能客服到代码助手,从内容生成到数据分析,几乎每个前端项目都在集成 AI 功能。但作为前端开发者,我们在调用 AI API 时,总会遇到各种各样的坑:

  • 跨域问题:直接在浏览器调用 OpenAI 等海外 API 会被 CORS 策略拦截
  • 安全隐患:把 API Key 写在前端代码里,分分钟被人爬取盗用
  • 流式输出复杂:处理 SSE 流式响应需要写大量样板代码
  • 多模型切换困难:想要支持多个模型,就要写多套不同的请求逻辑

我之前在开发一个在线 AI 代码助手时,就被这些问题折磨了整整一周。直到我用了4sapi(星链引擎) ,所有问题都迎刃而解。它不仅解决了跨域和安全问题,还提供了统一的 OpenAI 兼容接口,让我用一套代码就能调用所有主流大模型。

本文将带领大家用 Vue3+Vite,在 10 分钟内构建一个功能完整、支持流式输出、可一键切换 GPT-5.4/Claude 4.6/Gemini 3.1 Pro 的 AI 聊天机器人。所有代码开箱即用,你可以直接集成到自己的项目中。

一、前端调用 AI API 的四大痛点

在介绍 4sapi 之前,我们先盘点一下前端开发者在集成 AI 功能时最头疼的几个问题,相信你一定深有体会。

1.1 跨域问题:永远的拦路虎

这是最基础也最无解的问题。OpenAI、Anthropic、Google 等所有海外 AI 厂商的 API,都没有配置允许浏览器跨域访问的 CORS 头。这意味着你绝对不能在前端直接调用这些 API,必须自己搭建一个后端代理服务。

对于很多个人开发者和小团队来说,为了一个简单的 AI 功能专门去搭一个后端服务,成本太高了。而且还要维护服务器、处理负载均衡、保证服务可用性,简直是得不偿失。

1.2 API Key 安全:悬在头顶的利剑

如果你图省事,把 API Key 直接写在前端代码里,那你的账号离被盗刷就不远了。现在有大量的爬虫在 GitHub 和各种网站上扫描泄露的 API Key,一旦被发现,几个小时内就能给你刷出几千美元的账单。

正确的做法是把 API Key 保存在后端,所有请求都通过后端转发。但这又回到了上一个问题:你需要一个后端服务。

1.3 流式输出:看似简单实则复杂

流式输出是 AI 聊天体验的核心,它能让用户感觉 AI 是在 "实时思考"。但在前端实现流式输出并不简单:

  • 你需要手动处理 Fetch API 的 ReadableStream
  • 要处理断连重连和异常情况
  • 不同模型的流式响应格式不一样
  • 还要处理中文乱码和分段问题

光是写一个健壮的流式输出函数,就需要上百行代码。

1.4 多模型支持:代码量翻倍

如果你想让用户在 GPT-5.4、Claude 4.6 和 Gemini 3.1 Pro 之间自由切换,那你就要为每个模型写一套单独的请求和响应处理代码。不同模型的参数名称、响应结构、错误码都不一样,维护起来非常痛苦。

二、4sapi:前端 AI 开发的救星

4sapi 完美解决了上述所有问题。它专门为前端开发者做了大量优化,让你可以在浏览器中安全、便捷地调用所有主流大模型。

2.1 原生支持浏览器调用,彻底解决跨域

4sapi 的 API 接口配置了完整的 CORS 头,允许任何域名的浏览器直接调用。你不需要搭建任何后端代理服务,直接在前端 JavaScript 中就能发起请求。

这对于个人开发者和快速原型开发来说,简直是福音。你可以专注于前端界面和交互,完全不用关心后端的事情。

2.2 安全的 API Key 管理,杜绝盗刷风险

4sapi 提供了前端专用的临时 Token 机制,彻底解决了 API Key 泄露的问题:

  • 你可以在 4sapi 控制台生成一个临时 Token,设置有效期和调用次数限制
  • 即使 Token 被泄露,攻击者也只能在限制范围内使用
  • 你可以随时在控制台吊销泄露的 Token
  • 对于企业用户,还可以设置 IP 白名单和 Referer 白名单

这样一来,你就可以放心地把 Token 放在前端代码里,不用担心被盗刷了。

2.3 标准的流式响应,一行代码实现

4sapi 完全兼容 OpenAI 的流式响应格式,所有模型的流式输出都使用统一的标准。而且,它还对 Fetch API 进行了封装,让你用几行代码就能实现流畅的流式输出效果。

2.4 统一的接口,一键切换所有模型

和之前介绍的一样,4sapi 将所有模型的 API 都映射成了 OpenAI ChatCompletion 格式。你只需要写一套请求逻辑,就能调用 GPT-5.4、Claude 4.6、Gemini 3.1 Pro 等 650 + 款模型。切换模型,只需要改一个model参数即可。

三、实战:10 分钟构建流式聊天机器人

接下来,我们将用 Vue3+Vite 从零开始构建一个功能完整的 AI 聊天机器人。你只需要跟着步骤复制粘贴代码,就能得到一个可以直接运行的应用。

3.1 项目初始化

首先,创建一个新的 Vue3 项目:

bash

运行

npm create vite@latest ai-chat-demo -- --template vue
cd ai-chat-demo
npm install

我们不需要安装任何额外的依赖,直接使用浏览器原生的 Fetch API 即可。

3.2 获取 4sapi 临时 Token

  1. 访问4sapi.com,注册并登录账号
  2. 进入控制台,点击 "API Key 管理"
  3. 点击 "生成临时 Token",设置有效期为 7 天,调用次数限制为 1000 次
  4. 复制生成的 Token,我们后面会用到

3.3 核心代码实现

打开src/App.vue文件,替换为以下代码:

vue

<template>
  <div class="chat-container">
    <div class="chat-header">
      <h1>4sapi 多模型聊天机器人</h1>
      <select v-model="selectedModel" class="model-selector">
        <option value="gpt-5.4">GPT-5.4</option>
        <option value="claude-4.6">Claude 4.6</option>
        <option value="gemini-3.1-pro">Gemini 3.1 Pro</option>
        <option value="qwen-2.5-72b-instruct">通义千问2.5-72B</option>
      </select>
    </div>

    <div class="messages-container" ref="messagesContainer">
      <div v-for="(message, index) in messages" :key="index" class="message" :class="message.role">
        <div class="message-avatar">{{ message.role === 'user' ? '我' : 'AI' }}</div>
        <div class="message-content">{{ message.content }}</div>
      </div>
      <div v-if="isLoading" class="message assistant">
        <div class="message-avatar">AI</div>
        <div class="message-content typing">正在思考...</div>
      </div>
    </div>

    <div class="input-container">
      <textarea
        v-model="inputMessage"
        @keydown.enter.prevent="sendMessage"
        placeholder="输入你的问题..."
        rows="1"
        :disabled="isLoading"
      ></textarea>
      <button @click="sendMessage" :disabled="isLoading || !inputMessage.trim()">
        发送
      </button>
    </div>
  </div>
</template>

<script setup>
import { ref, watch, nextTick } from 'vue'

// 配置项
const API_BASE_URL = 'https://4sapi.com/v1'
const API_TOKEN = '你的4sapi临时Token' // 替换成你刚才生成的Token

// 响应式数据
const messages = ref([])
const inputMessage = ref('')
const selectedModel = ref('gpt-5.4')
const isLoading = ref(false)
const messagesContainer = ref(null)

// 自动滚动到底部
watch(messages, async () => {
  await nextTick()
  if (messagesContainer.value) {
    messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight
  }
}, { deep: true })

// 发送消息
async function sendMessage() {
  if (!inputMessage.trim() || isLoading.value) return

  // 添加用户消息
  const userMessage = {
    role: 'user',
    content: inputMessage.trim()
  }
  messages.value.push(userMessage)
  const currentInput = inputMessage.value
  inputMessage.value = ''
  isLoading.value = true

  try {
    // 添加AI回复占位符
    const aiMessage = {
      role: 'assistant',
      content: ''
    }
    messages.value.push(aiMessage)

    // 调用4sapi流式接口
    const response = await fetch(`${API_BASE_URL}/chat/completions`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${API_TOKEN}`
      },
      body: JSON.stringify({
        model: selectedModel.value,
        messages: messages.value.slice(0, -1), // 排除占位符
        stream: true,
        temperature: 0.7,
        max_tokens: 2000
      })
    })

    if (!response.ok) {
      throw new Error(`请求失败: ${response.status}`)
    }

    // 处理流式响应
    const reader = response.body.getReader()
    const decoder = new TextDecoder('utf-8')

    while (true) {
      const { done, value } = await reader.read()
      if (done) break

      // 解码响应数据
      const chunk = decoder.decode(value, { stream: true })
      const lines = chunk.split('\n').filter(line => line.trim() !== '')

      for (const line of lines) {
        if (line.startsWith('data: ')) {
          const data = line.slice(6)
          if (data === '[DONE]') continue

          try {
            const parsed = JSON.parse(data)
            const content = parsed.choices[0]?.delta?.content || ''
            aiMessage.content += content
          } catch (e) {
            console.error('解析响应失败:', e)
          }
        }
      }
    }
  } catch (error) {
    console.error('发送消息失败:', error)
    // 移除占位符,添加错误消息
    messages.value.pop()
    messages.value.push({
      role: 'assistant',
      content: `抱歉,发生了一个错误: ${error.message}`
    })
  } finally {
    isLoading.value = false
  }
}
</script>

<style scoped>
.chat-container {
  max-width: 800px;
  margin: 0 auto;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background-color: #f5f5f5;
}

.chat-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 24px;
  background-color: white;
  border-bottom: 1px solid #e0e0e0;
}

.chat-header h1 {
  margin: 0;
  font-size: 20px;
  color: #333;
}

.model-selector {
  padding: 8px 12px;
  border: 1px solid #ddd;
  border-radius: 6px;
  font-size: 14px;
}

.messages-container {
  flex: 1;
  overflow-y: auto;
  padding: 24px;
}

.message {
  display: flex;
  margin-bottom: 24px;
  max-width: 80%;
}

.message.user {
  margin-left: auto;
  flex-direction: row-reverse;
}

.message-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background-color: #007bff;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: bold;
  flex-shrink: 0;
}

.message.user .message-avatar {
  background-color: #28a745;
}

.message-content {
  padding: 12px 16px;
  border-radius: 12px;
  background-color: white;
  margin: 0 12px;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-word;
}

.message.user .message-content {
  background-color: #007bff;
  color: white;
}

.typing {
  color: #999;
  animation: typing 1.4s infinite;
}

@keyframes typing {
  0% { opacity: 0.3; }
  50% { opacity: 1; }
  100% { opacity: 0.3; }
}

.input-container {
  display: flex;
  padding: 16px 24px;
  background-color: white;
  border-top: 1px solid #e0e0e0;
}

.input-container textarea {
  flex: 1;
  padding: 12px 16px;
  border: 1px solid #ddd;
  border-radius: 24px;
  font-size: 14px;
  resize: none;
  max-height: 120px;
  margin-right: 12px;
}

.input-container textarea:focus {
  outline: none;
  border-color: #007bff;
}

.input-container button {
  padding: 12px 24px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 24px;
  font-size: 14px;
  cursor: pointer;
}

.input-container button:disabled {
  background-color: #ccc;
  cursor: not-allowed;
}
</style>

3.4 运行项目

将代码中的API_TOKEN替换成你自己的 4sapi 临时 Token,然后运行:

bash

运行

npm run dev

打开浏览器访问http://localhost:5173,你就拥有了一个功能完整的 AI 聊天机器人!你可以在顶部的下拉菜单中自由切换不同的模型,体验它们各自的特点。

四、进阶功能扩展

这个基础版的聊天机器人已经非常好用了,你还可以很容易地扩展更多功能:

4.1 添加代码高亮

使用highlight.js可以让 AI 生成的代码自动高亮显示:

bash

运行

npm install highlight.js

然后在App.vue中引入并使用:

javascript

运行

import hljs from 'highlight.js'
import 'highlight.js/styles/github.css'

// 在AI回复完成后添加
nextTick(() => {
  document.querySelectorAll('pre code').forEach((block) => {
    hljs.highlightElement(block)
  })
})

4.2 添加对话历史保存

使用localStorage保存对话历史,刷新页面后不会丢失:

javascript

运行

// 保存对话历史
watch(messages, (newMessages) => {
  localStorage.setItem('chat-history', JSON.stringify(newMessages))
}, { deep: true })

// 页面加载时恢复对话历史
onMounted(() => {
  const savedHistory = localStorage.getItem('chat-history')
  if (savedHistory) {
    messages.value = JSON.parse(savedHistory)
  }
})

4.3 添加清空对话功能

添加一个按钮,一键清空所有对话历史:

vue

<!-- 在chat-header中添加 -->
<button @click="clearChat" class="clear-button">清空对话</button>

javascript

运行

function clearChat() {
  messages.value = []
  localStorage.removeItem('chat-history')
}

五、更多前端 AI 应用场景

有了 4sapi,你可以快速构建各种有趣的前端 AI 应用:

  • AI 代码助手:实时生成和解释代码
  • 智能文档编辑器:自动补全、润色和翻译文档
  • AI 图片生成器:调用 DALL-E 3、Midjourney 等模型生成图片
  • 智能客服机器人:嵌入到你的网站中,自动回答用户问题
  • 在线翻译工具:支持多语言实时互译

所有这些应用,你都不需要搭建任何后端服务,直接在前端就能完成开发。

六、总结

4sapi 彻底改变了前端 AI 开发的方式。它解决了跨域、安全、流式输出和多模型支持这四大核心痛点,让前端开发者可以用极低的成本和极高的效率构建 AI 应用。

本文提供的聊天机器人代码是一个通用的模板,你可以根据自己的需求进行修改和扩展。无论是个人项目还是企业应用,4sapi 都能为你提供稳定、高效、安全的 AI 能力支持。

如果你也想在自己的前端项目中集成 AI 功能,不妨试试 4sapi。它会让你的 AI 开发体验变得前所未有的简单和愉快。