Răsfoiți Sursa

docs: add AxonHub model routing and multi-model workflow to upgrade plans

asus 3 luni în urmă
părinte
comite
02fd7383e8

+ 78 - 9
docs/architecture/2026-07-20-SB3-upgrade-plan.md

@@ -2,11 +2,11 @@
 
 > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
 
-**Goal:** 将 cfc-backend 从 Spring Boot 2.7.18 + Java 8 升级到 Spring Boot 3.4.x + Java 21 LTS,并用 Spring AI 替换 Dify 全部调用。
+**Goal:** 将 cfc-backend 从 Spring Boot 2.7.18 + Java 8 升级到 Spring Boot 3.4.x + Java 21 LTS,用 Spring AI + AxonHub 替换 Dify 全部功能。
 
-**Architecture:** 5 阶段串行,每阶段有独立编译/测试验证 + git tag 回退点。Phase 0-2 为纯框架升级和行为适配,Phase 3 为 Spring AI 集成和 Dify 替代,Phase 4 为回归验证和灰度上线。
+**Architecture:** 分层替代 Dify:Spring AI 承担 AI 应用层(对话/RAG/工具),AxonHub 承担模型路由层(分发/降级/计费),专用工具承担文档预处理。5 阶段串行,每阶段有独立编译/测试验证 + git tag 回退点。
 
-**Tech Stack:** Java 21, Spring Boot 3.4.x, Spring AI 1.x, MyBatis-Plus 3.5.9, springdoc-openapi 2.7, exp4j 0.4.8, jjwt 0.12.6, mysql-connector-java 8.0.33, sqlite-vec
+**Tech Stack:** Java 21, Spring Boot 3.4.x, Spring AI 1.x, AxonHub (axonhub.iwintrue.com), MyBatis-Plus 3.5.9, springdoc-openapi 2.7, exp4j 0.4.8, jjwt 0.12.6, mysql-connector-java 8.0.33, sqlite-vec, docling (PDF预处理)
 
 ---
 
@@ -21,6 +21,9 @@
 7. **统一 @PostMapping** — 遵循项目现有规范,禁止 @GetMapping/@PutMapping/@DeleteMapping
 8. **统一 Result 响应** — 所有接口返回 `Result<T>` (code/message/data)
 9. **不再依赖 Dify** — 全部 8 个 Service + 3 个 Controller 必须改为 Spring AI ChatClient
+10. **AxonHub 统一网关** — 所有模型调用经 `axonhub.iwintrue.com`,Spring AI 配置 `base-url` 指向 AxonHub
+11. **多模型编排** — 支持工作流各节点使用不同 `ChatModel` Bean(DeepSeek/Qwen/本地模型等),通过 `@Qualifier` 注入
+12. **PDF 预处理** — 复杂文档预处理使用 Python 专有工具(docling / unstructured.io),不在 Spring AI ETL 中处理
 
 ---
 
@@ -37,6 +40,7 @@
 | `resources/prompts/growth-plan.yaml` | 成长规划 System Prompt |
 | `resources/prompts/diet-recommend.yaml` | 饮食推荐 System Prompt |
 | `resources/prompts/math-tutor.yaml` | 数学辅导 System Prompt |
+| `scripts/doc-preprocess/` | PDF 预处理 Python 脚本(docling) |
 
 ### 修改
 | 文件 | 变更类型 |
@@ -474,18 +478,30 @@ git tag v3.0-build-ready
 </dependencies>
 ```
 
-- [ ] **Step 2:配置 Spring AI(application.yml)**
+- [ ] **Step 2:配置 AxonHub 模型网关(application.yml)**
+
+所有模型调用走 AxonHub 统一网关,不直连任何模型 Provider:
 
 ```yaml
 spring:
   ai:
-    openai:
-      base-url: https://api.deepseek.com
-      api-key: ${DEEPSEEK_API_KEY}
+    openai:                                     # 默认模型 → AxonHub → DeepSeek
+      base-url: https://axonhub.iwintrue.com/v1
+      api-key: ${AXONHUB_API_KEY}
       chat:
         options:
           model: deepseek-chat
           temperature: 0.7
+    dashscope:                                   # 第二模型 → AxonHub → Qwen (可选)
+      api-key: ${AXONHUB_API_KEY}
+      chat:
+        options:
+          model: qwen-max
+    ollama:                                      # 本地模型 (不经过 AxonHub)
+      base-url: http://localhost:11434
+      chat:
+        options:
+          model: qwen2.5:7b
     vectorstore:
       sqlite:
         path: ${cfc.data.dir:/data/cfc}/knowledge.db
