Java快速接入DeepSeek实现流式、联网、知识库以及多轮问答 ...

打印 上一主题 下一主题

主题 960|帖子 960|积分 2880

Java快速接入DeepSeek实现流式、联网、知识库以及多轮问答

本文将详细的阐明,如何使用Java、JDK8快速接入deepseek的聊天服务,包含官方的API服务,以及本地Ollama的服务。并搭建一个简朴的前端界面,用于流式输出、多轮问答、联网、知识库问答的结果展示。
1. 创建spring boot应用



2. 引入pom

引入ai4j库的依赖。
AI4J是一款JavaSDK用于快速接入AI大模子应用,整合多平台大模子,如OpenAi、Ollama、智谱Zhipu(ChatGLM)、深度求索DeepSeek、月之暗面Moonshot(Kimi)、腾讯混元Hunyuan、零一万物(01)等等,提供统一的输入输出(对齐OpenAi)消除差别化,优化函数调用(Tool Call),优化RAG调用、支持向量数据库(Pinecone),并且支持JDK1.8,为用户提供快速整合AI的能力。
AI4J-GitHub
  1.         <dependency>
  2.             <groupId>io.github.lnyo-cly</groupId>
  3.             <artifactId>ai4j-spring-boot-starter</artifactId>
  4.             <version>1.3.0</version>
  5.         </dependency>
复制代码
3. 设置application.yml

在deepseek官网,创建API-KEY,然后在application.yml中设置
DeepSeek-APIKEY
  1. ai:
  2.   deepseek:
  3.     api-key: "sk-123456789"
复制代码
4. 创建聊天服务Controller

接下来实现流式输出:
  1. @RestController
  2. public class OpenAiController {
  3.     // 注入Ai服务
  4.     @Autowired
  5.     private AiService aiService;
  6.    
  7.     @GetMapping("/chatStream")
  8.     public SseEmitter getChatMessageStream(@RequestParam String question) {
  9.         SseEmitter emitter = new SseEmitter();
  10.         // 获取DEEPSEEK的聊天服务
  11.         IChatService chatService = aiService.getChatService(PlatformType.DEEPSEEK);
  12.         // 创建请求参数
  13.         ChatCompletion chatCompletion = ChatCompletion.builder()
  14.                 .model("deepseek-chat")
  15.                 .message(ChatMessage.withUser(question))
  16.                 .build();
  17.         Executors.newSingleThreadExecutor().submit(() -> {
  18.             try {
  19.                 SseListener sseListener = new SseListener() {
  20.                     @Override
  21.                     protected void send() {
  22.                         try {
  23.                             emitter.send(this.getCurrData());
  24.                             System.out.println(this.getCurrData());  // 打印当前发送的内容
  25.                         } catch (IOException e) {
  26.                             emitter.completeWithError(e);
  27.                         }
  28.                     }
  29.                 };
  30.                 emitter.onCompletion(() -> {
  31.                     System.out.println("完成");
  32.                     sseListener.getEventSource().cancel();
  33.                 });
  34.                 // 发送流式数据
  35.                 chatService.chatCompletionStream(chatCompletion, sseListener);
  36.                 // 完成后关闭连接
  37.                 emitter.complete();
  38.             } catch (Exception e) {
  39.                 emitter.completeWithError(e);
  40.             }
  41.         });
  42.         return emitter;
  43.     }
  44. }
