LLM 应用的 Graceful Degradation 工程实践:5 层降级策略让你的 AI 功能永不完全崩溃

一句话定义:Graceful Degradation 不是"失败时报错",是"失败时给用户一个质量递减但仍可用的答案"。

为什么你现在就需要想清楚降级

凌晨 2:17,你的 LLM 服务商宣布维护窗口,预计 40 分钟。

你的 AI 写作助手有 6,000 个在线用户。你的选择是:

  • 返回 500,用户一脸懵
  • 显示"服务暂时不可用,请稍后再试"
  • 继续给用户提供一个质量稍差、但仍然有用的响应

第三条路就是 Graceful Degradation。

但它不是一个简单的 if-else。做不好,你会面临三个陷阱:

  1. 降级质量太差:用户宁愿等,也不要一个明显是模板的答案
  2. 降级成本比主路径更贵:某些 fallback 方案在大流量下比主模型更烧钱
  3. 降级监控缺失:你根本不知道你在降级,也不知道降了多少

这篇文章给你一个可落地的 5 层降级架构,从最高质量到最保底的静态响应,每一层都有实际代码和触发条件。


第一层:模型切换(Model Fallback)

触发条件

  • 主模型 HTTP 5xx / 超时(>= 10s)
  • 主模型 rate limit 429
  • 主模型 context length 超限

架构设计

typescript 复制代码
// fallback-chain.ts
interface ModelConfig {
  provider: string;
  model: string;
  maxTokens: number;
  timeoutMs: number;
  costPerKToken: number;
}

const FALLBACK_CHAIN: ModelConfig[] = [
  {
    provider: "deepseek",
    model: "deepseek-chat",
    maxTokens: 8192,
    timeoutMs: 30000,
    costPerKToken: 0.14,
  },
  {
    provider: "qwen",
    model: "qwen-plus",
    maxTokens: 4096,
    timeoutMs: 15000,
    costPerKToken: 0.08,
  },
  {
    provider: "zhipu",
    model: "glm-4-flash",
    maxTokens: 4096,
    timeoutMs: 20000,
    costPerKToken: 0.05,
  },
];

async function callWithFallback(
  messages: Message[],
  options: CallOptions = {}
): Promise<LLMResponse> {
  const errors: Error[] = [];

  for (const config of FALLBACK_CHAIN) {
    try {
      const result = await callModel(config, messages, options);

      // 记录降级事件
      if (config !== FALLBACK_CHAIN[0]) {
        metrics.increment("llm.fallback.used", {
          from: FALLBACK_CHAIN[0].model,
          to: config.model,
          reason: errors[0]?.constructor.name,
        });
      }

      return { ...result, degradedTo: config.model };
    } catch (err) {
      errors.push(err as Error);

      // 非降级性错误直接抛出(如认证失败)
      if (isNonRetryableError(err)) throw err;

      logger.warn(`Model ${config.model} failed, trying next`, {
        error: (err as Error).message,
        attempt: errors.length,
      });
    }
  }

  throw new AllModelsFailedError(errors);
}

function isNonRetryableError(err: unknown): boolean {
  const status = (err as any)?.status;
  // 401/403 不重试,是配置问题
  return status === 401 || status === 403;
}

关键细节:同步 vs 异步 fallback

同步 fallback(串行尝试) 适合单次请求场景,延迟叠加但实现简单。

异步 hedge(并行发出,取最快的) 适合对延迟极度敏感的场景,但成本加倍:

typescript 复制代码
async function hedgedCall(
  messages: Message[],
  hedgeAfterMs = 2000
): Promise<LLMResponse> {
  // 先发主模型
  const primaryPromise = callModel(FALLBACK_CHAIN[0], messages);

  // 2 秒后如果主模型还没回,同时发备用
  const hedgePromise = sleep(hedgeAfterMs).then(() =>
    callModel(FALLBACK_CHAIN[1], messages)
  );

  const result = await Promise.any([primaryPromise, hedgePromise]);

  // 赢者取全,输者的请求继续跑完但结果被丢弃
  // 注意:这里有成本浪费,需要评估值不值
  return result;
}

实测数据(基于我们自己的生产系统,p99 延迟改善):

策略 p50 延迟 p99 延迟 成本倍数
串行 fallback(超时触发) +0ms +12s(超时叠加) 1.0x
并行 hedge(2s 触发) +0ms +2.1s 1.6x(压力下)
并行 hedge(500ms 触发) +0ms +0.5s 2.1x

