26 - 运行监控面板

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 — 分布式追踪

TelemetryHook 是一个 @Component,通过 @ConditionalOnClass(Tracer.class) 条件激活,只有当 OpenTelemetry Tracer 在 classpath 上时才会创建。

支持的 4 个生命周期事件

事件触发时机Span 操作
BEFORE_AGENT_EXECUTIONAgent 开始执行前创建新 Span,设置 tags,存入 Map
AFTER_AGENT_EXECUTIONAgent 成功执行后从 Map 取出 Span,标记 ok,end()
ON_AGENT_ERRORAgent 执行异常时从 Map 取出 Span,记录异常,end()
ON_AGENT_TIMEOUTAgent 执行超时时从 Map 取出 Span,标记 timeout,end()

Span 标签

// 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();
}

三、4 个监控端点

端点路径描述关键指标
拦截器统计/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

四、拦截器统计(interceptor-stats)

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
  }
}
hitRate = 0.637 表示 63.7% 的工具调用命中了缓存,避免了重复执行。这个指标直接反映缓存策略的有效性——过低说明缓存 TTL 太短或调用参数变化大。

五、上下文统计(context-stats)

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
}
avgReductionRate = 0.712 表示上下文策略平均裁剪掉 71.2% 的消息。summarizeFallbackCount = 3 表示有 3 次 LLM 摘要调用失败,触发了降级(保留前 3 条消息)。

六、记忆统计(memory-stats)

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
}

七、编排统计(orchestration-stats)

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
  }
}
关键观察:Parallel 模式平均耗时 4200ms,远低于 Sequential 的 12500ms——并行执行的优势明显。routingAccuracy = 0.911 说明 LLM 路由 91.1% 的情况下选择了正确的 Agent,4 次降级到第一个 Agent。

八、面试高频问题

Q: TelemetryHook 为什么用 ConcurrentHashMap 存 Span?
A: Agent 执行是异步的。BEFORE_AGENT_EXECUTION 事件在提交任务的线程中触发(创建 Span),而 AFTER_AGENT_EXECUTION / ON_AGENT_ERROR / ON_AGENT_TIMEOUT 事件在执行任务的线程中触发(关闭 Span)。同一个 Span 需要跨线程传递:创建线程存入 Map,结束线程从 Map 取出。ConcurrentHashMap 提供了线程安全的跨生命周期 Span 访问,确保在并发环境下不会丢失或重复关闭 Span。
Q: @ConditionalOnClass 的作用?
A: @ConditionalOnClass(Tracer.class) 让 TelemetryHook 只有在 OpenTelemetry Tracer 类存在于 classpath 时才被创建为 Spring Bean。如果项目没有引入 OpenTelemetry 依赖,TelemetryHook 完全不会实例化——零开销,零副作用。这是可插拔设计:需要分布式追踪时加上 opentelemetry-api 依赖即可自动激活,不需要时不会引入任何性能损耗或依赖冲突。
Q: ON_AGENT_TIMEOUT 为什么需要单独处理?
A: 如果没有 ON_AGENT_TIMEOUT 事件处理,当 Agent 执行超时被中断时,AFTER_AGENT_EXECUTION 和 ON_AGENT_ERROR 都不会被触发——因为任务没有正常完成,也不是代码异常。此时 BEFORE 阶段创建的 Span 会一直留在 ConcurrentHashMap 中,永远不会被 end()(Span 泄漏)。这些泄漏的 Span 不仅浪费内存,还会导致追踪系统中出现"永不结束"的异常追踪记录。ON_AGENT_TIMEOUT 确保超时场景下 Span 被正确关闭,并标记 "timeout": true 标签供排查。

九、图基础设施监控(graphInfra)

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"
    }
  }
}

字段说明

字段类型说明
checkpointSaverstringCheckpoint 存储方式:memorymysql
observationEnabledboolean配置文件中是否启用了图执行可观测性
diagramEnabledboolean配置文件中是否启用了图拓扑可视化 API
observationRegistryActiveboolean实际运行时 ObservationRegistry 是否可用(非 NOOP)
longTermMemoryStoreAvailableboolean长期记忆 Store Bean 是否可用
agentRuntimeModesobject每个已注册 Agent 的运行时模式枚举值

新增管理 API

