SpringAI 本地調用 Ollama

  • 1 Ollama 軟件下載
  • 2 Ollama 模型下載
  • 2.1 PowerShell 下載腳本
  • 2.2 Ollama模型運行
  • 3 SpringAI
  • 1 依賴
  • 2 配置
  • 3 MemConf.java
  • 4 OllamaController.java

LangChain4j 詳細介紹
LangChain4j 是一個專為 Java 生態系統設計的開源框架,旨在簡化大語言模型(LLM)與應用程序的集成,提供了構建基於 LLM 的複雜應用所需的核心組件和工具。其設計理念借鑑了 Python 生態的 LangChain,但針對 Java 開發者的習慣和需求進行了優化。

LangChain4j 核心特點

  1. 模塊化設計:提供了清晰的組件劃分,包括模型接口、提示模板、記憶機制、工具調用、鏈(Chains)等,開發者可按需組合。
  2. 多模型支持:兼容主流 LLM,如 OpenAI、GPT-4、Claude、Llama 等,同時支持本地模型(如通過 Ollama 部署的開源模型)。
  3. 強大的記憶機制:支持對話歷史管理,包括臨時記憶、持久化記憶(如 Redis、數據庫存儲),方便構建多輪對話應用。
  4. 工具調用能力:允許 LLM 調用外部工具(如搜索引擎、數據庫、API),擴展模型的能力邊界。
  5. 與 Java 生態融合:無縫集成 Spring、Jakarta EE 等主流框架,支持依賴注入、異步處理等 Java 開發模式。
  6. 輕量級:核心庫體積小,無過多依賴,易於嵌入現有 Java 項目。

LangChain4j 應用場景

  • 構建智能客服、聊天機器人
  • 實現文檔問答(基於向量數據庫的檢索增強生成,RAG)
  • 開發自動化工作流(結合工具調用)
  • 集成 LLM 到企業級 Java 應用中

LangChain4j VS Spring AI

維度

LangChain4j

Spring AI

定位

專注於 LLM 應用開發的工具鏈,類似 Java 版 LangChain

Spring 生態的官方 AI 擴展,提供標準化集成

生態依賴

獨立框架,可與任何 Java 框架集成(包括 Spring)

深度依賴 Spring 生態(如 Spring Boot、Spring Cloud)

核心能力

提供完整的 LLM 應用組件(鏈、記憶、工具等)

聚焦於模型接口標準化、數據處理、集成能力

模型支持

支持主流閉源/開源模型,配置靈活

支持主流模型,通過統一接口抽象,符合 Spring 風格

易用性

需手動組合組件,靈活性高,但學習成本略高

遵循 Spring 約定優於配置,開箱即用,適合 Spring 開發者

企業級特性

需自行集成安全、監控等特性

天然支持 Spring 生態的企業級特性(如安全、審計、監控)

社區與更新

社區活躍,更新頻率中等

官方背書,更新頻繁,與 Spring 版本同步演進

適用場景

複雜 LLM 應用(如多鏈協作、自定義記憶策略)

快速集成 LLM 到 Spring 應用,追求標準化和穩定性

選擇建議

  • 若項目已基於 Spring 生態(如 Spring Boot),且希望快速集成 LLM 並利用 Spring 的企業級特性,優先選擇 Spring AI
  • 若需要更靈活的 LLM 應用構建能力,或項目不依賴 Spring 框架,LangChain4j 是更合適的選擇。
  • 若需實現複雜的 RAG、多輪對話記憶或工具調用邏輯,LangChain4j 的組件化設計可能更具優勢。

1 Ollama 軟件下載

Ollama

SpringAI 本地調用 Ollama - 詳解_Java

2 Ollama 模型下載

2.1 PowerShell 下載腳本

複製內容到ollama_download.ps1文件中

