架构设计文档.md 18 KB

AI PaaS 平台架构设计文档

1. 整体架构

AI PaaS 平台采用分层架构设计,主要包含以下层次:

  1. 接口层:RESTful API 接口,处理客户端请求
  2. 服务层:业务逻辑处理,包括 Agent 服务、RAG 服务等
  3. 核心层:Agent 核心实现,包括 BaseAgent、ReActAgent、ToolCallAgent 等
  4. 存储层:对话记忆存储,包括内存存储和 Redis 存储
  5. 客户端层:外部服务集成,包括 MCP 客户端和 RAG 客户端

2. 记忆层设计与实现

2.1 记忆存储方案

系统支持两种记忆存储方式:

  • 内存存储:适用于临时对话,重启后数据丢失
  • Redis 存储:适用于持久化对话,支持跨会话保持

2.2 Kryo 序列化实现

核心实现文件RedisBasedChatMemory.java

序列化流程

  1. Kryo 初始化:在静态代码块中初始化 Kryo 实例,设置实例化策略
  2. 消息序列化:将 Message 列表转换为字节数组
  3. 存储到 Redis:使用 RedisTemplate 将序列化后的数据存储到 Redis
  4. 反序列化:从 Redis 读取字节数组,反序列化为 Message 列表

关键代码

// 序列化消息列表
private byte[] serialize(List<Message> messages) {
   
}

// 反序列化消息列表
private List<Message> deserialize(byte[] data) {
}

2.3 对话历史查找

实现方式

  1. 键值设计:使用 chat:memory:{conversationId} 作为 Redis 键
  2. 获取历史:通过 get 方法获取指定对话的历史消息
  3. 限制数量:支持返回最近 N 轮对话历史
  4. 过期时间:默认设置 7 天过期时间,避免内存占用过大

3. RAG 核心流程

3.1 RAG 流程设计

核心实现文件RagService.java

流程步骤

  1. 查询词生成:使用本地模型将用户问题转换为精准的检索查询词
  2. 向量检索:调用 RAG 客户端进行向量检索
  3. 结果处理:对检索结果进行排序和数量限制
  4. 上下文构建:将检索结果拼接为上下文
  5. 关键信息提取:从上下文中提取关键信息
  6. 信息保存:判断是否需要保存提取的关键信息

3.2 查询词生成

实现方式:使用本地 Ollama 模型生成精准的检索查询词,考虑历史对话上下文

关键代码

public String generateRetrievalQuery(String userCurrentMsg, List<Message> historyDialog) {
    log.info("[RAG-Query] 开始生成检索查询词");
    log.info("[RAG-Query] 用户原始消息:{}", userCurrentMsg);

    // 构建提示词
    StringBuilder promptBuilder = new StringBuilder();
    promptBuilder.append("你是一个检索查询优化助手。请根据用户当前问题和历史对话,生成一个精准的检索查询词。\n");
    promptBuilder.append("要求:\n");
    promptBuilder.append("1. 提取核心关键词\n");
    promptBuilder.append("2. 去除无关词汇\n");
    promptBuilder.append("3. 保持简洁\n");
    promptBuilder.append("4. 只返回查询词,不要有其他内容\n\n");

    if (historyDialog != null && !historyDialog.isEmpty()) {
        promptBuilder.append("历史对话:\n");
        for (Message msg : historyDialog) {
            if (msg instanceof UserMessage) {
                promptBuilder.append("用户:").append(msg.getText()).append("\n");
            }
        }
        promptBuilder.append("\n");
    }

    promptBuilder.append("当前问题:").append(userCurrentMsg).append("\n");
    promptBuilder.append("生成的检索查询词:");

    String prompt = promptBuilder.toString();
    log.debug("[RAG-Query] 提示词:\n{}", prompt);

    try {
        // 使用本地模型生成查询词
        ChatResponse response = ollamaChatModel.call(new Prompt(prompt));
        String query = response.getResult().getOutput().getText().trim();
        
        log.info("[RAG-Query] 生成的检索查询词:{}", query);
        return query;
    } catch (Exception e) {
        log.error("[RAG-Query] 生成检索查询词失败,使用原始问题", e);
        return userCurrentMsg;
    }
}

3.3 检索与结果处理

实现方式:调用 RAG 客户端进行检索,对结果进行排序和限制

关键代码