复制代码
或者
  1.     @GetMapping("/chatStream")
  2.     public SseEmitter getChatMessageStream(@RequestParam String question) throws Exception {
  3.         SseEmitter emitter = new SseEmitter();
  4.         // 获取OLLAMA的聊天服务
  5.         IChatService chatService = aiService.getChatService(PlatformType.DEEPSEEK);
  6.         // 创建请求参数
  7.         ChatCompletion chatCompletion = ChatCompletion.builder()
  8.                 .model("deepseek-chat")
  9.                 .message(ChatMessage.withUser(question))
  10.                 .build();
  11.         SseListener sseListener = new SseListener() {
  12.             @Override
  13.             protected void send() {
  14.                 try {
  15.                     emitter.send(this.getCurrData());
  16.                     System.out.println(this.getCurrData());  // 打印当前发送的内容
  17.                     if ("[DONE]".equals(this.getCurrData())) {
  18.                         emitter.complete();
  19.                     }
  20.                 } catch (IOException e) {
  21.                     emitter.completeWithError(e);
  22.                 }
  23.             }
  24.         };
  25.         emitter.onCompletion(() -> {
  26.             System.out.println("完成");
  27.             sseListener.getEventSource().cancel();
  28.         });
  29.         // 发送流式数据
  30.         sseListener.getCountDownLatch().countDown(); // 取消同步阻塞
  31.         chatService.chatCompletionStream(chatCompletion, sseListener);
  32.         return emitter;
  33.     }
复制代码
测试流式接口如下:

5. 修改为ollama调用

  1.     @GetMapping("/chatStream")
  2.     public SseEmitter getChatMessageStream(@RequestParam String question) {
  3.         SseEmitter emitter = new SseEmitter(-1L);
  4.         // 获取OLLAMA的聊天服务
  5.         IChatService chatService = aiService.getChatService(PlatformType.OLLAMA);
  6.         // 创建请求参数
  7.         ChatCompletion chatCompletion = ChatCompletion.builder()
  8.                 .model("deepseek-r1:1.5b")
  9.                 .message(ChatMessage.withUser(question))
  10.                 .build();
  11.         Executors.newSingleThreadExecutor().submit(() -> {
  12.             try {
  13.                 SseListener sseListener = new SseListener() {
  14.                     @Override
  15.                     protected void send() {
  16.                         try {
  17.                             emitter.send(this.getCurrData());
  18.                             System.out.println(this.getCurrData());  // 打印当前发送的内容
  19.                         } catch (IOException e) {
  20.                             emitter.completeWithError(e);
  21.                         }
  22.                     }
  23.                 };
  24.                 emitter.onCompletion(() -> {
  25.                     System.out.println("完成");
  26.                     sseListener.getEventSource().cancel();
  27.                 });
  28.                 // 发送流式数据
  29.                 chatService.chatCompletionStream(chatCompletion, sseListener);
  30.                 // 完成后关闭连接
  31.                 emitter.complete();
  32.             } catch (Exception e) {
  33.                 emitter.completeWithError(e);
  34.             }
  35.         });
  36.         return emitter;
  37.     }
复制代码
修改两处即可:


  • 修改PlatformType为OLLAMA
  • 修改model为deepseek-r1:1.5b

6. 搭建前端界面

