TelemetryHook + 4 端点仪表盘,覆盖拦截器/上下文/记忆/编排子系统
监控数据源与仪表盘架构:
┌──────────────────────────────────────────────────────────────────┐
│ Agent 执行引擎 │
│ │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ InterceptorChain│ │ ContextStrategy│ │ MemoryStrategy │ │
│ │ (拦截器链) │ │ (上下文裁剪) │ │ (记忆管理) │ │
│ └───────┬────────┘ └───────┬────────┘ └───────┬────────┘ │
│ │ │ │ │
│ ┌────────────────┐ │
│ │ Orchestration │ ┌──────────────────────────────────┐ │
│ │ (编排引擎) │ │ TelemetryHook │ │
│ └───────┬────────┘ │ (OpenTelemetry 分布式追踪) │ │
│ │ │ @ConditionalOnClass(Tracer) │ │
│ │ └──────────────┬───────────────────┘ │
└──────────┼───────────────────────────┼──────────────────────────┘
│ │
▼ ▼
┌──────────────────────────────────────────────────────────┐
│ 监控仪表盘 (REST API) │
│ │
│ /interceptor-stats /context-stats /memory-stats │
│ /orchestration-stats │
│ │
│ + OpenTelemetry Spans → Jaeger / Zipkin │
└──────────────────────────────────────────────────────────┘
TelemetryHook 是一个 @Component,通过 @ConditionalOnClass(Tracer.class) 条件激活,只有当 OpenTelemetry Tracer 在 classpath 上时才会创建。
| 事件 | 触发时机 | Span 操作 |
|---|---|---|
| BEFORE_AGENT_EXECUTION | Agent 开始执行前 | 创建新 Span,设置 tags,存入 Map |
| AFTER_AGENT_EXECUTION | Agent 成功执行后 | 从 Map 取出 Span,标记 ok,end() |
| ON_AGENT_ERROR | Agent 执行异常时 | 从 Map 取出 Span,记录异常,end() |
| ON_AGENT_TIMEOUT | Agent 执行超时时 | 从 Map 取出 Span,标记 timeout,end() |
// BEFORE_AGENT_EXECUTION 创建 Span 时设置的标签:
Span span = tracer.spanBuilder("agent.execute")
.setAttribute("agent.id", agentId)
.setAttribute("task.id", taskId)
.setAttribute("conversation.id", conversationId)
.setAttribute("model.id", modelId)
.startSpan();
// 跨生命周期 Span 追踪:
// Agent 执行是异步的,BEFORE 在一个线程创建 Span,
// AFTER/ERROR/TIMEOUT 可能在另一个线程关闭 Span
ConcurrentHashMap<String, Span> activeSpans = new ConcurrentHashMap<>();
// BEFORE: 存入
activeSpans.put(taskId, span);
// AFTER/ERROR/TIMEOUT: 取出并关闭
Span span = activeSpans.remove(taskId);
if (span != null) {
span.end();
}
| 端点 | 路径 | 描述 | 关键指标 |
|---|---|---|---|
| 拦截器统计 | /api/debug/interceptor-stats | 缓存命中率、重试统计 | hitRate, totalRetries, retriesExhausted |
| 上下文统计 | /api/debug/context-stats | 上下文策略使用分布 | strategyUsage, avgReduction |
| 记忆统计 | /api/debug/memory-stats | 记忆策略使用分布 | strategyUsage, totalManaged |
| 编排统计 | /api/debug/orchestration-stats | 编排模式使用分布 | modeUsage, avgDuration |
GET /api/debug/interceptor-stats
{
"modelInterceptors": [
{"name": "LoggingModelInterceptor", "order": 10}
],
"toolInterceptors": [
{"name": "CachingToolInterceptor", "order": 20},
{"name": "RetryToolInterceptor", "order": 30}
],
"cacheStats": {
"cacheSize": 42,
"cacheHits": 156,
"cacheMisses": 89,
"hitRate": 0.637,
"ttlMs": 300000,
"lastEviction": "2026-04-28T10:30:00Z"
},
"retryStats": {
"maxRetries": 2,
"baseDelayMs": 500,
"totalRetries": 23,
"retriesExhausted": 3,
"avgRetryDelayMs": 750
}
}
GET /api/debug/context-stats
{
"totalInvocations": 1250,
"strategyUsage": {
"COMPOSITE": 820,
"SLIDING_WINDOW": 215,
"SUMMARIZE": 180,
"TRIM": 35
},
"avgMessagesBeforeTrim": 28.5,
"avgMessagesAfterTrim": 8.2,
"avgReductionRate": 0.712,
"summarizeFallbackCount": 3,
"truncationTriggered": 12
}
GET /api/debug/memory-stats
{
"totalManaged": 580,
"strategyUsage": {
"DELETE": 320,
"SUMMARIZE": 150,
"TRIM": 80,
"FILTER": 30
},
"avgMessagesDeleted": 15.3,
"summarizeSuccess": 145,
"summarizeFailed": 5,
"filterMatchRate": 0.42
}
GET /api/debug/orchestration-stats
{
"totalOrchestrations": 340,
"modeUsage": {
"SEQUENTIAL": 180,
"PARALLEL": 95,
"LLM_ROUTING": 45,
"LOOP": 20
},
"avgDurationMs": {
"SEQUENTIAL": 12500,
"PARALLEL": 4200,
"LLM_ROUTING": 3800,
"LOOP": 18900
},
"parallelStats": {
"avgAgentsPerExecution": 3.2,
"errorIsolationTriggered": 8,
"timeoutCount": 2
},
"loopStats": {
"avgIterations": 3.5,
"maxIterationsReached": 1
},
"routingStats": {
"fallbackTriggered": 4,
"routingAccuracy": 0.911
}
}
@ConditionalOnClass(Tracer.class) 让 TelemetryHook 只有在 OpenTelemetry Tracer 类存在于 classpath 时才被创建为 Spring Bean。如果项目没有引入 OpenTelemetry 依赖,TelemetryHook 完全不会实例化——零开销,零副作用。这是可插拔设计:需要分布式追踪时加上 opentelemetry-api 依赖即可自动激活,不需要时不会引入任何性能损耗或依赖冲突。
"timeout": true 标签供排查。
GET /api/admin/graph-hook-dashboard 端点在 data 字段中新增了 graphInfra 数据段,展示图基础设施的运行时状态。
SystemMonitorController.graphHookDashboard()
│
├─ AgentReactProperties → checkpointSaver / observationEnabled / diagramEnabled
├─ ObservationRegistry → observationRegistryActive(是否为 NOOP)
├─ Store → longTermMemoryStoreAvailable
└─ AgentRegistry → agentRuntimeModes(每个 Agent 的运行时模式)
{
"graphInfra": {
"checkpointSaver": "memory",
"observationEnabled": true,
"diagramEnabled": true,
"observationRegistryActive": false,
"longTermMemoryStoreAvailable": true,
"agentRuntimeModes": {
"chat": "GRAPH_PARALLEL",
"translate": "LEGACY",
"copywriting": "LEGACY"
}
}
}
| 字段 | 类型 | 说明 |
|---|---|---|
checkpointSaver | string | Checkpoint 存储方式:memory 或 mysql |
observationEnabled | boolean | 配置文件中是否启用了图执行可观测性 |
diagramEnabled | boolean | 配置文件中是否启用了图拓扑可视化 API |
observationRegistryActive | boolean | 实际运行时 ObservationRegistry 是否可用(非 NOOP) |
longTermMemoryStoreAvailable | boolean | 长期记忆 Store Bean 是否可用 |
agentRuntimeModes | object | 每个已注册 Agent 的运行时模式枚举值 |
| 端点 | 方法 | 说明 |
|---|---|---|
/api/admin/agents/{agentId}/diagram | GET | 获取单个 Agent 的 Mermaid 图拓扑 |
/api/admin/agents/diagrams | GET | 批量获取所有 Agent 的图拓扑 + 运行时模式 |
/api/admin/agents/graph-infra-status | GET | 图基础设施配置总览 + 每个 Agent 运行时配置 |
@Autowired(required = false),Bean 不存在时对应字段返回安全默认值("unknown" / false),不会导致启动失败或 NPE。
基于 Micrometer 的图执行可观测性,通过 GraphObservationHook 在图引擎的关键节点自动埋点。当 agent.react.observation-enabled=true 且 classpath 存在 MeterRegistry 时自动激活。
| Metric | Type | 含义 |
|---|---|---|
graph.node.execution.time | Timer | 单个节点执行耗时(含 LLM 调用 + 工具执行) |
graph.total.execution.time | Timer | 图从 START 到 END 的总执行时间 |
graph.node.error.count | Counter | 节点执行错误计数(按节点名称分 tag) |
graph.iteration.count | Counter | ReAct 迭代次数(每次 LLM→Tool→LLM 循环 +1) |
# Prometheus 查询示例 # 图总执行 P95 延迟 histogram_quantile(0.95, rate(graph_total_execution_time_seconds_bucket[5m])) # 各节点平均耗时 Top 10 topk(10, rate(graph_node_execution_time_seconds_sum[5m]) / rate(graph_node_execution_time_seconds_count[5m])) # 节点错误率 rate(graph_node_error_count_total[5m]) / rate(graph_node_execution_time_seconds_count[5m]) # ReAct 平均迭代次数(按 Agent 分组) rate(graph_iteration_count_total[5m]) / rate(graph_total_execution_time_seconds_count[5m])
/api/debug/interceptor-stats 端点在新增 InterceptorChainManager 后,返回数据扩展了动态启停状态和调用统计。InterceptorChainManager.getDiagnostics() 返回每个拦截器的运行时诊断信息。
GET /api/debug/interceptor-stats
{
"chainDiagnostics": {
"totalInterceptors": 5,
"enabledCount": 4,
"disabledCount": 1,
"interceptors": [
{
"name": "CachingToolInterceptor",
"order": 10,
"enabled": true,
"invocationCount": 1523,
"avgLatencyMs": 2.3,
"lastInvokedAt": "2026-04-29T09:15:30Z"
},
{
"name": "RetryToolInterceptor",
"order": 20,
"enabled": true,
"invocationCount": 1523,
"avgLatencyMs": 0.8,
"lastInvokedAt": "2026-04-29T09:15:30Z"
},
{
"name": "ModelFallbackInterceptor",
"order": 30,
"enabled": true,
"invocationCount": 1523,
"avgLatencyMs": 1.1,
"fallbackTriggered": 12,
"lastInvokedAt": "2026-04-29T09:15:30Z"
},
{
"name": "LargeResultEvictionInterceptor",
"order": 40,
"enabled": true,
"invocationCount": 890,
"evictionCount": 23,
"avgLatencyMs": 0.5,
"lastInvokedAt": "2026-04-29T09:14:55Z"
},
{
"name": "DebugLoggingInterceptor",
"order": 99,
"enabled": false,
"invocationCount": 0,
"disabledReason": "manual_disable",
"disabledAt": "2026-04-28T16:00:00Z"
}
]
},
"runtimeActions": {
"POST /api/admin/interceptors/{name}/enable": "启用指定拦截器",
"POST /api/admin/interceptors/{name}/disable": "禁用指定拦截器",
"POST /api/admin/interceptors/reload": "重新加载拦截器链配置"
}
}
InterceptorChainManager 支持通过 REST API 动态启停单个拦截器,无需重启应用。禁用的拦截器在链中被跳过(enabled=false),但保留在链的注册表中,随时可重新启用。
Prompt 评测平台提供 A/B 测试能力,支持多个 Prompt 变体的并行实验。核心看板展示实验状态、变体对比和统计显著性分析。
| 指标 | 计算方式 | 含义 |
|---|---|---|
| Quality Score | LLM-as-Judge 自动评分(1-10 分) | 回答质量综合评分,含准确性、完整性、相关性 |
| Latency P50/P95 | 从请求到首 token 的耗时分位数 | 用户体感延迟,P95 反映长尾情况 |
| Token Cost | input_tokens × 输入单价 + output_tokens × 输出单价 | 单次请求成本,用于 ROI 分析 |
| Statistical Significance | 双样本 t 检验,p-value < 0.05 | 变体间差异是否具有统计学意义 |
DRAFT → RUNNING → ANALYZING → COMPLETED。实验运行期间流量按配置比例分配到各变体(默认均分)。达到最小样本量(默认 100 次/变体)后自动进入分析阶段,计算统计显著性并生成报告。
AtomicLong 保证单个计数器的原子性(CAS 操作),但 getDiagnostics() 返回多个计数器的快照并不是全局原子的——读取 invocationCount 和读取 avgLatencyMs 之间可能有新的调用插入。这是可以接受的:监控数据本身就是近似值,允许微小的不一致性。如果需要全局快照一致性,需要加锁或使用 StampedLock 的乐观读,但这会引入锁竞争,对监控这种高频场景来说代价太大。
Timer.record() 在无锁竞争时约 100ns/call(底层使用 DoubleAdder/LongAdder 减少竞争)。关键瓶颈是 Tag cardinality:避免高基数 tag(如 userId),每个唯一 tag 组合产生一条独立时序。预算指导:监控开销应控制在总 CPU 的 1% 以内。指标采集频率 15s 对 Agent 场景足够——更短的间隔(如 5s)增加 Prometheus 存储压力但收益有限。
中间件监控系统对 Agent Hub 依赖的全部基础设施进行统一健康检查与运行状态采集。6 个监控服务实现统一接口,通过 MiddlewareController 暴露 REST API,前端 MiddlewareView.vue 提供可视化面板。
public interface MiddlewareMonitor {
String getName(); // 组件名称,如 "MySQL"
String getType(); // 类型标识,如 "relational_database"
Map<String, Object> getStatus(); // 运行状态快照
}
| 组件 | 类型标识 | Service | 条件装配 | 关键指标 |
|---|---|---|---|---|
| MySQL | relational_database | MySQLMonitorService | 始终加载 | 连接池使用率、响应时间、Uptime、进程列表 |
| Milvus | vector_database | MilvusMonitorService | 始终加载 | 集合数、向量总数、连接响应时间、fallback 检测 |
| LLM API | llm_api | LLMMonitorService | 始终加载 | 24h 调用量/成功率/Token 用量、趋势、模型延迟 |
| Caffeine Cache | in_process_cache | CacheMonitorService | 始终加载 | 命中率、缓存大小、淘汰次数、加载成功/失败 |
| Redis | key_value_store | RedisMonitorService | @ConditionalOnClass | 内存使用、键总数、OPS、集群节点、PING 延迟 |
| RocketMQ | message_queue | RocketMQMonitorService | @ConditionalOnClass | Producer 可用性、NameServer、事件推送状态 |
MiddlewareMonitor 接口与监控数据流:
┌─────────────────────────────────────────────────────────────┐
│ MiddlewareMonitor (interface) │
│ getName() / getType() / getStatus() │
└────────────────────────┬────────────────────────────────────┘
│ implements
┌──────────┬──────────┼──────────┬──────────┬──────────┐
│ │ │ │ │ │
MySQL Milvus LLM Cache Redis RocketMQ
Monitor Monitor Monitor Monitor Monitor Monitor
│ │ │ │ │ │
│ │ │ CacheRegistry │ │
│ │ │ (注册中心) │ │
│ │ LlmCallStats │ │
│ │ Repository │ │
└──────────┴──────────┼──────────┴──────────┴──────────┘
│
┌────────────┴────────────┐
│ MiddlewareController │
│ /api/middleware/* │
│ (26 个 REST 端点) │
└────────────┬────────────┘
│
┌────────────┴────────────┐
│ MiddlewareView.vue │
│ (7 Tab 可视化面板) │
│ 10s 自动刷新 │
└─────────────────────────┘
MySQLMonitorService 通过 JDBC DataSource 和 HikariCP MXBean 采集数据,支持表浏览、SQL 执行、进程查看。
// 连接池指标采集 (HikariCP)
HikariPoolMXBean poolBean = hds.getHikariPoolMXBean();
pool.put("activeConnections", poolBean.getActiveConnections());
pool.put("idleConnections", poolBean.getIdleConnections());
pool.put("totalConnections", poolBean.getTotalConnections());
pool.put("maximumPoolSize", hds.getMaximumPoolSize());
// 响应时间测试
SELECT 1 → responseTimeMs
// 运行时长
SHOW STATUS LIKE 'Uptime' → "12h 35m"
SQL 执行器安全机制:仅允许 SELECT / SHOW / DESCRIBE / EXPLAIN。禁止多语句(分号分隔)、INTO OUTFILE/DUMPFILE(正则检测)。30s 超时,结果上限 1000 行。
MilvusMonitorService 采用双层探测策略,30 秒 TTL 状态缓存避免瞬态抖动。
// 主探测:临时 MilvusClientV2,3s 超时,最多重试 2 次
ConnectConfig config = ConnectConfig.builder()
.uri("http://" + host + ":" + port)
.connectTimeoutMs(3000).build();
MilvusClientV2 client = new MilvusClientV2(config);
ListCollectionsResp resp = client.listCollections(); // 验证连通
// 遍历所有集合采集向量总数
for (String collName : resp.getCollectionNames()) {
GetCollectionStatsResp stats = client.getCollectionStats(
GetCollectionStatsReq.builder().collectionName(collName).build()
);
totalVectors += stats.getNumOfEntities();
}
// 备用探测:Spring AI VectorStore.similaritySearch("ping")
// Fallback 检测:vectorStore.getClass().isAnonymousClass()
isAnonymousClass() 检测到此情况后会提示"Milvus 未连接,当前使用空实现 VectorStore",帮助运维快速定位问题。
LLMMonitorService 基于 LlmCallStatsRepository 提供多维度统计分析。
// 24h 快速概览
List<LlmCallStatsRecord> records = repository.findByStatHourBetween(
LocalDateTime.now().minusHours(24), LocalDateTime.now()
);
int totalCalls = records.stream().mapToInt(r -> r.getCallCount()).sum();
int successCalls = records.stream().mapToInt(r -> r.getSuccessCount()).sum();
long totalTokens = inputTokens + outputTokens;
double successRate = (double) successCalls / totalCalls * 100;
// 趋势数据按小时分组
Map<LocalDateTime, List<LlmCallStatsRecord>> byHour =
records.stream().collect(Collectors.groupingBy(r -> r.getStatHour()));
// 每小时: callCount, successCount, failureCount,
// inputTokens, outputTokens, cacheTokens, reasoningTokens
// 模型测试
ChatResponse response = chatModel.call(new Prompt(prompt));
tokensPerSecond = completionTokens / (elapsedMs / 1000.0);
供应商发现策略:优先从 KeyPoolManagementService 动态读取通道配置(支持运行时管理),fallback 到 application.yml 静态配置(ZhiPu GLM + DashScope Qwen)。
CacheRegistry 使用 ConcurrentHashMap 管理所有 Caffeine Cache 实例的注册。业务代码创建 Cache 时调用 register(name, cache),监控服务即可遍历采集统计数据。
// CacheRegistry — 显式注册,不依赖 Spring Cache 抽象
@Component
public class CacheRegistry {
private final Map<String, Cache<?, ?>> caches = new ConcurrentHashMap<>();
public void register(String name, Cache<?, ?> cache) { caches.put(name, cache); }
}
// CacheMonitorService — 采集每个缓存的 Caffeine Stats
var stats = cache.stats();
stats.hitCount(); // 命中次数
stats.missCount(); // 未命中次数
stats.evictionCount(); // 淘汰次数
cache.estimatedSize(); // 当前条目数
// 整体命中率
overallHitRate = totalHits / (totalHits + totalMisses)
// 管理操作
cache.invalidate(key); // 删除单条目
cache.invalidateAll(); // 清空全部
Redis 和 RocketMQ 使用 @ConditionalOnClass 实现可插拔装配——对应依赖不在 classpath 时完全不创建 Bean。
// Redis: 通过 RedisCallback 执行 PING + 解析 INFO 原始输出
@Service
@ConditionalOnClass(RedisTemplate.class)
public class RedisMonitorService implements MiddlewareMonitor {
// PING 探测
Object pong = redisTemplate.execute(
(RedisCallback<Object>) RedisConnection::ping
);
// INFO 解析: version, mode, usedMemory, totalKeys, uptime, opsPerSec ...
// 集群: clusterGetNodes() → host, port, flags
}
// RocketMQ: 检测 Producer 内部工厂链
@Service
@ConditionalOnClass(RocketMQTemplate.class)
public class RocketMQMonitorService implements MiddlewareMonitor {
// Producer 可用性检测
boolean available = producer.getDefaultMQProducerImpl()
.getmQClientFactory() != null;
}
| 方法 | 端点 | 说明 |
|---|---|---|
| GET | /api/middleware/overview | 6 大组件状态总览 |
| MySQL (7 端点) | ||
| GET | /api/middleware/mysql/status | 连接状态与连接池 |
| GET | /api/middleware/mysql/tables | 表列表 |
| GET | /api/middleware/mysql/tables/{table}/schema | 表结构 |
| GET | /api/middleware/mysql/tables/{table}/data | 表数据预览 |
| POST | /api/middleware/mysql/query | 执行只读 SQL |
| GET | /api/middleware/mysql/processes | 进程列表 |
| GET | /api/middleware/mysql/status-variables | 状态变量 |
| Milvus (3 端点) | ||
| GET | /api/middleware/milvus/status | 连接状态与统计 |
| GET | /api/middleware/milvus/collections | 集合列表 |
| POST | /api/middleware/milvus/search | 向量搜索 |
| LLM (8 端点) | ||
| GET | /api/middleware/llm/providers | 供应商列表 |
| POST | /api/middleware/llm/test | 测试默认模型 |
| POST | /api/middleware/llm/test-channel | 测试指定通道 |
| GET | /api/middleware/llm/statistics | 按模型统计 |
| GET | /api/middleware/llm/trend | 调用趋势 |
| GET | /api/middleware/llm/agent-breakdown | 按 Agent 拆分 |
| GET | /api/middleware/llm/user-breakdown | 按用户拆分 |
| GET | /api/middleware/llm/latency | 延迟分布 |
| Cache (4 端点) | ||
| GET | /api/middleware/cache/stats | 缓存总览 |
| GET | /api/middleware/cache/{name}/keys | 缓存键列表 |
| DELETE | /api/middleware/cache/{name}/entries/{key} | 删除缓存条目 |
| DELETE | /api/middleware/cache/flush | 清空全部缓存 |
| Redis & RocketMQ (各 1 端点) | ||
| GET | /api/middleware/redis/status | Redis 状态 |
| GET | /api/middleware/rocketmq/status | RocketMQ 状态 |
所有端点通过 permissionService.requireMenuPermission("middleware") 权限校验。
MiddlewareView.vue 采用 Element Plus Tabs 导航,7 个标签页覆盖全部中间件,支持 10 秒自动刷新。
前端面板 Tab 结构: ┌──────────────────────────────────────────────────────────┐ │ [健康状态标签] [刷新] [自动刷新] │ ├──────────────────────────────────────────────────────────┤ │ 总览 │ MySQL │ Milvus │ LLM │ 缓存 │ Redis │ RocketMQ │ ├──────────────────────────────────────────────────────────┤ │ │ │ ┌─ 总览页 ─────────────────────────────────────────┐ │ │ │ 健康横幅 (全绿/部分异常) │ │ │ │ 摘要指标网格 (连接池/命中率/调用量/状态) │ │ │ │ 详情卡片 × 6 (点击跳转对应 Tab) │ │ │ └──────────────────────────────────────────────────┘ │ │ │ │ ┌─ MySQL 页 ───────────────────────────────────────┐ │ │ │ 状态栏 (版本/数据库/连接/运行时长) │ │ │ │ 子 Tab: 表浏览 │ SQL执行器 │ 进程列表 │ 状态变量 │ │ │ └──────────────────────────────────────────────────┘ │ │ │ │ ┌─ LLM 页 ────────────────────────────────────────┐ │ │ │ 子 Tab: 总览 │ 趋势(SVG图表) │ 明细 │ 测试 │ │ │ │ Key Pool 状态 / 降级通道 / Token 用量面积图 │ │ │ └──────────────────────────────────────────────────┘ │ │ │ │ ┌─ 缓存页 ────────────────────────────────────────┐ │ │ │ 科普横幅 + 总览指标 + 各缓存进度条卡片 │ │ │ │ 键浏览器 (查看/删除键) + 全量清空 │ │ │ └──────────────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────┘
关键交互设计:
localStorage 持久化,最多保留 20 条Caffeine.newBuilder().build(),不在 CacheManager 管辖范围内;2) 显式注册让每个 Cache 拥有明确的业务名称(如 "tool-result-cache"),比 Spring Cache 的自动命名更直观;3) 解耦——即使未来更换缓存框架,只需修改 register 调用即可。
@ConditionalOnClass(RedisTemplate.class) 只有当 spring-boot-starter-data-redis 在 classpath 上时才创建 RedisMonitorService Bean。前端通过检测 enabled: false 展示"未启用"状态而非报错。这让部署灵活性最大化——生产环境引入全部依赖获得完整功能,开发环境最小化启动。