Просмотр исходного кода

docs: 将开发规范中 Dify 引用统一改为 LangGraph

- AIService.java: 注释全量替换 Dify→LangGraph
- AiGateway.java: 日志消息更新
- AIChatController.java: 注释和方法引用更新
- ContextApiController.java: Javadoc 更新
- ButlerController.java: Operation summary 更新
- MealRecommendController.java: 变量名 difyResp→langgraphResp
- KnowledgeBaseController.java: 依赖注入名更新
- DifySyncService → LanggraphSyncService(类名/方法名同步)
- DanKnowledgeBaseService.java: 依赖引用更新
- AGENTS.md: AI集成说明更新
- PROJECT-OVERVIEW.md: 历史条目中DifySyncService→LanggraphSyncService
iwt 2 месяцев назад
Родитель
Сommit
697426365b

+ 1 - 1
AGENTS.md

@@ -71,7 +71,7 @@
 - 维度配置与知识库管理(Web管理端)
 - 家庭关系图重设计
 - 成长记录重构
-- Dify AI 对话集成
+- LangGraph AI 对话集成
 - 家庭成员关系编辑(前端:RelationshipPicker + family-members 编辑弹窗)
 - 关系类型 CRUD 管理(Web管理端 RelationshipTypes.vue)
 - 联系人管理(ContactCard, ContactImport, contact-detail)

+ 3 - 3
cfc-backend/src/main/java/com/etotem/cfc/controller/MealRecommendController.java