结论:hedge 不是免费的,只在 p99 > 5s 已经影响业务的情况下才值得。


第二层:Semantic Cache 命中(缓存降级)

当所有模型都挂了,或者你不想再花 API 费用,可以尝试从语义缓存里找一个"足够接近"的历史响应。

工作原理

markdown 复制代码
用户请求 → Embedding → 向量搜索 → 相似度 > 阈值 → 返回缓存结果
                                  ↓ 否
                             调用 LLM(正常路径)

实现

python 复制代码
# semantic_cache.py
import numpy as np
from typing import Optional

class SemanticCache:
    def __init__(
        self,
        embedding_model: EmbeddingModel,
        vector_store: VectorStore,
        similarity_threshold: float = 0.92,  # 关键参数
        max_age_hours: int = 24,
    ):
        self.embedding_model = embedding_model
        self.vector_store = vector_store
        self.similarity_threshold = similarity_threshold
        self.max_age_hours = max_age_hours

    async def get(self, query: str) -> Optional[CachedResponse]:
        query_embedding = await self.embedding_model.embed(query)

        results = await self.vector_store.search(
            vector=query_embedding,
            top_k=3,
            filter={"created_at": {"$gte": self._cutoff_timestamp()}},
        )

        if not results:
            return None

        best = results[0]

        # 相似度阈值是核心调参点
        if best.score < self.similarity_threshold:
            metrics.increment("semantic_cache.miss.below_threshold", {
                "score": round(best.score, 2)
            })
            return None

        metrics.increment("semantic_cache.hit", {"score": round(best.score, 2)})
        return CachedResponse(
            content=best.payload["response"],
            cache_score=best.score,
            original_query=best.payload["query"],
            is_degraded=True,  # 标记:这是降级响应
        )

    async def set(self, query: str, response: str, metadata: dict = {}):
        embedding = await self.embedding_model.embed(query)
        await self.vector_store.upsert(
            vector=embedding,
            payload={
                "query": query,
                "response": response,
                "created_at": time.time(),
                **metadata,
            },
        )

阈值怎么选?

这是语义缓存的核心调参问题。太高:命中率低,降级价值有限。太低:用户收到牛头不对马嘴的答案。

python 复制代码
# 建议的阈值校准方法:用你的历史 QA 对做测试
def calibrate_threshold(qa_pairs: list[tuple[str, str]], embedding_model) -> float:
    """
    对 1000 对历史 QA,计算「换了说法的同一问题」的 embedding 相似度分布。
    选取 P10 作为阈值(90% 的同语义问题能命中)。
    """
    same_intent_scores = []

    for q1, q2 in generate_paraphrases(qa_pairs):
        e1 = embedding_model.embed(q1)
        e2 = embedding_model.embed(q2)
        score = cosine_similarity(e1, e2)
        same_intent_scores.append(score)

    # 使用 P10 作为阈值
    threshold = np.percentile(same_intent_scores, 10)
    print(f"建议阈值:{threshold:.3f}")
    return threshold

我们的实测结果(5 万条生产 query 校准):

  • 相似度分布呈双峰:同语义问题集中在 0.93-0.99,不同问题集中在 0.5-0.75
  • 选 0.92 作为阈值,误命中率 < 0.3%,命中率约 18%(纯降级流量下)

第三层:规则引擎降级(Deterministic Fallback)

有一类问题不需要 LLM 也能回答好:有固定答案的问题。

typescript 复制代码
// rule-engine.ts
interface Rule {
  pattern: RegExp | string[];
  response: string | ((match: RegExpMatchArray) => string);
  confidence: number; // 0-1,用于记录日志和告警
}

const RULES: Rule[] = [
  {
    // 问候类
    pattern: /^(你好|hello|hi|嗨|早上好|下午好|晚上好)/i,
    response: "你好!我是 AI 助手,有什么可以帮你的?",
    confidence: 0.99,
  },
  {
    // 产品价格(从数据库/配置读取,不依赖 LLM)
    pattern: /价格|多少钱|收费/,
    response: async () => {
      const pricing = await pricingService.getCurrent();
      return `当前价格:基础版 ¥${pricing.basic}/月,专业版 ¥${pricing.pro}/月。`;
    },
    confidence: 0.95,
  },
  {
    // 用户账户状态(直接查数据库)
    pattern: /我的账户|余额|到期/,
    response: async (_, userId: string) => {
      const account = await accountService.get(userId);
      return `您的账户余额:¥${account.balance},有效期至 ${account.expiresAt}。`;
    },
    confidence: 0.98,
  },
];