@@ -505,26 +521,40 @@ mvn clean compile
 - Create: `src/main/java/com/etotem/cfc/service/ai/LLMService.java`
 - Create: `src/main/java/com/etotem/cfc/config/SpringAiConfig.java`
 
-- [ ] **Step 1:创建 SpringAiConfig**
+- [ ] **Step 1:创建 SpringAiConfig(含多模型支持)**
 
 ```java
 package com.etotem.cfc.config;
 
 import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.ai.chat.model.ChatModel;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
 @Configuration
 public class SpringAiConfig {
 
+    /** 默认聊天客户端(经 AxonHub 路由) */
     @Bean
     ChatClient chatClient(ChatClient.Builder builder) {
         return builder.build();
     }
+
+    /**
+     * 如需在工作流中使用不同模型,注入命名后的 ChatModel Bean:
+     * @Resource @Qualifier("openAiChatModel")  private ChatModel deepSeek;
+     * @Resource @Qualifier("dashScopeChatModel") private ChatModel qwen;
+     * @Resource @Qualifier("ollamaChatModel")   private ChatModel localModel;
+     *
+     * Spring AI 自动注册以下 Bean(按 application.yml 配置):
+     *   openAiChatModel     → AxonHub → DeepSeek
+     *   dashScopeChatModel  → AxonHub → Qwen (如果配置了 dashscope)
+     *   ollamaChatModel     → localhost:11434 (如果配置了 ollama)
+     */
 }
 ```
 
-- [ ] **Step 2:创建 LLMService**
+- [ ] **Step 2:创建 LLMService(含多模型编排)**
 
 ```java
 package com.etotem.cfc.service.ai;
@@ -533,7 +563,9 @@ import jakarta.annotation.Resource;
 import org.springframework.ai.chat.client.ChatClient;
 import org.springframework.ai.chat.memory.ChatMemory;
 import org.springframework.ai.chat.memory.InMemoryChatMemory;
+import org.springframework.ai.chat.model.ChatModel;
 import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Service;
 import reactor.core.publisher.Flux;
 
@@ -543,8 +575,18 @@ public class LLMService {
     @Resource
     private ChatClient chatClient;
 
+    @Resource @Qualifier("openAiChatModel")
+    private ChatModel deepSeekModel;         // → AxonHub → DeepSeek
+
+    @Resource @Qualifier("dashScopeChatModel")
+    private ChatModel qwenModel;             // → AxonHub → Qwen
+
+    @Resource @Qualifier("ollamaChatModel")
+    private ChatModel localModel;            // → 本地 Ollama
+
     private final ChatMemory chatMemory = new InMemoryChatMemory();
 
+    // 默认模型聊天
     public String chat(String query, String systemPrompt, String conversationId) {
         return chatClient.prompt()
                 .system(systemPrompt)
@@ -554,6 +596,19 @@ public class LLMService {
                 .content();
     }
 
+    // 指定模型的聊天(工作流各节点可用不同模型)
+    public String chatWithModel(String query, String systemPrompt,
+                                 String conversationId, ChatModel model) {
+        return ChatClient.builder(model).build()
+                .prompt()
+                .system(systemPrompt)
+                .user(query)
+                .advisors(new MessageChatMemoryAdvisor(chatMemory, conversationId, 10))
+                .call()
+                .content();
+    }
+
+    // 流式聊天
     public Flux<String> stream(String query, String systemPrompt, String conversationId) {
         return chatClient.prompt()
                 .system(systemPrompt)
@@ -562,6 +617,20 @@ public class LLMService {
                 .stream()
                 .content();
     }
+
+    // 多模型工作流示例
+    public WorkflowResult executeMultiModelWorkflow(String input) {
+        // Step 1: DeepSeek 做意图分类(强推理)
+        String intent = ChatClient.builder(deepSeekModel).build()
+                .prompt().user("分类用户意图: " + input).call().content();
+        // Step 2: Qwen 做长文本分析(长上下文)
+        String analysis = ChatClient.builder(qwenModel).build()
+                .prompt().system("分析专家").user(intent).call().content();
+        // Step 3: 本地模型格式化输出(零成本)
+        String output = ChatClient.builder(localModel).build()
+                .prompt().user("格式化: " + analysis).call().content();
+        return new WorkflowResult(intent, analysis, output);
+    }
 }
 ```
 

+ 214 - 51
docs/architecture/Dify替代迁移可行性分析.md