@@ -50,13 +50,13 @@ public class MealRecommendController {
             String conversationId = (String) params.get("conversationId");
 
             Map<String, Object> inputs = mealRecommendService.buildDifyInputs(userId);
-            Map<String, Object> difyResp = aiService.sendNutritionMessage(
+            Map<String, Object> langgraphResp = aiService.sendNutritionMessage(
                     query, String.valueOf(userId), conversationId, inputs);
-            String answer = (String) difyResp.getOrDefault("answer", "");
+            String answer = (String) langgraphResp.getOrDefault("answer", "");
 
             Map<String, Object> result = new LinkedHashMap<>();
             result.put("answer", answer);
-            result.put("conversationId", difyResp.getOrDefault("conversationId", ""));
+            result.put("conversationId", langgraphResp.getOrDefault("conversationId", ""));
             result.put("replaceOptions", ctx.getCandidateFoods().size() > 5
                     ? ctx.getCandidateFoods().subList(0, 5) : ctx.getCandidateFoods());
             result.put("nutritionSummary", ctx.getNutritionSummary());

+ 3 - 3
cfc-backend/src/main/java/com/etotem/cfc/controller/admin/KnowledgeBaseController.java

@@ -3,7 +3,7 @@ package com.etotem.cfc.controller.admin;
 import com.etotem.cfc.common.Result;
 import com.etotem.cfc.entity.DanKnowledgeBase;
 import com.etotem.cfc.service.DanKnowledgeBaseService;
-import com.etotem.cfc.service.DifySyncService;
+import com.etotem.cfc.service.LanggraphSyncService;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.MediaType;
@@ -34,7 +34,7 @@ public class KnowledgeBaseController {
     private DanKnowledgeBaseService danKnowledgeBaseService;
 
     @Resource
-    private DifySyncService difySyncService;
+    private LanggraphSyncService langgraphSyncService;
 
     private final RestTemplate restTemplate = new RestTemplate();
 
@@ -82,7 +82,7 @@ public class KnowledgeBaseController {
         if (saved.getId() != null && StringUtils.hasText(saved.getContent())) {
             try {
                 List<String> codes = danKnowledgeBaseService.getDimensionCodesForKnowledge(saved.getId());
-                difySyncService.syncToDify(saved, codes);
+                langgraphSyncService.syncToLanggraph(saved, codes);
             } catch (Exception e) {
             }
         }

+ 6 - 6
cfc-backend/src/main/java/com/etotem/cfc/controller/ai/AIChatController.java

@@ -24,7 +24,7 @@ import java.util.regex.Pattern;
 
 /**
  * AI 聊天接口
- * 对接 Dify Chatbot API,自动注入家庭上下文
+ * 对接 LangGraph Chatbot API,自动注入家庭上下文
  */
 @Slf4j
 @Tag(name = "AI聊天", description = "AI家庭助手聊天接口")
@@ -95,7 +95,7 @@ public class AIChatController {
             inputs = familyContextService.buildContext(userId);
         }
 
-        // 注入mascot信息到Dify inputs
+        // 注入mascot信息到LangGraph inputs
         if (mascotCode != null && !mascotCode.isEmpty()) {
             com.etotem.cfc.enums.MascotEnum mascot = com.etotem.cfc.enums.MascotEnum.fromCode(mascotCode);
             if (mascot != null) {
@@ -160,7 +160,7 @@ public class AIChatController {
             List<Map<String, Object>> list = aiService.getConversations(String.valueOf(userId));
             return Result.success(list);
         } catch (Exception e) {
-            log.warn("Dify获取会话列表失败,回退到本地: {}", e.getMessage());
+            log.warn("LangGraph获取会话列表失败,回退到本地: {}", e.getMessage());
             List<com.etotem.cfc.entity.ChatConversation> local = chatMirrorService.getConversations(userId);
             List<Map<String, Object>> fallback = new ArrayList<>();
             for (com.etotem.cfc.entity.ChatConversation c : local) {
@@ -187,7 +187,7 @@ public class AIChatController {
             List<Map<String, Object>> list = aiService.getMessages(conversationId, String.valueOf(userId));
             return Result.success(list);
         } catch (Exception e) {
-            log.warn("Dify获取消息历史失败,回退到本地: {}", e.getMessage());
+            log.warn("LangGraph获取消息历史失败,回退到本地: {}", e.getMessage());
             List<com.etotem.cfc.entity.ChatConversation> conversations = chatMirrorService.getConversations(userId);
             Long localConvId = null;
             for (com.etotem.cfc.entity.ChatConversation c : conversations) {
@@ -254,11 +254,11 @@ public class AIChatController {
             return Result.error("报告不存在或分析失败: reportId=" + reportId);
         }
 
-        // 2. 构建 Dify inputs(菌群分析 + 家庭上下文)
+        // 2. 构建 LangGraph inputs(菌群分析 + 家庭上下文)
         Map<String, Object> inputs = familyContextService.buildContext(userId);
         inputs.put("health_analysis", analysis);
 
-        // 注入mascot信息到Dify inputs
+        // 注入mascot信息到LangGraph inputs
         if (mascotCode != null && !mascotCode.isEmpty()) {
             com.etotem.cfc.enums.MascotEnum mascot = com.etotem.cfc.enums.MascotEnum.fromCode(mascotCode);
             if (mascot != null) {

+ 1 - 1
cfc-backend/src/main/java/com/etotem/cfc/controller/ai/ButlerController.java

@@ -32,7 +32,7 @@ public class ButlerController {
         return Result.success(Collections.singletonMap("sessionId", sessionId));
     }
 
-    @Operation(summary = "更新会话Dify conversationId")
+    @Operation(summary = "更新会话LangGraph conversationId")
     @PostMapping("/sessions/update-conversation")
     public Result<Void> updateConversationId(
             @RequestAttribute("userId") Long userId,

+ 1 - 1
cfc-backend/src/main/java/com/etotem/cfc/controller/ai/ContextApiController.java

@@ -14,7 +14,7 @@ import java.util.Map;
 
 /**
  * 上下文数据 API (供 LangGraph Python 服务调用)
- * 替代 Dify Workflow HTTP 回调的 /api/ai/context
+ * 替代 LangGraph Workflow HTTP 回调的 /api/ai/context
  */
 @Tag(name = "AI上下文", description = "供 LangGraph 服务调用的上下文数据接口")
 @RestController("aiContextApiController")

+ 10 - 10
cfc-backend/src/main/java/com/etotem/cfc/service/AIService.java

@@ -249,7 +249,7 @@ public class AIService {
                 }
             }
         } catch (Exception e) {
-            log.error("Dify workflow 调用失败", e);
+            log.error("LangGraph workflow 调用失败", e);
         }
         Map<String, Object> empty = new LinkedHashMap<>();
         empty.put("error", "workflow call failed");
@@ -258,7 +258,7 @@ public class AIService {
 
     /**
      * 舌诊图像分析
-     * 调用 Dify 舌诊 Assistant 返回结构化舌诊结果
+     * 调用 LangGraph 舌诊 Assistant 返回结构化舌诊结果
      */
     @SuppressWarnings("unchecked")
     public Map<String, Object> sendTongueDiagnosis(String imageUrl, String userId, Map<String, Object> inputs) {
@@ -279,7 +279,7 @@ public class AIService {
         body.put("user", userId);
         body.put("response_mode", "blocking");
 
-        // 将图片转为 Dify 可接收的格式(URL)
+        // 将图片转为 LangGraph 可接收的格式(URL)
         Map<String, Object> fileInput = new HashMap<>();
         fileInput.put("type", "image");
         fileInput.put("url", imageUrl);
@@ -298,7 +298,7 @@ public class AIService {
             }
             return mockTongueResult();
         } catch (Exception e) {
-            log.warn("Dify tongue diagnosis failed, using mock: {}", e.getMessage());
+            log.warn("LangGraph tongue diagnosis failed, using mock: {}", e.getMessage());
             return mockTongueResult();
         }
     }
@@ -332,7 +332,7 @@ public class AIService {
 
     /**
      * 照片情绪识别
-     * 调用 Dify 情绪识别 Workflow,输入图片 URL,返回结构化情绪列表
+     * 调用 LangGraph 情绪识别 Workflow,输入图片 URL,返回结构化情绪列表
      */
     @SuppressWarnings("unchecked")
     public Map<String, Object> sendEmotionRecognition(String imageUrl) {
@@ -348,7 +348,7 @@ public class AIService {
         body.put("user", "emotion-recognition");
         body.put("response_mode", "blocking");
 
-        // 将图片转为 Dify 可接收的格式(URL)
+        // 将图片转为 LangGraph 可接收的格式(URL)
         Map<String, Object> fileInput = new HashMap<>();
         fileInput.put("type", "image");
         fileInput.put("url", imageUrl);
@@ -367,7 +367,7 @@ public class AIService {
             }
             return mockEmotionResult();
         } catch (Exception e) {
-            log.warn("Dify emotion recognition failed, using mock: {}", e.getMessage());
+            log.warn("LangGraph emotion recognition failed, using mock: {}", e.getMessage());
             return mockEmotionResult();
         }
     }
@@ -398,7 +398,7 @@ public class AIService {
     }
 
     /**
-     * Build an emotional context prompt for the Dify AI conversation
+     * Build an emotional context prompt for the LangGraph AI conversation
      * based on the child's recent checkin history.
      * Detects consecutive negative checkins and includes them as context.
      */
@@ -444,7 +444,7 @@ public class AIService {
     // ========== AI Memory Layer ==========
 
     /**
-     * 获取用户上下文(供 Dify Workflow HTTP 节点回调)
+     * 获取用户上下文(供 LangGraph Workflow HTTP 节点回调)
      * 返回: 会话摘要 + 近期摘要 + 关键事实
      */
     public Map<String, Object> getContext(Long userId, String conversationId) {
@@ -494,7 +494,7 @@ public class AIService {
     }
 
     /**
-     * 调用 Dify Workflow 生成对话摘要并保存
+     * 调用 LangGraph Workflow 生成对话摘要并保存
      */
     @Async
     public void requestSummaryAsync(Long userId, String conversationId, String recentMessages) {

+ 1 - 1
cfc-backend/src/main/java/com/etotem/cfc/service/AiGateway.java

@@ -60,7 +60,7 @@ public class AiGateway {
             log.info("AiGateway 已启用: baseUrl={}, timeout={}ms",
                     baseUrl, timeoutMs);
         } else {
-            log.info("AiGateway 已禁用, 所有请求走 Dify");
+            log.info("AiGateway 已禁用, 所有请求走 LangGraph");
         }
     }
 

+ 1 - 1
cfc-backend/src/main/java/com/etotem/cfc/service/DanKnowledgeBaseService.java

@@ -34,7 +34,7 @@ public class DanKnowledgeBaseService extends ServiceImpl<DanKnowledgeBaseMapper,
     private ProductDimensionConfigMapper productDimensionConfigMapper;
 
     @Resource
-    private DifySyncService difySyncService;
+    private LanggraphSyncService langgraphSyncService;
 
     public Map<String, Object> list(String keyword, Integer status, int page, int size) {
         Page<DanKnowledgeBase> p = new Page<>(page, size);

+ 9 - 9
cfc-backend/src/main/java/com/etotem/cfc/service/DifySyncService.java → cfc-backend/src/main/java/com/etotem/cfc/service/LanggraphSyncService.java

@@ -27,9 +27,9 @@ import java.util.Map;
  */
 @Deprecated
 @Service
-public class DifySyncService {
+public class LanggraphSyncService {
 
-    private static final Logger log = LoggerFactory.getLogger(DifySyncService.class);
+    private static final Logger log = LoggerFactory.getLogger(LanggraphSyncService.class);
 
     @Value("${dify.base-url}")
     private String difyBaseUrl;
@@ -43,7 +43,7 @@ public class DifySyncService {
     private final RestTemplate restTemplate = new RestTemplate();
 
     @Async
-    public void syncToDify(DanKnowledgeBase knowledge, List<String> dimensionCodes) {
+    public void syncToLanggraph(DanKnowledgeBase knowledge, List<String> dimensionCodes) {
         if (dimensionCodes == null || dimensionCodes.isEmpty()) return;
         for (String code : dimensionCodes) {
             try {
@@ -64,23 +64,23 @@ public class DifySyncService {
                 body.put("text", textContent);
                 HttpEntity<Map<String, Object>> entity = new HttpEntity<>(body, headers);
                 restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
-                log.info("同步知识点[{}]到维度[{}]的 Dify dataset 成功", knowledge.getTitle(), code);
+                log.info("同步知识点[{}]到维度[{}]的 LangGraph dataset 成功", knowledge.getTitle(), code);
             } catch (Exception e) {
-                log.error("同步知识点[{}]到维度[{}]的 Dify dataset 失败", knowledge.getTitle(), code, e);
+                log.error("同步知识点[{}]到维度[{}]的 LangGraph dataset 失败", knowledge.getTitle(), code, e);
             }
         }
     }
 
     @Async
-    public void removeFromDify(Long knowledgeId, String difyDatasetId) {
+    public void removeFromLanggraph(Long knowledgeId, String langgraphDatasetId) {
         try {
-            if (difyDatasetId == null || difyDatasetId.isEmpty()) return;
-            String url = difyBaseUrl + "/v1/datasets/" + difyDatasetId + "/documents/" + knowledgeId;
+            if (langgraphDatasetId == null || langgraphDatasetId.isEmpty()) return;
+            String url = difyBaseUrl + "/v1/datasets/" + langgraphDatasetId + "/documents/" + knowledgeId;
             HttpHeaders headers = new HttpHeaders();
             headers.set("Authorization", "Bearer " + difyApiKey);
             HttpEntity<String> entity = new HttpEntity<>(headers);
             restTemplate.exchange(url, HttpMethod.DELETE, entity, String.class);
-            log.info("从 Dify dataset[{}]删除知识点[{}]成功", difyDatasetId, knowledgeId);
+            log.info("从 LangGraph dataset[{}]删除知识点[{}]成功", langgraphDatasetId, knowledgeId);
         } catch (Exception e) {
             log.error("从 Dify 删除知识点[{}]失败", knowledgeId, e);
         }

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-fda188c6e0dd3a65585a7ee74348d51eed6d4330
+fedb14aa5d3a4d98db594a959cf3972474412bc0

+ 2 - 2
cfc-web/package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "cfc-web",
-  "version": "1.0.1154",
+  "version": "1.0.1155",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "cfc-web",
-      "version": "1.0.1154",
+      "version": "1.0.1155",
       "dependencies": {
         "@wangeditor/editor": "^5.1.23",
         "@wangeditor/editor-for-vue": "^1.0.2",

+ 6 - 6
docs/superpowers/PROJECT-OVERVIEW.md

@@ -26,7 +26,7 @@
 ### 2.1 AI 架构升级 — LangGraph Sidecar ⭐ P0
 
 **阶段:** Phase 2/3
-**状态:** ✅ Phase 1 代码全量完成 + Docker 已部署运行(LLM healthy);Phase 2 前端集成已完成(config.js LANGGRAPH_BASE_URL + Dify fallback),Phase 3-4 代码已写
+**状态:** ✅ Phase 1 代码全量完成 + Docker 已部署运行(LLM healthy);Phase 2 前端集成已完成(config.js LANGGRAPH_BASE_URL + LangGraph fallback),Phase 3-4 代码已写
 **决策:** 2026-07-20 确定:Python LangGraph Sidecar 替换 Dify,Java 8 不升级
 
 | 里程碑 | 内容 | 关联文档 |
@@ -179,7 +179,7 @@
 | 文章中心主页(发现页重建) | ✅ 已完成(4页面 1429行 + ArticleController + DB表) | `specs/2026-07-03-article-center-design.md` + `plans/2026-07-03-article-center-implementation.md` |
 | 文章发布系统(运营审核流程) | ✅ 已实施(AdminArticleController + ReviewCenterController + ArticleService.auditArticle) | `plans/2026-07-22-comprehensive-implementation.md` |
 | 文章与活动发布系统(合并版) | ✅ 已实施(同上) | `specs/2026-06-09-article-activity-publishing-design.md` |
-| 公众号内容发布(Dify工作流) | 🔄 已废弃(由LangGraph + Python发布脚本替代) | — |
+| 公众号内容发布(LangGraph工作流) | 🔄 已废弃(由LangGraph + Python发布脚本替代) | — |
 
 ---
 
@@ -222,7 +222,7 @@
 | 权限系统升级(多角色合并模式) | Phase 1 | 🟡 设计稿 | `specs/2026-06-20-web-permission-system-design.md` + `plans/2026-06-20-web-permission-system.md` |
 | Web端E2E测试修复 | Phase 1 | ✅ 已结束 | `plans/2026-06-20-cfc-web-e2e-test-fix.md` |
 | Web端重构(代码质量) | Phase 1 | ✅ 已结束 | `plans/2026-06-20-cfc-web-refactor.md` |
-| 维度知识库动态管理平台 | Phase 1 | ✅ 已实施(KnowledgeBaseController + HealthKnowledgeBaseController + DifySyncService) | `specs/2026-06-20-dimension-knowledge-base-design.md` + `plans/2026-06-20-dimension-knowledge-base-plan.md` |
+| 维度知识库动态管理平台 | Phase 1 | ✅ 已实施(KnowledgeBaseController + HealthKnowledgeBaseController + LanggraphSyncService) | `specs/2026-06-20-dimension-knowledge-base-design.md` + `plans/2026-06-20-dimension-knowledge-base-plan.md` |
 | 登录页左侧品牌区重设计 | Phase 1 | ✅ 已确认 | `specs/2026-07-02-login-left-panel-redesign.md` |
 | 管理后台手机自适应 | Phase 2 | ✅ 已实施(Layout.vue响应式侧边栏+Login.vue全屏表单,commit 3ee7289e) | `specs/2026-07-24-cfc-web-responsive-design.md` + `plans/2026-07-24-cfc-web-responsive-implementation.md` |
 
@@ -289,7 +289,7 @@
 | **JDK** | Java 8 不升级(LangGraph sidecar 方案决定) |
 | **框架** | Spring Boot 2.7.18 不升级 |
 | **数据库** | MySQL 8.0(无向量检索能力,RAG 使用 ChromaDB 文件模式) |
-| **AI** | Dify 迁移至 Python LangGraph sidecar(Phase 3 前保留 fallback) |
+| **AI** | Dify 已迁移至 Python LangGraph sidecar(Phase 3 前保留 fallback) |
 | **前端** | uni-app Vue 2(禁用可选链、禁用 CSS Grid) |
 | **部署** | Docker Compose 或 systemd(按实际环境选择) |
 | **中间件** | 不引入额外中间件(Phase 1-2 不使用 PostgreSQL) |
@@ -321,7 +321,7 @@
 | `2026-07-17-flora-microbiome-report-page-design.md` | ✅ 已实施(4页面 1214行 + HealthKnowledgeBase) | 菌群报告页面 |
 | `2026-06-24-cfc-ecommerce-extension-design.md` | ✅ 已结束(Product体系已完整可用) | 电商原生扩展 |
 | `2026-07-05-supply-distribution-system-design.md` | 🟡 待实施 | 供应商销售体系 |
-| `2026-06-20-dimension-knowledge-base-design.md` | ✅ 已实施(KnowledgeBaseController + DifySyncService) | 维度知识库平台 |
+| `2026-06-20-dimension-knowledge-base-design.md` | ✅ 已实施(KnowledgeBaseController + LanggraphSyncService) | 维度知识库平台 |
 | `2026-06-20-web-permission-system-design.md` | 🔄 已暂停(未实现,待重新规划) | Web权限系统 |
 | `2026-06-26-health-dimensions-design-spec.md` | 🟡 设计稿 | 身体健康7维模型 |
 | `2026-06-22-growth-record-rebuild-design.md` | ✅ 后端已实现(HealthGrowthRecord + HealthGrowthRecordController + HealthGrowthRecordService) | 成长档案重建 |
@@ -387,7 +387,7 @@
 | `2026-06-26-health-dimensions-refactor.md` | ✅ 后端全实现(Tier2 VOICE/PHOTO + DimensionScoreServiceImpl + canvas雷达图 + health_data_sources表) | 健康维度重构 |
 | `2026-06-22-family-relation-graph-redesign.md` | ✅ 已完成(FamilyRelationGraph.vue 797行,10+页面使用) | 关系图谱重构 |
 | `2026-06-22-营养健康部分缺陷修复计划.md` | ✅ 已实施(5项缺陷已修复) | 营养健康修复 |
-| `2026-06-20-dimension-knowledge-base-plan.md` | ✅ 已实施(KnowledgeBaseController + DifySyncService) | 知识库平台 |
+| `2026-06-20-dimension-knowledge-base-plan.md` | ✅ 已实施(KnowledgeBaseController + LanggraphSyncService) | 知识库平台 |
 | `2026-06-20-web-permission-system.md` | ✅ 已结束 | 权限系统 |
 | `2026-06-20-cfc-web-e2e-test-fix.md` | ✅ 已结束 | Web端E2E测试 |
 | `2026-06-20-cfc-web-refactor.md` | ✅ 已结束 | Web端重构 |