官网智能问答机器人(DeepSeek-R1 API 版)落地实践全流程

本次落地基于 Java 生态的主流技术栈(Spring Boot 3.x),遵循 "接口验证→知识库向量化→多轮问答核心逻辑→前端集成→测试上线" 的思路,聚焦 "精准问答 + 多轮上下文保留 + 低成本适配现有官网",以下是可直接落地的实操步骤:

一、前期准备(1-2 天完成)
1. 资源与环境准备

表格

类别 具体内容
API 密钥 登录DeepSeek 开发者平台,创建应用,获取API Key(注意开通 "DeepSeek-R1" 和 "Embedding" 接口权限)
技术栈选型 后端:Java 17+、Spring Boot 3.2.x(轻量易集成)、Maven(依赖管理)、OkHttp(HTTP 请求)、Chroma Java 客户端(向量数据库);前端:原生 JS(适配任意官网技术栈);向量数据库:Chroma(轻量级,支持 Java 客户端)
数据准备 整理官网知识库(产品 FAQ、售后政策等),拆分为 200-500 字的语义完整短片段,清洗冗余内容
环境依赖 Maven 核心依赖(添加到pom.xml):```xml
XML 复制代码
<dependencies><!-- Spring Boot Web核心 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- OkHttp HTTP客户端 --><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.12.0</version></dependency><!-- Jackson JSON解析 --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency><!-- Chroma Java客户端 --><dependency><groupId>io.github.chroma-ai</groupId><artifactId>chroma-java</artifactId><version>1.4.0</version></dependency><!-- 环境变量配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency></dependencies>
2. 核心需求确认
  • 多轮问答:保留对话历史,支持追问(如 "产品 A 保修期多久?"→"保修期内维修收费吗?");
  • 回答边界:仅基于企业知识库回答,无相关内容时返回预设兜底话术;
  • 性能要求:单轮响应时间 < 1.5s,支持 10-20 并发请求。
二、核心落地步骤(3-5 天完成)
步骤 1:DeepSeek-R1 API 对接验证(Java 版)

先验证 API 连通性,确保 Java 端能正常调用 DeepSeek 接口,避免后续开发踩坑。

1.1 配置文件管理(避免硬编码 API Key)

src/main/resources/application.yml中添加配置:

html 复制代码
deepseek:
  api-key: 你的DeepSeek API Key
  chat-url: https://api.deepseek.com/v1/chat/completions
  embedding-url: https://api.deepseek.com/v1/embeddings
chroma:
  collection-name: official_website_kb
  persist-path: ./chroma_db  # 向量库本地存储路径
1.2 API 调用工具类(通用 HTTP 请求)

创建src/main/java/com/company/chatbot/util/DeepSeekApiUtil.java

java 复制代码
package com.company.chatbot.util;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;

@Component
public class DeepSeekApiUtil {
    @Value("${deepseek.api-key}")
    private String apiKey;

    private final OkHttpClient okHttpClient = new OkHttpClient();
    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 通用调用DeepSeek API的方法
     */
    public JsonNode callApi(String url, Map<String, Object> requestBody) throws IOException {
        // 构建请求头
        RequestHeaders headers = new RequestHeaders.Builder()
                .add("Content-Type", "application/json")
                .add("Authorization", "Bearer " + apiKey)
                .build();

        // 转换请求体为JSON
        String jsonBody = objectMapper.writeValueAsString(requestBody);
        RequestBody body = RequestBody.create(jsonBody, MediaType.parse("application/json"));

        // 发送请求
        Request request = new Request.Builder()
                .url(url)
                .headers(headers)
                .post(body)
                .build();

        try (Response response = okHttpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("API调用失败:" + response);
            }
            // 解析响应
            return objectMapper.readTree(response.body().string());
        }
    }
}
1.3 对话接口测试(验证连通性)

创建测试类src/test/java/com/company/chatbot/DeepSeekApiTest.java

java 复制代码
package com.company.chatbot;

import com.company.chatbot.util.DeepSeekApiUtil;
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.HashMap;
import java.util.Map;

@SpringBootTest
public class DeepSeekApiTest {
    @Autowired
    private DeepSeekApiUtil deepSeekApiUtil;

    @Value("${deepseek.chat-url}")
    private String chatUrl;

    @Test
    public void testChatApi() throws Exception {
        // 构建请求参数
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("model", "deepseek-r1");
        requestBody.put("temperature", 0.1);

        // 对话消息(单轮测试)
        Map<String, String> message = new HashMap<>();
        message.put("role", "user");
        message.put("content", "你们公司的核心产品是什么?");
        requestBody.put("messages", new Object[]{message});

        // 调用API
        JsonNode response = deepSeekApiUtil.callApi(chatUrl, requestBody);
        // 输出回答
        String answer = response.get("choices").get(0).get("message").get("content").asText();
        System.out.println("AI回答:" + answer);
    }
}

预期结果:控制台输出 DeepSeek-R1 的回答,验证接口调用成功。

步骤 2:知识库构建与向量化对接(核心)

通过 DeepSeek Embedding 接口将知识库文本转为向量,存入 Chroma 向量库,实现 "相似问题检索"(多轮问答精准性的核心)。

2.1 向量数据库初始化工具类

创建src/main/java/com/company/chatbot/util/ChromaUtil.java

java 复制代码
package com.company.chatbot.util;

import io.github.chroma-ai.ChromaClient;
import io.github.chroma-ai.Collection;
import io.github.chroma-ai.beans.Embedding;
import io.github.chroma-ai.utils.CollectionUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class ChromaUtil {
    @Value("${chroma.collection-name}")
    private String collectionName;

    @Value("${chroma.persist-path}")
    private String persistPath;

    private ChromaClient chromaClient;
    private Collection collection;

    // 初始化Chroma客户端和集合
    public void init() {
        this.chromaClient = new ChromaClient.Builder()
                .persist(persistPath)  // 本地持久化
                .build();
        // 获取/创建集合
        this.collection = chromaClient.getOrCreateCollection(collectionName);
    }

    // 导入知识库文本到向量库
    public void importKnowledgeBase(List<String> texts, List<String> ids, List<List<Float>> embeddings) {
        init();
        collection.add(ids, texts, embeddings, null);
    }

    // 检索相似文本
    public List<String> retrieveSimilarTexts(List<Float> queryEmbedding, int topK) {
        init();
        // 向量检索
        Embedding embedding = CollectionUtils.embedding(queryEmbedding);
        List<Embedding> queryEmbeddings = List.of(embedding);
        // 获取最相似的topK条
        return collection.query(queryEmbeddings, null, topK).getDocuments().get(0);
    }
}
2.2 Embedding 接口调用(文本转向量)

DeepSeekApiUtil.java中新增方法:

java 复制代码
/**
 * 调用Embedding接口,将文本转为向量
 */
public List<Float> getEmbedding(String text) throws IOException {
    String url = "https://api.deepseek.com/v1/embeddings";
    Map<String, Object> requestBody = new HashMap<>();
    requestBody.put("model", "text-embedding-ada-002");
    requestBody.put("input", text);

    JsonNode response = callApi(url, requestBody);
    // 提取向量
    JsonNode embeddingNode = response.get("data").get(0).get("embedding");
    return objectMapper.convertValue(embeddingNode, List.class);
}
2.3 知识库导入服务(首次执行)

创建src/main/java/com/company/chatbot/service/KnowledgeBaseService.java

java 复制代码
package com.company.chatbot.service;

import com.company.chatbot.util.ChromaUtil;
import com.company.chatbot.util.DeepSeekApiUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Service
public class KnowledgeBaseService {
    @Autowired
    private DeepSeekApiUtil deepSeekApiUtil;

    @Autowired
    private ChromaUtil chromaUtil;

    /**
     * 导入知识库到向量库(仅首次执行,更新知识库时重新调用)
     */
    public void importKB() throws IOException {
        // 示例知识库(替换为企业实际内容)
        List<String> texts = List.of(
                "产品A的系统要求:支持Windows 10及以上系统。",
                "产品A的保修政策:保修期2年,支持全国联保。",
                "产品A的售后联系方式:售后电话400-XXX-XXXX。"
        );
        List<String> ids = List.of("kb_001", "kb_002", "kb_003");
        List<List<Float>> embeddings = new ArrayList<>();

        // 批量转换为向量
        for (String text : texts) {
            embeddings.add(deepSeekApiUtil.getEmbedding(text));
        }

        // 导入向量库
        chromaUtil.importKnowledgeBase(texts, ids, embeddings);
        System.out.println("知识库导入成功,共" + texts.size() + "条");
    }
}

✅ 执行方式:可通过 Test 类调用importKB()方法,完成知识库初始化。

步骤 3:多轮问答核心逻辑开发(Spring Boot 后端)

核心流程:用户提问 → 检索相似知识库 → 拼接上下文 → 调用 DeepSeek-R1 → 保留对话历史 → 返回回答。

3.1 实体类定义(请求 / 响应)
5.2 上线部署
三、上线后运维与优化

总结

这套方案完全适配有基础 Java 开发团队的企业,代码可直接复用,仅需替换知识库内容和 API Key 即可落地,中小企业月度 API 调用成本通常低于 1000 元,且能无缝集成到现有官网系统。

  • 对话消息实体:src/main/java/com/company/chatbot/entity/ChatMessage.java

    java 复制代码
    package com.company.chatbot.entity;
    
    import lombok.Data;
    
    @Data
    public class ChatMessage {
        // 角色:user/assistant
        private String role;
        // 消息内容
        private String content;
    }

    问答请求实体:src/main/java/com/company/chatbot/entity/ChatRequest.java

    java 复制代码
    package com.company.chatbot.entity;
    
    import lombok.Data;
    import java.util.List;
    
    @Data
    public class ChatRequest {
        // 用户当前问题
        private String question;
        // 对话历史(多轮问答核心)
        private List<ChatMessage> history;
    }
    java 复制代码
    问答响应实体:src/main/java/com/company/chatbot/entity/ChatResponse.java
    java 复制代码
    package com.company.chatbot.entity;
    
    import lombok.Data;
    import java.util.List;
    
    @Data
    public class ChatResponse {
        private int code;
        private String msg;
        private DataDTO data;
    
        @Data
        public static class DataDTO {
            // AI回答
            private String answer;
            // 更新后的对话历史
            private List<ChatMessage> history;
        }
    }
    java 复制代码
    3.2 多轮问答核心服务
    创建src/main/java/com/company/chatbot/service/ChatBotService.java:
    java 复制代码
    package com.company.chatbot.service;
    
    import com.company.chatbot.entity.ChatMessage;
    import com.company.chatbot.util.ChromaUtil;
    import com.company.chatbot.util.DeepSeekApiUtil;
    import com.fasterxml.jackson.databind.JsonNode;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Service;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    @Service
    public class ChatBotService {
        @Autowired
        private DeepSeekApiUtil deepSeekApiUtil;
    
        @Autowired
        private ChromaUtil chromaUtil;
    
        @Value("${deepseek.chat-url}")
        private String chatUrl;
    
        // 兜底话术
        private static final String DEFAULT_ANSWER = "暂无相关答案,可联系人工客服:400-XXX-XXXX";
    
        /**
         * 多轮问答核心方法
         */
        public ChatResponse.DataDTO chat(String question, List<ChatMessage> history) throws IOException {
            // 1. 检索相似知识库片段(topK=3,平衡精准性和效率)
            List<Float> queryEmbedding = deepSeekApiUtil.getEmbedding(question);
            List<String> similarTexts = chromaUtil.retrieveSimilarTexts(queryEmbedding, 3);
    
            // 2. 拼接上下文(限定AI仅基于知识库回答)
            String context = similarTexts.isEmpty() ? "" : String.join("\n", similarTexts);
            String prompt = String.format("请基于以下企业官网知识库内容回答用户问题,仅使用知识库中的信息,不要编造内容。\n知识库内容:\n%s\n\n用户问题:%s", context, question);
    
            // 3. 构建多轮对话消息(历史+当前prompt)
            List<ChatMessage> messages = history == null ? new ArrayList<>() : new ArrayList<>(history);
            ChatMessage currentMessage = new ChatMessage();
            currentMessage.setRole("user");
            currentMessage.setContent(prompt);
            messages.add(currentMessage);
    
            // 4. 调用DeepSeek-R1接口
            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("model", "deepseek-r1");
            requestBody.put("temperature", 0.1);  // 低温度保证回答精准
            requestBody.put("max_tokens", 500);   // 限制回答长度
            requestBody.put("messages", messages);
    
            JsonNode response = deepSeekApiUtil.callApi(chatUrl, requestBody);
            String answer = response.get("choices").get(0).get("message").get("content").asText();
    
            // 5. 兜底逻辑:无知识库内容时返回预设话术
            if (context.isEmpty()) {
                answer = DEFAULT_ANSWER;
            }
    
            // 6. 更新对话历史(添加AI回答)
            ChatMessage aiMessage = new ChatMessage();
            aiMessage.setRole("assistant");
            aiMessage.setContent(answer);
            messages.add(aiMessage);
    
            // 7. 封装返回数据
            ChatResponse.DataDTO dataDTO = new ChatResponse.DataDTO();
            dataDTO.setAnswer(answer);
            dataDTO.setHistory(messages);
            return dataDTO;
        }
    }
    java 复制代码
    3.3 暴露 HTTP 接口(供前端调用)
    创建src/main/java/com/company/chatbot/controller/ChatBotController.java:
    java 复制代码
    package com.company.chatbot.controller;
    
    import com.company.chatbot.entity.ChatRequest;
    import com.company.chatbot.entity.ChatResponse;
    import com.company.chatbot.service.ChatBotService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.io.IOException;
    
    @RestController
    @RequestMapping("/api/chat")
    public class ChatBotController {
        @Autowired
        private ChatBotService chatBotService;
    
        @PostMapping
        public ChatResponse chat(@RequestBody ChatRequest request) {
            ChatResponse response = new ChatResponse();
            try {
                ChatResponse.DataDTO data = chatBotService.chat(request.getQuestion(), request.getHistory());
                response.setCode(200);
                response.setMsg("success");
                response.setData(data);
            } catch (IOException e) {
                response.setCode(500);
                response.setMsg("系统异常:" + e.getMessage());
                ChatResponse.DataDTO errorData = new ChatResponse.DataDTO();
                errorData.setAnswer("系统暂时无法回答,请稍后再试");
                errorData.setHistory(request.getHistory());
                response.setData(errorData);
            }
            return response;
        }
    }
    java 复制代码
    3.4 跨域配置(解决前端跨域问题)
    创建src/main/java/com/company/chatbot/config/CorsConfig.java:
    java 复制代码
    package com.company.chatbot.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
    import org.springframework.web.filter.CorsFilter;
    
    @Configuration
    public class CorsConfig {
        @Bean
        public CorsFilter corsFilter() {
            CorsConfiguration config = new CorsConfiguration();
            // 允许所有域名(生产环境需指定企业官网域名)
            config.addAllowedOriginPattern("*");
            config.addAllowedHeader("*");
            config.addAllowedMethod("*");
            config.setAllowCredentials(true);
    
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**", config);
            return new CorsFilter(source);
        }
    }
    步骤 4:前端集成(原生 JS,支持多轮问答)

    开发轻量级问答组件,嵌入官网侧边栏 / 弹窗,核心是保留对话历史并传递给后端:

    html 复制代码
    <!-- 官网多轮问答组件(可嵌入官网任意位置) -->
    <div id="chatbot-container" style="position: fixed; bottom: 20px; right: 20px; width: 380px; border: 1px solid #eee; border-radius: 8px; background: #fff; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
        <div style="padding: 12px; background: #007bff; color: #fff; border-radius: 8px 8px 0 0; font-size: 16px;">官网智能问答</div>
        <!-- 对话历史展示区 -->
        <div id="chat-history" style="height: 320px; padding: 10px; overflow-y: auto; border-bottom: 1px solid #eee;"></div>
        <!-- 输入区 -->
        <div style="padding: 10px; display: flex; gap: 8px;">
            <input type="text" id="question-input" placeholder="请输入您的问题(支持追问)..." style="flex: 1; padding: 8px; border: 1px solid #eee; border-radius: 4px; outline: none;">
            <button id="send-btn" style="padding: 8px 18px; background: #007bff; color: #fff; border: none; border-radius: 4px; cursor: pointer;">发送</button>
        </div>
    </div>
    
    <script>
    // 全局变量:存储多轮对话历史(核心)
    let chatHistory = [];
    // 后端API地址(生产环境替换为企业服务器域名)
    const API_URL = "http://localhost:8080/api/chat";
    
    /**
     * 发送消息并处理多轮对话
     */
    async function sendMessage() {
        const inputEl = document.getElementById("question-input");
        const question = inputEl.value.trim();
        if (!question) return;
    
        // 1. 显示用户问题到聊天窗口
        const historyEl = document.getElementById("chat-history");
        historyEl.innerHTML += `<div style="margin: 8px 0;"><strong>您:</strong>${question}</div>`;
        inputEl.value = "";
    
        try {
            // 2. 调用后端API(传递问题+对话历史)
            const response = await fetch(API_URL, {
                method: "POST",
                headers: {
                    "Content-Type": "application/json"
                },
                body: JSON.stringify({
                    question: question,
                    history: chatHistory
                })
            });
    
            const result = await response.json();
            if (result.code === 200) {
                // 3. 显示AI回答
                historyEl.innerHTML += `<div style="margin: 8px 0;"><strong>智能客服:</strong>${result.data.answer}</div>`;
                // 4. 更新全局对话历史(用于下一轮追问)
                chatHistory = result.data.history;
                // 5. 滚动到聊天窗口底部
                historyEl.scrollTop = historyEl.scrollHeight;
            } else {
                historyEl.innerHTML += `<div style="margin: 8px 0; color: red;"><strong>智能客服:</strong>${result.msg}</div>`;
            }
        } catch (e) {
            historyEl.innerHTML += `<div style="margin: 8px 0; color: red;"><strong>智能客服:</strong>网络异常,请稍后再试</div>`;
        }
    }
    
    // 绑定发送按钮点击事件
    document.getElementById("send-btn").addEventListener("click", sendMessage);
    // 绑定回车发送
    document.getElementById("question-input").addEventListener("keypress", (e) => {
        if (e.key === "Enter") sendMessage();
    });
    </script>
    步骤 5:测试与上线
    5.1 测试要点(聚焦多轮问答)
  • 功能测试:测试多轮追问(如 "产品 A 保修期多久?"→"保修期内维修收费吗?"),验证历史上下文是否保留;

  • 精准性测试:覆盖所有知识库问题,验证回答仅来自知识库,无编造内容;

  • 边界测试:测试无相关内容的问题、超长问题、特殊字符问题;

  • 性能测试:用 Postman 模拟 20 并发请求,确保响应时间 < 1.5s。

  • 后端部署:将 Spring Boot 项目打包为 JAR 包(mvn clean package),部署到企业服务器 / 云服务器(如阿里云 ECS),配置 JDK 17 环境,启动命令:java -jar chatbot-0.0.1-SNAPSHOT.jar

  • 前端集成:将问答组件代码嵌入官网公共模板(如 header/footer),替换 API_URL 为服务器域名;

  • 监控配置:在后端添加日志(记录用户问题、API 调用次数、错误信息),便于后续优化。

  • 知识库迭代:更新知识库时,重新调用KnowledgeBaseService.importKB()方法,覆盖 / 新增向量库内容;

  • 成本控制:在 DeepSeek 开发者平台监控 API 调用量,设置月度调用阈值,避免超额;

  • 多轮体验优化:分析用户高频追问场景,优化知识库文本拆分(提升检索准确率),调整temperature参数(0.1-0.3 为宜);

  • 性能优化:生产环境可将 Chroma 向量库部署为独立服务,而非本地存储,提升检索效率。

  • 核心技术栈:后端基于 Spring Boot 3.x+OkHttp 实现 DeepSeek API 调用,Chroma Java 客户端完成向量检索,前端通过原生 JS 保留多轮对话历史;

  • 多轮问答关键:后端接收并传递history(对话历史)参数,前端全局存储历史数据,确保追问上下文连贯;

  • 落地核心:知识库的 "短文本拆分 + 向量检索" 是保证回答精准的关键,需按语义拆分文本(200-500 字 / 段),并调试topK值(建议 3-5)。

相关推荐
九.九7 小时前
ops-transformer:AI 处理器上的高性能 Transformer 算子库
人工智能·深度学习·transformer
春日见7 小时前
拉取与合并:如何让个人分支既包含你昨天的修改,也包含 develop 最新更新
大数据·人工智能·深度学习·elasticsearch·搜索引擎
恋猫de小郭7 小时前
AI 在提高你工作效率的同时,也一直在增加你的疲惫和焦虑
前端·人工智能·ai编程
deephub8 小时前
Agent Lightning:微软开源的框架无关 Agent 训练方案,LangChain/AutoGen 都能用
人工智能·microsoft·langchain·大语言模型·agent·强化学习
大模型RAG和Agent技术实践8 小时前
从零构建本地AI合同审查系统:架构设计与流式交互实战(完整源代码)
人工智能·交互·智能合同审核
老邋遢8 小时前
第三章-AI知识扫盲看这一篇就够了
人工智能
互联网江湖8 小时前
Seedance2.0炸场:长短视频们“修坝”十年,不如AI放水一天?
人工智能
2601_949146538 小时前
Shell语音通知接口使用指南:运维自动化中的语音告警集成方案
运维·自动化
PythonPioneer8 小时前
在AI技术迅猛发展的今天,传统职业该如何“踏浪前行”?
人工智能
儒雅的晴天8 小时前
大模型幻觉问题
运维·服务器