async function tryRuleEngine(
  query: string,
  context: RequestContext
): Promise<RuleResponse | null> {
  for (const rule of RULES) {
    const match = matchRule(query, rule.pattern);
    if (!match) continue;

    const response =
      typeof rule.response === "function"
        ? await rule.response(match, context.userId)
        : rule.response;

    metrics.increment("rule_engine.matched", {
      rule_id: rule.pattern.toString().slice(0, 20),
    });

    return {
      content: response,
      is_degraded: true,
      degradation_tier: "rule_engine",
      confidence: rule.confidence,
    };
  }

  return null;
}

规则引擎的适用场景

  • FAQ 类问题(高频 + 固定答案)
  • 涉及实时数据的查询(账户、订单、价格)
  • 安全边界检查(某些问题永远不应该走 LLM)

不适用

  • 开放式创作
  • 需要上下文推理的问题
  • 答案随时间快速变化的领域

第四层:模板响应(Template Degradation)

规则引擎匹配不上,但你仍然知道用户在问什么类别的问题------这时可以返回一个带占位符的模板响应。

python 复制代码
# template_degradation.py
from enum import Enum
from dataclasses import dataclass

class IntentCategory(Enum):
    WRITING_HELP = "writing_help"
    CODE_REVIEW = "code_review"
    TRANSLATION = "translation"
    SUMMARIZATION = "summarization"
    UNKNOWN = "unknown"

TEMPLATES = {
    IntentCategory.WRITING_HELP: """
抱歉,AI 写作助手暂时无法提供完整服务。

**当前可用的临时方案:**
1. 您可以先写出草稿框架,稍后我们恢复后帮您润色
2. 参考我们的写作指南:[链接]
3. 预计恢复时间:{eta}

我们已记录您的需求,服务恢复后将优先处理。
""",
    IntentCategory.CODE_REVIEW: """
代码审查服务暂时受限。建议:
- 使用 ESLint / SonarQube 进行自动化检查
- 参考 {language} 最佳实践文档
- 预计 {eta} 内恢复完整服务
""",
    IntentCategory.SUMMARIZATION: """
文本摘要服务暂时不可用。

您可以:
1. 手动提取关键段落(通常文章前 1/4 包含主要观点)
2. 使用关键词搜索定位重要内容
3. {eta} 后重试
""",
    IntentCategory.UNKNOWN: """
AI 服务暂时受限,预计 {eta} 恢复。

对于您的请求,建议:
- 先保存您的输入内容
- 稍后重试,或联系客服获取帮助
"""
}

class IntentClassifier:
    """轻量分类器,不依赖 LLM,用关键词 + TF-IDF"""

    def __init__(self):
        self.keyword_map = {
            IntentCategory.WRITING_HELP: ["写", "文章", "润色", "改写", "创作"],
            IntentCategory.CODE_REVIEW: ["代码", "bug", "review", "审查", "调试"],
            IntentCategory.TRANSLATION: ["翻译", "translate", "英文", "中文"],
            IntentCategory.SUMMARIZATION: ["总结", "摘要", "summarize", "概括"],
        }

    def classify(self, text: str) -> IntentCategory:
        scores = {}
        for intent, keywords in self.keyword_map.items():
            score = sum(1 for kw in keywords if kw in text)
            scores[intent] = score

        best = max(scores, key=scores.get)
        return best if scores[best] > 0 else IntentCategory.UNKNOWN


def get_template_response(query: str, eta: str = "30 分钟") -> str:
    classifier = IntentClassifier()
    intent = classifier.classify(query)
    template = TEMPLATES.get(intent, TEMPLATES[IntentCategory.UNKNOWN])

    return template.format(
        eta=eta,
        language=detect_language(query),
    )

模板响应的原则

  • 承认降级,不要伪装成正常响应
  • 给用户一个 ETA(即使是估算的)
  • 提供可操作的替代方案
  • 记录用户的原始请求,服务恢复后主动推送结果