注意:此前端界面由AI天生,并未颠末严格测试,仅供参考。
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>AI聊天助手</title>
  7.     <!-- Font Awesome CDN -->
  8.     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
  9.     <style>
  10.         :root {
  11.             --primary-color: #6366f1;
  12.             --primary-light: #818cf8;
  13.             --primary-dark: #4f46e5;
  14.             --text-light: #ffffff;
  15.             --text-dark: #1e293b;
  16.             --bg-light: #f8fafc;
  17.             --bg-dark: #0f172a;
  18.             --message-user: #6366f1;
  19.             --message-bot: #f1f5f9;
  20.             --border-color: #e2e8f0;
  21.         }
  22.         * {
  23.             margin: 0;
  24.             padding: 0;
  25.             box-sizing: border-box;
  26.             font-family: 'Inter', 'Arial', sans-serif;
  27.         }
  28.         body {
  29.             background-color: var(--bg-light);
  30.             color: var(--text-dark);
  31.             display: flex;
  32.             justify-content: center;
  33.             align-items: center;
  34.             min-height: 100vh;
  35.             padding: 20px;
  36.         }
  37.         .chat-container {
  38.             width: 100%;
  39.             max-width: 900px;
  40.             background: white;
  41.             border-radius: 16px;
  42.             box-shadow: 0 10px 25px rgba(0, 0, 0, 0.05);
  43.             overflow: hidden;
  44.             display: flex;
  45.             flex-direction: column;
  46.             height: 85vh;
  47.             position: relative;
  48.             border: 1px solid var(--border-color);
  49.         }
  50.         .chat-header {
  51.             background: var(--primary-color);
  52.             color: var(--text-light);
  53.             padding: 18px 24px;
  54.             display: flex;
  55.             align-items: center;
  56.             gap: 12px;
  57.             box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  58.             z-index: 10;
  59.         }
  60.         .chat-header i {
  61.             font-size: 1.5rem;
  62.         }
  63.         .chat-header h1 {
  64.             font-size: 1.3rem;
  65.             font-weight: 600;
  66.         }
  67.         .chat-messages {
  68.             flex: 1;
  69.             overflow-y: auto;
  70.             padding: 24px;
  71.             display: flex;
  72.             flex-direction: column;
  73.             gap: 20px;
  74.             scroll-behavior: smooth;
  75.         }
  76.         .message-container {
  77.             display: flex;
  78.             gap: 12px;
  79.             max-width: 85%;
  80.         }
  81.         .user-container {
  82.             align-self: flex-end;
  83.             flex-direction: row-reverse;
  84.         }
  85.         .bot-container {
  86.             align-self: flex-start;
  87.         }
  88.         .avatar {
  89.             width: 38px;
  90.             height: 38px;
  91.             border-radius: 50%;
  92.             display: flex;
  93.             justify-content: center;
  94.             align-items: center;
  95.             font-size: 1.2rem;
  96.             flex-shrink: 0;
  97.         }
  98.         .user-avatar {
  99.             background: var(--primary-light);
  100.             color: var(--text-light);
  101.         }
  102.         .bot-avatar {
  103.             background: var(--primary-dark);
  104.             color: var(--text-light);
  105.         }
  106.         .message {
  107.             padding: 14px 20px;
  108.             border-radius: 18px;
  109.             font-size: 1rem;
  110.             line-height: 1.6;
  111.             position: relative;
  112.             max-width: 100%;
  113.         }
  114.         .user-message {
  115.             background: var(--message-user);
  116.             color: var(--text-light);
  117.             border-top-right-radius: 4px;
  118.         }
  119.         .bot-message {
  120.             background: var(--message-bot);
  121.             color: var(--text-dark);
  122.             border-top-left-radius: 4px;
  123.         }
  124.         .message-time {
  125.             font-size: 0.7rem;
  126.             opacity: 0.7;
  127.             margin-top: 6px;
  128.             text-align: right;
  129.         }
  130.         .user-message .message-time {
  131.             color: rgba(255, 255, 255, 0.9);
  132.         }
  133.         .bot-message .message-time {
  134.             color: rgba(0, 0, 0, 0.6);
  135.         }
  136.         .chat-input-container {
  137.             padding: 16px 24px;
  138.             background: white;
  139.             border-top: 1px solid var(--border-color);
  140.             display: flex;
  141.             align-items: center;
  142.             gap: 14px;
  143.             z-index: 10;
  144.         }
  145.         .chat-input {
  146.             flex: 1;
  147.             padding: 14px 20px;
  148.             border: 1px solid var(--border-color);
  149.             border-radius: 30px;
  150.             font-size: 1rem;
  151.             outline: none;
  152.             transition: all 0.3s;
  153.             background: var(--bg-light);
  154.         }
  155.         .chat-input:focus {
  156.             border-color: var(--primary-color);
  157.             box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
  158.         }
  159.         .send-button {
  160.             width: 50px;
  161.             height: 50px;
  162.             border: none;
  163.             background: var(--primary-color);
  164.             color: var(--text-light);
  165.             border-radius: 50%;
  166.             cursor: pointer;
  167.             transition: all 0.3s;
  168.             display: flex;
  169.             justify-content: center;
  170.             align-items: center;
  171.             box-shadow: 0 2px 10px rgba(99, 102, 241, 0.3);
  172.         }
  173.         .send-button:hover {
  174.             background: var(--primary-dark);
  175.             transform: translateY(-2px);
  176.             box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);
  177.         }
  178.         .send-button:active {
  179.             transform: translateY(0);
  180.         }
  181.         .send-button i {
  182.             font-size: 1.2rem;
  183.         }
  184.         /* 滚动条样式 */
  185.         .chat-messages::-webkit-scrollbar {
  186.             width: 6px;
  187.         }
  188.         .chat-messages::-webkit-scrollbar-track {
  189.             background: transparent;
  190.         }
  191.         .chat-messages::-webkit-scrollbar-thumb {
  192.             background: #d1d5db;
  193.             border-radius: 10px;
  194.         }
  195.         .chat-messages::-webkit-scrollbar-thumb:hover {
  196.             background: #9ca3af;
  197.         }
  198.         /* 打字机效果 */
  199.         .typing {
  200.             display: flex;
  201.             align-items: center;
  202.             gap: 4px;
  203.             padding: 8px 12px;
  204.             border-radius: 18px;
  205.             background: var(--message-bot);
  206.             width: fit-content;
  207.         }
  208.         .typing-dot {
  209.             width: 8px;
  210.             height: 8px;
  211.             background: var(--primary-color);
  212.             border-radius: 50%;
  213.             animation: typing-animation 1.4s infinite both;
  214.         }
  215.         .typing-dot:nth-child(2) {
  216.             animation-delay: 0.2s;
  217.         }
  218.         .typing-dot:nth-child(3) {
  219.             animation-delay: 0.4s;
  220.         }
  221.         @keyframes typing-animation {
  222.             0%, 100% {
  223.                 opacity: 0.3;
  224.                 transform: scale(0.8);
  225.             }
  226.             50% {
  227.                 opacity: 1;
  228.                 transform: scale(1);
  229.             }
  230.         }
  231.         /* 消息进入动画 */
  232.         @keyframes message-in {
  233.             from {
  234.                 opacity: 0;
  235.                 transform: translateY(20px);
  236.             }
  237.             to {
  238.                 opacity: 1;
  239.                 transform: translateY(0);
  240.             }
  241.         }
  242.         .message-container {
  243.             animation: message-in 0.3s ease-out forwards;
  244.         }
  245.         /* 响应式调整 */
  246.         @media (max-width: 768px) {
  247.             .chat-container {
  248.                 height: 90vh;
  249.                 border-radius: 12px;
  250.             }
  251.             .message-container {
  252.                 max-width: 90%;
  253.             }
  254.             .chat-header h1 {
  255.                 font-size: 1.1rem;
  256.             }
  257.         }
  258.         @media (max-width: 480px) {
  259.             .chat-container {
  260.                 height: 92vh;
  261.                 border-radius: 8px;
  262.             }
  263.             .message {
  264.                 padding: 12px 16px;
  265.             }
  266.             .avatar {
  267.                 width: 32px;
  268.                 height: 32px;
  269.                 font-size: 1rem;
  270.             }
  271.             .chat-input {
  272.                 padding: 12px 16px;
  273.             }
  274.             .send-button {
  275.                 width: 45px;
  276.                 height: 45px;
  277.             }
  278.             .chat-header {
  279.                 padding: 14px 20px;
  280.             }
  281.             .chat-messages {
  282.                 padding: 20px;
  283.             }
  284.         }
  285.     </style>
  286. </head>
  287. <body>
  288. <div class="chat-container">
  289.     <div class="chat-header">
  290.         <i class="fas fa-robot"></i>
  291.         <h1>AI聊天助手</h1>
  292.     </div>
  293.     <div class="chat-messages" id="chat-messages">
  294.         <div class="message-container bot-container">
  295.             <div class="avatar bot-avatar">
  296.                 <i class="fas fa-robot"></i>
  297.             </div>
  298.             <div class="message-content">
  299.                 <div class="message bot-message">
  300.                     您好!我是AI助手,很高兴为您服务。请问有什么我可以帮助您的吗?
  301.                 </div>
  302.                 <div class="message-time">
  303.                     刚刚
  304.                 </div>
  305.             </div>
  306.         </div>
  307.     </div>
  308.     <div class="chat-input-container">
  309.         <input type="text" class="chat-input" id="user-input" placeholder="输入您的问题..." autofocus>
  310.         <button class="send-button" id="send-button">
  311.             <i class="fas fa-paper-plane"></i>
  312.         </button>
  313.     </div>
  314. </div>
  315. <script>
  316.     document.addEventListener('DOMContentLoaded', function() {
  317.         const chatMessages = document.getElementById('chat-messages');
  318.         const userInput = document.getElementById('user-input');
  319.         const sendButton = document.getElementById('send-button');
  320.         let eventSource = null;
  321.         // 获取当前时间
  322.         function getCurrentTime() {
  323.             const now = new Date();
  324.             let hours = now.getHours();
  325.             let minutes = now.getMinutes();
  326.             // 确保分钟为两位数
  327.             minutes = minutes < 10 ? '0' + minutes : minutes;
  328.             return `${hours}:${minutes}`;
  329.         }
  330.         // 添加用户消息
  331.         function addUserMessage(message, time) {
  332.             const messageContainer = document.createElement('div');
  333.             messageContainer.className = 'message-container user-container';
  334.             const avatarDiv = document.createElement('div');
  335.             avatarDiv.className = 'avatar user-avatar';
  336.             avatarDiv.innerHTML = '<i class="fas fa-user"></i>';
  337.             const messageContentDiv = document.createElement('div');
  338.             messageContentDiv.className = 'message-content';
  339.             const messageDiv = document.createElement('div');
  340.             messageDiv.className = 'message user-message';
  341.             messageDiv.textContent = message;
  342.             const timeDiv = document.createElement('div');
  343.             timeDiv.className = 'message-time';
  344.             timeDiv.textContent = time;
  345.             messageContentDiv.appendChild(messageDiv);
  346.             messageContentDiv.appendChild(timeDiv);
  347.             messageContainer.appendChild(avatarDiv);
  348.             messageContainer.appendChild(messageContentDiv);
  349.             chatMessages.appendChild(messageContainer);
  350.             chatMessages.scrollTop = chatMessages.scrollHeight;
  351.         }
  352.         // 发送消息函数
  353.         function sendMessage() {
  354.             const message = userInput.value.trim();
  355.             if (!message) return;
  356.             // 添加用户消息到聊天区域
  357.             const time = getCurrentTime();
  358.             addUserMessage(message, time);
  359.             // 清空输入框
  360.             userInput.value = '';
  361.             // 添加机器人正在输入的指示
  362.             const typingContainer = document.createElement('div');
  363.             typingContainer.className = 'message-container bot-container';
  364.             typingContainer.id = 'bot-typing';
  365.             const avatarDiv = document.createElement('div');
  366.             avatarDiv.className = 'avatar bot-avatar';
  367.             avatarDiv.innerHTML = '<i class="fas fa-robot"></i>';
  368.             const typingDiv = document.createElement('div');
  369.             typingDiv.className = 'typing';
  370.             typingDiv.innerHTML = '<span class="typing-dot"></span><span class="typing-dot"></span><span class="typing-dot"></span>';
  371.             typingContainer.appendChild(avatarDiv);
  372.             typingContainer.appendChild(typingDiv);
  373.             chatMessages.appendChild(typingContainer);
  374.             chatMessages.scrollTop = chatMessages.scrollHeight;
  375.             // 更改按钮为暂停
  376.             sendButton.innerHTML = '<i class="fas fa-pause"></i>';
  377.             sendButton.onclick = stopStream;
  378.             // 创建EventSource连接
  379.             const url = `http://127.0.0.1:8080/chatStream?question=${encodeURIComponent(message)}`;
  380.             eventSource = new EventSource(url);
  381.             let botResponse = '';
  382.             let responseContainer = null;
  383.             eventSource.onmessage = function(event) {
  384.                 // 如果这是第一条消息,创建回复容器
  385.                 if (!responseContainer) {
  386.                     // 移除打字指示器
  387.                     const typingIndicator = document.getElementById('bot-typing');
  388.                     if (typingIndicator) {
  389.                         typingIndicator.remove();
  390.                     }
  391.                     // 创建回复容器
  392.                     responseContainer = document.createElement('div');
  393.                     responseContainer.className = 'message-container bot-container';
  394.                     responseContainer.id = 'current-bot-response';
  395.                     const avatarDiv = document.createElement('div');
  396.                     avatarDiv.className = 'avatar bot-avatar';
  397.                     avatarDiv.innerHTML = '<i class="fas fa-robot"></i>';
  398.                     const messageContentDiv = document.createElement('div');
  399.                     messageContentDiv.className = 'message-content';
  400.                     const messageDiv = document.createElement('div');
  401.                     messageDiv.className = 'message bot-message';
  402.                     messageDiv.id = 'current-bot-message';
  403.                     const timeDiv = document.createElement('div');
  404.                     timeDiv.className = 'message-time';
  405.                     timeDiv.textContent = getCurrentTime();
  406.                     messageContentDiv.appendChild(messageDiv);
  407.                     messageContentDiv.appendChild(timeDiv);
  408.                     responseContainer.appendChild(avatarDiv);
  409.                     responseContainer.appendChild(messageContentDiv);
  410.                     chatMessages.appendChild(responseContainer);
  411.                 }
  412.                 // 更新回复内容
  413.                 botResponse += event.data;
  414.                 const messageDiv = document.getElementById('current-bot-message');
  415.                 if (messageDiv) {
  416.                     messageDiv.textContent = botResponse;
  417.                 }
  418.                 // 自动滚动到底部
  419.                 chatMessages.scrollTop = chatMessages.scrollHeight;
  420.             };
  421.             eventSource.onerror = function() {
  422.                 // 处理完成或错误时
  423.                 completeResponse();
  424.             };
  425.         }
  426.         // 停止流式响应
  427.         function stopStream() {
  428.             if (eventSource) {
  429.                 eventSource.close();
  430.                 completeResponse();
  431.             }
  432.         }
  433.         // 完成响应处理
  434.         function completeResponse() {
  435.             // 关闭连接
  436.             if (eventSource) {
  437.                 eventSource.close();
  438.                 eventSource = null;
  439.             }
  440.             // 移除打字指示器
  441.             const typingIndicator = document.getElementById('bot-typing');
  442.             if (typingIndicator) {
  443.                 typingIndicator.remove();
  444.             }
  445.             // 恢复发送按钮
  446.             sendButton.innerHTML = '<i class="fas fa-paper-plane"></i>';
  447.             sendButton.onclick = sendMessage;
  448.             // 移除id,以便下次使用
  449.             const currentBotResponse = document.getElementById('current-bot-response');
  450.             if (currentBotResponse) {
  451.                 currentBotResponse.removeAttribute('id');
  452.             }
  453.             const currentBotMessage = document.getElementById('current-bot-message');
  454.             if (currentBotMessage) {
  455.                 currentBotMessage.removeAttribute('id');
  456.             }
  457.         }
  458.         // 设置事件监听器
  459.         sendButton.addEventListener('click', sendMessage);
  460.         userInput.addEventListener('keydown', function(e) {
  461.             if (e.key === 'Enter') {
  462.                 sendMessage();
  463.             }
  464.         });
  465.         // 自动聚焦到输入框
  466.         userInput.focus();
  467.     });
  468. </script>
  469. </body>
  470. </html>
复制代码

7. 多轮对话

只必要简朴修改,即可携带汗青上下文举行对话:
  1.     private List<ChatMessage> history = new ArrayList<>(); // 1. 创建历史消息列表
  2.     @GetMapping("/chatStream")
  3.     public SseEmitter getChatMessageStream(@RequestParam String question) {
  4.         // ......
  5.         history.add(ChatMessage.withUser(question)); // 2. 向历史中添加用户输入
  6.         // 创建请求参数
  7.         ChatCompletion chatCompletion = ChatCompletion.builder()
  8.                 .model("deepseek-chat")
  9.                 .messages(history) // 3. 添加完整历史消息
  10.                 .build();
  11.         // ......        
  12.         Executors.newSingleThreadExecutor().submit(() -> {
  13.             try {
  14.                 // ......
  15.                 emitter.onCompletion(() -> {
  16.                     System.out.println("完成");
  17.                     history.add(ChatMessage.withAssistant(sseListener.getOutput().toString())); // 4. 向历史中添加AI回复
  18.                     sseListener.getEventSource().cancel();
  19.                 });
  20.                 // ......
  21.             } catch (Exception e) {
  22.                 emitter.completeWithError(e);
  23.             }
  24.         });
  25.         // ......
  26.     }
复制代码

8. 联网对话


  • 设置application.yml中的searxng,将此中的url更换为你已经摆设的searxng服务的地点。
  1. ai:
  2.   websearch:
  3.     searxng:
  4.       url: "http://127.0.0.1:29080/search"
  5.       nums: 10