public Mono<RagRetrieveResponse> retrieve(String query, AgentConfiguration.RagConfig ragConfig) {
    if (ragConfig == null || !Boolean.TRUE.equals(ragConfig.getEnabled())) {
        log.debug("RAG 未启用,跳过检索");
        return Mono.empty();
    }

    // 转换为 RAG 客户端请求
    List<RagRetrieveRequest.VectorStoreConfig> vectorStores = ragConfig.getVectorStores().stream()
            .map(config -> RagRetrieveRequest.VectorStoreConfig.builder()
                    .vectorStoreId(config.getVectorStoreId())
                    .topK(config.getTopK())
                    .scoreThreshold(config.getScoreThreshold())
                    .build())
            .collect(Collectors.toList());

    RagRetrieveRequest request = RagRetrieveRequest.builder()
            .query(query)
            .vectorStores(vectorStores)
            .build();

    return ragClient.retrieve(request);
}

public List<RagRetrieveResponse.Result> sortAndLimitResults(
        RagRetrieveResponse response, 
        Integer maxResults) {
    
    if (response == null || response.getResults() == null) {
        log.warn("[RAG-Sort] 响应或结果为空");
        return List.of();
    }

    log.info("[RAG-Sort] 开始排序,原始结果数:{},最大结果数:{}", response.getResults().size(), maxResults);
    
    List<RagRetrieveResponse.Result> sorted = response.getResults().stream()
            .sorted(Comparator.comparing(RagRetrieveResponse.Result::getScore).reversed())
            .limit(maxResults)
            .collect(Collectors.toList());
    
    log.info("[RAG-Sort] 排序完成,保留结果数:{}", sorted.size());
    return sorted;
}

4. ReAct 实现

4.1 ReAct 架构设计

核心实现文件ReActAgent.java

设计理念

  • Think 阶段:分析当前情况,决定是否需要调用工具
  • Act 阶段:执行工具调用并处理结果
  • 状态管理:跟踪 Agent 的执行状态
  • 步数限制:防止无限循环

4.2 核心实现

关键代码

/**
 * 运行 Agent
 * 
 * @param userPrompt 用户输入
 * @return 执行结果
 */
public String run(String userPrompt) {
    if (this.state != AgentState.IDLE) {
        throw new RuntimeException("Cannot run agent from state: " + this.state);
    }
    if (userPrompt == null || userPrompt.trim().isEmpty()) {
        throw new RuntimeException("Cannot run agent with empty user prompt");
    }

    state = AgentState.RUNNING;
    messageList.add(new UserMessage(userPrompt));
    List<String> results = new ArrayList<>();

    try {
        for (int i = 0; i < maxSteps && state != AgentState.FINISHED; i++) {
            currentStep = i + 1;
            log.info("[{}] 执行第 {} 步", name, currentStep);
            String stepResult = step();
            results.add("Step " + currentStep + ": " + stepResult);
        }

        if (currentStep >= maxSteps) {
            state = AgentState.FINISHED;
            results.add("Terminated: Reached max steps (" + maxSteps + ")");
        }

        return String.join("\n", results);
    } catch (Exception e) {
        state = AgentState.ERROR;
        log.error("[{}] 执行错误: {}", name, e.getMessage(), e);
        return "执行错误: " + e.getMessage();
    } finally {
        this.cleanup();
    }
}

/**
 * 单步执行
 * 将 think() 和 act() 组合在一起
 * 
 * @return 步骤执行结果
 */
public String step() {
    try {
        // 思考阶段:决定是否需要调用工具
        boolean shouldAct = think();
        if (!shouldAct) {
            return "思考完成 - 无需行动";
        }
        // 行动阶段:执行工具调用
        return act();
    } catch (Exception e) {
        log.error("[{}] 步骤执行失败: {}", name, e.getMessage(), e);
        return "步骤执行失败: " + e.getMessage();
    }
}

/**
 * 思考阶段
 * 分析当前情况,决定是否需要调用工具
 * 
 * @return true - 需要调用工具,false - 无需调用工具
 */
public abstract boolean think();

/**
 * 行动阶段
 * 执行工具调用并处理结果
 * 
 * @return 行动结果
 */
public abstract String act();

5. 配置管理架构

5.1 配置管理设计

核心实现文件

  • AgentConfigRegistry.java:Agent 配置注册表
  • AgentConfiguration.java:Agent 配置类
  • ConfigMapWatcher.java:配置监视器

设计理念

  • 集中管理:所有 Agent 配置集中在注册表中管理
  • 动态更新:支持配置的动态刷新和监听
  • 结构化配置:使用 YAML 配置文件,支持结构化配置

5.2 配置结构

