14 KiB
14 KiB
Fintec AI Framework 开发指南
📚 文档目录
快速开始
环境要求
- JDK 17+
- Spring Boot 3.4.6+
- Spring AI 1.1.1+
- Maven 3.6+
引入依赖
在项目的 pom.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 模型:
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 │
└─────────────────────────────────────────────┘
设计原则
- 分层解耦: 技术实现与业务编排分离
- 多模型支持: 通过 Spring AI 抽象,支持切换不同模型厂商
- 开箱即用: 自动配置,零样板代码
- 可扩展性: 基于 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 工作流)
@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 工作流示例
@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);
}
}
@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 模块示例
@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. 对话记忆管理
✅ 推荐做法:
// 使用业务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 优化
✅ 推荐做法:
// 调整相似度阈值(根据业务场景)
ragTemplate.askWithConfig(question, 0.75, 3);
// 文档切片策略
// - 技术文档: 500-800 tokens/chunk
// - FAQ: 按问题拆分
// - 合同: 按条款拆分
// 添加元数据过滤
SearchRequest.builder()
.similarityThreshold(0.8)
.topK(5)
.filterExpression("category == 'technical'")
.build();
❌ 避免做法:
- TopK 设置过大(增加成本)
- 相似度阈值过低(返回不相关内容)
- 不处理文档切片重叠
4. 工作流设计
✅ 推荐做法:
// 为节点命名(便于调试)
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. 性能优化
✅ 推荐做法:
// 使用流式输出提升用户体验
@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 配置即可:
# 切换到 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 方法:
// 第一次对话
String answer1 = agentTemplate.askWithMemory("session_001", "你好");
// 第二次对话(能记住上下文)
String answer2 = agentTemplate.askWithMemory("session_001", "我刚才说了什么?");
Q3: RAG 检索不到相关内容怎么办?
A: 检查以下几点:
- 降低相似度阈值(如从 0.8 降到 0.6)
- 增加 TopK 数量
- 优化文档切片策略
- 检查 Embedding 模型质量
- 添加更多相关文档
Q4: 如何调试工作流?
A: 启用 DEBUG 日志:
logging:
level:
com.ccb.fintec.graph: DEBUG
查看每个节点的输入输出和执行时间。
Q5: 如何保证 AI 输出的安全性?
A: 多层防护:
- 配置敏感词过滤 (
app.ai.safety.block-keywords) - 设置输出长度限制 (
max-tokens-per-request) - 人工审核关键业务输出