Files
spring-ai-demo/docs/开发指南.md

14 KiB
Raw Blame History

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              │
└─────────────────────────────────────────────┘

设计原则

  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 工作流)

@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: 检查以下几点:

  1. 降低相似度阈值(如从 0.8 降到 0.6)
  2. 增加 TopK 数量
  3. 优化文档切片策略
  4. 检查 Embedding 模型质量
  5. 添加更多相关文档

Q4: 如何调试工作流?

A: 启用 DEBUG 日志:

logging:
  level:
    com.ccb.fintec.graph: DEBUG

查看每个节点的输入输出和执行时间。


Q5: 如何保证 AI 输出的安全性?

A: 多层防护:

  1. 配置敏感词过滤 (app.ai.safety.block-keywords)
  2. 设置输出长度限制 (max-tokens-per-request)
  3. 人工审核关键业务输出