547 lines
14 KiB
Markdown
547 lines
14 KiB
Markdown
# Fintec AI Framework 开发指南
|
||
|
||
## 📚 文档目录
|
||
|
||
- [快速开始](#快速开始)
|
||
- [架构概览](#架构概览)
|
||
- [模块说明](#模块说明)
|
||
- [使用示例](#使用示例)
|
||
- [最佳实践](#最佳实践)
|
||
- [常见问题](#常见问题)
|
||
|
||
---
|
||
|
||
## 快速开始
|
||
|
||
### 环境要求
|
||
|
||
- JDK 17+
|
||
- Spring Boot 3.4.6+
|
||
- Spring AI 1.1.1+
|
||
- Maven 3.6+
|
||
|
||
### 引入依赖
|
||
|
||
在项目的 `pom.xml` 中添加依赖:
|
||
|
||
```xml
|
||
<!-- Agent Starter(对话能力) -->
|
||
<dependency>
|
||
<groupId>com.ccb.fintec</groupId>
|
||
<artifactId>fintec-framework-agent-spring-boot-starter</artifactId>
|
||
</dependency>
|
||
|
||
<!-- RAG Starter(知识库问答) -->
|
||
<dependency>
|
||
<groupId>com.ccb.fintec</groupId>
|
||
<artifactId>fintec-framework-rag-spring-boot-starter</artifactId>
|
||
</dependency>
|
||
|
||
<!-- MCP Server Starter(工具暴露) -->
|
||
<dependency>
|
||
<groupId>com.ccb.fintec</groupId>
|
||
<artifactId>fintec-framework-mcp-server-spring-boot-starter</artifactId>
|
||
</dependency>
|
||
```
|
||
|
||
### 基础配置
|
||
|
||
在 `application.yml` 中配置 AI 模型:
|
||
|
||
```yaml
|
||
spring:
|
||
ai:
|
||
openai:
|
||
api-key: ${OPENAI_API_KEY}
|
||
base-url: https://api.openai.com/v1
|
||
chat:
|
||
options:
|
||
model: gpt-4
|
||
temperature: 0.7
|
||
|
||
app:
|
||
ai:
|
||
# 安全防护
|
||
safety:
|
||
enabled: true
|
||
block-keywords:
|
||
- 暴力
|
||
- 色情
|
||
- 赌博
|
||
# 限流配置
|
||
rate-limit:
|
||
max-requests-per-minute: 60
|
||
# 重试策略(自动处理网络波动)
|
||
retry:
|
||
enabled: true
|
||
max-attempts: 3
|
||
backoff-ms: 1000
|
||
```
|
||
|
||
---
|
||
|
||
## 架构概览
|
||
|
||
### 整体架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────┐
|
||
│ 业务应用层 (Business App) │
|
||
├─────────────────────────────────────────────┤
|
||
│ Agent+Graph+MCP Client │ RAG │
|
||
│ Starter │ Starter │
|
||
├─────────────────────────────────────────────┤
|
||
│ Core 核心层 (统一抽象) │
|
||
├─────────────────────────────────────────────┤
|
||
│ Spring AI (模型适配层) │
|
||
├─────────────────────────────────────────────┤
|
||
│ OpenAI │ Ollama │ Anthropic │ 其他模型 │
|
||
└─────────────────────────────────────────────┘
|
||
↑
|
||
┌─────────────────────────────────────────────┐
|
||
│ MCP Server (独立部署) │
|
||
│ SSE + STDIO + Streamable-HTTP │
|
||
└─────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 设计原则
|
||
|
||
1. **分层解耦**: 技术实现与业务编排分离
|
||
2. **多模型支持**: 通过 Spring AI 抽象,支持切换不同模型厂商
|
||
3. **开箱即用**: 自动配置,零样板代码
|
||
4. **可扩展性**: 基于 Spring Boot Starter 机制,易于扩展
|
||
|
||
---
|
||
|
||
## 模块说明
|
||
|
||
### 1. fintec-framework-ai-core (核心模块)
|
||
|
||
**职责**: 提供统一的 AI 配置和能力抽象
|
||
|
||
**功能**:
|
||
- 全局 AI 配置管理 (`AiCoreProperties`)
|
||
- 公共的接口和DTO,公共回复等解耦...
|
||
|
||
---
|
||
|
||
### 2. fintec-framework-agent-spring-boot-starter (Agent 模块)
|
||
|
||
**职责**: 提供对话式 AI、Graph 工作流编排和 MCP Client 能力
|
||
|
||
**核心组件**:
|
||
- `AgentTemplate`: 封装常用对话模式(内置重试)
|
||
- `MultimodalTemplate`: 多模态能力(图片/文档理解)
|
||
- `ChatClient`: 统一的聊天客户端
|
||
- `ChatMemory`: 对话记忆管理
|
||
- Graph 工作流: `Node`, `Condition`, `SequentialWorkflow`, `ParallelWorkflow`, `RoutingWorkflow`, `LoopWorkflow`
|
||
- `McpClientAutoConfiguration`: MCP Client 自动配置
|
||
- `RetryAutoConfiguration`: 自动重试机制
|
||
- `SafetyAspect`: 敏感词过滤切面
|
||
|
||
**主要功能**:
|
||
- 简单问答
|
||
- 带系统提示的对话
|
||
- Function Calling(工具调用)
|
||
- 结构化输出
|
||
- 流式响应
|
||
- 对话记忆
|
||
- **图片理解**: 分析图片内容、OCR识别
|
||
- **Graph 工作流**: Sequential/Parallel/Routing/Loop
|
||
- **MCP Client**: 连接外部 MCP Server
|
||
- **安全防护**: 敏感词过滤(请求+响应双向检查)
|
||
- **自动重试**: 处理网络波动、临时故障
|
||
|
||
**典型场景**: 智能客服、AI助手、代码生成、多模态应用、复杂工作流编排
|
||
|
||
---
|
||
|
||
### 3. fintec-framework-rag-spring-boot-starter (RAG 模块)
|
||
|
||
**职责**: 提供检索增强生成、ETL 和图片生成能力
|
||
|
||
**核心组件**:
|
||
- `RagTemplate`: 封装 RAG 调用模式
|
||
- `ImageGenerationTemplate`: 图片生成能力
|
||
- `QuestionAnswerAdvisor`: 向量检索顾问
|
||
|
||
**主要功能**:
|
||
- **ETL**: 文档读取和解析(PDF、Word、TXT等)
|
||
- **Embedding**: 文本向量化
|
||
- **VectorStore**: 向量存储和检索
|
||
- 基于知识库的问答
|
||
- 自定义相似度阈值和 TopK
|
||
- 流式输出
|
||
- 结合对话记忆
|
||
- **图片生成**: 根据文本描述生成图片
|
||
|
||
**典型场景**: 企业知识库问答、文档检索、智能搜索、AI绘画
|
||
|
||
---
|
||
|
||
### 4. fintec-framework-mcp-server-spring-boot-starter (MCP Server 模块)
|
||
|
||
**职责**: 提供 Model Context Protocol Server 支持,将工具暴露给外部 Agent
|
||
|
||
**核心组件**:
|
||
- `McpAutoConfiguration`: MCP Server 自动配置
|
||
- `ToolCallbackProvider`: 自动扫描 `@Tool` 注解
|
||
|
||
**主要功能**:
|
||
- 自动注册工具函数
|
||
- **SSE**: Server-Sent Events 传输
|
||
- **STDIO**: 标准输入输出传输
|
||
- **Streamable-HTTP**: 流式 HTTP 传输
|
||
- 工具发现与调用
|
||
|
||
**典型场景**: AI 调用外部 API、数据库查询、业务系统集成(独立部署)
|
||
|
||
---
|
||
|
||
## 使用示例
|
||
|
||
### Agent 模块示例(包含 Graph 工作流)
|
||
|
||
```java
|
||
@Service
|
||
public class CustomerService {
|
||
|
||
@Autowired
|
||
private AgentTemplate agentTemplate;
|
||
|
||
// 简单问答
|
||
public String answer(String question) {
|
||
return agentTemplate.ask(question);
|
||
}
|
||
|
||
// 带系统提示
|
||
public String answerWithRole(String question) {
|
||
return agentTemplate.ask(
|
||
"你是一位专业的客服代表,请用礼貌的语气回答",
|
||
question
|
||
);
|
||
}
|
||
|
||
// 流式输出
|
||
public Flux<String> streamAnswer(String question) {
|
||
return agentTemplate.stream(question);
|
||
}
|
||
|
||
// 带对话记忆
|
||
public String chat(String conversationId, String message) {
|
||
return agentTemplate.askWithMemory(conversationId, message);
|
||
}
|
||
|
||
// 结构化输出
|
||
public OrderInfo extractOrder(String text) {
|
||
return agentTemplate.askForObject(
|
||
"从以下文本中提取订单信息: " + text,
|
||
OrderInfo.class
|
||
);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Graph 工作流示例
|
||
|
||
```java
|
||
@Service
|
||
public class WorkflowService {
|
||
|
||
@Autowired
|
||
private GraphTemplate graphTemplate;
|
||
|
||
// 顺序工作流: 数据清洗 -> 翻译 -> 总结
|
||
public String processText(String text) {
|
||
var workflow = GraphTemplate.sequential(
|
||
GraphTemplate.node("清洗", t -> t.trim()),
|
||
GraphTemplate.node("翻译", t -> translate(t)),
|
||
GraphTemplate.node("总结", t -> summarize(t))
|
||
);
|
||
return workflow.execute(text);
|
||
}
|
||
|
||
// 并行工作流: 多维度分析
|
||
public List<String> analyzeText(String text) {
|
||
var workflow = GraphTemplate.parallel(
|
||
GraphTemplate.node("情感分析", t -> sentiment(t)),
|
||
GraphTemplate.node("关键词提取", t -> keywords(t)),
|
||
GraphTemplate.node("分类", t -> classify(t))
|
||
);
|
||
return workflow.execute(text);
|
||
}
|
||
|
||
// 路由工作流: 根据问题类型选择处理逻辑
|
||
public String routeQuestion(String question) {
|
||
var workflow = GraphTemplate.routing()
|
||
.addBranch("技术",
|
||
q -> ((String)q).contains("技术"),
|
||
GraphTemplate.node("技术回答", q -> techAnswer(q)))
|
||
.addBranch("业务",
|
||
q -> ((String)q).contains("业务"),
|
||
GraphTemplate.node("业务回答", q -> businessAnswer(q)))
|
||
.setDefault(GraphTemplate.node("通用回答", q -> generalAnswer(q)));
|
||
|
||
return workflow.execute(question);
|
||
}
|
||
}
|
||
```
|
||
|
||
```java
|
||
@Service
|
||
public class KnowledgeService {
|
||
|
||
@Autowired
|
||
private RagTemplate ragTemplate;
|
||
|
||
// 基于知识库问答
|
||
public String askKnowledge(String question) {
|
||
return ragTemplate.ask(question);
|
||
}
|
||
|
||
// 自定义检索参数
|
||
public String askWithConfig(String question) {
|
||
return ragTemplate.askWithConfig(
|
||
question,
|
||
0.8, // 相似度阈值
|
||
5 // TopK
|
||
);
|
||
}
|
||
|
||
// 流式输出
|
||
public Flux<String> streamAsk(String question) {
|
||
return ragTemplate.stream(question);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Graph 模块示例
|
||
|
||
```java
|
||
@Service
|
||
public class WorkflowService {
|
||
|
||
@Autowired
|
||
private GraphTemplate graphTemplate;
|
||
|
||
// 顺序工作流: 数据清洗 -> 翻译 -> 总结
|
||
public String processText(String text) {
|
||
var workflow = GraphTemplate.sequential(
|
||
GraphTemplate.node("清洗", t -> t.trim()),
|
||
GraphTemplate.node("翻译", t -> translate(t)),
|
||
GraphTemplate.node("总结", t -> summarize(t))
|
||
);
|
||
return workflow.execute(text);
|
||
}
|
||
|
||
// 并行工作流: 多维度分析
|
||
public List<String> analyzeText(String text) {
|
||
var workflow = GraphTemplate.parallel(
|
||
GraphTemplate.node("情感分析", t -> sentiment(t)),
|
||
GraphTemplate.node("关键词提取", t -> keywords(t)),
|
||
GraphTemplate.node("分类", t -> classify(t))
|
||
);
|
||
return workflow.execute(text);
|
||
}
|
||
|
||
// 路由工作流: 根据问题类型选择处理逻辑
|
||
public String routeQuestion(String question) {
|
||
var workflow = GraphTemplate.routing()
|
||
.addBranch("技术",
|
||
q -> ((String)q).contains("技术"),
|
||
GraphTemplate.node("技术回答", q -> techAnswer(q)))
|
||
.addBranch("业务",
|
||
q -> ((String)q).contains("业务"),
|
||
GraphTemplate.node("业务回答", q -> businessAnswer(q)))
|
||
.setDefault(GraphTemplate.node("通用回答", q -> generalAnswer(q)));
|
||
|
||
return workflow.execute(question);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 最佳实践
|
||
|
||
### 1. 对话记忆管理
|
||
|
||
✅ **推荐做法**:
|
||
```java
|
||
// 使用业务ID作为会话ID
|
||
String conversationId = "user_" + userId + "_session_" + sessionId;
|
||
agentTemplate.askWithMemory(conversationId, message);
|
||
|
||
// 定期清理过期会话
|
||
@Component
|
||
public class MemoryCleanupTask {
|
||
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点
|
||
public void cleanup() {
|
||
// 清理7天前的会话
|
||
}
|
||
}
|
||
```
|
||
|
||
❌ **避免做法**:
|
||
- 使用 UUID 作为会话ID(每次都是新会话)
|
||
- 不清理会话数据(内存泄漏)
|
||
- 不同用户共用同一会话ID
|
||
|
||
---
|
||
|
||
### 3. RAG 优化
|
||
|
||
✅ **推荐做法**:
|
||
```java
|
||
// 调整相似度阈值(根据业务场景)
|
||
ragTemplate.askWithConfig(question, 0.75, 3);
|
||
|
||
// 文档切片策略
|
||
// - 技术文档: 500-800 tokens/chunk
|
||
// - FAQ: 按问题拆分
|
||
// - 合同: 按条款拆分
|
||
|
||
// 添加元数据过滤
|
||
SearchRequest.builder()
|
||
.similarityThreshold(0.8)
|
||
.topK(5)
|
||
.filterExpression("category == 'technical'")
|
||
.build();
|
||
```
|
||
|
||
❌ **避免做法**:
|
||
- TopK 设置过大(增加成本)
|
||
- 相似度阈值过低(返回不相关内容)
|
||
- 不处理文档切片重叠
|
||
|
||
---
|
||
|
||
### 4. 工作流设计
|
||
|
||
✅ **推荐做法**:
|
||
```java
|
||
// 为节点命名(便于调试)
|
||
GraphTemplate.node("数据验证", data -> validate(data));
|
||
|
||
// 设置合理的循环上限
|
||
GraphTemplate.loop(node, condition, 5); // 最多5次迭代
|
||
|
||
// 异常处理
|
||
try {
|
||
return workflow.execute(input);
|
||
} catch (Exception e) {
|
||
log.error("工作流执行失败", e);
|
||
return fallbackResult;
|
||
}
|
||
```
|
||
|
||
❌ **避免做法**:
|
||
- 循环不设上限(死循环风险)
|
||
- 节点逻辑过于复杂(应拆分为多个节点)
|
||
- 不记录工作流执行日志
|
||
|
||
---
|
||
|
||
### 5. 性能优化
|
||
|
||
✅ **推荐做法**:
|
||
```java
|
||
// 使用流式输出提升用户体验
|
||
@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||
public Flux<String> streamChat(String message) {
|
||
return agentTemplate.stream(message);
|
||
}
|
||
|
||
// 并行执行独立任务
|
||
var results = GraphTemplate.parallel(
|
||
node1, node2, node3
|
||
).execute(input);
|
||
|
||
// 缓存常见问题的答案
|
||
@Cacheable(value = "faq", key = "#question")
|
||
public String getFaqAnswer(String question) {
|
||
return ragTemplate.ask(question);
|
||
}
|
||
```
|
||
|
||
❌ **避免做法**:
|
||
- 同步等待大模型响应(超时风险)
|
||
- 串行执行可并行的任务
|
||
- 重复调用相同的 Prompt
|
||
|
||
---
|
||
|
||
## 常见问题
|
||
|
||
### Q1: 如何切换不同的 AI 模型?
|
||
|
||
**A**: 修改 `application.yml` 配置即可:
|
||
|
||
```yaml
|
||
# 切换到 Ollama
|
||
spring:
|
||
ai:
|
||
ollama:
|
||
base-url: http://localhost:11434
|
||
chat:
|
||
options:
|
||
model: llama3
|
||
|
||
# 切换到通义千问
|
||
spring:
|
||
ai:
|
||
dashscope:
|
||
api-key: ${DASHSCOPE_API_KEY}
|
||
chat:
|
||
options:
|
||
model: qwen-max
|
||
```
|
||
|
||
---
|
||
|
||
### Q2: 如何实现多轮对话?
|
||
|
||
**A**: 使用 `askWithMemory` 方法:
|
||
|
||
```java
|
||
// 第一次对话
|
||
String answer1 = agentTemplate.askWithMemory("session_001", "你好");
|
||
|
||
// 第二次对话(能记住上下文)
|
||
String answer2 = agentTemplate.askWithMemory("session_001", "我刚才说了什么?");
|
||
```
|
||
|
||
---
|
||
|
||
### Q3: RAG 检索不到相关内容怎么办?
|
||
|
||
**A**: 检查以下几点:
|
||
1. 降低相似度阈值(如从 0.8 降到 0.6)
|
||
2. 增加 TopK 数量
|
||
3. 优化文档切片策略
|
||
4. 检查 Embedding 模型质量
|
||
5. 添加更多相关文档
|
||
|
||
---
|
||
|
||
### Q4: 如何调试工作流?
|
||
|
||
**A**: 启用 DEBUG 日志:
|
||
|
||
```yaml
|
||
logging:
|
||
level:
|
||
com.ccb.fintec.graph: DEBUG
|
||
```
|
||
|
||
查看每个节点的输入输出和执行时间。
|
||
|
||
---
|
||
|
||
### Q5: 如何保证 AI 输出的安全性?
|
||
|
||
**A**: 多层防护:
|
||
1. 配置敏感词过滤 (`app.ai.safety.block-keywords`)
|
||
2. 设置输出长度限制 (`max-tokens-per-request`)
|
||
3. 人工审核关键业务输出
|