第五层:完全静态响应(Static Fallback)

最后一道防线。所有 LLM、缓存、规则都挂了。

typescript 复制代码
// static-fallback.ts
interface StaticFallback {
  message: string;
  retryAfterSeconds?: number;
  statusPageUrl?: string;
  supportContact?: string;
}

const STATIC_FALLBACK: StaticFallback = {
  message:
    "AI 服务当前不可用,我们正在紧急修复。您的请求已记录,服务恢复后会通知您。",
  retryAfterSeconds: 300,
  statusPageUrl: "https://status.yourapp.com",
  supportContact: "support@yourapp.com",
};

async function staticFallback(
  request: LLMRequest,
  context: RequestContext
): Promise<Response> {
  // 尝试将请求入队(非阻塞)
  try {
    await offlineQueue.enqueue(
      {
        request,
        userId: context.userId,
        timestamp: Date.now(),
      },
      { timeout: 500 }
    );
  } catch {
    metrics.increment("static_fallback.queue_also_down");
  }

  return {
    status: 503,
    body: STATIC_FALLBACK,
    headers: {
      "Retry-After": String(STATIC_FALLBACK.retryAfterSeconds),
      "X-Degradation-Tier": "static",
    },
  };
}

把 5 层串起来:统一降级编排器

typescript 复制代码
// degradation-orchestrator.ts
type DegradationTier = "primary" | "model_fallback" | "semantic_cache" | "rule_engine" | "template" | "static";

interface DegradedResponse {
  content: string;
  tier: DegradationTier;
  latencyMs: number;
  confidence?: number;
  cacheScore?: number;
}

class DegradationOrchestrator {
  constructor(
    private readonly semanticCache: SemanticCache,
    private readonly ruleEngine: RuleEngine,
    private readonly modelFallback: ModelFallbackChain,
    private readonly config: OrchestratorConfig
  ) {}

  async handle(
    request: LLMRequest,
    context: RequestContext
  ): Promise<DegradedResponse> {
    const startTime = Date.now();

    const {
      enableSemanticCache = true,
      enableRuleEngine = true,
      enableTemplates = true,
      primaryTimeoutMs = 30000,
    } = this.config;

    // 第 1 层:正常路径 + 模型 fallback
    try {
      const result = await this.modelFallback.call(
        request.messages,
        { timeoutMs: primaryTimeoutMs }
      );
      return {
        content: result.content,
        tier: result.degradedTo ? "model_fallback" : "primary",
        latencyMs: Date.now() - startTime,
      };
    } catch (err) {
      logger.error("All models failed", { error: err });
      metrics.increment("degradation.all_models_failed");
    }

    // 第 2 层:语义缓存
    if (enableSemanticCache) {
      try {
        const cached = await this.semanticCache.get(
          extractLastUserMessage(request.messages)
        );
        if (cached) {
          return {
            content: this.wrapDegradedContent(cached.content, "缓存响应"),
            tier: "semantic_cache",
            latencyMs: Date.now() - startTime,
            cacheScore: cached.cacheScore,
          };
        }
      } catch (err) {
        logger.warn("Semantic cache failed", { error: err });
      }
    }

    // 第 3 层:规则引擎
    if (enableRuleEngine) {
      const query = extractLastUserMessage(request.messages);
      const ruleResult = await this.ruleEngine.match(query, context);
      if (ruleResult) {
        return {
          content: ruleResult.content,
          tier: "rule_engine",
          latencyMs: Date.now() - startTime,
          confidence: ruleResult.confidence,
        };
      }
    }

    // 第 4 层:模板响应
    if (enableTemplates) {
      const query = extractLastUserMessage(request.messages);
      const eta = await this.estimateRecoveryEta();
      return {
        content: getTemplateResponse(query, eta),
        tier: "template",
        latencyMs: Date.now() - startTime,
      };
    }

    // 第 5 层:静态降级
    const staticResult = await staticFallback(request, context);
    return {
      content: staticResult.body.message,
      tier: "static",
      latencyMs: Date.now() - startTime,
    };
  }

  private wrapDegradedContent(content: string, label: string): string {
    return content;
  }

  private async estimateRecoveryEta(): Promise<string> {
    try {
      const status = await statusMonitor.getIncidentEta();
      return status?.eta ?? "30 分钟";
    } catch {
      return "30 分钟";
    }
  }
}

