React + Fastify + DeepSeek 实现一个简单的对话式 AI 应用

目录

  • 一、核心思路(高层)
  • 二、推荐架构(简单图)
  • [三、关键 API 流程(说明 + 代码示例)](#三、关键 API 流程(说明 + 代码示例))
    • [1、对话 API(LLM)](#1、对话 API(LLM))
    • [2、Vision API(图像问答)](#2、Vision API(图像问答))
    • [3、Embedding API](#3、Embedding API)
  • 四、后端实现(Fastify)
    • 1、安装项目依赖
    • [2、env 配置](#2、env 配置)
    • [3、index.js(Fastify 主服务)](#3、index.js(Fastify 主服务))
  • [五、前端:React 客户端(简易聊天 UI)](#五、前端:React 客户端(简易聊天 UI))
    • [1、API 封装 api.js(封装前端 API)](#1、API 封装 api.js(封装前端 API))
    • [2、app.jsx(简单对话 UI)](#2、app.jsx(简单对话 UI))
  • [六、加上 Vision 上传按钮(选做)](#六、加上 Vision 上传按钮(选做))
  • [七、加上 Embedding 演示(选做)](#七、加上 Embedding 演示(选做))
  • 八、项目结构图
  • [九、DeepSeek + React + Fastify 常见坑](#九、DeepSeek + React + Fastify 常见坑)

一、核心思路(高层)

前端(React):负责用户交互,包括聊天输入、消息展示、图片上传、触发 Embedding 计算等。

后端(Fastify):代理 DeepSeek API 请求,隐藏 API Key,处理逻辑(LLM / Vision / Embedding)。

DeepSeek API:提供三类能力:

  • LLM:自然语言对话
  • Vision:图像理解和问答
  • Embedding:文本向量生成,用于检索或语义搜索

核心原则:

  • API Key 永不暴露在前端
  • 消息与图像统一通过后端转发
  • Embedding / 文档检索可用于 RAG(可选拓展)

二、推荐架构(简单图)

说明:

  • 前端仅和 Fastify 后端 通信
  • 后端根据请求类型调用 DeepSeek 的不同接口
  • 前端可以显示消息、图片解析结果、embedding 长度等(可选)

三、关键 API 流程(说明 + 代码示例)

1、对话 API(LLM)

流程说明:

  • 前端发送 messages 数组到 /api/chat
  • Fastify 读取 env 中的 API Key,调用 DeepSeek Chat API
  • 返回 LLM 回复内容给前端

示例代码:

typescript 复制代码
// Fastify 后端
app.post("/api/chat", async (req, reply) => {
  const { messages } = req.body;

  const completion = await deepseek.chat.completions.create({
    model: "deepseek-chat",
    messages,
  });

  reply.send({ reply: completion.choices[0].message.content });
});

2、Vision API(图像问答)

流程说明:

  • 前端上传图片(Base64 编码) + 问题
  • 后端调用 DeepSeek Vision 模型
  • 返回模型对图片的理解

示例代码:

typescript 复制代码
app.post("/api/vision", async (req, reply) => {
  const { imageBase64, question } = req.body;

  const completion = await deepseek.chat.completions.create({
    model: "deepseek-vision",
    messages: [
      { role: "user", content: [
        { type: "input_text", text: question },
        { type: "input_image", image_url: `data:image/jpeg;base64,${imageBase64}` }
      ]}
    ]
  });

  reply.send({ reply: completion.choices[0].message.content });
});

3、Embedding API

流程说明:

  • 前端发送文本
  • 后端生成向量 embedding
  • 可用于向量数据库存储或语义搜索

示例代码:

typescript 复制代码
app.post("/api/embed", async (req, reply) => {
  const { text } = req.body;

  const emb = await deepseek.embeddings.create({
    model: "deepseek-embedding",
    input: text,
  });

  reply.send({ embedding: emb.data[0].embedding });
});

四、后端实现(Fastify)

1、安装项目依赖

bash 复制代码
npm install fastify fastify-cors dotenv openai

2、env 配置

bash 复制代码
DEEPSEEK_API_KEY=你的key
DEEPSEEK_BASE_URL=https://api.deepseek.com

3、index.js(Fastify 主服务)

typescript 复制代码
import Fastify from "fastify";
import cors from "fastify-cors";
import { deepseek } from "./deepseek.js";

const app = Fastify();
app.register(cors, { origin: "*" });

/** ================================
 *  LLM 对话接口
 *  ================================
 */
app.post("/api/chat", async (req, reply) => {
  const { messages } = req.body;

  const completion = await deepseek.chat.completions.create({
    model: "deepseek-chat",
    messages,
  });

  reply.send({ reply: completion.choices[0].message.content });
});

/** ================================
 *  Vision 图像识别 + 对话
 *  ================================
 */
app.post("/api/vision", async (req, reply) => {
  const { imageBase64, question } = req.body;

  const completion = await deepseek.chat.completions.create({
    model: "deepseek-vision",
    messages: [
      { role: "user", content: [
        { type: "input_text", text: question },
        {
          type: "input_image",
          image_url: `data:image/jpeg;base64,${imageBase64}`
        }
      ]}
    ]
  });

  reply.send({ reply: completion.choices[0].message.content });
});

/** ================================
 *  Embedding
 *  ================================
 */
app.post("/api/embed", async (req, reply) => {
  const { text } = req.body;

  const emb = await deepseek.embeddings.create({
    model: "deepseek-embedding",
    input: text,
  });

  reply.send({ embedding: emb.data[0].embedding });
});

app.listen(3001, () => {
  console.log("Fastify server running at http://localhost:3001");
});

五、前端:React 客户端(简易聊天 UI)

1、API 封装 api.js(封装前端 API)

typescript 复制代码
import axios from "axios";

const API = "http://localhost:3001/api";

/** LLM 对话 */
export const chat = (messages) =>
  axios.post(`${API}/chat`, { messages });

/** Vision */
export const visionAsk = (imageBase64, question) =>
  axios.post(`${API}/vision`, { imageBase64, question });

/** Embeddings */
export const embed = (text) =>
  axios.post(`${API}/embed`, { text });

2、app.jsx(简单对话 UI)

typescript 复制代码
import { useState } from "react";
import { chat, embed, visionAsk } from "./api";

export default function App() {
  const [messages, setMessages] = useState([]);
  const [input, setInput] = useState("");

  async function send() {
    const newMessages = [...messages, { role: "user", content: input }];
    setMessages(newMessages);

    const res = await chat(newMessages);
    setMessages([
      ...newMessages,
      { role: "assistant", content: res.data.reply }
    ]);

    setInput("");
  }

  return (
    <div style={{ padding: 30, maxWidth: 600, margin: "0 auto" }}>
      <h1>DeepSeek Chat (React + Fastify)</h1>

      {/* message list */}
      <div style={{ height: 400, overflowY: "auto", border: "1px solid #ddd", padding: 16 }}>
        {messages.map((m, i) => (
          <div key={i} style={{ marginBottom: 16 }}>
            <b>{m.role}:</b> {m.content}
          </div>
        ))}
      </div>

      {/* input */}
      <div style={{ marginTop: 20 }}>
        <input
          value={input}
          onChange={e => setInput(e.target.value)}
          style={{ width: "80%", padding: 8 }}
        />
        <button onClick={send} style={{ padding: "8px 16px" }}>Send</button>
      </div>
    </div>
  );
}

六、加上 Vision 上传按钮(选做)

typescript 复制代码
<input
  type="file"
  accept="image/*"
  onChange={async e => {
    const file = e.target.files[0];
    const base64 = await fileToBase64(file);
    const res = await visionAsk(base64, "图里有什么?");

    setMessages(m => [
      ...m,
      { role: "user", content: "[图片上传]" },
      { role: "assistant", content: res.data.reply }
    ]);
  }}
/>
typescript 复制代码
function fileToBase64(file) {
  return new Promise(r => {
    const reader = new FileReader();
    reader.onload = () => r(reader.result.split(",")[1]);
    reader.readAsDataURL(file);
  });
}

七、加上 Embedding 演示(选做)

在 React 中加入一个按钮:

typescript 复制代码
<button
  onClick={async () => {
    const res = await embed("Hello DeepSeek!");
    alert("Embedding length: " + res.data.embedding.length);
  }}
>
  Generate Embedding
</button>

八、项目结构图

bash 复制代码
frontend/ (React)
  ├─ src/
  │   ├─ api.js
  │   └─ App.jsx

backend/ (Fastify)
  ├─ index.js
  └─ .env

九、DeepSeek + React + Fastify 常见坑

问题 原因 解决方案
CORS 报错 Fastify 默认不开 CORS fastify-cors
前端暴露 API Key 不安全 只能放到 Fastify 后端
Vision Base64 太大导致请求失败 默认 JSON 限制较小 app.addContentTypeParser 开大限制 或 nginx 上传限制
Chat 需要 message 数组 OpenAI 格式固定 [{ role, content }]
Fastify body 不解析 需要 app.register(require("@fastify/formbody"))(如使用 form) 使用 JSON body 无需
Embedding 返回维度太大(1024/2048) 正常 用向量数据库存储
前端直接调用 DeepSeek 接口跨域失败 DeepSeek 不支持前端 CORS 调用 必须走 Fastify 后端
相关推荐
لا معنى له3 小时前
目标检测的内涵、发展和经典模型--学习笔记
人工智能·笔记·深度学习·学习·目标检测·机器学习
AKAMAI4 小时前
Akamai Cloud客户案例 | CloudMinister借助Akamai实现多云转型
人工智能·云计算
小a杰.6 小时前
Flutter 与 AI 深度集成指南:从基础实现到高级应用
人工智能·flutter
colorknight6 小时前
数据编织-异构数据存储的自动化治理
数据仓库·人工智能·数据治理·数据湖·数据科学·数据编织·自动化治理
Lun3866buzha7 小时前
篮球场景目标检测与定位_YOLO11-RFPN实现详解
人工智能·目标检测·计算机视觉
janefir7 小时前
LangChain框架下DirectoryLoader使用报错zipfile.BadZipFile
人工智能·langchain
齐齐大魔王7 小时前
COCO 数据集
人工智能·机器学习
AI营销实验室8 小时前
原圈科技AI CRM系统赋能销售新未来,行业应用与创新点评
人工智能·科技
开发者小天8 小时前
react中useEffect的用法,以及订阅模式的原理
前端·react.js·前端框架
爱笑的眼睛118 小时前
超越MSE与交叉熵:深度解析损失函数的动态本质与高阶设计
java·人工智能·python·ai