最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于SpringBoot+SpringAI+Ollama開發(fā)智能問答系統(tǒng)

 更新時間:2025年06月08日 10:23:21   作者:碼農(nóng)阿豪@新空間  
在人工智能技術飛速發(fā)展的今天,大語言模型(LLM)已成為開發(fā)者工具箱中不可或缺的一部分,本文將介紹如何利用SpringBoot、SpringAI框架結合Ollama本地大模型服務,搭建一個完全運行在本地Windows環(huán)境下的智能問答系統(tǒng),有需要的可以了解下

引言

在人工智能技術飛速發(fā)展的今天,大語言模型(LLM)已成為開發(fā)者工具箱中不可或缺的一部分。然而,依賴云端API服務不僅存在數(shù)據(jù)隱私問題,還可能產(chǎn)生高昂成本。本文將介紹如何利用SpringBoot、SpringAI框架結合Ollama本地大模型服務,搭建一個完全運行在本地Windows環(huán)境下的智能問答系統(tǒng)。

技術棧概述

SpringBoot與SpringAI

SpringBoot作為Java生態(tài)中最流行的應用框架,提供了快速構建生產(chǎn)級應用的能力。SpringAI是Spring生態(tài)系統(tǒng)中的新興成員,專門為AI集成設計,它簡化了與各種大語言模型的交互過程,提供了統(tǒng)一的API接口。

Ollama本地模型服務

Ollama是一個開源項目,允許開發(fā)者在本地運行和管理大型語言模型。它支持多種開源模型,包括Llama、Mistral等,并提供了簡單的API接口。通過Ollama,我們可以在不依賴互聯(lián)網(wǎng)連接的情況下使用強大的語言模型能力。

環(huán)境準備

硬件要求

Windows 10/11操作系統(tǒng)

至少16GB RAM(推薦32GB或以上)

NVIDIA顯卡(可選,可加速推理)

軟件安裝

1.安裝Ollama:

訪問Ollama官網(wǎng)(https://ollama.ai)下載Windows版本并安裝

2.驗證Ollama安裝:

ollama list

項目搭建

創(chuàng)建SpringBoot項目

使用Spring Initializr(https://start.spring.io)創(chuàng)建項目,選擇以下依賴:

  • Spring Web
  • Lombok
  • Spring AI (如未列出可手動添加)

配置pom.xml

確保包含SpringAI Ollama依賴:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
    <version>0.8.1</version>
</dependency>

應用配置

application.yml配置:

spring:
  ai:
    ollama:
      base-url: http://localhost:11434
      chat:
        model: deepseek
        options:
          temperature: 0.7
          top-p: 0.9

核心功能實現(xiàn)

問答服務層

創(chuàng)建QAService類:

@Service
public class QAService {
    
    private final OllamaChatClient chatClient;
    
    public QAService(OllamaChatClient chatClient) {
        this.chatClient = chatClient;
    }
    
    public String generateAnswer(String prompt) {
        return chatClient.call(prompt);
    }
    
    public Flux<String> generateStreamAnswer(String prompt) {
        return chatClient.stream(prompt);
    }
}

控制器實現(xiàn)

QAController.java:

@RestController
@RequestMapping("/api/qa")
public class QAController {
    
    private final QAService qaService;
    
    public QAController(QAService qaService) {
        this.qaService = qaService;
    }
    
    @PostMapping("/ask")
    public ResponseEntity<String> askQuestion(@RequestBody String question) {
        String answer = qaService.generateAnswer(question);
        return ResponseEntity.ok(answer);
    }
    
    @GetMapping(value = "/ask-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> askQuestionStream(@RequestParam String question) {
        return qaService.generateStreamAnswer(question);
    }
}

提示工程優(yōu)化

為提高回答質量,我們可以實現(xiàn)提示模板:

PromptTemplateService.java:

@Service
public class PromptTemplateService {
    
    private static final String QA_TEMPLATE = """
            你是一個專業(yè)的AI助手,請根據(jù)以下要求回答問題:
            1. 回答要專業(yè)、準確
            2. 如果問題涉及不確定信息,請明確說明
            3. 保持回答簡潔明了
            
            問題:{question}
            """;
    
    public String buildPrompt(String question) {
        return QA_TEMPLATE.replace("{question}", question);
    }
}

更新QAService使用提示模板:

public String generateAnswer(String prompt) {
    String formattedPrompt = promptTemplateService.buildPrompt(prompt);
    return chatClient.call(formattedPrompt);
}

高級功能實現(xiàn)

對話歷史管理

實現(xiàn)簡單的對話記憶功能:

ConversationManager.java:

@Service
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ConversationManager {
    
    private final List<String> conversationHistory = new ArrayList<>();
    
    public void addExchange(String userInput, String aiResponse) {
        conversationHistory.add("用戶: " + userInput);
        conversationHistory.add("AI: " + aiResponse);
    }
    
    public String getConversationContext() {
        return String.join("\n", conversationHistory);
    }
    
    public void clear() {
        conversationHistory.clear();
    }
}

更新提示模板以包含歷史:

public String buildPrompt(String question, String history) {
    return QA_TEMPLATE.replace("{history}", history)
                     .replace("{question}", question);
}

文件內(nèi)容問答

實現(xiàn)基于上傳文檔的問答功能:

DocumentService.java:

@Service
public class DocumentService {
    
    private final ResourceLoader resourceLoader;
    private final TextSplitter textSplitter;
    
    public DocumentService(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
        this.textSplitter = new TokenTextSplitter();
    }
    
    public List<String> processDocument(MultipartFile file) throws IOException {
        String content = new String(file.getBytes(), StandardCharsets.UTF_8);
        return textSplitter.split(content);
    }
    
    public String extractRelevantParts(List<String> chunks, String question) {
        // 簡化的相關性匹配 - 實際項目應使用嵌入向量
        return chunks.stream()
                .filter(chunk -> chunk.toLowerCase().contains(question.toLowerCase()))
                .findFirst()
                .orElse("");
    }
}

添加文檔問答端點:

@PostMapping(value = "/ask-with-doc", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<String> askWithDocument(
        @RequestParam String question,
        @RequestParam MultipartFile document) throws IOException {
    
    List<String> chunks = documentService.processDocument(document);
    String context = documentService.extractRelevantParts(chunks, question);
    
    String prompt = """
            基于以下文檔內(nèi)容回答問題:
            
            文檔相關部分:
            {context}
            
            問題:{question}
            """.replace("{context}", context)
              .replace("{question}", question);
    
    String answer = qaService.generateAnswer(prompt);
    return ResponseEntity.ok(answer);
}

前端交互實現(xiàn)

簡單HTML界面

resources/static/index.html:

<!DOCTYPE html>
<html>
<head>
    <title>本地AI問答系統(tǒng)</title>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
    <h1>本地問答系統(tǒng)</h1>
    <div>
        <textarea id="question" rows="4" cols="50"></textarea>
    </div>
    <button onclick="askQuestion()">提問</button>
    <div id="answer" style="margin-top: 20px; border: 1px solid #ccc; padding: 10px;"></div>
    
    <script>
        function askQuestion() {
            const question = document.getElementById('question').value;
            document.getElementById('answer').innerText = "思考中...";
            
            axios.post('/api/qa/ask', question, {
                headers: { 'Content-Type': 'text/plain' }
            })
            .then(response => {
                document.getElementById('answer').innerText = response.data;
            })
            .catch(error => {
                document.getElementById('answer').innerText = "出錯: " + error.message;
            });
        }
    </script>
</body>
</html>

流式響應界面

添加流式問答HTML:

<div style="margin-top: 30px;">
    <h2>流式問答</h2>
    <textarea id="streamQuestion" rows="4" cols="50"></textarea>
    <button onclick="askStreamQuestion()">流式提問</button>
    <div id="streamAnswer" style="margin-top: 20px; border: 1px solid #ccc; padding: 10px;"></div>
</div>

???????<script>
    function askStreamQuestion() {
        const question = document.getElementById('streamQuestion').value;
        const answerDiv = document.getElementById('streamAnswer');
        answerDiv.innerText = "";
        
        const eventSource = new EventSource(`/api/qa/ask-stream?question=${encodeURIComponent(question)}`);
        
        eventSource.onmessage = function(event) {
            answerDiv.innerText += event.data;
        };
        
        eventSource.onerror = function() {
            eventSource.close();
        };
    }
</script>

性能優(yōu)化與調(diào)試

模型參數(shù)調(diào)優(yōu)

在application.yml中調(diào)整模型參數(shù):

spring:
  ai:
    ollama:
      chat:
        options:
          temperature: 0.5  # 控制創(chuàng)造性(0-1)
          top-p: 0.9        # 核采樣閾值
          num-predict: 512  # 最大token數(shù)

日志記錄

配置日志以監(jiān)控AI交互:

@Configuration
public class LoggingConfig {
    
    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
    
    @Bean
    public OllamaApi ollamaApi(Client client, ObjectProvider<HttpMessageConverterCustomizer> customizers) {
        return new OllamaApiInterceptor(new OllamaApi(client, customizers));
    }
}

???????class OllamaApiInterceptor implements OllamaApi {
    
    private static final Logger log = LoggerFactory.getLogger(OllamaApiInterceptor.class);
    private final OllamaApi delegate;
    
    public OllamaApiInterceptor(OllamaApi delegate) {
        this.delegate = delegate;
    }
    
    @Override
    public GenerateResponse generate(GenerateRequest request) {
        log.info("Ollama請求: {}", request);
        GenerateResponse response = delegate.generate(request);
        log.debug("Ollama響應: {}", response);
        return response;
    }
}

超時設置

配置連接超時:

spring:
  ai:
    ollama:
      client:
        connect-timeout: 30s
        read-timeout: 5m

安全加固

API認證

添加簡單的API密鑰認證:

SecurityConfig.java:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Value("${app.api-key}")
    private String apiKey;
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/**").authenticated()
                .anyRequest().permitAll()
            )
            .addFilterBefore(new ApiKeyFilter(apiKey), UsernamePasswordAuthenticationFilter.class)
            .csrf().disable();
        return http.build();
    }
}
???????class ApiKeyFilter extends OncePerRequestFilter {
    
    private final String expectedApiKey;
    
    public ApiKeyFilter(String expectedApiKey) {
        this.expectedApiKey = expectedApiKey;
    }
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                   HttpServletResponse response, 
                                   FilterChain filterChain) throws ServletException, IOException {
        String apiKey = request.getHeader("X-API-KEY");
        
        if (!expectedApiKey.equals(apiKey)) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "無效的API密鑰");
            return;
        }
        
        filterChain.doFilter(request, response);
    }
}

部署與運行

啟動Ollama服務

在Windows命令行中:

ollama serve

運行SpringBoot應用

在IDE中直接運行主類,或使用Maven命令:

mvn spring-boot:run

系統(tǒng)測試

訪問 http://localhost:8080 測試問答功能,或使用Postman測試API端點。

擴展思路

向量數(shù)據(jù)庫集成

考慮集成Chroma或Milvus等向量數(shù)據(jù)庫實現(xiàn)更精準的文檔檢索:

@Configuration
public class VectorStoreConfig {
    
    @Bean
    public VectorStore vectorStore(EmbeddingClient embeddingClient) {
        return new SimpleVectorStore(embeddingClient);
    }
    
    @Bean
    public EmbeddingClient embeddingClient(OllamaApi ollamaApi) {
        return new OllamaEmbeddingClient(ollamaApi);
    }
}

多模型切換

實現(xiàn)動態(tài)模型選擇:

@Service
public class ModelSelectorService {
    
    private final Map<String, ChatClient> clients;
    
    public ModelSelectorService(
            OllamaChatClient deep seekClient,
            OllamaChatClient llamaClient) {
        this.clients = Map.of(
            "deep seek", deep seekClient,
            "llama", llamaClient
        );
    }
    
    public ChatClient getClient(String modelName) {
        return clients.getOrDefault(modelName, clients.get("deep seek"));
    }
}

總結

本文詳細介紹了如何使用SpringBoot、SpringAI和Ollama在本地Windows環(huán)境搭建一個功能完整的大模型問答系統(tǒng)。通過這個方案,開發(fā)者可以:

  • 完全在本地運行AI服務,保障數(shù)據(jù)隱私
  • 利用Spring生態(tài)快速構建生產(chǎn)級應用
  • 靈活選擇不同的開源模型
  • 實現(xiàn)基礎的問答到復雜的文檔分析功能

隨著本地AI技術的不斷進步,這種架構將為更多企業(yè)應用提供安全、可控的AI解決方案。讀者可以根據(jù)實際需求擴展本文示例,如增加更多模型支持、優(yōu)化提示工程或集成更復雜的業(yè)務邏輯。

到此這篇關于基于SpringBoot+SpringAI+Ollama開發(fā)智能問答系統(tǒng)的文章就介紹到這了,更多相關SpringBoot SpringAI Ollama實現(xiàn)智能問答內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • java銀行管理系統(tǒng)源碼

    java銀行管理系統(tǒng)源碼

    這篇文章主要為大家詳細介紹了java銀行管理系統(tǒng)源碼,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • Mybatis_plus基礎教程(總結篇)

    Mybatis_plus基礎教程(總結篇)

    這篇文章主要介紹了Mybatis_plus基礎教程(總結篇),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • Java輸出多位小數(shù)的三種方法(附代碼)

    Java輸出多位小數(shù)的三種方法(附代碼)

    這篇文章主要給大家介紹了關于Java輸出多位小數(shù)的三種方法的相關資料,在實際工作中常常需要設定數(shù)字的輸出格式,如以百分比的形式輸出,或者設定小數(shù)位數(shù)等,需要的朋友可以參考下
    2023-07-07
  • Java創(chuàng)建和填充PDF表單域方法

    Java創(chuàng)建和填充PDF表單域方法

    在本篇文章中小編給大家分享了關于Java創(chuàng)建和填充PDF表單域方法和步驟,有需要的朋友們學習下。
    2019-01-01
  • Java多線程下解決資源競爭的7種方法詳解

    Java多線程下解決資源競爭的7種方法詳解

    這篇文章主要介紹了Java多線程下解決資源競爭的7種方法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-08-08
  • 布隆過濾器面試如何快速判斷元素是否在集合里

    布隆過濾器面試如何快速判斷元素是否在集合里

    這篇文章主要為大家介紹了布隆過濾器面試中如何快速判斷元素是否在集合里的完美回復,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2022-03-03
  • Mybatis配置之typeAlias標簽的用法

    Mybatis配置之typeAlias標簽的用法

    這篇文章主要介紹了Mybatis配置之typeAlias標簽的用法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java深入講解static操作符

    Java深入講解static操作符

    static關鍵字基本概念我們可以一句話來概括:方便在沒有創(chuàng)建對象的情況下來進行調(diào)用。也就是說:被static關鍵字修飾的不需要創(chuàng)建對象去調(diào)用,直接根據(jù)類名就可以去訪問,讓我們來了解一下你可能還不知道情況
    2022-07-07
  • java 如何實現(xiàn)多語言配置i18n

    java 如何實現(xiàn)多語言配置i18n

    這篇文章主要介紹了java 如何實現(xiàn)多語言配置i18n的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java實現(xiàn)阿里云短信接口的示例

    Java實現(xiàn)阿里云短信接口的示例

    這篇文章主要介紹了Java實現(xiàn)阿里云短信接口的示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-09-09

最新評論

庆云县| 江华| 葵青区| 泾川县| 宁波市| 安陆市| 屯门区| 信宜市| 扶绥县| 瓮安县| 安溪县| 和顺县| 惠来县| 陆川县| 石楼县| 南华县| 资中县| 罗城| 永昌县| 江门市| 北川| 兴城市| 分宜县| 嘉黎县| 桦甸市| 桃源县| 华坪县| 土默特右旗| 福贡县| 石泉县| 固始县| 襄城县| 平舆县| 东平县| 增城市| 贵阳市| 吉林市| 扶沟县| 开远市| 密山市| 宜丰县|