降级监控:你必须知道你在降级

降级监控是最容易被忽视的部分。我见过不少团队,降级逻辑写了,但完全不知道降级在生产中有多频繁。

必须追踪的指标

typescript 复制代码
// 1. 降级率(按层分)
metrics.gauge("degradation.rate_by_tier", {
  tier: response.tier,
  value: 1,
});

// 2. 降级持续时长
metrics.histogram("degradation.duration_minutes", {
  tier: response.tier,
  value: incidentDurationMinutes,
});

// 3. 语义缓存命中率和相似度分布
metrics.histogram("semantic_cache.hit_score", {
  value: cached.cacheScore,
  buckets: [0.88, 0.90, 0.92, 0.95, 0.98, 1.0],
});

// 4. 用户行为指标(降级期间的留存 vs 离开)
metrics.track("user.action_during_degradation", {
  tier: response.tier,
  action: userAction, // "retry" | "exit" | "continue"
});

告警阈值建议

指标 告警阈值 级别
主模型失败率 > 1% (5min 内) Warning
进入 semantic_cache 层 > 5% (5min 内) Warning
进入 template 层 > 1% (5min 内) Critical
进入 static 层 任何 1 次 Critical
语义缓存命中率跌破 10% - Info(缓存冷却)

Dashboard 设计

一个好的降级 Dashboard 应该在一屏内回答:

  1. 现在在第几层降级?(大字显示当前 p95 降级层)
  2. 降级影响多少用户?(受影响请求数/总请求数)
  3. 什么时候开始的?(降级事件时间轴)
  4. 预期什么时候结束?(ETA from 服务状态页)
python 复制代码
# 伪代码:Grafana Dashboard 数据源查询
SELECT
  tier,
  count(*) as requests,
  count(*) / sum(count(*)) OVER () as percentage
FROM degradation_events
WHERE timestamp > now() - interval '5 minutes'
GROUP BY tier
ORDER BY
  CASE tier
    WHEN 'primary' THEN 1
    WHEN 'model_fallback' THEN 2
    WHEN 'semantic_cache' THEN 3
    WHEN 'rule_engine' THEN 4
    WHEN 'template' THEN 5
    WHEN 'static' THEN 6
  END

5 个真实踩坑

坑 1:没有给降级响应打标记

现象:语义缓存命中的响应被用户截图传播,但内容是 3 天前的旧答案,引发舆情。

修复 :所有降级响应必须带 is_degraded: true 的 flag,UI 层根据这个 flag 决定是否显示"此响应来自缓存"的角标。

typescript 复制代码
headers: {
  "X-Response-Source": response.tier,
  "X-Cache-Score": response.cacheScore?.toFixed(2),
  "X-Degraded": response.tier !== "primary" ? "1" : "0",
}

坑 2:Semantic Cache 阈值太低导致"幻觉缓存"

现象:用户问"明天的天气怎么样",缓存命中了"今天的天气"的历史响应(相似度 0.89)。

修复:对时间敏感类问题,在 embedding 之前过滤,或强制绕过缓存:

python 复制代码
TIME_SENSITIVE_PATTERNS = [
    r"今天|明天|现在|最新|当前|今年|本月|昨天",
    r"today|tomorrow|now|current|latest|yesterday",
    r"最近|近期|刚刚|刚才",
]

def should_bypass_cache(query: str) -> bool:
    return any(
        re.search(pattern, query, re.IGNORECASE)
        for pattern in TIME_SENSITIVE_PATTERNS
    )

坑 3:Fallback 模型成本比主模型更贵

现象:主模型超时,fallback 到一个调用更频繁的备用模型,在流量高峰期的 fallback 成本是主模型的 3 倍。

根因:Fallback 链设计时只考虑了可用性,没考虑成本。每次 fallback 的成本 × fallback 频率才是真实成本。

修复:在 fallback 链中设置每日成本上限:

typescript 复制代码
class CostAwareFallback {
  private dailyFallbackCost = 0;
  private readonly maxDailyFallbackCost = 100; // USD

  async callWithCostGuard(messages: Message[]): Promise<LLMResponse> {
    if (this.dailyFallbackCost > this.maxDailyFallbackCost) {
      throw new CostLimitExceededError();
    }

    const result = await this.call(messages);
    this.trackCost(result);
    return result;
  }
}