配置文件支持以下结构:

  • Agent 基本配置:名称、描述、类型等
  • ReAct 配置:是否启用 ReAct 模式
  • RAG 配置:向量存储、TopK、分数阈值等
  • 记忆配置:存储类型、过期时间等
  • MCP 配置:MCP Hub 地址、鉴权信息等

6. MCP 功能集成

6.1 MCP 架构设计

核心实现文件

  • McpClientService.java:MCP 客户端服务,负责与 MCP Hub 通信
  • McpTool.java:MCP 工具封装,将 MCP 工具转换为 Spring AI 工具
  • McpServerConfig.java:MCP 服务器配置

设计理念

  • 模块化设计:独立的 MCP 客户端模块,与其他模块解耦
  • 会话管理:支持多会话管理,每个会话对应一个 MCP 连接
  • 智能路由:支持智能路由模式,提供更智能的工具搜索和执行

6.2 MCP 核心功能

功能列表

  1. 连接管理:建立和管理与 MCP Hub 的 SSE 连接
  2. 工具列表:获取 MCP Hub 提供的工具列表
  3. 工具调用:调用 MCP Hub 中的工具
  4. 智能路由:使用智能路由搜索和执行工具

关键代码

// MCP 连接管理
public synchronized String connect(String group) {
    // 如果已有连接,先关闭
    if (sessions.containsKey(group)) {
        closeSession(sessions.get(group));
    }

    SessionInfo sessionInfo = new SessionInfo();
    sessionInfo.setGroup(group);
    sessionInfo.setSessionId(UUID.randomUUID().toString()); // 临时ID,会被覆盖
    
    try {
        // 获取 sessionId
        String sessionId = getMCPSessionId();
        
        sessionInfo.setSessionId(sessionId);
        sessions.put(group, sessionInfo);
        
        log.info("MCP 连接成功 [group={}, sessionId={}]", group, sessionId);
        return sessionId;
        
    } catch (Exception e) {
        log.error("MCP 连接失败 [group={}]: {}", group, e.getMessage());
        throw new RuntimeException("无法建立 MCP 连接: " + e.getMessage(), e);
    }
}

// 工具调用
public ToolCallResult callTool(String group, String toolName, Map<String, Object> arguments) {
    SessionInfo session = getOrCreateSession(group);
    
    ToolCallParams params = ToolCallParams.builder()
            .name(toolName)
            .arguments(arguments)
            .build();
    
    JsonRpcRequest request = JsonRpcRequest.builder()
            .jsonrpc("2.0")
            .id(requestIdGenerator.incrementAndGet())
            .method("tools/call")
            .params(objectMapper.valueToTree(params))
            .build();

    JsonNode response = sendRequest(session, request);
    return parseToolCallResult(response);
}

// 智能路由搜索工具
public ToolCallResult searchTools(String group, String query, int limit) {
    SessionInfo session = getOrCreateSession(group);
    
    if (!"intelligence".equals(session.getMode())) {
        // 如果不是智能路由模式,先切换到智能路由
        connectIntelligence(group);
        session = sessions.get(group);
    }
    
    Map<String, Object> arguments = Map.of(
            "query", query,
            "limit", limit
    );
    
    ToolCallParams params = ToolCallParams.builder()
            .name("search_tools")
            .arguments(arguments)
            .build();
    
    JsonRpcRequest request = JsonRpcRequest.builder()
            .jsonrpc("2.0")
            .id(requestIdGenerator.incrementAndGet())
            .method("tools/call")
            .params(objectMapper.valueToTree(params))
            .build();

    JsonNode response = sendRequest(session, request);
    return parseToolCallResult(response);
}

6.3 MCP 工具集成

McpTool 实现

public class McpTool implements ToolCallback {

    private final String name;
    private final String description;
    private final String group;
    private final McpClientService mcpClientService;

    @Override
    public ToolDefinition getToolDefinition() {
        return ToolDefinition.builder()
                .name(name)
                .description(description)
                // MCP 工具的参数 schema 由 MCP Hub 提供
                // 这里使用一个通用的 object schema
                .inputSchema("""
                    {
                        "type": "object",
                        "properties": {
                            "args": {
                                "type": "object",
                                "description": "工具参数,由 MCP Hub 定义"
                            }
                        }
                    }
                    """)
                .build();
    }