复制代码

  • 修改代码,添加联网对话的功能:
  1.     @GetMapping("/chatStream")
  2.     public SseEmitter getChatMessageStream(@RequestParam String question) {
  3.         // ......
  4.         // 获取DEEPSEEK的聊天服务
  5.         IChatService chatService = aiService.webSearchEnhance(aiService.getChatService(PlatformType.DEEPSEEK)); // 1. 使用webSearchEnhance对原本chat服务增加联网功能,该联网服务使用的为searxng
  6.         // ......
  7.         // ......
  8.     }
复制代码

9. 搭建知识库

本文使用的向量数据库为Pinecone
9.1 创建Pinecone

各人可以进入Pinecone官网举行注册和登录,至于注册账号,这里不在演示,信赖各人都会。
选择Database->Indexes->Create Index来创建索引

在这里可以输入你的维度,或者点击Setup by model,根据模子来选择向量维度。这里我以text-embedding-3-large模子为例子


创建完成后,记载自己的Host,我们后面要用到

创建自己的API Key

9.2 设置application.yml

请将上文得到的Host和API Key填入application.yml中
  1. ai:
  2.   vector:
  3.     pinecone:
  4.       host: "https://XXXXXXX-XXXXXXXXX.io"
  5.       key: "XXXXXX"