端点方法说明
/api/admin/agents/{agentId}/diagramGET获取单个 Agent 的 Mermaid 图拓扑
/api/admin/agents/diagramsGET批量获取所有 Agent 的图拓扑 + 运行时模式
/api/admin/agents/graph-infra-statusGET图基础设施配置总览 + 每个 Agent 运行时配置
注入安全:所有新增字段来源的 Bean(AgentReactProperties、ObservationRegistry、Store)均为 @Autowired(required = false),Bean 不存在时对应字段返回安全默认值("unknown" / false),不会导致启动失败或 NPE。

十、Graph Observation 监控

基于 Micrometer 的图执行可观测性,通过 GraphObservationHook 在图引擎的关键节点自动埋点。当 agent.react.observation-enabled=true 且 classpath 存在 MeterRegistry 时自动激活。

MetricType含义
graph.node.execution.timeTimer单个节点执行耗时(含 LLM 调用 + 工具执行)
graph.total.execution.timeTimer图从 START 到 END 的总执行时间
graph.node.error.countCounter节点执行错误计数(按节点名称分 tag)
graph.iteration.countCounterReAct 迭代次数(每次 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])
Tag 设计原则:避免高基数 tag(如 userId、conversationId),只使用 agentId、nodeName、status 等低基数标签。高基数 tag 会导致 Prometheus 时序数据爆炸(每个唯一 tag 组合 = 一条独立时序),监控系统反而成为性能瓶颈。

十一、拦截器诊断面板

/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 评测看板

Prompt 评测平台提供 A/B 测试能力,支持多个 Prompt 变体的并行实验。核心看板展示实验状态、变体对比和统计显著性分析。

核心指标

指标计算方式含义
Quality ScoreLLM-as-Judge 自动评分(1-10 分)回答质量综合评分,含准确性、完整性、相关性
Latency P50/P95从请求到首 token 的耗时分位数用户体感延迟,P95 反映长尾情况
Token Costinput_tokens × 输入单价 + output_tokens × 输出单价单次请求成本,用于 ROI 分析
Statistical Significance双样本 t 检验,p-value < 0.05变体间差异是否具有统计学意义

实验生命周期

DRAFT → RUNNING → ANALYZING → COMPLETED。实验运行期间流量按配置比例分配到各变体(默认均分)。达到最小样本量(默认 100 次/变体)后自动进入分析阶段,计算统计显著性并生成报告。

自动评分机制:使用独立的 LLM(Judge Model)对每个变体的回答进行评分,评分 Prompt 包含标准化的评分维度和评分标准。Judge Model 与被测模型隔离,避免自我评价偏差。

十三、面试高频问题(续)

Q: Prometheus的pull模式在Agent短生命周期场景有什么问题?
A: Agent 执行可能在两次 Prometheus pull(默认 15s 间隔)之间完成,导致瞬时指标丢失——Gauge 类型指标在下次 pull 时已归零。解决方案:1) 使用 Histogram/Summary 聚合指标而非 Gauge,聚合值在 pull 间隔内持续有效;2) 对于 batch job 场景使用 Pushgateway 主动推送;3) 配合 event log(如写入 Kafka → Elasticsearch)实现完整追踪,Prometheus 负责聚合告警,日志系统负责明细查询。
Q: 拦截器诊断面板的数据一致性如何保证?AtomicLong够用吗?
A: AtomicLong 保证单个计数器的原子性(CAS 操作),但 getDiagnostics() 返回多个计数器的快照并不是全局原子的——读取 invocationCount 和读取 avgLatencyMs 之间可能有新的调用插入。这是可以接受的:监控数据本身就是近似值,允许微小的不一致性。如果需要全局快照一致性,需要加锁或使用 StampedLock 的乐观读,但这会引入锁竞争,对监控这种高频场景来说代价太大。
Q: 监控系统本身的性能开销如何量化?
A: Micrometer Timer.record() 在无锁竞争时约 100ns/call(底层使用 DoubleAdder/LongAdder 减少竞争)。关键瓶颈是 Tag cardinality:避免高基数 tag(如 userId),每个唯一 tag 组合产生一条独立时序。预算指导:监控开销应控制在总 CPU 的 1% 以内。指标采集频率 15s 对 Agent 场景足够——更短的间隔(如 5s)增加 Prometheus 存储压力但收益有限。
Q: 如何设计Agent执行的SLA告警规则?
A: 分层告警策略:1) 延迟告警:P95 延迟 > 10s 触发 Warning,> 30s 触发 Critical;2) 错误率告警:5 分钟窗口错误率 > 5% 触发 Warning,> 15% 触发 Critical;3) 死循环检测:连续 3 次 ReAct 迭代无 tool_call 触发 Warning(可能是 LLM 陷入无效推理);4) 基础设施告警:Checkpoint 写入失败、向量检索超时等。告警分级路由:Warning → 邮件/企业微信;Critical → PagerDuty 电话通知 + 自动创建 Incident。