坑 4:规则引擎覆盖了 LLM 应该回答的问题

现象:规则引擎匹配到"价格"关键词,返回了固定价格,但用户实际上在问竞品的价格对比分析。

修复:规则引擎只在降级路径激活,不作为主路径的前置过滤器。并且规则匹配条件要足够精确:

python 复制代码
# 错误:过于宽泛
pattern: "价格"

# 正确:精确匹配
pattern: r"你们(的)?价格|产品定价|收费标准|费用是多少"

坑 5:没有"降级退出"机制

现象:主模型恢复了,但流量仍然因为旧的超时 flag 停留在 semantic_cache 层,持续 20 分钟。

修复:降级状态必须有自动恢复机制,不能靠手动切换:

typescript 复制代码
class DegradationStateManager {
  private currentTier: DegradationTier = "primary";
  private lastHealthCheckAt = 0;

  async getCurrentTier(): Promise<DegradationTier> {
    const now = Date.now();

    // 每 30 秒做一次主路径健康探测
    if (now - this.lastHealthCheckAt > 30_000) {
      this.lastHealthCheckAt = now;
      const isHealthy = await this.probe();

      if (isHealthy && this.currentTier !== "primary") {
        logger.info("Primary recovered, exiting degraded mode", {
          from: this.currentTier,
        });
        this.currentTier = "primary";
        metrics.increment("degradation.auto_recovery");
      }
    }

    return this.currentTier;
  }

  private async probe(): Promise<boolean> {
    try {
      await callModel(FALLBACK_CHAIN[0], [
        { role: "user", content: "ping" }
      ], { timeoutMs: 5000 });
      return true;
    } catch {
      return false;
    }
  }
}

架构总结:5 层降级速查表

层级 触发条件 典型延迟 质量 成本
主模型 正常 p50: 2-5s ★★★★★ 基准
模型 fallback 主模型 5xx/超时/429 p50: 3-8s ★★★★☆ 0.5-2x
Semantic Cache 所有模型失败 / 成本熔断 p50: 50-200ms ★★★☆☆ 极低
规则引擎 缓存未命中 + 规则可匹配 p50: 1-10ms ★★★★☆(特定场景) 极低
模板响应 规则未匹配 p50: <1ms ★★☆☆☆
静态响应 全部失败 p50: <1ms ★☆☆☆☆

最后

Graceful Degradation 本质上是一个工程品位问题:你对用户体验的尊重程度,决定了你在系统故障时愿意付出多少额外的工程成本来维持一个"还能用"的体验。

三个核心原则:

  1. 每层降级都要有监控:不知道自己在第几层,就是裸奔
  2. 降级要有出口:30 秒健康探测,自动恢复,不要靠人工切换
  3. 语义缓存是双刃剑:阈值设对了是救命药,设错了是毒药

从这 5 层里选你现在最缺的那一层先做,比什么都不做强。


本文代码示例基于生产系统提炼,TypeScript 和 Python 片段均可直接集成。如有问题,评论区见。

相关推荐
To_OC1 小时前
对接大模型流式接口,我被一个 ReadableStream 卡了半小时
前端·node.js·llm
FII工业富联科技服务10 小时前
工业设备运维如何Agentic化?拆解Factory Brain的设备运维架构
大数据·运维·人工智能·架构·制造
qq295311 小时前
2026 财搭子决策模拟器:多智能体投研架构能力拆解
大数据·人工智能·架构
花椒技术12 小时前
我们把 26 个接口自动化场景接进了 Agent,效果真是没想到!!
agent·ai编程·测试
2601_9557596213 小时前
如何借助 Claude Opus 5 自动整理日报、周报和项目进展
ai编程
桦说编程14 小时前
炮轰SDD:Spec驱动开发为何不适合绝大多数项目
llm·ai编程·代码规范
东小西14 小时前
第15篇:《检索效果太烂?我用Query改写和重排序把准确率翻了一倍》
openai·ai编程
旋生万物15 小时前
五条几何公理重构元素周期表:从量子力学到螺旋拓扑的降维(AI 编程时代的微观启示)
重构·ai编程·代码审计·cursor·ai agent·mcp协议·claudecode
我是大卫16 小时前
【图】一图理解LLM、LangChain、RAG、AI Agent、MCP、token、context、prompt、tool概念
langchain·llm·agent