复制代码
9.3 构建RAG知识库文档

既然要创建RAG应用,那肯定少不了知识库。
本文搭建的是一个简朴的法律AI助手,以是我们必要一个法律知识库。
接下来我以刑法知识库为例为各人讲解
可以将所必要的知识库,存入一个文本文档当中:

注意:如果有现成的文档,你也可以忽略这一步,比方你已经有了txt、word、pdf等文件的知识库文档。
9.4 存储至Pinecone向量数据库中

  1. @SpringBootTest
  2. public class RagTest {
  3.     // 1. 注入Pinecone服务
  4.     @Autowired
  5.     private PineconeService pineconeService;
  6.     // 2. 注入AI服务
  7.     @Autowired
  8.     private AiService aiService;
  9.     @Test
  10.     public void test_rag_store() throws Exception {
  11.         // 3. 获取Embedding服务
  12.         IEmbeddingService embeddingService = aiService.getEmbeddingService(PlatformType.OPENAI);
  13.         // 4. Tika读取file文件内容
  14.         String fileContent = TikaUtil.parseFile(new File("D:\\data\\test.txt"));
  15.         System.out.println(fileContent);
  16.         // 5. 分割文本内容
  17.         RecursiveCharacterTextSplitter recursiveCharacterTextSplitter = new RecursiveCharacterTextSplitter(1000, 200);
  18.         List<String> contentList = recursiveCharacterTextSplitter.splitText(fileContent);
  19.         System.out.println(contentList.size());
  20.         // 6. 转为向量
  21.         Embedding build = Embedding.builder()
  22.                 .input(contentList)
  23.                 .model("text-embedding-3-large")
  24.                 .build();
  25.         EmbeddingResponse embedding = embeddingService.embedding(build);
  26.         List<List<Float>> vectors = embedding.getData().stream().map(EmbeddingObject::getEmbedding).collect(Collectors.toList());
  27.         VertorDataEntity vertorDataEntity = new VertorDataEntity();
  28.         vertorDataEntity.setVector(vectors);
  29.         vertorDataEntity.setContent(contentList);
  30.         System.out.println(vertorDataEntity);
  31.         // 7. 向量存储至pinecone
  32.         Integer count = pineconeService.insert(vertorDataEntity, "abc-123-abc");
  33.         System.out.println(count > 0 ? "存储成功" : "存储失败");
  34.     }
  35. }