# 定義模型名稱避免硬編碼
$modelName = "gemma3:27b"
# 設置重試參數
$maxRetries = 50
$retryInterval = 3  # 秒
$downloadTimeout = 80  # 秒
for ($retry = 1; $retry -le $maxRetries; $retry++) {
# 精確檢查模型是否存在
$modelExists = ollama list | Where-Object { $_ -match "\b$modelName\b" }
if ($modelExists) {
Write-Host "[$(Get-Date)] model is already downloaded!"
exit 0
}
Write-Host "[$(Get-Date)] start $retry times downloading..."
# 啓動進程並顯示實時輸出
$process = Start-Process -FilePath "ollama" `
-ArgumentList "run", $modelName `
-PassThru `
-NoNewWindow
# 等待下載完成或超時
try {
$process | Wait-Process -Timeout $downloadTimeout -ErrorAction Stop
} catch {
# 超時處理
Write-Host "download timeout, safe terminate process..."
$process | Stop-Process -Force
}
if (-not $process.HasExited) {
$process | Stop-Process -Force
Write-Host "process terminated due to timeout."
} else {
# 檢查退出代碼
if ($process.ExitCode -eq 0) {
Write-Host "download success!"
exit 0
}
Write-Host "download failed, exit code: $($process.ExitCode)"
}
Start-Sleep -Seconds $retryInterval
}
Write-Host "exceeded maximum retries ($maxRetries), download failed."
exit 1
PS C:\Users\xuhya\Desktop> .\ollama_download.ps1
[10/09/2025 21:42:20] start 1 times downloading...
pulling manifest
pulling e796792eba26:  98% ▕████████████████████████████████████████████████████████  ▏  17 GB/ 17 GB  1.7 MB/s   3m20sdownload timeout, safe terminate process...
download failed, exit code: -1
[10/09/2025 21:43:43] start 2 times downloading...
pulling manifest
pulling e796792eba26: 100% ▕██████████████████████████████████████████████████████████▏  17 GB
pulling e0a42594d802: 100% ▕██████████████████████████████████████████████████████████▏  358 B
pulling dd084c7d92a3: 100% ▕██████████████████████████████████████████████████████████▏ 8.4 KB
pulling 3116c5225075: 100% ▕██████████████████████████████████████████████████████████▏   77 B
pulling f838f048d368: 100% ▕██████████████████████████████████████████████████████████▏  490 B
verifying sha256 digest ⠸ download timeout, safe terminate process...
download failed, exit code: -1
[10/09/2025 21:45:06] start 3 times downloading...
pulling manifest
pulling e796792eba26: 100% ▕██████████████████████████████████████████████████████████▏  17 GB
pulling e0a42594d802: 100% ▕██████████████████████████████████████████████████████████▏  358 B
pulling dd084c7d92a3: 100% ▕██████████████████████████████████████████████████████████▏ 8.4 KB
pulling 3116c5225075: 100% ▕██████████████████████████████████████████████████████████▏   77 B
pulling f838f048d368: 100% ▕██████████████████████████████████████████████████████████▏  490 B
verifying sha256 digest
writing manifest
success
⠏ download timeout, safe terminate process...
download failed, exit code: -1
[10/09/2025 21:46:29] model is already downloaded!
PS C:\Users\xuhya\Desktop>

2.2 Ollama模型運行

PS C:\Users\xuhya\Desktop> ollama list
NAME             ID              SIZE      MODIFIED
gemma3:27b       a418f5838eaf    17 GB     2 minutes ago
gemma3:latest    a2af6cc3eb7f    3.3 GB    7 minutes ago
PS C:\Users\xuhya\Desktop>

SpringAI 本地調用 Ollama - 詳解_spring_02

3 SpringAI

1 依賴

<?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
  <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>3.5.6</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
<groupId>com.xu</groupId>
<artifactId>spring-ai</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-ai</name>
<description>Demo project for Spring Boot</description>
  <properties>
  <java.version>25</java.version>
  </properties>
  <dependencyManagement>
    <dependencies>
      <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-bom</artifactId>
      <version>1.0.3</version>
      <type>pom</type>
      <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-ollama</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
    </dependency>
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <annotationProcessorPaths>
            <path>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            </path>
          </annotationProcessorPaths>
        </configuration>
      </plugin>
      <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
          <excludes>
            <exclude>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            </exclude>
          </excludes>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

2 配置

spring:
application:
name: spring-ai
ai:
ollama:
base-url: http://localhost:11434
chat:
model: gemma3:latest

3 MemConf.java

package com.xu.conf;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MemConf {
@Bean
public ChatMemory chatMemory() {
return MessageWindowChatMemory.builder()
.chatMemoryRepository(new InMemoryChatMemoryRepository())
.maxMessages(20)
.build();
}
}

4 OllamaController.java

package com.xu.ollama;
import lombok.AllArgsConstructor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.content.Media;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.http.MediaType;
import org.springframework.util.MimeType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import reactor.core.publisher.Flux;
@RestController
@AllArgsConstructor
@RequestMapping("/ollama")
public class OllamaController {
private final ChatMemory chatMemory;
private final OllamaChatModel model;
/**
* 聊天
*
* @param content 聊天內容
* @return 聊天結果
*/
@RequestMapping("/common/chat")
public Object chat(String content) {
return model.call(content);
}
/**
* 聊天記憶
*
* @param content 聊天內容
* @return 聊天結果
*/
@RequestMapping("/common/memory")
public Object memory(String content, String msgId) {
UserMessage message = UserMessage.builder().text(content).build();
chatMemory.add(msgId, message);
ChatResponse response = model.call(new Prompt(message));
chatMemory.add(msgId, response.getResult().getOutput());
return response.getResult().getOutput().getText();
}
/**
* 聊天圖片
*
* @param content 聊天內容
* @return 聊天結果
*/
@RequestMapping("/common/image")
public Object image(@RequestParam("content") String content, @RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return model.call(content);
}
var media = new Media(new MimeType("image", "png"), file.getResource());
return model.call(new Prompt(UserMessage.builder().media(media).text(content).build()));
}
/**
* 聊天流式
*
* @param content 聊天內容
* @return 聊天結果
*/
@RequestMapping(value = "/stream/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(String content) {
  return model.stream(content);
  }
  }

SpringAI 本地調用 Ollama - 詳解_spring_03