getSupportedFactories() {
+ return new ArrayList<>(applicationContext.getBeansOfType(RerankModelService.class)
+ .keySet());
+ }
+
+ /**
+ * 创建具体的模型实例
+ * 根据提供的工厂名称和配置信息创建并配置模型实例
+ *
+ * @param factory 工厂名称,用于标识模型类型(providerCode)
+ * @param config 模型配置信息
+ * @return RerankModelService 配置好的模型实例
+ * @throws IllegalArgumentException 当无法获取指定的模型实例时抛出
+ */
+ private RerankModelService createModelInstance(String factory, ChatModelVo config) {
+ try {
+ // 优先尝试使用 providerCode + "Rerank" 作为 Bean 名称
+ // 例如:zhipu -> zhipuRerank,jina -> jinaRerank
+ String rerankBeanName = factory + "Rerank";
+ RerankModelService model = applicationContext.getBean(rerankBeanName, RerankModelService.class);
+ model.configure(config);
+ log.info("成功创建重排序模型: factory={}, modelName={}", rerankBeanName, config.getModelName());
+ return model;
+ } catch (NoSuchBeanDefinitionException e) {
+ // 如果找不到,尝试使用原始的 providerCode
+ try {
+ RerankModelService model = applicationContext.getBean(factory, RerankModelService.class);
+ model.configure(config);
+ log.info("成功创建重排序模型: factory={}, modelName={}", factory, config.getModelName());
+ return model;
+ } catch (NoSuchBeanDefinitionException ex) {
+ throw new IllegalArgumentException("获取不到重排序模型: " + factory + " 或 " + factory + "Rerank", ex);
+ }
+ }
+ }
+}
diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/observability/ChatModelListenerProvider.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/observability/ChatModelListenerProvider.java
new file mode 100644
index 00000000..cb791740
--- /dev/null
+++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/observability/ChatModelListenerProvider.java
@@ -0,0 +1,41 @@
+package org.ruoyi.observability;
+
+import cn.hutool.core.collection.CollUtil;
+import dev.langchain4j.model.chat.listener.ChatModelListener;
+import dev.langchain4j.model.embedding.listener.EmbeddingModelListener;
+import lombok.Getter;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * LangChain4j 监听器共享提供者。
+ *
+ * 供所有 {@link dev.langchain4j.model.chat.StreamingChatModel} 构建器使用,
+ * 将可观测性监听器注入到模型实例中。
+ *
+ * @author evo
+ */
+@Component
+@Getter
+@Lazy
+public class ChatModelListenerProvider {
+
+ private final List chatModelListeners;
+ private final List embeddingModelListeners;
+
+ public ChatModelListenerProvider(@Nullable List chatModelListeners,
+ @Nullable List embeddingModelListeners) {
+ if (CollUtil.isEmpty(chatModelListeners)) {
+ chatModelListeners = Collections.emptyList();
+ }
+ if (CollUtil.isEmpty(embeddingModelListeners)) {
+ embeddingModelListeners = Collections.emptyList();
+ }
+ this.chatModelListeners = chatModelListeners;
+ this.embeddingModelListeners = embeddingModelListeners;
+ }
+}
diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/observability/EmbeddingModelListenerProvider.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/observability/EmbeddingModelListenerProvider.java
new file mode 100644
index 00000000..c404bd10
--- /dev/null
+++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/observability/EmbeddingModelListenerProvider.java
@@ -0,0 +1,34 @@
+package org.ruoyi.observability;
+
+import cn.hutool.core.collection.CollUtil;
+import dev.langchain4j.model.embedding.listener.EmbeddingModelListener;
+import lombok.Getter;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * EmbeddingModel 监听器共享提供者。
+ *
+ * 供所有 {@link dev.langchain4j.model.embedding.EmbeddingModel} 构建器使用,
+ * 将可观测性监听器注入到模型实例中。
+ *
+ * @author evo
+ */
+@Component
+@Getter
+@Lazy
+public class EmbeddingModelListenerProvider {
+
+ private final List embeddingModelListeners;
+
+ public EmbeddingModelListenerProvider(@Nullable List embeddingModelListeners) {
+ if (CollUtil.isEmpty(embeddingModelListeners)) {
+ embeddingModelListeners = Collections.emptyList();
+ }
+ this.embeddingModelListeners = embeddingModelListeners;
+ }
+}
diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/observability/LangChain4jObservabilityConfig.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/observability/LangChain4jObservabilityConfig.java
new file mode 100644
index 00000000..3ada96b0
--- /dev/null
+++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/observability/LangChain4jObservabilityConfig.java
@@ -0,0 +1,129 @@
+package org.ruoyi.observability;
+
+import dev.langchain4j.Experimental;
+import dev.langchain4j.mcp.client.McpClientListener;
+import dev.langchain4j.model.chat.listener.ChatModelListener;
+import dev.langchain4j.model.embedding.listener.EmbeddingModelListener;
+import dev.langchain4j.observability.api.AiServiceListenerRegistrar;
+import dev.langchain4j.observability.api.listener.*;
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+
+/**
+ * LangChain4j 可观测性配置类。
+ *
+ * 负责注册所有 langchain4j 的监听器:
+ *
+ * - {@link AiServiceListener} - AI服务级别的事件监听器(通过 AiServiceListenerRegistrar 注册)
+ * - {@link ChatModelListener} - ChatModel 级别的监听器(注入到模型构建器)
+ * - {@link EmbeddingModelListener} - EmbeddingModel 级别的监听器(注入到模型构建器)
+ *
+ *
+ * @author evo
+ */
+@Configuration
+@RequiredArgsConstructor
+@Slf4j
+public class LangChain4jObservabilityConfig {
+
+ private final AiServiceListenerRegistrar registrar = AiServiceListenerRegistrar.newInstance();
+
+ /**
+ * 注册 AI 服务级别的事件监听器
+ */
+ @PostConstruct
+ public void registerAiServiceListeners() {
+ log.info("正在注册 LangChain4j AI Service 事件监听器...");
+ registrar.register(
+ new MyAiServiceStartedListener(),
+ new MyAiServiceRequestIssuedListener(),
+ new MyAiServiceResponseReceivedListener(),
+ new MyAiServiceCompletedListener(),
+ new MyAiServiceErrorListener(),
+ new MyInputGuardrailExecutedListener(),
+ new MyOutputGuardrailExecutedListener(),
+ new MyToolExecutedEventListener()
+ );
+ log.info("LangChain4j AI Service 事件监听器注册完成");
+ }
+
+ // ==================== AI Service 监听器 Beans ====================
+
+ @Bean
+ public AiServiceStartedListener aiServiceStartedListener() {
+ return new MyAiServiceStartedListener();
+ }
+
+ @Bean
+ public AiServiceRequestIssuedListener aiServiceRequestIssuedListener() {
+ return new MyAiServiceRequestIssuedListener();
+ }
+
+ @Bean
+ public AiServiceResponseReceivedListener aiServiceResponseReceivedListener() {
+ return new MyAiServiceResponseReceivedListener();
+ }
+
+ @Bean
+ public AiServiceCompletedListener aiServiceCompletedListener() {
+ return new MyAiServiceCompletedListener();
+ }
+
+ @Bean
+ public AiServiceErrorListener aiServiceErrorListener() {
+ return new MyAiServiceErrorListener();
+ }
+
+ @Bean
+ public InputGuardrailExecutedListener inputGuardrailExecutedListener() {
+ return new MyInputGuardrailExecutedListener();
+ }
+
+ @Bean
+ public OutputGuardrailExecutedListener outputGuardrailExecutedListener() {
+ return new MyOutputGuardrailExecutedListener();
+ }
+
+ @Bean
+ public ToolExecutedEventListener toolExecutedEventListener() {
+ return new MyToolExecutedEventListener();
+ }
+
+ // ==================== ChatModel 监听器 ====================
+
+ @Bean
+ public ChatModelListener chatModelListener() {
+ return new MyChatModelListener();
+ }
+
+ @Bean
+ public List chatModelListeners() {
+ return List.of(new MyChatModelListener());
+ }
+
+ // ==================== EmbeddingModel 监听器 ====================
+
+ @Bean
+ @Experimental
+ public EmbeddingModelListener embeddingModelListener() {
+ return new MyEmbeddingModelListener();
+ }
+
+ @Bean
+ @Experimental
+ public List embeddingModelListeners() {
+ return List.of(new MyEmbeddingModelListener());
+ }
+
+ // ==================== MCP Client 监听器 ====================
+
+ @Bean
+ public McpClientListener mcpClientListener() {
+ return new MyMcpClientListener();
+ }
+}
diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/observability/MyAgentListener.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/observability/MyAgentListener.java
new file mode 100644
index 00000000..42c1720b
--- /dev/null
+++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/observability/MyAgentListener.java
@@ -0,0 +1,145 @@
+package org.ruoyi.observability;
+
+import dev.langchain4j.agentic.observability.AgentInvocationError;
+import dev.langchain4j.agentic.observability.AgentRequest;
+import dev.langchain4j.agentic.observability.AgentResponse;
+import dev.langchain4j.agentic.planner.AgentInstance;
+import dev.langchain4j.agentic.scope.AgenticScope;
+import dev.langchain4j.service.tool.BeforeToolExecution;
+import dev.langchain4j.service.tool.ToolExecution;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * 自定义的 AgentListener 的监听器。
+ * 监听 Agent 相关的所有可观测性事件,包括:
+ *
+ * - Agent 调用前/后的生命周期事件
+ * - Agent 执行错误事件
+ * - AgenticScope 的创建/销毁事件
+ * - 工具执行前/后的生命周期事件
+ *
+ *
+ * @author evo
+ */
+@Slf4j
+public class MyAgentListener implements dev.langchain4j.agentic.observability.AgentListener {
+
+ /** 最终捕获到的思考结果(主 Agent 完成后写入,供外部获取) */
+ private final AtomicReference sharedOutputRef = new AtomicReference<>();
+
+ public String getCapturedResult() {
+ return sharedOutputRef.get();
+ }
+
+ // ==================== Agent 调用生命周期 ====================
+
+ @Override
+ public void beforeAgentInvocation(AgentRequest agentRequest) {
+ AgentInstance agent = agentRequest.agent();
+ AgenticScope scope = agentRequest.agenticScope();
+ Map inputs = agentRequest.inputs();
+
+ log.info("【Agent调用前】Agent名称: {}", agent.name());
+ log.info("【Agent调用前】Agent ID: {}", agent.agentId());
+ log.info("【Agent调用前】Agent类型: {}", agent.type().getName());
+ log.info("【Agent调用前】Agent描述: {}", agent.description());
+ log.info("【Agent调用前】Planner类型: {}", agent.plannerType());
+ log.info("【Agent调用前】输出类型: {}", agent.outputType());
+ log.info("【Agent调用前】输出Key: {}", agent.outputKey());
+ log.info("【Agent调用前】是否为异步: {}", agent.async());
+ log.info("【Agent调用前】是否为叶子节点: {}", agent.leaf());
+ log.info("【Agent调用前】Agent参数列表:");
+ for (var arg : agent.arguments()) {
+ log.info(" - 参数名: {}, 类型: {}, 默认值: {}",
+ arg.name(), arg.rawType().getName(), arg.defaultValue());
+ }
+ log.info("【Agent调用前】Agent输入参数: {}", inputs);
+ log.info("【Agent调用前】AgenticScope memoryId: {}", scope.memoryId());
+ log.info("【Agent调用前】AgenticScope当前状态: {}", scope.state());
+ log.info("【Agent调用前】Agent调用历史记录数: {}", scope.agentInvocations().size());
+
+ // 打印嵌套的子Agent信息
+ if (!agent.subagents().isEmpty()) {
+ log.info("【Agent调用前】子Agent列表:");
+ for (AgentInstance sub : agent.subagents()) {
+ log.info(" - 子Agent: {} ({})", sub.name(), sub.type().getName());
+ }
+ }
+
+ // 打印父Agent信息
+ if (agent.parent() != null) {
+ log.info("【Agent调用前】父Agent: {}", agent.parent().name());
+ }
+ }
+
+ @Override
+ public void afterAgentInvocation(AgentResponse agentResponse) {
+ AgentInstance agent = agentResponse.agent();
+ Map inputs = agentResponse.inputs();
+ Object output = agentResponse.output();
+ String outputStr = output != null ? output.toString() : "";
+
+ log.info("【Agent调用后】Agent名称: {}", agent.name());
+ log.info("【Agent调用后】Agent ID: {}", agent.agentId());
+ log.info("【Agent调用后】Agent输入参数: {}", inputs);
+ log.info("【Agent调用后】Agent输出结果: {}", output);
+ log.info("【Agent调用后】是否为叶子节点: {}", agent.leaf());
+
+ // 捕获主 Agent 的最终输出,供外部获取
+ if ("invoke".equals(agent.agentId()) && !outputStr.isEmpty()) {
+ sharedOutputRef.set(outputStr);
+ log.info("【Agent调用后】已捕获主Agent输出: {}", outputStr);
+ }
+ }
+
+ @Override
+ public void onAgentInvocationError(AgentInvocationError error) {
+ AgentInstance agent = error.agent();
+ Map inputs = error.inputs();
+ Throwable throwable = error.error();
+
+ log.error("【Agent执行错误】Agent名称: {}", agent.name());
+ log.error("【Agent执行错误】Agent ID: {}", agent.agentId());
+ log.error("【Agent执行错误】Agent类型: {}", agent.type().getName());
+ log.error("【Agent执行错误】Agent输入参数: {}", inputs);
+ log.error("【Agent执行错误】错误类型: {}", throwable.getClass().getName());
+ log.error("【Agent执行错误】错误信息: {}", throwable.getMessage(), throwable);
+ }
+
+ // ==================== AgenticScope 生命周期 ====================
+
+ @Override
+ public void afterAgenticScopeCreated(AgenticScope agenticScope) {
+ log.info("【AgenticScope已创建】memoryId: {}", agenticScope.memoryId());
+ log.info("【AgenticScope已创建】初始状态: {}", agenticScope.state());
+ }
+
+ @Override
+ public void beforeAgenticScopeDestroyed(AgenticScope agenticScope) {
+ log.info("【AgenticScope即将销毁】memoryId: {}", agenticScope.memoryId());
+ log.info("【AgenticScope即将销毁】最终状态: {}", agenticScope.state());
+ log.info("【AgenticScope即将销毁】总调用次数: {}", agenticScope.agentInvocations().size());
+ }
+
+ // ==================== 工具执行生命周期 ====================
+//
+// @Override
+// public void beforeToolExecution(BeforeToolExecution beforeToolExecution) {
+// var toolRequest = beforeToolExecution.request();
+// log.info("【工具执行前】工具请求ID: {}", toolRequest.id());
+// log.info("【工具执行前】工具名称: {}", toolRequest.name());
+// log.info("【工具执行前】工具参数: {}", toolRequest.arguments());
+// }
+//
+// @Override
+// public void afterToolExecution(ToolExecution toolExecution) {
+// var toolRequest = toolExecution.request();
+// log.info("【工具执行后】工具请求ID: {}", toolRequest.id());
+// log.info("【工具执行后】工具名称: {}", toolRequest.name());
+// log.info("【工具执行后】工具执行结果: {}", toolExecution.result());
+// log.info("【工具执行后】工具执行是否失败: {}", toolExecution.hasFailed());
+// }
+}
diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/observability/MyAiServiceCompletedListener.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/observability/MyAiServiceCompletedListener.java
new file mode 100644
index 00000000..b90f30fe
--- /dev/null
+++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/observability/MyAiServiceCompletedListener.java
@@ -0,0 +1,41 @@
+package org.ruoyi.observability;
+
+import dev.langchain4j.invocation.InvocationContext;
+import dev.langchain4j.observability.api.event.AiServiceCompletedEvent;
+import dev.langchain4j.observability.api.listener.AiServiceCompletedListener;
+import lombok.extern.slf4j.Slf4j;
+
+import java.time.Instant;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+/**
+ * 自定义的 AiServiceCompletedEvent 的监听器。
+ * 它表示在 AI 服务调用完成时发生的事件。
+ *
+ * @author evo
+ */
+@Slf4j
+public class MyAiServiceCompletedListener implements AiServiceCompletedListener {
+
+ @Override
+ public void onEvent(AiServiceCompletedEvent event) {
+ InvocationContext invocationContext = event.invocationContext();
+ Optional