复制代码
下图是插入成功的数据

9.5 知识库查询

下面代码只多了对embedding的处理,chat部门基本稳定。
  1.     @Autowired
  2.     private AiService aiService;
  3.     @Autowired
  4.     private PineconeService pineconeService;
  5.     private List<ChatMessage> history = new ArrayList<>();
  6.     @GetMapping("/chatStream")
  7.     public SseEmitter getChatMessageStream(@RequestParam String question) throws Exception {
  8.         SseEmitter emitter = new SseEmitter(-1L);
  9.         // 获取Embedding服务
  10.         IEmbeddingService embeddingService = aiService.getEmbeddingService(PlatformType.OPENAI);
  11.         // 构建要查询的问题,转为向量
  12.         Embedding build = Embedding.builder()
  13.                 .input(question)
  14.                 .model("text-embedding-3-large")
  15.                 .build();
  16.         EmbeddingResponse embedding = embeddingService.embedding(build);
  17.         List<Float> questionEmbedding = embedding.getData().get(0).getEmbedding();
  18.         // 构建向量数据库的查询对象
  19.         PineconeQuery pineconeQueryReq = PineconeQuery.builder()
  20.                 .namespace("abc-123-abc")
  21.                 .topK(5)
  22.                 .vector(questionEmbedding)
  23.                 .build();
  24.         // 查询
  25.         // PineconeQueryResponse queryResponse = pineconeService.query(pineconeQueryReq);
  26.         // delimiter为想用什么字符拼接查询出来的内容
  27.         String retrievalContent = pineconeService.query(pineconeQueryReq, " ");
  28.         String contentFormat = "你是一个善于回答中华人民共和国刑法相关问题的助手。请使用以下提供的检索内容和自身知识来回答问题。如果你不知道答案,请直接说不知道,不要杜撰答案。请用三句话以内回答,保持简洁。\n" +
  29.                 "\n" +
  30.                 "问题:%s\n" +
  31.                 "\n" +
  32.                 "检索内容:%s";
  33.         String content = String.format(contentFormat, question, retrievalContent);
  34.         // 获取DEEPSEEK的聊天服务
  35.         IChatService chatService = aiService.getChatService(PlatformType.DEEPSEEK);
  36.         history.add(ChatMessage.withUser(content)); // 向历史中添加用户输入
  37.         // 创建请求参数
  38.         ChatCompletion chatCompletion = ChatCompletion.builder()
  39.                 .model("deepseek-chat")
  40.                 .messages(history) // 添加完整历史消息
  41.                 .build();
  42.         Executors.newSingleThreadExecutor().submit(() -> {
  43.             try {
  44.                 SseListener sseListener = new SseListener() {
  45.                     @Override
  46.                     protected void send() {
  47.                         try {
  48.                             emitter.send(this.getCurrStr());
  49.                             System.out.println(this.getCurrData());  // 打印当前发送的内容
  50.                         } catch (IOException e) {
  51.                             emitter.completeWithError(e);
  52.                         }
  53.                     }
  54.                 };
  55.                 emitter.onCompletion(() -> {
  56.                     System.out.println("完成");
  57.                     history.add(ChatMessage.withAssistant(sseListener.getOutput().toString())); // 向历史中添加AI回复
  58.                     sseListener.getEventSource().cancel();
  59.                 });
  60.                 // 发送流式数据
  61.                 chatService.chatCompletionStream(chatCompletion, sseListener);
  62.                 // 完成后关闭连接
  63.                 emitter.complete();
  64.             } catch (Exception e) {
  65.                 emitter.completeWithError(e);
  66.             }
  67.         });
  68.         return emitter;
  69.     }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

惊落一身雪

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表