十四、中间件监控 — 6 大组件健康检查

中间件监控系统对 Agent Hub 依赖的全部基础设施进行统一健康检查与运行状态采集。6 个监控服务实现统一接口,通过 MiddlewareController 暴露 REST API,前端 MiddlewareView.vue 提供可视化面板。

MiddlewareMonitor 统一接口

public interface MiddlewareMonitor {
    String getName();                  // 组件名称,如 "MySQL"
    String getType();                  // 类型标识,如 "relational_database"
    Map<String, Object> getStatus();   // 运行状态快照
}

6 大监控实现

组件类型标识Service条件装配关键指标
MySQLrelational_databaseMySQLMonitorService始终加载连接池使用率、响应时间、Uptime、进程列表
Milvusvector_databaseMilvusMonitorService始终加载集合数、向量总数、连接响应时间、fallback 检测
LLM APIllm_apiLLMMonitorService始终加载24h 调用量/成功率/Token 用量、趋势、模型延迟
Caffeine Cachein_process_cacheCacheMonitorService始终加载命中率、缓存大小、淘汰次数、加载成功/失败
Rediskey_value_storeRedisMonitorService@ConditionalOnClass内存使用、键总数、OPS、集群节点、PING 延迟
RocketMQmessage_queueRocketMQMonitorService@ConditionalOnClassProducer 可用性、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 自动刷新           │
              └─────────────────────────┘

MySQL 监控详解

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 行。

Milvus 监控详解

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()
Fallback 检测:当 Milvus 不可用时,Spring AI 注入匿名类空实现的 VectorStore。isAnonymousClass() 检测到此情况后会提示"Milvus 未连接,当前使用空实现 VectorStore",帮助运维快速定位问题。

LLM 监控详解

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 与缓存监控

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 条件装配

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;
}
本地模式兼容:当 Redis 或 RocketMQ 未配置时,前端面板显示"未启用"标签而非报错。系统以本地模式运行,分布式功能(分布式锁、消息事件)不可用但不影响核心功能。

REST API 端点一览

方法端点说明
GET/api/middleware/overview6 大组件状态总览
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/statusRedis 状态
GET/api/middleware/rocketmq/statusRocketMQ 状态

所有端点通过 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 用量面积图      │   │
  │  └──────────────────────────────────────────────────┘   │
  │                                                          │
  │  ┌─ 缓存页 ────────────────────────────────────────┐   │
  │  │  科普横幅 + 总览指标 + 各缓存进度条卡片           │   │
  │  │  键浏览器 (查看/删除键) + 全量清空                │   │
  │  └──────────────────────────────────────────────────┘   │
  │                                                          │
  └──────────────────────────────────────────────────────────┘

关键交互设计:

面试高频问题

Q: 为什么 Milvus 探测需要 30 秒状态缓存?
A: Milvus 探测需要创建临时 gRPC 连接(3s 超时 + 重试),如果每 10 秒前端自动刷新都执行一次完整探测,会给 Milvus 服务带来不必要的连接压力。30 秒 TTL 缓存确保同一探测结果在短时间内复用,同时避免网络瞬态波动导致前端状态频繁闪烁(flapping)。
Q: CacheRegistry 为什么不使用 Spring 的 CacheManager 自动发现?
A: 三个原因:1) 项目中的 Caffeine Cache 实例并非全部通过 Spring Cache 抽象创建,有些是业务代码直接 Caffeine.newBuilder().build(),不在 CacheManager 管辖范围内;2) 显式注册让每个 Cache 拥有明确的业务名称(如 "tool-result-cache"),比 Spring Cache 的自动命名更直观;3) 解耦——即使未来更换缓存框架,只需修改 register 调用即可。
Q: @ConditionalOnClass 对 Redis 和 RocketMQ 的意义?
A: 实现可插拔依赖:Agent Hub 可以在没有 Redis 和 RocketMQ 的环境下启动运行(本地开发模式)。@ConditionalOnClass(RedisTemplate.class) 只有当 spring-boot-starter-data-redis 在 classpath 上时才创建 RedisMonitorService Bean。前端通过检测 enabled: false 展示"未启用"状态而非报错。这让部署灵活性最大化——生产环境引入全部依赖获得完整功能,开发环境最小化启动。