本文基于 SpringAI 1.0.0-M6(里程碑版本)编写,正式版 API 可能有调整。核心架构思路(Ollama + PgVector + RAG)不变。
一、部署 Ollama
1. 编写 docker-compose.yml 文件
yaml
services:
ollama:
volumes:
- ./models:/root/.ollama # 模型下载位置
container_name: ollama
pull_policy: always
tty: true
restart: unless-stopped
image: ollama/ollama:latest
ports:
- "11434:11434" # Ollama API 端口
2. 部署 Ollama
docker-compose up -d
3. 验证部署
arduino
docker ps
# ollama容器为Up状态说明部署成功
4. 部署 DeepSeek 大模型
arduino
docker exec -it ollama ollama run deepseek-r1:1.5b
5. 部署 Embedding 模型 all-minilm
python
docker exec -it ollama ollama pull all-minilm
6. 验证模型
bash
docker exec -it ollama ollama list
# deepseek和all-minilm都在说明运行正常
二、部署 PgVector 向量数据库
1. 拉取镜像
bash
docker pull ankane/pgvector
2. 创建并运行容器
css
docker run -d \
--name pgvector-container \
-e POSTGRES_PASSWORD=postgres \
-p 5432:5432 \
ankane/pgvector
3. 验证容器
arduino
docker ps
# pgvector-container为Up状态说明运行成功
4. 创建数据库和向量表
使用 DBeaver 连接 PG,创建 drone_ai 数据库,执行:
sql
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE public.vector_store (
id uuid DEFAULT uuid_generate_v4() NOT NULL,
"content" text NULL,
metadata json NULL,
embedding public.vector(384) NULL,
CONSTRAINT vector_store_pkey PRIMARY KEY (id)
);
CREATE INDEX vector_store_embedding_idx ON public.vector_store
USING hnsw (embedding vector_cosine_ops);
向量字段 embedding 纬度设置为 384,对应 all-minilm 模型支持的纬度。
三、编写 SpringAI 代码
1. Maven 依赖
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.9</version>
</parent>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.0.0-M6</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2. application.yaml 配置
yaml
spring:
application:
name: ai-chat
ai:
ollama:
chat:
options:
model: deepseek-r1:1.5b
base-url: http://localhost:11434
embedding:
enabled: true
model: all-minilm
datasource:
url: jdbc:postgresql://localhost:5432/drone_ai
username: postgres
password: postgres
vectorstore:
pgvector:
dimensions: 384
index-type: hnsw
distance-type: cosine_distance
3. AIConfig --- 初始化 ChatClient
java
@Configuration
@RequiredArgsConstructor
public class AIConfig {
final OllamaChatModel ollamaChatModel;
final VectorStore vectorStore;
@Bean
public ChatClient chatClient() {
return ChatClient.builder(ollamaChatModel)
.defaultSystem("你需要根据知识库内的数据回答问题")
.defaultAdvisors(
new MessageChatMemoryAdvisor(chatMemory()),
new QuestionAnswerAdvisor(vectorStore))
.build();
}
@Bean
public ChatMemory chatMemory() {
return new InMemoryChatMemory();
}
}
4. VectorService --- 知识库初始化
typescript
@Service
@Slf4j
@RequiredArgsConstructor
public class VectorServiceImpl implements IVectorService {
final VectorStore vectorStore;
@PostConstruct
public void init() {
try {
vectorStore.write(buildDroneDocument());
vectorStore.write(buildObstacleDocument());
log.info("知识库初始化完成");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public List<Document> buildDroneDocument() throws IOException {
log.info("开始构建无人机知识库");
List<Document> documents = new ArrayList<>();
String droneJson = FileUtils.readFileToString(new File("/data/ai/无人机.json"));
List<DroneEntity> droneEntities = JSON.parseArray(droneJson, DroneEntity.class);
Map<String, Object> metadata = Map.of("type", "无人机");
for (DroneEntity entity : droneEntities) {
documents.add(new Document(entity.toString(), metadata));
}
log.info("无人机知识库构建完成");
return documents;
}
@Override
public List<Document> buildObstacleDocument() throws IOException {
log.info("开始构建障碍物知识库");
List<Document> documents = new ArrayList<>();
String obstacleJson = FileUtils.readFileToString(new File("/data/ai/障碍物数据.json"));
JSONObject jsonObject = JSON.parseObject(obstacleJson);
JSONArray jsonArray = jsonObject.getJSONArray("features");
Map<String, Object> metadata = Map.of("type", "障碍物");
for (int i = 0; i < jsonArray.size(); i++) {
jsonObject = jsonArray.getJSONObject(i);
ObstacleEntity entity = new ObstacleEntity();
entity.setObstacleId("Obstacle_" + jsonObject.getString("id"));
entity.setGeometry(jsonObject.getString("geometry"));
entity.setType(jsonObject.getJSONObject("properties").getString("type"));
entity.setHeight(jsonObject.getJSONObject("properties").getInteger("height"));
documents.add(new Document(entity.toString(), metadata));
}
log.info("障碍物知识库构建完成");
return documents;
}
}
5. AIService --- 问答逻辑
less
@Service
@Slf4j
@RequiredArgsConstructor
public class AIServiceImpl {
private final ChatClient chatClient;
/**
* 向AI提问,基于RAG知识库回答
* @param question 自然语言描述的问题
* @return AI的回答
*/
public String askAI(String question) {
return chatClient.prompt().user(question).call().content();
}
}
6. ChatAPIController --- HTTP 接口
less
@RequiredArgsConstructor
@RestController
@RequestMapping("/ai")
public class ChatAPIController {
private final AIServiceImpl aiService;
@GetMapping("/ask")
public String plan(@RequestParam("question") String question) {
return aiService.askAI(question);
}
}
四、架构总结
scss
用户问题 → ChatClient → RAG检索(PgVector) → DeepSeek生成回答
↓
QuestionAnswerAdvisor
↓
VectorStore(PgVector)
↓
无人机知识库 + 障碍物知识库
- Ollama:本地部署大模型,无需云API
- DeepSeek R1:推理能力强,适合复杂航线规划
- PgVector:PostgreSQL向量扩展,存储和检索知识库
- SpringAI:统一AI调用接口,支持RAG开箱即用
- all-minilm:轻量级Embedding模型,384维向量
关于作者
独立开发者,主业 Java 后端。一个人用 SpringBoot + AI 交付过企业级管理平台和微信小程序,业余接外包。
- 代码和架构图放 Gitee 了:gitee.com/yao113088/j...