一、核心进阶能力全景图
text
┌─────────────────────────────────────────────────────┐
│ 生产级 Spring AI 应用 │
├─────────────────────────────────────────────────────┤
│ ① MCP (Model Context Protocol) - 标准化工具集成 │
│ ② 动态工具注册 - 运行时扩展能力 │
│ ③ LLM as Judge - 自评估与纠偏 │
│ ④ 高级 RAG - 混合检索+重排序 │
│ ⑤ 多模态 Embedding - 图文向量化 │
│ ⑥ 向量数据库高级特性 - 过滤+HNSW+分区 │
│ ⑦ 自定义 ETL 管道 - 生产级数据清洗 │
│ ⑧ Agent 编排器+工作流 - 复杂任务拆解 │
└─────────────────────────────────────────────────────┘
二、MCP(Model Context Protocol)深度实践
2.1 什么是 MCP?
MCP 是 Spring AI 1.0+ 引入的标准化工具协议,让 AI 模型能够以统一方式调用外部工具(数据库、API、文件系统等)。
2.2 实现一个 MCP Server(提供工具)
java
// 1. 定义工具接口
@McpTool(name = "database-query", description = "查询数据库")
@Component
public class DatabaseQueryTool {
@McpToolFunction(description = "根据 SQL 查询数据")
public String query(@McpToolParam(description = "SQL 语句") String sql) {
// 实际执行 SQL(带防注入)
return jdbcTemplate.queryForList(sql).toString();
}
@McpToolFunction(description = "获取表结构")
public List<String> getTableSchema(@McpToolParam String tableName) {
return jdbcTemplate.queryForList("DESC " + tableName, String.class);
}
}
// 2. 配置 MCP Server(application.yml)
spring:
ai:
mcp:
server:
enabled: true
name: "database-mcp-server"
version: "1.0"
transport: "stdio" # 或 sse, streamable-http
2.3 MCP Client 调用(在 ChatClient 中使用)
java
@Service
public class McpAIService {
@Autowired
private McpClient mcpClient;
public String askWithTools(String userQuestion) {
// MCP Client 会自动发现并注册所有 @McpTool
return chatClient.prompt()
.user(userQuestion)
.tools(mcpClient.getToolCallbacks()) // 注入 MCP 工具
.call()
.content();
}
}
2.4 动态工具更新(运行时注册新工具)
java
// 场景:用户上传自定义函数,动态注册
@PostMapping("/register-tool")
public void registerCustomTool(@RequestBody ToolDefinition toolDef) {
McpTool dynamicTool = McpTool.builder()
.name(toolDef.getName())
.description(toolDef.getDesc())
.inputSchema(toolDef.getSchema())
.executor(params -> executeGroovyScript(toolDef.getScript(), params))
.build();
mcpClient.registerTool(dynamicTool); // 运行时注册
}
三、LLM as Judge:自评估与质量保证
3.1 实现一个评估器(Judge)
java
@Component
public class ResponseJudge {
private final ChatClient judgeClient;
public ResponseJudge(ChatClient.Builder builder) {
this.judgeClient = builder
.defaultSystem("""
你是严格的回答质量评估专家。评估标准:
1. 相关性(0-10分):是否直接回答问题
2. 准确性(0-10分):事实是否正确
3. 完整性(0-10分):是否遗漏关键信息
4. 幻觉检测(是/否):是否存在编造内容
输出格式 JSON:
{"scores": {"relevance":0, "accuracy":0, "completeness":0},
"hallucination": false, "reason": "..."}
""")
.build();
}
public JudgeResult evaluate(String question, String answer, String context) {
return judgeClient.prompt()
.user("问题:%s\n上下文:%s\n答案:%s", question, context, answer)
.call()
.entity(JudgeResult.class); // 结构化输出
}
}
3.2 在 RAG 中自动重试(低分重新生成)
java
@PostMapping("/rag-with-judge")
public String robustRag(@RequestBody String question) {
int maxRetries = 3;
for (int i = 0; i < maxRetries; i++) {
String answer = basicRag(question);
JudgeResult judge = judge.evaluate(question, answer, retrievedContext);
if (judge.scores().relevance() > 7 && !judge.hallucination()) {
return answer;
}
log.warn("回答质量不达标,重试 {}/{},原因:{}", i+1, maxRetries, judge.reason());
}
return "无法生成高质量回答,请简化问题。";
}
四、高级 RAG:混合检索 + 重排序
4.1 架构设计
text
用户问题 → 关键词检索(BM25) + 向量检索(Embedding) → 合并去重 → 重排序模型 → Top-K → LLM 生成
4.2 实现混合检索器
java
@Component
public class HybridRetriever {
@Autowired
private VectorStore vectorStore;
@Autowired
private ElasticsearchRestTemplate esTemplate;
public List<Document> hybridSearch(String query, int topK) {
// 1. 向量检索
List<Document> vectorResults = vectorStore.similaritySearch(
SearchRequest.query(query).withTopK(topK * 2)
);
// 2. BM25 关键词检索
List<Document> keywordResults = esTemplate.search(
NativeQuery.builder()
.withQuery(QueryBuilders.matchQuery("content", query))
.withPageable(PageRequest.of(0, topK * 2))
.build(),
Document.class
).get().map(SearchHit::getContent).toList();
// 3. 合并去重(基于文档ID)
Map<String, Document> merged = new LinkedHashMap<>();
vectorResults.forEach(d -> merged.put(d.getId(), d));
keywordResults.forEach(d -> merged.putIfAbsent(d.getId(), d));
// 4. 重排序(使用 Cross-Encoder)
return rerank(query, new ArrayList<>(merged.values()), topK);
}
private List<Document> rerank(String query, List<Document> docs, int topK) {
// 调用本地 Cross-Encoder 模型(如 BAAI/bge-reranker-base)
List<ScorePair> scores = crossEncoderModel.encode(query, docs);
return scores.stream()
.sorted((a,b) -> Float.compare(b.score(), a.score()))
.limit(topK)
.map(ScorePair::document)
.collect(Collectors.toList());
}
}
4.3 在 Advisor 中使用混合检索
java
@Configuration
public class RAGConfig {
@Bean
public QuestionAnswerAdvisor hybridRagAdvisor(HybridRetriever retriever) {
return QuestionAnswerAdvisor.builder()
.retrievalFunction(retriever::hybridSearch)
.build();
}
}
五、向量数据库高级特性(以 PGVector 为例)
5.1 元数据过滤 + HNSW 索引
java
// 创建带元数据的文档
Document doc = new Document("内容", Map.of(
"category", "法律",
"publishYear", 2024,
"author", "张三"
));
// 查询时使用 SQL-like 过滤
List<Document> results = vectorStore.similaritySearch(
SearchRequest
.query("劳动合同纠纷")
.withTopK(5)
.withSimilarityThreshold(0.7)
.withFilterExpression("category == '法律' && publishYear >= 2020")
);
// 手动创建 HNSW 索引(性能提升 10x)
@Component
public class VectorIndexInitializer {
@EventListener(ApplicationReadyEvent.class)
public void initIndex() {
jdbcTemplate.execute("""
CREATE INDEX IF NOT EXISTS law_hnsw_idx
ON vector_store
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
""");
}
}
5.2 分区表策略(按月/按类别)
sql
-- 按月分区
CREATE TABLE documents_2024_01 PARTITION OF vector_store
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
-- 查询时自动分区裁剪
SELECT * FROM vector_store
WHERE publish_date >= '2024-01-01' AND publish_date < '2024-02-01'
ORDER BY embedding <=> '[0.1, 0.2, ...]' LIMIT 10;
六、生产级 ETL 管道
6.1 自定义 DocumentTransformer(敏感信息脱敏)
java
@Component
public class SensitiveInfoTransformer implements DocumentTransformer {
private final Pattern idCardPattern = Pattern.compile("\\d{17}[\\dXx]");
private final Pattern phonePattern = Pattern.compile("1[3-9]\\d{9}");
@Override
public List<Document> transform(List<Document> documents) {
return documents.stream()
.map(doc -> {
String content = doc.getText();
content = idCardPattern.matcher(content).replaceAll("***");
content = phonePattern.matcher(content).replaceAll("***");
doc.setText(content);
return doc;
})
.collect(Collectors.toList());
}
@Override
public int getOrder() {
return 0; // 优先执行
}
}
6.2 完整 ETL 流程(生产级)
java
@Component
public class ProductionETLPipeline {
@Autowired
private List<DocumentReader> readers; // PDF/Word/HTML
@Autowired
private List<DocumentTransformer> transformers; // 清洗/脱敏/去重
@Autowired
private TokenTextSplitter splitter;
@Autowired
private EmbeddingModel embeddingModel;
@Autowired
private VectorStore vectorStore;
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行
public void runFullPipeline() {
// 1. 读取
List<Document> allDocs = readers.stream()
.flatMap(reader -> reader.get().stream())
.collect(Collectors.toList());
// 2. 转换(链式调用)
for (DocumentTransformer transformer : transformers) {
allDocs = transformer.transform(allDocs);
}
// 3. 切分
List<Document> chunks = splitter.apply(allDocs);
// 4. 向量化(批量,控制并发)
List<Document> embedded = embeddingModel.embed(chunks);
// 5. 写入(幂等:先删后加)
vectorStore.delete(chunks.stream().map(Document::getId).toList());
vectorStore.add(embedded);
log.info("ETL 完成,处理文档数:{},生成块数:{}", allDocs.size(), chunks.size());
}
}
七、Agent 编排器深度实践(多步骤复杂任务)
7.1 实现一个"智能数据分析 Agent"
java
@Component
public class DataAnalysisOrchestrator {
@Autowired
private ChatClient chatClient;
public AnalysisReport analyze(String userRequest) {
// Step 1: 意图识别与任务拆解
TaskPlan plan = decomposeTask(userRequest);
// Step 2: 并行执行子任务
List<CompletableFuture<SubTaskResult>> futures = plan.getSubTasks().stream()
.map(task -> CompletableFuture.supplyAsync(() -> executeSubTask(task)))
.collect(Collectors.toList());
List<SubTaskResult> results = futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
// Step 3: 结果聚合与反思
String draft = aggregateResults(results);
String finalReport = reflectAndPolish(draft);
return new AnalysisReport(finalReport, results);
}
private TaskPlan decomposeTask(String request) {
return chatClient.prompt()
.user("将以下任务拆解为 2-4 个可并行的子任务:\n" + request)
.call()
.entity(TaskPlan.class);
}
private String reflectAndPolish(String draft) {
return chatClient.prompt()
.system("你是批判性思维专家,请检查以下报告的逻辑漏洞、数据矛盾,并给出改进版本")
.user(draft)
.call()
.content();
}
}
7.2 结合 MCP 工具的 Agent
java
@Component
public class ToolAwareAgent {
public String execute(String instruction) {
return chatClient.prompt()
.user(instruction)
.tools(
databaseQueryTool, // MCP 工具1
fileSystemTool, // MCP 工具2
webSearchTool // MCP 工具3
)
.advisors(new ReReadingAdvisor()) // 增强推理
.call()
.content();
}
}
八、完整项目:企业智能客服(含全部进阶特性)
技术栈
-
Spring AI 1.0.5 + MCP + PGVector
-
混合检索(BM25 + Vector)
-
LLM as Judge(自动重试)
-
ETL 定时管道
-
Agent 编排(复杂问题拆解)
核心代码结构
text
com.enterprise.customer/
├── mcp/
│ ├── OrderQueryTool.java # MCP 工具:查订单
│ ├── ReturnApplyTool.java # MCP 工具:处理退货
│ └── McpServerConfig.java
├── rag/
│ ├── HybridRetriever.java # 混合检索
│ ├── ResponseJudge.java # LLM as Judge
│ └── RAGAdvisorConfig.java
├── etl/
│ ├── KnowledgeBaseETL.java # 定时管道
│ └── SensitiveFilter.java
├── agent/
│ └── CustomerServiceOrchestrator.java
└── controller/
└── CustomerAIController.java
运行效果
-
简单问题:直接 RAG 回答(<500ms)
-
复杂问题:Agent 自动拆解 + 调用 MCP 工具(2-3秒)
-
低分回答:自动重试(最多3次)
-
新知识入库:每天凌晨自动 ETL
九、性能调优 Checklist
| 优化点 | 具体措施 | 预期提升 |
|---|---|---|
| 向量检索 | HNSW 索引 + 分区表 | 10x QPS |
| 并发处理 | 自定义线程池 + 分批 embedding | 3x 吞吐量 |
| 缓存 | Caffeine 缓存常见问题 | 50% 延迟降低 |
| 流式响应 | stream() + SSE |
首字延迟 <200ms |
| 批处理 | BatchProcessor 批量 embedding |
5x ETL 速度 |
十、总结:从基础到精通的 Checklist
-
掌握
ChatClient所有 API(entity、stream、advisor) -
实现至少 2 个自定义 Advisor(如 Re-Reading、日志)
-
完成一个完整的 RAG 项目(文档问答)
-
集成 MCP Server/Client,动态注册工具
-
实现 LLM as Judge 并接入生产流程
-
搭建混合检索(向量+关键词)+ 重排序
-
配置向量数据库高级索引(HNSW/IVF)
-
编写生产级 ETL 管道(含清洗/脱敏)
-
实现 Agent 编排器(任务拆解+并行执行)
-
添加可观测性(Micrometer + Tracing)
-
压测与性能调优(缓存、线程池、连接池)
完成以上 11 项,您就已经是 Spring AI 生产级实践专家。
