我们在把 Agent 做到企业级生产环境之后,很快会遇到一个更基础、也更现实的问题:

为什么同样是一个 Agent,有时响应很快,有时特别慢?为什么上下文一长,首字响应就变慢?为什么输出一多,整体耗时明显增加?为什么每次都带着同样的系统提示词、工具定义和输出格式,成本却居高不下?为什么自建推理服务时,显存明明很大,却很快被打满?

这些问题背后,都绕不开一个关键词:

缓存。

不过,在大模型和 Agent 系统里,“缓存”不是一个单一概念。至少要分清三类:

KV Cache: 模型推理内部缓存,用于加速单次生成过程。

Prompt Caching: 推理服务侧缓存重复 prompt 前缀,用于跨请求复用。

应用缓存: 业务系统缓存检索结果、工具结果、中间产物或最终结果。

它们都叫缓存,但所在层次、缓存内容、生命周期、风险点和治理方式完全不同。

如果不理解这三类缓存,Agent 很容易变成一个又慢、又贵、又难以追溯的“长 prompt 系统”。

一、为什么大模型推理需要缓存?

大语言模型不是一次性“想好答案”再输出,而是一个 token 一个 token 地生成。

例如,当模型回答一句话时,它并不是同时生成整段文字,而是先生成第一个 token,再根据已有内容生成第二个 token,然后继续生成下一个 token。

这类生成方式叫做自回归生成

在 Transformer 架构中,每个 token 都会通过 Attention 机制关注前面的 token。简单说,模型每生成一个新 token,都要“回看”前面已经出现过的内容,判断哪些信息对当前生成最重要。

Attention 机制中有三个关键概念:

Query:当前 token 想查询什么信息。

Key:历史 token 提供的可匹配特征。

Value:历史 token 真正贡献给当前 token 的信息。

经典 Attention 公式可以简化表示为:

Attention(Q, K, V) = softmax(QKᵀ / √dₖ) V

这意味着,模型在生成每一个新 token 时,都要拿当前 token 的 Query 去匹配历史 token 的 Key,再根据匹配结果加权读取 Value。

问题来了:

如果每生成一个 token,都重新计算前面所有 token 的 Key 和 Value,成本会非常高。

所以,KV Cache 出现了。

二、KV Cache:模型内部的“推理笔记”

KV Cache 的基本思想很简单:

已经计算过的历史 token 的 Key 和 Value,不要重复算,直接缓存起来。

大模型推理通常可以拆成两个阶段。

第一阶段叫Prefill

模型一次性读取完整输入,包括系统提示词、用户问题、历史对话、工具定义、检索内容、输出格式等。这个阶段会计算输入中所有 token 在每一层 Attention 里的 Key 和 Value,并写入缓存。

第二阶段叫Decode

模型开始逐 token 生成答案。每生成一个新 token,只需要计算这个新 token 的 Query、Key 和 Value。历史 token 的 Key 和 Value 可以直接从 KV Cache 里读取,不需要重新计算。

可以用一个比喻理解。

Prefill 阶段像是“先把材料通读一遍,并做好笔记”。

Decode 阶段像是“基于已经做好的笔记,一句一句写报告”。

如果没有 KV Cache,模型每写一句话,都要把整份材料重新读一遍。

有了 KV Cache,模型就可以复用前面做好的“注意力笔记”。

这就是为什么 KV Cache 对推理速度非常重要。

三、KV Cache 不是记忆,更不是业务状态

这里有一个常见误解:

很多人会把 KV Cache 理解成模型的“记忆”。

这并不准确。

KV Cache 不是长期记忆,也不是用户偏好记忆,更不是业务状态。

它只是模型推理过程中的中间张量缓存,缓存的是每一层 Attention 已经计算出来的 Key 和 Value。

用户长期偏好、任务状态、审批记录、工具调用结果、证据版本、业务流程状态,都必须由企业系统自己的数据库、日志系统或状态管理系统维护,不能指望 KV Cache 帮你保存。

从工程角度看,KV Cache 的内存占用可以近似理解为:

KV Cache Memory
batch_size
  × sequence_length
  × num_layers
  × 2
  × num_kv_heads
  × head_dim
  × bytes_per_element

其中:

2 表示 Key 和 Value 两份缓存;
sequence_length 表示上下文长度和生成长度;
num_layers 表示模型层数;
bytes_per_element 取决于 FP16BF16INT8 等精度。

这个公式说明一个重要事实:

KV Cache 的显存占用,会随着上下文长度、输出长度和并发请求数线性增长。

所以在自建推理服务时,瓶颈往往不只是模型参数本身,还包括大量并发请求带来的 KV Cache 显存压力。

长上下文、多轮对话、长输出、大批量并发,都会让 KV Cache 快速膨胀。

四、Prompt Caching:重复 prompt 前缀不要每次重算

KV Cache 解决的是一次生成过程内部的重复计算问题。

但 Agent 系统还有另一个非常常见的浪费:

很多请求的前半部分几乎一模一样。

例如系统角色说明;安全边界;工具定义;输出 JSON Schema;固定 few-shot 示例;公司内部通用规则;Agent 的通用工作原则。

这些内容在每次调用模型时都会重复出现。如果每次都从头处理一遍,就会浪费 Prefill 计算。

这就引出了 Prompt Caching。

Prompt Caching 是推理服务侧对重复 prompt 前缀的跨请求复用。

它的核心逻辑是:如果多个请求拥有相同的 prompt 前缀,推理服务就可以复用之前处理过的前缀结果,从而降低延迟和输入 token 成本。

这对 Agent 架构非常重要。

因为 Agent 往往会多次调用模型,而每次调用都会带上类似的系统提示词、工具定义和输出格式。如果这些静态内容能够稳定复用,就能显著降低成本和延迟。

但 Prompt Caching 有一个关键前提:

重复内容必须尽量保持完全一致,并且最好放在 prompt 的前部。

如果每次都动态拼接 prompt,工具顺序随机变化,系统提示词经常调整,Schema 字段顺序不稳定,或者把用户问题、检索结果放在最前面,就会破坏缓存命中率。

五、三种缓存必须分清楚

KV Cache、Prompt Caching 和应用缓存经常被混在一起讲,但它们本质上不是一回事。

类型 缓存位置 缓存内容 生命周期 主要收益
KV Cache 模型推理引擎内部 Attention 层的 Key / Value 张量 通常随一次生成或会话存在 加速 Decode,避免重复计算历史 token
Prompt Caching 服务商或推理服务侧 重复 prompt 前缀对应的中间结果 由服务策略决定,通常是短时或延长保留 降低重复前缀的 Prefill 成本和延迟
应用缓存 企业业务系统 检索结果、工具结果、上下文编译结果、Agent 输出 由业务系统设置 TTL、版本和权限 减少重复业务调用,提高响应速度,控制成本

可以更简单地概括为:KV Cache是解决同一次生成中不要重复计算历史 token;Prompt Caching是解决跨请求不要重复处理相同 prompt 前缀。应用缓存是解决业务系统不要重复查同一份数据、跑同一个工具、生成同一个中间结果。三者解决的问题不同,不能相互替代。

Prompt Caching 不能替代业务缓存。

业务缓存也不能替代 KV Cache。

KV Cache 更不能当成 Agent 的长期记忆。

六、Agent 的 prompt 组织方式,会直接影响性能

Agent 系统里,每一轮模型调用都要构造上下文。

上下文怎么组织,会直接影响输入 token 成本、首字延迟、Prompt Caching 命中率、模型是否容易抓住重点、工具调用是否稳定、后续 trace 和回放是否清晰。

一种不利于缓存的结构是把用户问题、动态检索结果、临时任务状态、系统指令、工具定义、输出 Schema、历史对话放在前面。这种结构的问题是,最前面的内容每次都在变化,导致后面即使有大量相同的系统指令和工具定义,也难以形成稳定前缀

更合理的结构应该是:

静态前缀:
  系统身份
  安全边界
  通用工作原则
  稳定工具定义
  输出 Schema
  固定示例
