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 后端
相关推荐
大千AI助手1 小时前
概率单位回归(Probit Regression)详解
人工智能·机器学习·数据挖掘·回归·大千ai助手·概率单位回归·probit回归
狂炫冰美式2 小时前
3天,1人,从0到付费产品:AI时代个人开发者的生存指南
前端·人工智能·后端
用户600071819102 小时前
【翻译】使用 React 19 操作构建可复用组件
react.js
LCG元2 小时前
垂直Agent才是未来:详解让大模型"专业对口"的三大核心技术
人工智能
禁止摆烂_才浅3 小时前
Taro 小程序页面返回传参完整示例
react.js·微信小程序·taro
我不是QI3 小时前
周志华《机器学习—西瓜书》二
人工智能·安全·机器学习
操练起来3 小时前
【昇腾CANN训练营·第八期】Ascend C生态兼容:基于PyTorch Adapter的自定义算子注册与自动微分实现
人工智能·pytorch·acl·昇腾·cann
KG_LLM图谱增强大模型3 小时前
[500页电子书]构建自主AI Agent系统的蓝图:谷歌重磅发布智能体设计模式指南
人工智能·大模型·知识图谱·智能体·知识图谱增强大模型·agenticai
声网3 小时前
活动推荐丨「实时互动 × 对话式 AI」主题有奖征文
大数据·人工智能·实时互动