- 從 SimpleVectorStore 切換到 PGVector 只需改
application.yml——Service 層零程式碼修改,這就是 Spring AI 抽象層的威力 - PGVector = PostgreSQL + pgvector 擴充,不需要額外的向量資料庫服務——用你已有的 PostgreSQL 基礎設施,Docker 一行啟動
- Metadata 權限管控透過 FilterExpression 實現行級安全——不同部門只能查詢被授權的文件,機密文件只有特定角色可存取
- Spring Boot Actuator + Micrometer 提供 AI 應用監控——追蹤請求量、回應延遲、Token 用量,是控制成本與保障 SLA 的基礎
從開發到生產:為什麼需要 PGVector?
前面課程使用的 SimpleVectorStore 是記憶體儲存——應用重啟資料就消失。企業應用需要持久化[1]:
| 特性 | SimpleVectorStore | PGVector |
|---|---|---|
| 儲存 | 記憶體 | PostgreSQL 持久化 |
| 重啟後 | 資料消失 | 資料保留 |
| 資料量 | < 10,000 筆 | 百萬筆級 |
| 多節點 | 不支援 | 共用資料庫 |
| Metadata 過濾 | 基本 | 完整 SQL 能力 |
| 適合 | 開發 / 測試 | 生產環境 |
PGVector[2] 的最大優勢:不需要額外引入新的資料庫服務。如果你的企業已經在用 PostgreSQL,加一個 pgvector 擴充就能搞定向量搜尋。
Step 1:Docker 部署 PGVector
# docker-compose.yml
version: '3.8'
services:
pgvector:
image: pgvector/pgvector:pg16
container_name: pgvector
environment:
POSTGRES_DB: vectordb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
volumes:
- pgvector_data:/var/lib/postgresql/data
volumes:
pgvector_data:
# 啟動
docker compose up -d
# 驗證 pgvector 擴充
docker exec pgvector psql -U postgres -d vectordb \
-c "CREATE EXTENSION IF NOT EXISTS vector; SELECT extversion FROM pg_extension WHERE extname='vector';"
預期輸出:
extversion
------------
0.8.2
(1 row)
Step 2:零程式碼切換到 PGVector
這是 Spring AI 最優雅的設計——從 SimpleVectorStore 切換到 PGVector,Service 層完全不用改:
# application-pgvector.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/vectordb
username: postgres
password: postgres
ai:
vectorstore:
pgvector:
dimensions: 1536 # text-embedding-3-small
index-type: hnsw # 高效能近似搜尋索引
distance-type: cosine # 餘弦相似度
initialize-schema: true # 自動建表
# 以 PGVector 模式啟動
./gradlew bootRun --args='--spring.profiles.active=pgvector'
Spring Boot 啟動時會自動建立 vector_store 表,並使用 HNSW 索引加速搜尋。你的 ChatService、ChatController 不需要任何修改——因為它們都是對 VectorStore 介面程式設計,不依賴具體實作。
驗證資料持久化
# 查看向量資料表
docker exec pgvector psql -U postgres -d vectordb \
-c "SELECT count(*) as total_rows FROM vector_store;"
# 預覽儲存的文件
docker exec pgvector psql -U postgres -d vectordb \
-c "SELECT substring(content, 1, 60) as preview,
metadata->>'source' as source
FROM vector_store LIMIT 5;"
重啟 Spring Boot 後再查——資料依然存在。這就是持久化的威力。
Step 3:PDF 文件解析與入庫
企業最常見的文件格式是 PDF。使用 Apache Tika[4] 自動解析:
@Service
class DocumentService(private val vectorStore: VectorStore) {
private val splitter = TokenTextSplitter.builder()
.withChunkSize(500)
.withMinChunkSizeChars(100)
.build()
fun processAndStore(file: MultipartFile, category: String?): List<Document> {
// Step 1: Tika 自動偵測格式並解析(PDF/Word/PPT/Excel 都支援)
val reader = TikaDocumentReader(InputStreamResource(file.inputStream))
val rawDocs = reader.read()
// Step 2: 切割成小段落
val chunks = splitter.split(rawDocs)
// Step 3: 附加 Metadata
chunks.forEach { doc ->
doc.metadata["source"] = file.originalFilename ?: "unknown"
doc.metadata["category"] = category ?: "general"
doc.metadata["uploadTime"] = java.time.Instant.now().toString()
}
// Step 4: 存入向量資料庫
vectorStore.add(chunks)
return chunks
}
}
# 上傳 PDF
curl -X POST http://localhost:8080/api/documents/upload \
-F "[email protected]" \
-F "category=員工手冊"
Tika 會自動提取 PDF 中的文字(包括表格),TokenTextSplitter 切割後存入 PGVector。整個流程全自動。
Step 4:權限管控——不同角色看不同文件
企業知識庫最重要的需求之一:工程部不該看到財務的薪資報表,實習生不該查到機密文件。Spring AI 的 FilterExpression 可以實現行級安全:
@Service
class SecureRagService(
private val chatClientBuilder: ChatClient.Builder,
private val vectorStore: VectorStore
) {
fun ask(question: String, user: UserInfo): String {
val b = FilterExpressionBuilder()
// 建立權限過濾條件
val filter = b.and(
// 只能看自己部門或全公司文件
b.or(
b.eq("department", user.department),
b.eq("department", "全公司")
),
// 只能看被授權的機密等級
b.in("accessLevel", *user.accessLevels.toTypedArray())
).build()
val chatClient = chatClientBuilder
.defaultAdvisors(
QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder()
.topK(3)
.filterExpression(filter)
.build())
.build()
)
.build()
return chatClient.prompt()
.system("根據知識庫回答問題,找不到就說不知道。")
.user(question)
.call().content() ?: "(無回應)"
}
}
data class UserInfo(
val name: String,
val department: String, // "工程部" / "財務部" / "人資部"
val accessLevels: List<String> // ["public", "internal"] 或 ["public", "internal", "confidential"]
)
權限控制的效果:
| 使用者 | 問「薪資結構」 | 問「部署流程」 |
|---|---|---|
| 工程師 Alice (工程部, internal) | 只回傳「全公司」的薪資概要 | 完整回答(工程部文件) |
| 財務主管 Bob (財務部, confidential) | 完整薪資報表(含機密數據) | 只回傳「全公司」的部署概要 |
| 實習生 Charlie (工程部, public) | 僅公開的薪資福利資訊 | 僅公開的技術文件 |
Step 5:完整企業 RAG 架構
┌─────────────────────────────────────────────────────┐
│ Frontend(Vue 3 / React) │
│ 聊天介面 / 文件上傳 / 管理後台 │
└──────────────────┬──────────────────────────────────┘
│ REST API + SSE
┌──────────────────┴──────────────────────────────────┐
│ Spring Boot Backend │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │
│ │ChatController│ │DocController │ │ AuthFilter │ │
│ └──────┬───────┘ └──────┬───────┘ └─────┬──────┘ │
│ ┌──────┴───────┐ ┌──────┴───────┐ ┌─────┴──────┐ │
│ │ ChatService │ │ DocService │ │UserService │ │
│ │ +Memory +RAG │ │ +Tika +Split │ │ +JWT │ │
│ └──────┬───────┘ └──────┬───────┘ └────────────┘ │
│ │ │ │
│ Spring AI Core: ChatClient / VectorStore / Embedding │
└─────────┬──────────────────┬────────────────────────┘
│ │
┌──────┴──────┐ ┌─────┴──────┐
│ OpenAI API │ │ PGVector │
│ (GPT-4o) │ │(PostgreSQL)│
└─────────────┘ └────────────┘
Step 6:監控與可觀測性
生產環境需要監控 AI 應用的健康狀態與成本[3]:
# application.yml
management:
endpoints:
web:
exposure:
include: health, metrics, info
metrics:
tags:
application: spring-ai-rag
自訂 AI 指標:
@Service
class MonitoredChatService(
private val chatService: ChatService,
private val meterRegistry: MeterRegistry
) {
private val chatCounter = Counter.builder("ai.chat.requests")
.description("AI 聊天請求次數")
.register(meterRegistry)
private val chatTimer = Timer.builder("ai.chat.duration")
.description("AI 聊天回應時間")
.register(meterRegistry)
fun chat(message: String, sessionId: String): ChatResult {
chatCounter.increment()
return chatTimer.record<ChatResult> {
chatService.chat(message, sessionId)
}!!
}
}
關鍵監控指標:
| 指標 | 用途 | 警報閾值 |
|---|---|---|
| ai.chat.requests | 請求量追蹤 | 突然暴增可能被濫用 |
| ai.chat.duration | 回應延遲 | > 10 秒需告警 |
| ai.token.usage | Token 成本控制 | 日用量超預算 |
| ai.rag.search.duration | 向量搜尋效能 | > 500ms 需優化索引 |
| pgvector.pool.active | DB 連線池 | 接近上限需擴容 |
本課重點回顧
| 概念 | 重點 |
|---|---|
| PGVector | PostgreSQL + pgvector 擴充,Docker 一行啟動 |
| 零程式碼切換 | 改 application.yml 即可,Service 層不用動 |
| PDF 解析 | Tika + TokenTextSplitter + VectorStore |
| 權限管控 | Metadata + FilterExpression 實現行級安全 |
| 企業架構 | Controller → Service → Spring AI → PGVector + OpenAI |
| 監控 | Actuator + Micrometer 自訂 AI 指標 |
下一步:Lesson 12 — 雲端部署
企業級 RAG 系統已就緒,最後一步是部署到雲端:
- Docker 容器化 — 多階段建構、映像最佳化
- Google Cloud Run — 無伺服器部署、自動擴展
- CI/CD Pipeline — GitHub Actions 自動建構與部署