半静态内容:
  当前 Agent Skill 说明
  当前业务模块配置
  规则库版本摘要
  工具版本说明
动态后缀:
  用户问题
  当前任务状态
  检索结果
  工具返回结果
  本轮任务目标

也就是说,Agent 的上下文不应该随意拼接,而应该由专门的Context BuilderContext Compiler生成。

它要保证系统指令稳定、工具定义顺序稳定、输出 Schema 稳定、固定示例稳定、静态规则摘要稳定、版本号明确、动态内容集中放在后部。

这不仅有利于缓存,也有利于模型稳定输出。

七、大体量知识不要直接塞进上下文

很多企业建设 Agent 时,会自然产生一个想法:

既然现在大模型上下文窗口越来越长,那能不能把制度、方法论、规则、历史案例、字段说明、工具文档全部塞进去?

通常不建议这么做。

原因有五个。

第一,长上下文会增加 Prefill 成本,首字响应会变慢。

第二,过多无关信息会稀释重点,模型更难抓住当前任务真正需要的约束。

第三,长上下文不等于可靠引用,模型仍可能引用错误版本,或者忽略关键条款。

第四,大量动态内容会破坏 Prompt Caching 的前缀稳定性。

第五,敏感材料进入模型上下文后,缓存、日志、审计和数据出域治理都会变复杂。

更好的方式是大体量知识放在外部系统中,本轮只加载最相关的部分

例如在指数研发 Agent 中,不应每次把所有指数方法论、指标元数据、公式模板、历史案例和规则组件全部塞进 prompt,而应该把它们放在:

知识库;
规则库;
方法论库;
指标元数据库;
公式库;
文件系统;
数据库;
MCP Resource;
DSL 配置库。

模型每一轮只接收当前任务真正需要的证据片段,并且证据片段要带上元数据:

{
  "evidence_id": "METHODOLOGY-CSI-AI-20260401-001",
  "source_type": "index_methodology",
  "title": "中证人工智能主题指数编制方案",
  "version": "2026-04-01",
  "effective_date": "2026-04-15",
  "status": "current",
  "snippet": "样本空间由满足流动性要求的 A 股上市公司证券组成……",
  "related_fields": ["universe", "liquidity_filter", "selection_rule"],
  "permission_scope": "internal_research",
  "citation_required": true
}

这样,模型看到的是与当前任务有关的证据摘要、版本、生效日期、权限范围和引用位置,而不是整个文档仓库。

这就是 Context Compiler 的价值。

它不是简单的 top-k 检索,而是根据任务阶段、业务类型、用户权限、版本时点和证据质量,把外部知识编译成适合本轮模型调用的上下文。

八、长对话要做状态压缩,而不是无限累积历史

Agent 往往不是一轮完成任务,而是多轮推进。

例如:

用户提出需求;
Agent 抽取字段;
用户补充规则;
Agent 检索依据;
Agent 调用工具;
用户修改参数;
Agent 生成草稿;
用户要求重写;
Agent 发起复核。

如果每一轮都把完整历史对话、全部工具结果、全部检索证据放进上下文,prompt 会越来越长,成本和延迟都会持续上升。

更合理的做法是context compaction,也就是把长期运行中的上下文压缩成结构化状态。

但企业级 Agent 的状态压缩,不能只是让模型写一段自然语言总结。

更好的方式是形成结构化任务状态,例如像下面所示指数编制的任务:

{
  "task_id": "IDX-RD-20260521-0001",
  "task_type": "index_methodology_draft",
  "confirmed_fields": {
    "asset_class": "A股股票",
    "theme": "人工智能",
    "universe": "中证全指",
    "selection_count": 50
  },
  "pending_fields": [
    "主题评分口径",
    "缓冲区规则",
    "权重上限比例"
  ],
  "tool_results": [
    {
      "tool": "search_methodology_docs",
      "result_id": "SRCH-001",
      "summary": "找到 3 个相似主题指数方案",
      "version": "2026-05-20"
    }
  ],
  "decisions": [
    {
      "field": "universe",
      "value": "中证全指",
      "confirmed_by": "user",
      "time": "2026-05-21T10:00:00"
    }
  ],
  "risk_flags": [
    "主题边界仍需人工确认",
    "权重上限未明确"
  ],
  "status": "AI_DRAFT"
}

