微服务日志智能诊断系统(六) Fast Path vs Slow Path——双路径日志诊断策略


项目地址gitee.com/guoxuezha/e...


1. 问题的本质:速度与深度的博弈

在微服务日志诊断中,存在一个核心矛盾:

  • 快速响应:大多数异常(如 NullPointerException、SQL 语法错误)只需看一眼 error.log 的堆栈信息就能定位,此时 SSH + grep 几秒内就能搞定。
  • 深度分析:少数复杂异常(如偶发的线程池耗尽、跨服务调用超时)需要查看完整日志上下文,甚至需要在海量日志中精准定位异常锚点。

如果每次都走"深度分析",下载完整日志 + 构建 RAG 索引,可能要花费 30 秒到数分钟;但如果只走"快速抓取",又可能因为日志截断、关键字不匹配等原因导致漏判。

我们的解决方案是:两条路同时准备,哪条先通走哪条

2. 双路径策略全景图

ini 复制代码
 用户输入
    │
    ▼
┌─────────────────────┐
│  fetchServiceLog    │  ◄──── Fast Path: SSH + grep
│  (SSH 远程抓取)      │
└──────────┬──────────┘
           │
           ▼
┌─────────────────────┐
│  assessLogQuality   │  ◄──── LLM 质量评分 (0-10)
│  (日志质量评估)      │
└──────┬───────┬──────┘
       │       │
  [>= 6分]   [< 6分]
  Pass        Fail
       │       │
       │       ├─────────────────────────┐
       │       │                         │
       │       ▼                         ▼
       │  ┌───────────┐          ┌──────────────────┐
       │  │ fetchRetry│          │ triggerRagBuild  │ ◄── 异步启动
       │  │ (标准重试) │          │ (后台下载+构建RAG)│
       │  └─────┬─────┘          └──────────────────┘
       │        │                         │
       │   [回到fetch]             [主循环继续]
       │                                  │
       │         ┌────────────────────────┘
       │         │ (RAG就绪后)
       │         ▼
       │  ┌───────────────────┐
       │  │ragDrivenPrecise   │ ◄── Slow Path: RAG 精准抓取
       │  │Fetch              │
       │  └────────┬──────────┘
       │           │
       │      [回到fetch]
       │
       ▼
┌─────────────────────┐
│  extractKeyInfo     │  ◄──── 提取关键信息
└──────────┬──────────┘
           │
           ▼
┌─────────────────────┐
│performRootCause     │  ◄──── 根因分析
│Analysis             │
└─────────────────────┘

3. Fast Path:SSH + grep 快速命中

Fast Path 是默认的首选路径,目标是"用最少的代价解决问题"。

3.1 首次抓取

fetchServiceLog Action 根据用户输入的服务编码和发生时间,通过 SSH 连接到目标服务器,执行 grep 命令抓取日志片段:

java 复制代码
@Action(
    description = "通过 SSH 远程抓取服务日志",
    pre = {"hasValidFetchParams"},
    post = {"logNotEvaluatedYet"},
    canRerun = true  // 关键:允许重复执行
)
public SshLogResult fetchServiceLog(FetchServiceLogInput input, OperationContext ctx) {
    List<ServiceResource> resources = knowledgeBaseService.findServiceResources(serviceCode);
    SshLogResult sshLogResult;
    if (StrUtil.isEmpty(keyWord)) {
        // 按时间匹配(默认模式)
        sshLogResult = sshService.firstLogCatch(resources, occurTime, ...);
    } else {
        // 按关键字精确匹配
        sshLogResult = sshService.logSectionCatch(resources, occurTime, keyWord, ...);
    }
    // ...
}

底层执行的是类似这样的 Linux 命令:

bash 复制代码
cd /log/dir && grep -F '10:30' error.log -A 50 -B 50 | tail -n 500

3.2 质量评估

抓取到日志后,assessLogQuality Action 调用 LLM 对日志质量进行评分(0-10分)。评分标准非常细致:

分数段 含义 系统行为
0-3 严重不足(空日志、完全无关) 进入重试或 RAG
4-5 基本可用但信息不足 标准重试(调整参数)
6-10 信息充分,可直接诊断 进入根因分析

评估的 Prompt 设计是整个系统的精髓之一。我们让 LLM 不仅输出分数,还要输出详细的评估理由和下一步建议:

json 复制代码
{
  "qualityScore": 4,
  "needsMoreContext": true,
  "needsKeywordFilter": false,
  "extractedKeywords": ["TooManyResultsException", "orderId=12345"],
  "switchFileRecommended": true,
  "missingInfoTypes": ["stacktrace"],
  "isTruncated": true,
  "isRelevantToProblem": true,
  "deviationRisk": "low",
  "recommendedAction": "extendByKeyword",
  "reasoning": "日志包含部分异常信息但堆栈被截断,建议增大下边界行数..."
}

这些结构化输出,为后续的重试策略提供了精确的指导。

4. 标准重试:LLM 驱动的自适应策略

当质量评估不通过(score < 6)且 RAG 尚未就绪时,系统进入 fetchRetry Action------这是 Fast Path 的"自我修复"机制。

核心思路是:将历次评估结果全部交给 LLM,让它生成下一轮的抓取参数

java 复制代码
@Action(
    description = "根据质量评分决定下一轮延伸策略",
    pre = {"qualityScoreFail", "notRagReady"},
    post = {"hasValidFetchParams"},
    canRerun = true
)
public FetchServiceLogInput fetchRetry(FetchServiceLogInput lastFetch,
                                        AssessLogQualityOutput qualityOutput,
                                        OperationContext ctx) {
    // 查询所有历史评估结果
    List<AssessLogQualityOutput> assessments = ctx.objectsOfType(AssessLogQualityOutput.class);

    // 防无限循环
    if (assessments.size() > 5) {
        // 终止诊断
        return FetchServiceLogInput.builder().needBreak(true).build();
    }

    // LLM 生成下一轮参数
    String prompt = PromptBuilder.buildNextFetchRequestPrompt(assessments);
    return ctx.ai().withDefaultLlm().createObject(prompt, FetchServiceLogInput.class);
}

LLM 可以调整的参数维度非常丰富:

参数 含义 调整策略示例
occurTime grep 匹配的时间关键字 前后偏移 5-30 秒(如 10:30:15 → 10:30:10)
keyWord 替代时间戳的精确关键字 使用异常类名、orderId 等
upperBoundary grep -B 行数(上文) 从 50 增到 200(堆栈被截断时)
lowerBoundary grep -A 行数(下文) 从 50 增到 500(需要更多后文时)
maxLine tail -n 行数(最大输出) 从 500 增到 2000(日志密度大时)

这种"LLM 自适应调参"的设计,让系统在面对各种日志场景时都能逐步逼近最优的抓取策略,而不需要硬编码任何重试规则。

5. Slow Path:RAG 驱动的深度挖掘

当标准重试多轮仍未达标时,后台的 RAG 构建可能已经完成。此时系统切换到 Slow Path------利用 RAG 索引的全文检索能力,实现"大海捞针"般的精准定位。

5.1 异步 RAG 构建

当第一次质量评估不通过时,系统在后台悄悄启动 RAG 构建:

java 复制代码
private void triggerRagBuildIfNeeded(FetchServiceLogInput fetchInput,
                                      SshLogResult logResult,
                                      OperationContext ctx) {
    // 检查是否已启动过
    RagBuildProgress existing = ctx.last(RagBuildProgress.class);
    if (existing != null && (existing.isBuilding() || existing.isReady())) {
        return;  // 幂等性保护
    }

    RagBuildProgress progress = new RagBuildProgress();
    progress.setBuilding(true);
    ctx.set("ragBuildProgress", progress);  // 写入黑板,供 Condition 检测

    // 异步执行:下载 + 解析 + 索引
    CompletableFuture.runAsync(() -> {
        // Step 1: SSH下载日志文件(gzip压缩传输)
        String localFileName = sshService.downloadLogFile(resources.getFirst(), ...);

        if (progress.isCancelled()) return;  // Fast Path 成功了,取消

        // Step 2: 解析日志并构建 RAG 索引
        IngestionResult result = logIngestionService.ingestFromFile(serviceCode, localFileName);

        // Step 3: 标记就绪
        progress.markReady(result.totalTraces(), result.totalChunks());
    });
}

关键设计点:

  • 不阻塞主循环 :RAG 构建在后台线程执行,主循环的 fetchRetry → fetchServiceLog → assessLogQuality 继续运转。
  • 可取消 :如果 Fast Path 在 RAG 就绪前就成功了,extractKeyInfo Action 会取消后台任务,避免浪费资源。
  • 幂等性 :通过检查 RagBuildProgress 的状态,确保不会重复构建。

5.2 RAG 精准抓取

当 RAG 就绪(ragReady = true)且仍有 qualityScoreFail 时,规划器会路由到 ragDrivenPreciseFetch Action:

java 复制代码
@Action(
    description = "RAG驱动的深度精准抓取",
    pre = {"qualityScoreFail", "ragReady"},
    post = {"hasValidFetchParams"},
    canRerun = true
)
public FetchServiceLogInput ragDrivenPreciseFetch(AssessLogQualityOutput qualityOutput,
                                                    RagBuildProgress ragProgress,
                                                    OperationContext ctx) {
    // 收集历次评估中的关键字和推理信息
    String searchQuery = buildSearchQueryFromAssessments(ctx);

    // 利用 RAG 索引 + LLM 生成精准抓取参数
    FetchServiceLogInput result = ctx.ai()
            .withDefaultLlm()
            .withReference(insuranceRag)  // 注入 RAG 工具
            .createObject(ragSearchPrompt, FetchServiceLogInput.class);

    return result;
}

此时 LLM 拥有了 RAG 的全文检索能力(通过 ToolishRag 暴露的 textSearch / vectorSearch 工具),可以:

  1. 在完整日志中搜索异常锚点(如 NullPointerException at com.guo.doctor.service)。
  2. 定位锚点的精确时间戳和上下文。
  3. 输出高度精准的 FetchServiceLogInput(仅针对锚点前后小范围)。

然后系统用这个精准参数再次执行 SSH grep,得到的日志片段质量通常会大幅提升。

6. 竞速与协调:两条路如何协作

整个双路径策略可以用一个"竞速模型"来理解:

ini 复制代码
时间线 ─────────────────────────────────────────────────────────────────────────────────────────→

Fast Path:  [fetch₁] → [assess₁] → [retry] → [fetch₂] → [assess₂] → [PASS!] → [extract] → [report]
                                                              │
                                              cancel ─────────┘
                                                              │
Slow Path:  ──── [download] ──── [parse] ──── [index] ──── [cancelled]
                    (异步启动)         (后台执行)

如果 Fast Path 在第 2 轮就成功了,Slow Path 的 RAG 构建会被取消,不浪费任何额外资源。

如果 Fast Path 5 轮重试仍未成功,而 Slow Path 的 RAG 已就绪,系统会自动切换到 RAG 精准抓取模式。

如果两条路都失败(5 轮重试 + RAG 也未找到有效信息),系统会触发 generateFailureReport,输出一份包含完整诊断过程的降级报告。

7. 防无限循环与终止机制

为了防止 Agent 陷入无限循环,系统设置了多层保护:

  1. 最大重试次数assessLogQualityOutputs.size() > 5 时强制终止。
  2. DiagnosisTermination :终止标志写入黑板,触发 shouldTerminate 条件,路由到 generateFailureReport
  3. Agent 级超时AgentServicerunWithTimeout 方法设置 300 秒总超时。
  4. StuckHandler:超时后触发诊断钩子,输出黑板状态帮助排查。

8. 实际效果对比

场景 Fast Path 命中 Slow Path 命中 均未命中
典型异常(NPE、SQL 错误) 1-2 轮,5-15秒 --- ---
复杂异常(线程池耗尽) 3-4 轮,20-40秒 1 轮 RAG,40-60秒 失败报告,60秒
偶发异常(间歇性超时) 通常不命中 1-2 轮 RAG,30-60秒 失败报告

在实际测试中,约 70% 的异常 在 Fast Path 的 1-2 轮内就被定位,20% 需要借助 Slow Path 的 RAG 能力,剩余 10% 因日志本身信息不足而输出降级报告。

9. 小结

双路径策略的核心思想是"乐观快速,悲观兜底"------大部分问题走 Fast Path 快速解决,少部分问题通过 Slow Path 深度挖掘。两条路径并行竞速,由 GOAP 规划器根据实时状态自动路由。这种设计既保证了响应速度,又不牺牲诊断深度,是 Agent 架构带来的最大收益之一。


相关推荐
添砖java_8571 小时前
网页聊天室测试报告
java·spring boot·spring·maven
apprentice1 小时前
跳出余弦相似度陷阱:RAG检索本质是筛选
agent·ai编程
ZOnePieceC1 小时前
Go之context
后端
GrowthGPT1 小时前
平台自报的 ROAS 在骗你
agent
先吃饱再说1 小时前
手写一个 mini-cursor:LangChain Agent 开发完全实战
langchain·llm·agent
chanalbert1 小时前
Agent Skill 工程化指南(三):运行与生效 — 运行时的全链路机制
llm·agent
柒和远方1 小时前
Worker Readiness:为什么 `/health` 不能证明后台任务能跑
前端·后端·架构
hai3152475431 小时前
九章编译法:DEEPSEEK V3.2汇编编译实例
java·开发语言