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 后端
相关推荐
LinQingYanga3 小时前
极客时间多模态大模型训练营毕业总结(2026年2月8日)
人工智能
pccai-vip3 小时前
过去24小时AI创业趋势分析
人工智能
SEO_juper3 小时前
AI SEO实战:整合传统技术与AI生成搜索的优化框架
人工智能·chatgpt·facebook·seo·geo·aeo
pp起床3 小时前
Gen_AI 补充内容 Logit Lens 和 Patchscopes
人工智能·深度学习·机器学习
方见华Richard3 小时前
自指-认知几何架构 可行性边界白皮书(务实版)
人工智能·经验分享·交互·原型模式·空间计算
冬奇Lab3 小时前
AI时代的"工具自由":我是如何进入细糠时代的
人工智能·ai编程
m0_719084113 小时前
React笔记张天禹
前端·笔记·react.js
Ziky学习记录3 小时前
从零到实战:React Router 学习与总结
前端·学习·react.js
CODECOLLECT3 小时前
技术解析|MDM移动设备管理系统无终身买断制度的底层逻辑
人工智能
北京迅为4 小时前
《【北京迅为】itop-3568开发板NPU使用手册》- 第 7章 使用RKNN-Toolkit-lite2
linux·人工智能·嵌入式·npu