这样做有两个好处。

一是减少上下文长度。

二是降低模型误解长历史对话的概率。

业务状态应该由系统显式保存,而不是寄希望于模型“还记得前面说过什么”。

九、应用缓存:企业 Agent 必须自己做

虽然模型服务商提供了 Prompt Caching,企业业务系统就还是要自己做缓存了。因为Prompt Caching 只能复用重复 prompt 前缀,不能判断业务结果是否可复用。这些都是需要工程化来实现的。

十、自建推理服务时,KV Cache 会成为核心工程问题

如果企业只是调用外部大模型 API,重点关注的是:

输入 token;
输出 token;
请求次数;
工具调用耗时;
Prompt Caching 命中率;
应用缓存命中率;
整体链路延迟。

但如果企业要自建模型推理服务,问题会进一步下沉到推理系统层面。

这时需要关注事项还是比较多的,包括但是不限于KV Cache 显存占用、、请求调度、``显存碎片、``PagedAttention、``模型量化、``tensor parallelism、``pipeline parallelism、``speculative decoding、``prefix cache、``多租户隔离、``GPU 利用率、``吞吐与延迟平衡。

其中,PagedAttention 是一个很有代表性的优化方向。它借鉴操作系统分页思想,把 KV Cache 管理从简单的连续显存分配,转向更灵活的分页管理,从而降低显存碎片和重复复制,提高高并发服务能力。

这说明企业自建推理服务,不是把模型部署到 GPU 上就结束了。真正困难的是推理工程、显存管理、调度策略和服务治理,这是需要较强的对系统的理解的。

因此,外部 API 和自建推理并不是简单二选一。

更现实的做法通常是分层使用:低敏、高复杂任务可以使用外部强模型。高敏、内部数据任务使用私有模型、专有通道或本地化部署模型。``确定性计算任务仍由内部业务系统、规则引擎、计算引擎完成。``高频、简单任务使用小模型、轻量模型或规则系统。

对企业而言,关键不是追求“所有任务都用一个大模型解决”,而是建立合理的模型分层和任务分流机制。

十一、Agent 系统中常见的缓存错误

建设 Agent 时,以下错误非常常见。

第一,把 KV Cache 当成长期记忆。

KV Cache 只是推理中间张量,不是业务记忆。用户偏好、任务状态、审批状态、证据版本和工具结果,都必须由业务系统保存。

第二,以为上下文越长越好。

上下文越长,Prefill 成本越高,首字延迟越高,模型也更容易被无关信息干扰。长上下文必须配合 Context Compiler,而不是简单堆材料。

第三,把动态内容放在 prompt 最前面。

用户问题、检索结果、工具返回结果放在最前面,会破坏 Prompt Caching 的前缀匹配。静态内容应尽量前置,动态内容应尽量后置。

第四,工具定义和 Schema 每次动态变化。

工具顺序、字段名、Schema 结构频繁变化,会影响缓存命中率,也会降低模型工具调用的稳定性。

第五,只优化输入 token,不控制输出 token。

很多 Agent 慢,不是因为输入太长,而是因为输出太长、串行步骤太多。中间步骤应尽量输出结构化短结果,最终阶段再生成完整文本。

第六,缓存业务结果但不带版本。

指数规则、样本空间、主题池、行业分类、回测数据、工具版本都会变化。缓存 key 如果不包含这些版本信息,就很容易误用旧结果。

第七,缓存忽略权限。

不同用户、部门、项目的权限不同。应用缓存必须把权限范围纳入 cache key 或访问控制,否则可能发生数据泄露。

第八,把 Prompt Caching 当成确定性复用。

Prompt Caching 复用的是重复 prompt 前缀的处理结果,不是复用最终回答。它不能替代业务缓存,也不能保证业务输出天然一致。