    @Override
    public String call(String toolInput) {
        log.info("执行 MCP 工具 [name={}, group={}, input={}]", name, group, toolInput);
        
        try {
            // 解析输入参数
            Map<String, Object> args = parseInput(toolInput);
            
            // 调用 MCP Hub
            ToolCallResult result;
            // 这里简化处理,实际应该根据会话的模式来选择调用方法
            // 暂时使用普通的 callTool 方法
            result = mcpClientService.callTool(group, name, args);
            
            if (result.isError()) {
                log.error("MCP 工具执行失败 [name={}]: {}", name, result.getContent());
                return "工具执行失败: " + result.getContent();
            }
            
            // 返回结果内容
            String content = result.getContent();
            log.info("MCP 工具执行成功 [name={}], 结果长度: {}", name, 
                    content != null ? content.length() : 0);
            
            return content != null ? content : "工具执行成功,但无返回内容";
            
        } catch (Exception e) {
            log.error("MCP 工具执行异常 [name={}]: {}", name, e.getMessage(), e);
            return "工具执行异常: " + e.getMessage();
        }
    }
}

6.4 MCP 配置

配置项

@Data
@Configuration
@ConfigurationProperties(prefix = "mcp.hub")
public class McpServerConfig {
    
    /**
     * MCP Hub 基础 URL
     */
    private String baseUrl = "https://ai-paas-mcp-endpoint.njuu.top";
    
    /**
     * 鉴权 token
     */
    private String authorization = "sqGYuMvKgdxmzmTM5lNBgLdVpl6XNnPX";
    
    /**
     * 连接超时(秒)
     */
    private int connectTimeout = 10;
    
    /**
     * 请求超时(秒)
     */
    private int requestTimeout = 60;
    
    /**
     * 是否自动重连
     */
    private boolean autoReconnect = true;
}

7. 系统工作流程

7.1 典型流程

  1. 客户端请求:客户端发送聊天请求
  2. 配置加载:加载对应 Agent 的配置
  3. 记忆检索:获取对话历史
  4. RAG 处理:执行 RAG 检索,获取相关知识
  5. ReAct 处理:执行 ReAct 思考和行动
    • 工具调用:如需调用工具,可选择本地工具或 MCP 工具
    • MCP 工具调用:通过 McpClientService 调用 MCP Hub 中的工具
  6. 响应生成:生成响应并返回给客户端
  7. 记忆更新:更新对话记忆

7.2 MCP 集成流程

  1. MCP 连接:建立与 MCP Hub 的 SSE 连接
  2. 工具发现:获取 MCP Hub 提供的工具列表
  3. 工具注册:将 MCP 工具注册为 Spring AI 工具
  4. 工具调用:在 ReAct 过程中调用 MCP 工具
  5. 结果处理:处理 MCP 工具返回的结果

7.3 异步处理

系统使用 Spring 的异步处理能力,将保全信息等耗时操作异步解耦,提高系统响应速度。

8. 技术选型

技术 版本 用途
Spring Boot 3.x 后端框架
Spring AI 1.x AI 集成框架
Redis 7.x 持久化存储
Kryo 5.x 高效序列化
Ollama 0.1.x 本地模型
Maven 3.x 构建工具
MCP Hub - 工具服务平台

9. 未来扩展

9.1 存储扩展

  • MySQL 存储:实现多一级别的 MySQL 存储,支持更复杂的查询和分析

9.2 模型扩展

  • 模型列表:补充更多模型支持
  • 多模态调用:实现完整的多模态调用功能

9.3 客户端集成

  • MCP 功能增强:扩展 MCP 客户端功能,支持更多 MCP Hub 特性
  • A2A 集成:实现 A2A 相关功能

9.4 工具完善

  • 工具连接:完善工具连接和使用
  • 辅助工具:补充更多辅助工具
  • 工具管理:实现工具的动态注册和管理

9.5 智能路由优化

  • 智能路由增强:优化智能路由算法,提高工具匹配准确性
  • 多模态工具:支持多模态工具的智能路由

10. 总结

AI PaaS 平台采用分层架构设计,实现了配置管理、Agent 核心、记忆系统、RAG 功能和 MCP 集成。系统架构清晰,代码组织合理,功能完整。

  • 记忆层:使用 Kryo 序列化和 Redis 存储,实现了高效的对话记忆管理
  • RAG:实现了完整的 RAG 流程,包括查询词生成、向量检索、结果处理和关键信息提取
  • ReAct:实现了 ReAct 模式,包含思考和行动两个阶段,支持工具调用
  • MCP 集成:实现了与 MCP Hub 的完整集成,支持工具发现、调用和智能路由

系统已经具备了 AI PaaS 平台的核心功能,为未来的扩展和优化奠定了基础。MCP 功能的集成使得系统能够利用外部工具服务,大大扩展了 Agent 的能力范围。