三種Spring AI本地/低成本模型接入方案
1. 為什么需要本地模型接入?
在生產(chǎn)環(huán)境中,調(diào)用 OpenAI / Claude API 存在三大痛點:
| 痛點 | 影響 |
|---|---|
| 數(shù)據(jù)隱私 | 用戶數(shù)據(jù)不能離開企業(yè)內(nèi)網(wǎng),合規(guī)審計嚴格 |
| 網(wǎng)絡延遲 | 國內(nèi)訪問海外 API 往返 >300ms,體驗差 |
| 成本壓力 | GPT-4o 約 $2.5/1M tokens,中小項目難以承受 |
Spring AI 提供了三大接入路徑:本地 Ollama、DashScope API(國內(nèi)直連低價)、API 代理(如 OneAPI)統(tǒng)一管理多模型,本篇逐一拆解。

2. Ollama:本地大模型零成本運行
2.1 安裝與模型下載
macOS / Linux 一行安裝:
# 一鍵安裝 curl -fsSL https://ollama.com/install.sh | sh # Windows 直接下載安裝包:https://ollama.com/download
拉取常用模型(根據(jù)顯存選型):
# 通義千問 7B(推薦入門,int4 量化約 4GB 顯存) ollama pull qwen2.5:7b # Llama 3.1 8B(通用能力強) ollama pull llama3.1:8b # Mistral 7B(指令遵循優(yōu)秀) ollama pull mistral # Phi-3.5(微軟小模型,CPU 可跑) ollama pull phi3.5:latest # bge-m3 嵌入模型(知識庫必備) ollama pull nomic-embed-text
顯存參考表:
| 模型 | 參數(shù)量級 | FP16 顯存 | int4 量化顯存 | CPU 可跑 |
|---|---|---|---|---|
| Phi-3.5 | 3.8B | ~8GB | ~2GB | ? |
| Qwen2.5 7B | 7B | ~14GB | ~4GB | ?? 慢 |
| Llama 3.1 8B | 8B | ~16GB | ~5GB | ? |
| Qwen2.5 14B | 14B | ~28GB | ~8GB | ? |
| Qwen2.5 32B | 32B | ~64GB | ~18GB | ? |
驗證安裝:
ollama list # NAME ID SIZE MODIFIED # qwen2.5:7b a3........ 4.7GB 5 minutes ago # nomic-embed-text 0a........ 274MB 2 hours ago ollama run qwen2.5:7b "用一句話解釋什么是 RAG" # RAG(檢索增強生成)是通過檢索外部知識庫來增強大語言模型生成能力的技術
2.2 Modelfile:自定義模型行為
Ollama 支持用 Modelfile 配置文件深度定制模型行為,無需重新訓練。
場景一:固定系統(tǒng)提示詞,打造專屬客服人格
# 創(chuàng)建文件:./modelfiles/rag-assistant
FROM qwen2.5:7b
PARAMETER temperature 0.3
PARAMETER top_p 0.85
PARAMETER num_ctx 8192
TEMPLATE """
{% for message in messages %}
{{ '[BOT]' if message['role'] == 'assistant' else '[USER]' }}
{{ message['content'] }}
{% endfor %}
[BOT]
"""
SYSTEM """
你是一個專業(yè)的企業(yè)知識庫助手?;卮鸨仨殻?
1. 基于提供的上下文信息回答,不要編造
2. 如果上下文沒有相關信息,誠實告知用戶
3. 回答簡潔有條理,優(yōu)先使用列表格式
"""場景二:流式輸出配置(工程化必須)
# ./modelfiles/fast-response FROM qwen2.5:7b PARAMETER temperature 0.7 PARAMETER num_predict 512 # 最大生成長度 PARAMETER stop "[DONE]" # 停止詞 PARAMETER num_ctx 4096 # 上下文窗口
創(chuàng)建并測試自定義模型:
ollama create rag-assistant -f ./modelfiles/rag-assistant ollama run rag-assistant "Spring Boot 如何集成 Redis?"
2.3 Spring AI + Ollama 集成
Maven 依賴:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>application.yml 配置:
spring:
application:
name: spring-ai-ollama-demo
ai:
ollama:
base-url: http://localhost:11434
chat:
options:
model: qwen2.5:7b
temperature: 0.7
numCtx: 8192
keepAlive: 5m
# 嵌入模型單獨配置(知識庫用)
rag:
embedding:
model: nomic-embed-text基礎對話代碼:
@RestController
@RequestMapping("/ai")
@RequiredArgsConstructor
public class OllamaChatController {
private final ChatClient chatClient;
@GetMapping("/chat")
public String chat(@RequestParam(defaultValue = "你好,介紹一下你自己") String question) {
return chatClient.prompt()
.user(question)
.call()
.content();
}
// 流式響應:適合前端 SSE 推送
@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chatStream(@RequestParam String question) {
return chatClient.prompt()
.user(question)
.stream()
.content();
}
}知識庫嵌入配置:
@Configuration
public class OllamaEmbeddingConfig {
@Bean
public OllamaEmbeddingModel embeddingModel(OllamaApi api) {
return new OllamaEmbeddingModel(api);
}
@Bean
public VectorStore vectorStore(OllamaEmbeddingModel embeddingModel) {
// 使用 SimpleVectorStore(輕量級,生產(chǎn)推薦 PGVector/Chroma)
return new SimpleVectorStore(embeddingModel);
}
}
導入文檔到向量庫:
@Service
@RequiredArgsConstructor
public class DocumentIngestionService {
private final OllamaEmbeddingModel embeddingModel;
private final VectorStore vectorStore;
public void ingestDocument(Path pdfPath) throws Exception {
// PDF 解析
PdfDocumentReader pdfReader = new PdfDocumentReader(pdfPath.toUri().toString());
var document = pdfReader.get();
// 文本分割(控制每段 token 數(shù)量)
var splitter = new TokenTextSplitter(800, 200, 10, 5000, true);
var chunks = splitter.apply(List.of(document));
// 寫入向量庫
vectorStore.add(chunks);
System.out.println("? 已導入 " + chunks.size() + " 個文本塊到向量庫");
}
public List<Document> search(String query, int topK) {
return vectorStore.similaritySearch(
SearchRequest.query(query).withTopK(topK)
);
}
}
3. DashScope API:國內(nèi)低價直連通義千問
3.1 為什么選 DashScope?
| 對比項 | OpenAI API | DashScope |
|---|---|---|
| 網(wǎng)絡 | 需代理,延遲高 | 國內(nèi)直連 <50ms |
| 價格 | GPT-4o $2.5/1M | Qwen-Turbo ¥0.002/1K tokens |
| 政策 | 存在封號風險 | 阿里云合規(guī)保障 |
| 開源模型 | ? | ? 支持開源模型 API |
獲取 API Key:
- 阿里云百煉平臺 → 開通服務 → 創(chuàng)建 API Key
- 或阿里云 DashScope 控制臺
3.2 Spring AI DashScope 配置
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-alibaba-spring-boot-starter</artifactId>
</dependency>spring:
ai:
dashscope:
api-key: ${DASHSCOPE_API_KEY} # 推薦從環(huán)境變量讀取
base-url: https://dashscope.aliyuncs.com
chat:
options:
model: qwen-turbo
# qwen-plus / qwen-max 也可選
temperature: 0.8
max-tokens: 2000@RestController
@RequestMapping("/ai")
public class DashScopeController {
private final ChatClient chatClient;
public DashScopeController(ChatClient.Builder builder) {
this.chatClient = builder
.defaultSystem("你是一個 Java 技術專家,用簡潔專業(yè)的語言回答問題")
.build();
}
@PostMapping("/ask")
public Map<String, Object> ask(@RequestBody Map<String, String> request) {
String question = request.get("question");
String answer = chatClient.prompt()
.user(question)
.call()
.content();
return Map.of("question", question, "answer", answer);
}
}
3.3 DashScope 嵌入模型(知識庫必備)
spring:
ai:
dashscope:
embedding:
options:
model: text-embedding-v3 # 阿里自研嵌入模型,1536 維// 配合 RAG 使用
@Service
public class RAGService {
private final VectorStore vectorStore;
private final ChatClient chatClient;
public String ragQuery(String question) {
// Step 1: 檢索相關上下文
List<Document> contextDocs = vectorStore.similaritySearch(
SearchRequest.query(question).withTopK(5)
);
String context = contextDocs.stream()
.map(Document::getText)
.collect(Collectors.joining("\n\n"));
// Step 2: 構造有上下文的 Prompt
String prompt = String.format("""
基于以下參考資料回答用戶問題。如果資料中沒有相關信息,請如實說明。
參考資料:
%s
用戶問題:%s
""", context, question);
// Step 3: 調(diào)用通義千問
return chatClient.prompt()
.user(prompt)
.call()
.content();
}
}
4. API 代理:OneAPI 統(tǒng)一管理多模型
4.1 為什么需要 API 代理層?
在企業(yè)場景中,通常需要同時接入多個 AI 供應商:
業(yè)務代碼
↓ 統(tǒng)一調(diào)用
OneAPI(本地代理)
├── OpenAI(主力)
├── Claude(備選)
├── DashScope(國內(nèi)備選)
└── Ollama(內(nèi)網(wǎng)補充)核心價值:
- 統(tǒng)一接口:業(yè)務代碼只對接一個端點,換模型改配置不碰代碼
- 負載均衡:多渠道 token 輪詢,降低單渠道限流風險
- 用量統(tǒng)計:一個面板看所有模型的調(diào)用量和費用
- 密鑰管理:API Key 集中存儲,不分散在各個服務中
4.2 OneAPI 快速部署
# Docker 一鍵部署 docker run -d \ --name oneapi \ -p 3000:3000 \ -v ~/oneapi/data:/data \ --restart always \ ghcr.io/songquanpeng/one-api:latest # 訪問 http://localhost:3000 # 默認賬號:root 默認密碼:123456
配置渠道(以 OpenAI 為例):
- 登錄管理后臺 → 渠道管理 → 添加渠道
- 類型選
OpenAI,填入 API Key 和 base-url(國內(nèi)需代理) - 模型列表填入:
gpt-4o,gpt-4o-mini,gpt-3.5-turbo,gpt-4-turbo - 額度設置合理閾值
配置渠道(以 DashScope 為例):
- 類型選
自定義,base-url 填:https://dashscope.aliyuncs.com/compatible-mode/v1 - API Key 填阿里云 DashScope Key
- 模型列表:
qwen-turbo,qwen-plus,qwen-max,text-embedding-v3
4.3 Spring AI 通過 OneAPI 接入
spring:
ai:
openai:
base-url: http://localhost:3000/v1 # OneAPI 代理地址
api-key: ${ONEAPI_API_KEY} # OneAPI 的 API Key(管理后臺→個人API密鑰)
chat:
options:
model: gpt-4o # 可動態(tài)切換:gpt-4o / qwen-turbo / claude-3-5-sonnet效果:業(yè)務代碼完全不用改,只需改配置中的模型名稱!
// 同一個 ChatClient,換模型只需改 yml
@RestController
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder builder) {
// 配置統(tǒng)一從 yml 讀取,這里零改動
this.chatClient = builder.build();
}
}
4.4 模型切換策略實戰(zhàn)
@Service
public class SmartChatService {
private final OpenAiApi openAiApi;
// 簡單路由:根據(jù)輸入長度選擇模型
public String chatWithRouting(String input) {
String model;
if (input.length() < 200) {
// 簡單問題用小模型,省錢
model = "gpt-4o-mini";
} else if (input.length() < 2000) {
// 中等復雜度用主力模型
model = "gpt-4o";
} else {
// 復雜任務用最強模型
model = "gpt-4-turbo";
}
return ChatClient.builder(new OpenAiChatModel(openAiApi, OpenAiChatOptions.builder()
.withModel(model)
.withTemperature(0.7)
.build()))
.build()
.prompt()
.user(input)
.call()
.content();
}
// 熔斷降級:主模型失敗自動切換備選
public String chatWithFallback(String input) {
try {
return primaryChat(input);
} catch (Exception e) {
System.err.println("主模型調(diào)用失敗,切換備選:" + e.getMessage());
return fallbackChat(input);
}
}
private String primaryChat(String input) {
return ChatClient.builder(new DashScopeChatModel(DashScopeApi.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.build(),
DashScopeChatOptions.builder()
.withModel("qwen-plus")
.build()))
.build()
.prompt()
.user(input)
.call()
.content();
}
private String fallbackChat(String input) {
return ChatClient.builder(new OpenAiApi("http://localhost:3000/v1",
System.getenv("ONEAPI_API_KEY")))
.build()
.prompt()
.user(input)
.call()
.content();
}
}5. 混合路由:本地 + 云端智能調(diào)度
在實際生產(chǎn)中,最優(yōu)方案是分層使用:
用戶請求
│
▼
┌─────────────────────────────┐
│ 路由層(Spring AI Router) │
└─────────────────────────────┘
│
├── 簡單/隱私數(shù)據(jù) ──→ Ollama 本地(零成本,極速)
│
├── 復雜推理 ──→ DashScope(國內(nèi)低價,高性能)
│
└── 最高質(zhì)量要求 ──→ OpenAI / Claude(最強能力)
5.1 路由策略設計
@Component
public class ModelRouter {
private final OllamaChatModel localModel;
private final DashScopeChatModel dashscopeModel;
private final OpenAiApi openaiApi;
/**
* 根據(jù)請求特征選擇最優(yōu)模型
* @param input 用戶輸入
* @param mode 業(yè)務場景:SIMPLE / COMPLEX / PREMIUM
*/
public String route(String input, String mode) {
return switch (mode) {
case "SIMPLE" -> callLocal(input);
case "COMPLEX" -> callDashScope(input);
case "PREMIUM" -> callOpenAI(input);
default -> callDashScope(input); // 默認用 DashScope
};
}
private String callLocal(String input) {
return ChatClient.builder(localModel)
.build()
.prompt().user(input).call().content();
}
private String callDashScope(String input) {
return ChatClient.builder(dashscopeModel)
.build()
.prompt().user(input).call().content();
}
private String callOpenAI(String input) {
return ChatClient.builder(new OpenAiChatModel(openaiApi,
OpenAiChatOptions.builder().withModel("gpt-4o").build()))
.build()
.prompt().user(input).call().content();
}
}
5.2 根據(jù)隱私程度自動路由
public String privacyAwareRoute(String input, boolean containsUserData) {
if (containsUserData) {
// 敏感數(shù)據(jù)強制走本地,避免合規(guī)風險
return "?? 包含用戶隱私數(shù)據(jù),已路由至本地模型處理:" + callLocal(input);
} else {
// 非敏感數(shù)據(jù)走云端,享受最強能力
return "?? 已路由至云端高性能模型:" + callDashScope(input);
}
}
6. 性能對比實測
測試環(huán)境:MacBook M2 Pro(32GB),網(wǎng)絡為上海家寬
| 方案 | 模型 | 首次響應 | 成本 | 適合場景 |
|---|---|---|---|---|
| Ollama | Qwen2.5 7B int4 | ~1.2s | ¥0 | 內(nèi)網(wǎng)/隱私/開發(fā) |
| Ollama | Llama 3.1 8B int4 | ~1.8s | ¥0 | 通用對話 |
| DashScope | Qwen-Turbo | ~0.8s | ¥0.002/1K | 國內(nèi)生產(chǎn)主力 |
| DashScope | Qwen-Plus | ~1.2s | ¥0.02/1K | 高質(zhì)量需求 |
| OneAPI→OpenAI | GPT-4o-mini | ~1.5s | ~$0.15/1M | 復雜推理 |
7. 選型決策樹
輸入類型是什么?
│
├─ 包含敏感/隱私數(shù)據(jù)?
│ └─ ? 是 ──→ Ollama 本地(qwen2.5:7b)
│
├─ 國內(nèi)生產(chǎn)環(huán)境,低成本優(yōu)先?
│ └─ ? 是 ──→ DashScope Qwen-Turbo/Plus
│
├─ 需要最強推理能力(代碼/復雜分析)?
│ └─ ? 是 ──→ OpenAI GPT-4o / Claude 3.5
│
└─ 多模型混合使用?
└─ ? 是 ──→ OneAPI + 智能路由層
8. 常見問題
Q1: Ollama 模型更新后需要重啟嗎?
不需要。ollama pull 會自動更新。正在運行的模型不受影響,下次調(diào)用時自動使用新版本。
Q2: OneAPI 和直接調(diào) API 的性能差異?
OneAPI 多一跳 HTTP 轉(zhuǎn)發(fā),延遲增加約 5-20ms。對于 LLM 的 token 生成時間(通常數(shù)百毫秒~數(shù)秒),這個開銷可以忽略。
Q3: Ollama 顯存不夠怎么辦?
- 換用更小的量化版本(如 qwen2.5:3b)
- 使用 Phi-3.5(3.8B,CPU 可跑)
- 調(diào)低 num_ctx 參數(shù)減少 KV Cache 顯存占用
Q4: DashScope 和 OpenAI API 語法兼容嗎?
不完全兼容。Spring AI 的 spring-ai-alibaba 提供了獨立適配器,需要單獨引入依賴并配置,不能和 spring-ai-openai 混用同一個 ChatClient。
Q5: 如何監(jiān)控各模型的調(diào)用量和費用?
OneAPI 自帶統(tǒng)計面板,可以按渠道、模型查看調(diào)用次數(shù)和預估費用。也可接入 Prometheus + Grafana 展示自定義指標。
以上就是三種Spring AI本地/低成本模型接入方案的詳細內(nèi)容,更多關于Spring AI本地/低成本模型接入的資料請關注腳本之家其它相關文章!
相關文章
SpringSecurity JWT基于令牌的無狀態(tài)認證實現(xiàn)
Spring Security中實現(xiàn)基于JWT的無狀態(tài)認證是一種常見的做法,本文就來介紹一下SpringSecurity JWT基于令牌的無狀態(tài)認證實現(xiàn),感興趣的可以了解一下2025-04-04
springBoot 打war包 程序包com.sun.istack.internal不存在的問題及解決方案
這篇文章主要介紹了springBoot 打war包 程序包com.sun.istack.internal不存在的問題及解決方案,親測試過可以,需要的朋友可以參考下2018-07-07

