Initial commit: Fintec AI Framework with Agent, RAG, and MCP modules
This commit is contained in:
546
docs/开发指南.md
Normal file
546
docs/开发指南.md
Normal file
@@ -0,0 +1,546 @@
|
||||
# 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. 人工审核关键业务输出
|
||||
Reference in New Issue
Block a user