refactor: move RAG trace classes to argtrace package and update SQL schema

- Rename org.ruoyi.trace package to org.ruoyi.argtrace for RagTraceNodeTypes
  and RagTracePayloadBuilder; fix package declarations and update imports in
  ChatServiceFacade and KnowledgeRetrievalServiceImpl
- Remove obsolete RagTracePayloadBuilderTest
- Switch supervisor response strategy from SUMMARY to LAST in ChatServiceFacade
- Refresh docs/script/sql/ruoyi-ai.sql with the latest full schema dump
- Add live demo links (admin panel, user frontend, commercial edition) to
  README.md and README_ZH.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
ageerle
2026-08-04 17:43:28 +08:00
parent 0a08c489be
commit 9d439d1dcf
8 changed files with 810 additions and 91 deletions

View File

@@ -23,7 +23,13 @@
</div> </div>
## 🚀 Live Demo
| Service | URL | Default Account |
|---|---|---|
| Admin Panel | http://129.226.199.247:25666 | admin / admin123 |
| User Frontend | http://129.226.199.247:25137 | admin / admin123 |
| Commercial Edition | https://web.ruoyiai.chat | WeChat QR code login |
## ✨ Core Features ## ✨ Core Features

View File

@@ -23,6 +23,14 @@
</div> </div>
## 🚀 演示地址
| 服务 | 访问地址 | 默认账号 |
|---|---|---|
| 管理端 | http://129.226.199.247:25666 | admin / admin123 |
| 用户端 | http://129.226.199.247:25137 | admin / admin123 |
| 商业版 | https://web.ruoyiai.chat | 微信扫码登录 |
## ✨ 核心亮点 ## ✨ 核心亮点
| 模块 | 现有能力 | 模块 | 现有能力

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
package org.ruoyi.trace; package org.ruoyi.argtrace;
/** /**
* RAG trace 业务与节点类型常量 * RAG trace 业务与节点类型常量

View File

@@ -1,4 +1,4 @@
package org.ruoyi.trace; package org.ruoyi.argtrace;
import org.ruoyi.common.chat.domain.dto.request.ChatRequest; import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo; import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;

View File

@@ -80,8 +80,8 @@ import org.ruoyi.service.chat.impl.memory.PersistentChatMemoryStore;
import org.ruoyi.service.knowledge.IKnowledgeInfoService; import org.ruoyi.service.knowledge.IKnowledgeInfoService;
import org.ruoyi.service.retrieval.KnowledgeRetrievalService; import org.ruoyi.service.retrieval.KnowledgeRetrievalService;
import org.ruoyi.service.knowledge.retriever.CustomVectorRetriever; import org.ruoyi.service.knowledge.retriever.CustomVectorRetriever;
import org.ruoyi.trace.RagTraceNodeTypes; import org.ruoyi.argtrace.RagTraceNodeTypes;
import org.ruoyi.trace.RagTracePayloadBuilder; import org.ruoyi.argtrace.RagTracePayloadBuilder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
@@ -348,7 +348,7 @@ public class ChatServiceFacade implements IChatService {
.subAgents(skillsAgent, searchAgent, sqlAgent, chartGenerationAgent, echartsAgent, chitChatAgent) .subAgents(skillsAgent, searchAgent, sqlAgent, chartGenerationAgent, echartsAgent, chitChatAgent)
.supervisorContext("仅当请求是问候或简单闲聊、不需要任何数据、搜索、技能或图表时,才使用 chitChatAgent;" .supervisorContext("仅当请求是问候或简单闲聊、不需要任何数据、搜索、技能或图表时,才使用 chitChatAgent;"
+ "其余情况必须使用对应的专业 Agent") + "其余情况必须使用对应的专业 Agent")
.responseStrategy(SupervisorResponseStrategy.SUMMARY); .responseStrategy(SupervisorResponseStrategy.LAST);
SupervisorAgent supervisor = supervisorBuilder.build(); SupervisorAgent supervisor = supervisorBuilder.build();
// 知识库增强:智能体绑定了知识库时,对 supervisor 输入做一次 RAG 增强(全程唯一一次检索) // 知识库增强:智能体绑定了知识库时,对 supervisor 输入做一次 RAG 增强(全程唯一一次检索)

View File

@@ -21,8 +21,8 @@ import org.ruoyi.mapper.knowledge.KnowledgeFragmentMapper;
import org.ruoyi.service.rerank.RerankModelService; import org.ruoyi.service.rerank.RerankModelService;
import org.ruoyi.service.retrieval.KnowledgeRetrievalService; import org.ruoyi.service.retrieval.KnowledgeRetrievalService;
import org.ruoyi.service.vector.VectorStoreService; import org.ruoyi.service.vector.VectorStoreService;
import org.ruoyi.trace.RagTraceNodeTypes; import org.ruoyi.argtrace.RagTraceNodeTypes;
import org.ruoyi.trace.RagTracePayloadBuilder; import org.ruoyi.argtrace.RagTracePayloadBuilder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.*; import java.util.*;

View File

@@ -1,78 +0,0 @@
package org.ruoyi.trace;
import org.junit.jupiter.api.Test;
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
import org.ruoyi.domain.bo.vector.QueryVectorBo;
import org.ruoyi.domain.vo.knowledge.KnowledgeRetrievalVo;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class RagTracePayloadBuilderTest {
@Test
void chatRequestSummaryShouldHandleNullsAndAvoidPromptBody() {
ChatRequest request = new ChatRequest();
request.setSessionId(100L);
request.setModel("qwen-plus");
request.setKnowledgeId("200");
request.setContent("secret prompt body");
ChatModelVo model = new ChatModelVo();
model.setProviderCode("dashscope");
request.setChatModelVo(model);
String payload = RagTracePayloadBuilder.chatRequestSummary(request);
assertTrue(payload.contains("\"sessionId\":100"));
assertTrue(payload.contains("\"contentLength\":18"));
assertTrue(payload.contains("\"providerCode\":\"dashscope\""));
assertFalse(payload.contains("secret prompt body"));
}
@Test
void retrievalInputSummaryShouldUseSummaryOnly() {
QueryVectorBo query = new QueryVectorBo();
query.setKid("200");
query.setQuery("private retrieval query");
query.setMaxResults(5);
query.setEnableRerank(true);
query.setRerankModelName("gte-rerank");
query.setRerankTopN(null);
String payload = RagTracePayloadBuilder.retrievalInputSummary(query);
assertTrue(payload.contains("\"kid\":\"200\""));
assertTrue(payload.contains("\"queryLength\":23"));
assertTrue(payload.contains("\"enableRerank\":true"));
assertFalse(payload.contains("private retrieval query"));
}
@Test
void retrievalOutputSummaryShouldAvoidFragmentContent() {
KnowledgeRetrievalVo result = new KnowledgeRetrievalVo();
result.setId("fragment-1");
result.setDocId("doc-1");
result.setIdx(1);
result.setScore(0.85);
result.setContent("sensitive knowledge fragment");
String payload = RagTracePayloadBuilder.retrievalOutputSummary(List.of(result));
assertTrue(payload.contains("\"resultCount\":1"));
assertTrue(payload.contains("\"contentLength\":28"));
assertTrue(payload.contains("\"fragment-1\""));
assertFalse(payload.contains("sensitive knowledge fragment"));
}
@Test
void summariesShouldAcceptNullValuesWithoutMapOfNpe() {
assertTrue(RagTracePayloadBuilder.chatRequestSummary(null).contains("\"requestPresent\":false"));
assertTrue(RagTracePayloadBuilder.retrievalInputSummary(null).contains("\"queryPresent\":false"));
assertTrue(RagTracePayloadBuilder.retrievalOutputSummary(null).contains("\"resultCount\":0"));
assertTrue(RagTracePayloadBuilder.rerankInputSummary(null, 0, null).contains("\"candidateCount\":0"));
}
}