@@ -11,11 +11,12 @@
 1. [背景与现状](#1-背景与现状)
 2. [Dify 依赖全景](#2-dify-依赖全景)
 3. [替代方案评估矩阵](#3-替代方案评估矩阵)
-4. [推荐方案:模块化混合替换](#4-推荐方案模块化混合替换)
-5. [分阶段实施计划](#5-分阶段实施计划)
-6. [风险评估与应对](#6-风险评估与应对)
-7. [成本收益分析](#7-成本收益分析)
-8. [决策树](#8-决策树)
+4. [推荐方案:Spring AI + AxonHub 分层替换](#4-推荐方案spring-ai--axonhub-分层替换)
+5. [Dify 全功能可替代性结论](#5-dify-全功能可替代性结论)
+6. [分阶段实施计划](#6-分阶段实施计划)
+7. [风险评估与应对](#7-风险评估与应对)
+8. [成本收益分析](#8-成本收益分析)
+9. [决策树](#9-决策树)
 
 ---
 
@@ -180,56 +181,114 @@ OpenClaw 的流行不代表它适合替换 Dify。两者定位完全不同:
 
 ---
 
-## 4. 推荐方案:模块化混合替换
+## 4. 推荐方案:Spring AI + AxonHub 分层替换
 
-### 4.1 架构演进
+### 4.1 最终架构(2026-07-20 决策)
 
 ```
-当前架构:
-┌─────────────────────────────────────────────────────────────┐
-│  Frontend                                                   │
-│  chat.vue → AIChatController → AIService → Dify → LLM API  │
-└─────────────────────────────────────────────────────────────┘
-
-目标架构:
-┌─────────────────────────────────────────────────────────────┐
-│  Frontend                                                   │
-│  chat.vue → AIChatController → LLMService                   │
-│                                  ├─ DeepSeekLLMService       │
-│                                  ├─ RAGService (pgvector)    │
-│                                  └─ ContextStrategy          │
-│                                                                 
-│  本地持久化:ChatMirrorService (转正)                          │
-│  本地记忆:AiConversationSummary + AiUserFact (来源替换)       │
-│  本地推荐:RecommendationService (已有)                       │
-└─────────────────────────────────────────────────────────────┘
+升级前:
+┌──────────────────────────────────────────────────────────────┐
+│  Frontend                                                     │
+│  chat.vue → AIChatController → AIService → Dify → LLM API    │
+│                                      (应用层+路由层一体)       │
+└──────────────────────────────────────────────────────────────┘
+
+升级后:
+┌──────────────────────────────────────────────────────────────┐
+│  Frontend                                                     │
+│  chat.vue → AIChatController → LLMService (Spring AI)        │
+│                                  ├─ ChatClient (Chat)         │
+│                                  ├─ @Tool (Function Calling)  │
+│                                  ├─ VectorStore (RAG)         │
+│                                  └─ MemoryAdvisor (记忆)      │
+│                                       │                        │
+│                                       ▼                        │
+│                              AxonHub (模型路由层)               │
+│                              axonhub.iwintrue.com              │
+│                              ├─ 模型分发 / 自动降级            │
+│                              ├─ 用量计费 / 密钥管理            │
+│                              └─ 多模型统一网关                 │
+│                                       │                        │
+│                  ┌────────────────────┼──────────────────┐     │
+│                  ▼                    ▼                  ▼     │
+│             DeepSeek               Qwen              本地模型  │
+│                                                                 │
+│  本地持久化:                                                    │
+│  ├─ ChatMirrorService (对话缓存,转正)                           │
+│  ├─ ChatMemory (JDBC/Redis 持久化)                              │
+│  ├─ sqlite-vec (知识库向量存储)                                 │
+│  └─ docling / unstructured (复杂PDF预处理,Python脚本)          │
+└──────────────────────────────────────────────────────────────┘
+
+分层职责:
+┌──────────────┬──────────────────┬──────────────────────────────┐
+│ 层           │ 技术             │ 职责                         │
+├──────────────┼──────────────────┼──────────────────────────────┤
+│ AI 应用层    │ Spring AI        │ 对话、RAG、Tool Calling、    │
+│              │                  │ 记忆管理、多模型编排、MCP    │
+├──────────────┼──────────────────┼──────────────────────────────┤
+│ 模型路由层   │ AxonHub          │ 模型分发、自动降级、         │
+│              │ (axonhub.iwintrue.com) │ 用量计费、统一网关       │
+├──────────────┼──────────────────┼──────────────────────────────┤
+│ 文档预处理   │ docling /        │ 复杂PDF/表格/多栏布局解析   │
+│              │ unstructured     │                              │
+└──────────────┴──────────────────┴──────────────────────────────┘
 ```
 
 ### 4.2 核心替换策略
 
-#### 4.2.1 新增接口层
+#### 4.2.1 Spring AI ChatClient 封装(替代手写 LLMService)
 
 ```java
-// 新增抽象接口,AIService 改为其中一个实现
-public interface LLMService {
-    ChatResult sendChat(ChatRequest request);
-    ChatResult sendChatWithStream(ChatRequest request);
-    String getProviderName();
-    boolean isAvailable();
-}
-
-// Dify 实现(保留作降级通道)
-@Component
-public class DifyLLMService implements LLMService { ... }
-
-// 直接 LLM API 实现(主通道)
-@Component
-@Primary
-public class DirectLLMService implements LLMService {
-    // 调用 DeepSeek / Qwen / GLM 的 /v1/chat/completions
+@Service
+public class LLMService {
+
+    @Resource
+    private ChatClient chatClient;    // Spring AI 自动配置
+
+    @Resource @Qualifier("openAiChatModel")
+    private ChatModel deepSeekModel;  // → AxonHub → DeepSeek
+
+    @Resource @Qualifier("dashScopeChatModel")
+    private ChatModel qwenModel;      // → AxonHub → Qwen
+
+    // 默认聊天
+    public String chat(String query, String systemPrompt, String conversationId) {
+        return chatClient.prompt()
+            .system(systemPrompt)
+            .user(query)
+            .advisors(new MessageChatMemoryAdvisor(chatMemory, conversationId, 10))
+            .call()
+            .content();
+    }
+
+    // 工作流各节点可用不同模型
+    public String chatWithModel(String query, String systemPrompt,
+                                 String conversationId, ChatModel model) {
+        return ChatClient.builder(model).build()
+            .prompt()
+            .system(systemPrompt)
+            .user(query)
+            .call()
+            .content();
+    }
+
+    // 多模型编排
+    public String executeWorkflow(String input) {
+        String intent = ChatClient.builder(deepSeekModel).build()
+            .prompt().user("分类: " + input).call().content();
+        return ChatClient.builder(qwenModel).build()
+            .prompt().system("分析专家").user(intent).call().content();
+    }
 }
 ```
 
+对比原方案的手写 `DirectLLMService`,Spring AI 省去了:
+- 自己维护 HTTP 调用(Spring AI 内置)
+- 自己解析 JSON 响应(`StructuredOutput` 直接转 POJO)
+- 自己处理流式(`Flux<String>` 原生)
+- 自己实现重试/降级(Advisors 链式扩展)
+
 #### 4.2.2 System Prompt 外部化
 
 从 Dify 应用配置迁移到本地:
@@ -254,7 +313,36 @@ ai:
       temperature: 0.3
 ```
 
-#### 4.2.3 Workflow 拆解
+#### 4.2.3 AxonHub 模型路由配置
+
+所有模型调用统一经 AxonHub 网关,不直连任何 Provider:
+
+```yaml
+# application.yml
+spring:
+  ai:
+    openai:                                     # 默认模型 → AxonHub → DeepSeek
+      base-url: https://axonhub.iwintrue.com/v1
+      api-key: ${AXONHUB_API_KEY}
+      chat:
+        options:
+          model: deepseek-chat
+          temperature: 0.7
+    dashscope:                                   # 第二模型 → AxonHub → Qwen
+      api-key: ${AXONHUB_API_KEY}
+      chat:
+        options:
+          model: qwen-max
+    ollama:                                      # 本地模型 (不经过 AxonHub)
+      base-url: http://localhost:11434
+      chat:
+        options:
+          model: qwen2.5:7b
+```
+
+AxonHub 接管原 Dify 的模型路由职责:自动分发请求到对应模型、故障时降级到备用模型、统一计费和密钥管理。Spring AI 只需指定 model 参数。
+
+#### 4.2.4 Workflow 拆解
 
 | 当前 Dify Workflow | 替换方式 | 工作量 |
 |--------------------|---------|--------|
@@ -264,11 +352,11 @@ ai:
 | 数学判题 | 已有 Eval 兜底,永久化 | 0 天 |
 | 食谱推荐 | 直接调 LLM API + `MealRecommendService` 聚合上下文 | 0.5 天 |
 
-#### 4.2.4 知识库 RAG 本地化
+#### 4.2.5 知识库 RAG 本地化
 
 ```
 当前:管理端创建知识点 → DifySyncService → Dify Datasets → Dify 检索
-替换后:管理端创建知识点 → TextEmbeddingService → pgvector (或 sqlite-vec) → 相似度检索
+替换后:管理端创建知识点 → TextEmbeddingService → sqlite-vec → 相似度检索
 ```
 
 **向量方案选择:**
@@ -282,9 +370,84 @@ ai:
 
 **推荐 sqlite-vec 理由**:项目当前使用 MySQL,无法做向量检索。sqlite-vec 作为一个零配置的 SQLite 扩展,可以独立运行,不需要额外部署服务。适合当前几千条知识库的规模。
 
+#### 4.2.6 文档预处理(PDF 复杂解析)
+
+Dify 内置的 PDF 解析对简单文档足够,但复杂 PDF(表格、多栏布局)效果一般。可用 Python 专有工具替代:
+
+| 工具 | 说明 | 推荐度 |
+|------|------|--------|
+| **docling** (IBM) | 开源 PDF 解析,表格/多栏布局强,输出 Markdown/JSON | ⭐⭐⭐ 首选 |
+| unstructured.io | 商业级文档解析,多格式支持 | ⭐⭐ 次选 |
+| Marker | 开源 PDF 转 Markdown,精度好 | ⭐⭐ 备选 |
+
+Spring AI ETL Pipeline 处理简单文档,复杂文档走 Python 预处理脚本。
+
 ---
 
-## 5. 分阶段实施计划
+## 5. Dify 全功能可替代性结论
+
+### 5.1 功能对照表
+
+| 编号 | Dify 功能 | Spring AI | AxonHub | 其他工具 | 可替代? | 本项目需要? |
+|------|----------|-----------|---------|---------|---------|------------|
+| 1 | 聊天机器人 (Chatbot) | ChatClient + MemoryAdvisor | — | — | ✅ | ✅ |
+| 2 | 流式聊天响应 | Flux<String> | — | — | ✅ | ✅ |
+| 3 | 对话记忆管理 | MessageChatMemoryAdvisor | — | — | ✅ | ✅ |
+| 4 | 文本生成 (Completion) | ChatClient.call() | — | — | ✅ | ✅ |
+| 5 | **可视化工作流画布** | ❌ 无 | — | 代码编排 | ❌ **不可替代** | ❌ **不需要(只调API)** |
+| 6 | 工作流: LLM 节点 | ChatClient | AxonHub 路由 | — | ✅ | ✅ |
+| 7 | 工作流: 条件分支 | Java if/switch | — | — | ✅ | ✅ |
+| 8 | 工作流: 循环/迭代 | Java for/while | — | — | ✅ | ✅ |
+| 9 | 工作流: 代码节点 | Java 方法 | — | — | ✅ **更好** | ✅ |
+| 10 | 工作流: HTTP 请求 | RestTemplate/WebClient | — | — | ✅ **更好** | ✅ |
+| 11 | **工作流: 各节点不同模型** | @Qualifier 多 ChatModel | ✅ 统一网关 | — | ✅ **更灵活** | ✅ |
+| 12 | RAG: 文档分块/向量化 | ETL Pipeline | — | — | ✅ | ✅ |
+| 13 | RAG: 混合搜索 | 依赖 VectorStore | — | — | ⚠️ 部分 | ⚠️ 未来 |
+| 14 | RAG: 重排序 (Rerank) | 需手动实现 | — | — | ⚠️ 需手动 | ⚠️ 未来 |
+| 15 | **RAG: 复杂PDF/表格解析** | ❌ 较弱 | — | docling/unstructured | ✅ **第三方更强** | ⚠️ 未来 |
+| 16 | **模型路由/自动降级** | ❌ 无 | ✅ **AxonHub** | — | ✅ **更专业** | ✅ |
+| 17 | **模型用量计费** | ❌ 无 | ✅ **AxonHub** | — | ✅ **更专业** | ⚠️ 可选项 |
+| 18 | 50+ 内置工具 | @Tool 手动封装 | — | — | ⚠️ 需手动 | ❌ 不需要 |
+| 19 | **Prompt IDE/版本管理** | ❌ 无 | — | Git 管理 | ❌ **不可替代** | ❌ **不需要** |
+| 20 | **LLMOps 仪表盘** | Micrometer + Grafana | AxonHub 计费 | — | ✅ **标准化更强** | ✅ |
+| 21 | Token 用量追踪 | gen_ai.client.token.usage | AxonHub | — | ✅ | ✅ |
+| 22 | 用户反馈/标注 | 需手动实现 | — | — | ⚠️ 需手动 | ❌ 不需要 |
+| 23 | A/B Prompt 测试 | ❌ 无 | — | — | ❌ 不可替代 | ❌ 不需要 |
+| 24 | 函数调用 (Function Calling) | @Tool 注解 | — | — | ✅ **类型安全** | ✅ |
+| 25 | 多 Agent 协作 | Advisors + @Tool | — | — | ✅ | ❌ 不需要 |
+| 26 | 语音转文字 (STT) | AudioTranscription | — | — | ✅ | ❌ 不需要 |
+| 27 | 文字转语音 (TTS) | TextToSpeech | — | — | ✅ | ❌ 不需要 |
+| 28 | 图片生成 | ImageModel | — | — | ✅ | ❌ 不需要 |
+| 29 | 图片理解 (Vision) | ChatClient 多模态 | — | — | ✅ | ✅ |
+| 30 | 内容审核 | ModerationModel | — | — | ✅ | ❌ 不需要 |
+| 31 | SSO/RBAC | Spring Security | — | — | ✅ **更成熟** | ❌ 不需要 |
+| 32 | API 限流 | Spring Cloud Gateway | AxonHub | — | ✅ | ❌ 不需要 |
+| 33 | MCP 集成 | MCP Boot Starter | — | — | ✅ **一级支持** | ⚠️ 未来 |
+| 34 | 批处理 | Spring Batch | — | — | ✅ | ❌ 不需要 |
+
+### 5.2 结论
+
+```
+不可替代且本项目需要的功能:0 个
+不可替代但不需要的功能:   3 个(可视化画布、Prompt IDE、A/B 测试—因为只调 API 不需要 UI)
+可替代且更好的功能:     大多数(类型安全、零网络跳、Spring 原生)
+```
+
+## 7. 分阶段实施计划(详见独立文档)
+
+> 完整的 5 阶段 Task 级计划见 `docs/architecture/2026-07-20-SB3-upgrade-plan.md`
+> 设计概览见 `docs/architecture/SpringBoot3升级+Dify替代实施计划.md`
+
+### 总览
+
+| 阶段 | 内容 | 时间 |
+|------|------|------|
+| Phase 0 | 前置准备(JDK 21、依赖对齐) | 3 天 |
+| Phase 1 | javax → jakarta 迁移 | 5 天 |
+| Phase 2 | 行为适配 + Nashorn 替换 | 4 天 |
+| Phase 3 | Spring AI + AxonHub + Dify 替换 | 5 天 |
+| Phase 4 | 回归验证 + 灰度上线 | 3 天 |
+| **合计** | | **20 人天** |
 
 ### 阶段 1:基础设施(1 周)
 
@@ -409,9 +572,9 @@ Week 4
 
 ---
 
-## 6. 风险评估与应对
+## 8. 风险评估与应对
 
-### 6.1 风险矩阵
+### 8.1 风险矩阵
 
 | 风险 | 概率 | 影响 | 应对措施 |
 |------|------|------|---------|
@@ -440,7 +603,7 @@ Week 4
 
 ---
 
-## 7. 成本收益分析
+## 9. 成本收益分析
 
 ### 7.1 开发成本
 
@@ -475,7 +638,7 @@ Week 4
 
 ---
 
-## 8. 决策树
+## 10. 决策树
 
 ```
 需要替换 Dify?

+ 173 - 5
docs/architecture/SpringBoot3升级+Dify替代实施计划.md

@@ -1,6 +1,6 @@
 # Spring Boot 3.x 升级 + Dify 替代 实施计划
 
-> 版本: v1.0
+> 版本: v1.1
 > 日期: 2026-07-20
 > 范围: cfc-backend (Spring Boot 2.7.18 + Java 8 → Spring Boot 3.4.x + Java 21)
 
@@ -13,7 +13,9 @@
 | Java | 1.8 (JDK 8) | **21 LTS** |
 | Spring Boot | 2.7.18 | **3.4.x** (最新稳定) |
 | AI 框架 | 直调 Dify API | **Spring AI 1.x** |
+| 模型路由 | Dify 内置 | **AxonHub** (`axonhub.iwintrue.com`) |
 | 知识库 | Dify Datasets | **本地 sqlite-vec** |
+| 文档预处理 | Dify 内置 | **Python 专有工具** (docling / unstructured) |
 | 数学引擎 | Nashorn (JDK 15 已移除) | **exp4j / SpEL** |
 | API 文档 | springdoc-openapi 1.8.0 | **springdoc-openapi 2.7+** |
 | ORM | MyBatis-Plus 3.5.3.1 | **MyBatis-Plus 3.5.7+** |
@@ -21,7 +23,82 @@
 
 ---
 
-## 二、总体路线
+## 二、架构变更
+
+### 2.1 分层架构演进
+
+```
+升级前:
+用户 → Spring Boot → Dify API (应用层+路由层一体) → DeepSeek
+                        ↑
+                Dify 同时承担应用逻辑和模型路由
+
+升级后:
+用户 → Spring Boot → AxonHub (模型路由/降级/计费) → DeepSeek/Qwen/...
+           ↑
+    Spring AI (应用层: 对话/RAG/工具/记忆)
+                      
+    文档预处理 → Python 专有工具 (docling/unstructured)
+```
+
+**三层各司其职:**
+
+| 层 | 技术 | 职责 |
+|---|------|------|
+| AI 应用层 | **Spring AI** | 对话、RAG、Tool Calling、记忆、MCP、多模型编排 |
+| 模型路由层 | **AxonHub** | 模型分发、自动降级/回退、用量计费、密钥管理 |
+| 文档预处理 | **专用工具** | 复杂 PDF/表格/多栏布局解析、文档清洗 |
+
+### 2.2 Dify 全功能可替代性结论
+
+| Dify 功能模块 | 替代方案 | 不可替代? | 本项目需要? | 说明 |
+|--------------|---------|-----------|------------|------|
+| 聊天/对话 | Spring AI `ChatClient` | ✅ 替代 | ✅ | 类型安全,省掉 Dify 网络一跳 |
+| 流式响应 | Spring AI `Flux<String>` | ✅ 替代 | ✅ | Reactor 原生 |
+| 对话记忆 | Spring AI `MemoryAdvisor` | ✅ 替代 | ✅ | 可插拔持久化后端 |
+| RAG 知识库 | Spring AI `VectorStore` + ETL | ✅ 替代 | ✅ | 20+ 向量数据库可选 |
+| Function Calling | Spring AI `@Tool` | ✅ 替代 | ✅ | 直接调 Java 方法 |
+| 可视化工作流 | 代码编排 (Java) | **❌ 不可替代** | ❌ 不需要 | 本项目通过 API 调用,不用画布 |
+| Prompt IDE | YAML/代码管理 | **❌ 不可替代** | ❌ 不需要 | Git 管理更专业 |
+| 50+ 内置工具 | 按需写 `@Tool` | ⚠️ 需封装 | ❌ 不需要 | 只用 Function Calling |
+| 模型路由 | **AxonHub** | ✅ 替代 | ✅ | 专用路由网关,比 Dify 内置更强 |
+| 文档预处理 | **Python 专有工具** | ✅ 替代 | ⚠️ 未来 | docling 等更适合复杂 PDF |
+| 多模态 (STT/TTS/Image) | Spring AI 全覆盖 | ✅ 替代 | ❌ 不需要 | — |
+| LLMOps 仪表盘 | Micrometer + Grafana | ✅ 替代 | ✅ | 标准化 OpenTelemetry |
+| 企业安全 (SSO/RBAC) | Spring Security | ✅ 替代 | ❌ 不需要 | Spring 原生 |
+| MCP 集成 | Spring AI MCP 一级支持 | ✅ 替代 | ⚠️ 未来 | Spring AI 2.0 核心特性 |
+
+### 2.3 多模型编排(工作流各节点用不同模型)
+
+Spring AI 支持在同一工作流的不同步骤调用不同模型:
+
+```java
+// 注入多个模型
+@Resource @Qualifier("openAiChatModel")  private ChatModel deepSeek;   // → AxonHub → DeepSeek
+@Resource @Qualifier("dashScopeChatModel") private ChatModel qwen;     // → AxonHub → Qwen
+@Resource @Qualifier("ollamaChatModel")   private ChatModel local;     // → 本地模型
+
+public Result process(String input) {
+    ChatClient client1 = ChatClient.builder(deepSeek).build();
+    ChatClient client2 = ChatClient.builder(qwen).build();
+    ChatClient client3 = ChatClient.builder(local).build();
+
+    // Step 1: DeepSeek 做意图分类(强推理)
+    String intent = client1.prompt().user("分类: " + input).call().content();
+
+    // Step 2: Qwen 做长文本分析(长上下文)
+    String analysis = client2.prompt().system("分析专家").user(intent).call().content();
+
+    // Step 3: 本地模型做格式化(零成本)
+    String output = client3.prompt().user("格式化: " + analysis).call().content();
+}
+```
+
+对比 Dify:Dify 工作流也支持节点级模型切换,但 Spring AI **更灵活** — 不受 50 节点上限限制,无画布约束,可在任意代码分支换模型。配合 AxonHub,所有模型调用走统一网关。
+
+---
+
+## 三、总体路线
 
 ```
 5 阶段 / 20 人天 / 可回退 / 每阶段独立验证
@@ -224,7 +301,47 @@ double result = exp.evaluate();
 </dependencies>
 ```
 
-#### 3.2 ChatClient 封装(替代 AIService)
+#### 3.2 AxonHub 模型路由配置
+
+所有模型调用经 AxonHub 统一路由(模型分发、自动降级、用量计费):
+
+```yaml
+# application.yml — AxonHub 作为统一模型网关
+spring:
+  ai:
+    openai:
+      base-url: https://axonhub.iwintrue.com/v1
+      api-key: ${AXONHUB_API_KEY}       # AxonHub 统一 API Key
+      chat:
+        options:
+          model: deepseek-chat           # 由 AxonHub 路由到对应模型
+
+# 多模型配置(如需在同一个流程中使用不同模型)
+---
+spring:
+  ai:
+    dashscope:                           # Qwen 通过 DashScope 协议接入
+      api-key: ${AXONHUB_API_KEY}
+      chat:
+        options:
+          model: qwen-max
+  config:
+    activate:
+      on-profile: qwen-enabled
+```
+
+AxonHub 接管了原 Dify 的模型路由能力,Spring AI 只需指定 model 参数:
+
+```java
+// 所有模型通过 AxonHub 统一出口
+ChatModel model = openAiChatModel;
+String result = ChatClient.builder(model).build()
+    .prompt().user("hello").call().content();
+// AxonHub 根据 model=deepseek-chat 自动路由到 DeepSeek
+// 故障时自动降级到备用模型
+```
+
+#### 3.3 ChatClient 封装(替代 AIService)
 
 ```java
 // 核心抽象 — 替代原来的 AIService + OkHttp 直调
@@ -234,7 +351,15 @@ public class LLMService {
     @Resource
     private ChatClient.Builder chatClientBuilder;
 
-    // 聊天(带 System Prompt + 会话记忆)
+    @Resource
+    @Qualifier("openAiChatModel")
+    private ChatModel deepSeekModel;         // → AxonHub → DeepSeek
+
+    @Resource
+    @Qualifier("dashScopeChatModel")
+    private ChatModel qwenModel;             // → AxonHub → Qwen
+
+    // 默认聊天(使用配置的默认模型)
     public String chat(String query, String systemPrompt, String conversationId) {
         return chatClientBuilder.build()
             .prompt()
@@ -245,6 +370,18 @@ public class LLMService {
             .content();
     }
 
+    // 指定模型的聊天(工作流各节点可用不同模型)
+    public String chatWithModel(String query, String systemPrompt,
+                                 String conversationId, ChatModel model) {
+        return ChatClient.builder(model).build()
+            .prompt()
+            .system(systemPrompt)
+            .user(query)
+            .advisors(new MessageChatMemoryAdvisor(chatMemory, conversationId, 10))
+            .call()
+            .content();
+    }
+
     // 流式
     public Flux<String> stream(String query, String systemPrompt, String conversationId) {
         return chatClientBuilder.build()
@@ -255,6 +392,19 @@ public class LLMService {
             .stream()
             .content();
     }
+
+    // 多模型工作流示例:不同步骤使用不同模型
+    public WorkflowResult executeMultiModelWorkflow(String input) {
+        // Step 1: DeepSeek 做意图分类(强推理模型)
+        String intent = ChatClient.builder(deepSeekModel).build()
+            .prompt().user("分类用户意图: " + input).call().content();
+
+        // Step 2: Qwen 做长文本分析(长上下文优势)
+        String analysis = ChatClient.builder(qwenModel).build()
+            .prompt().system("分析专家").user(intent).call().content();
+
+        return new WorkflowResult(intent, analysis);
+    }
 }
 ```
 
@@ -271,7 +421,25 @@ public class LLMService {
 | ChatMirrorService | 日志镜像 | 转正为缓存层 | ⭐ 小 |
 | 其余 3 个 Service | 各异 | 具体分析 | ⭐⭐ 中 |
 
-#### 3.4 知识库本地化
+#### 3.5 多模型编排能力
+
+Spring AI 支持工作流各节点使用不同模型,不受 Dify 50 节点上限限制:
+
+```java
+@Resource
+private LLMService llmService;
+
+@PostMapping("/api/ai/workflow/process")
+public Result process(@RequestBody WorkflowRequest req) {
+    // 不同节点注入不同模型
+    // 所有模型调用统一走 AxonHub 网关
+    return Result.success(llmService.executeMultiModelWorkflow(req.getInput()));
+}
+```
+
+配合 AxonHub,可在不修改代码的前提下切换任何 AxonHub 支持的模型。
+
+#### 3.6 知识库本地化
 
 Dify Datasets 替代方案: