第4章:鲲鹏路线 1——基于 UB 内存池的上下文缓存系统
把"中间数据搬出 LLM 上下文"做成跨节点共享的工程底座——展开数据流、内存布局、典型负载下的延迟分布;和路线 D 单机上下文缓存做精确对照;给出可复现的最小工程骨架
Ch3 结尾给出三处缝隙——这一章正面攻破第一条:跨节点共享的”中间数据池”。鲲鹏 2026 公开方案把这件事建在 UB(统一总线)共享域内存池上,目的是让多步工具链 / 多 Agent 协作时的中间结果不再走 LLM 上下文中转——而是落到一份所有 Agent 进程都能访问的高速共享内存里。这件事的工程红利不只是”省 token”——更深的是把 prefill TTFT 的平方代价直接砍掉。本章把这套系统的数据流、内存布局、延迟分布逐一拆开。
📑 目录
- 1. 鲲鹏路线 1 要解决的具体痛点
- 2. UB 内存池:硬件底座的关键属性
- 3. 上下文缓存的数据流与内存布局
- 4. 典型负载下的延迟与成本分布
- 5. 与单机上下文缓存的精确对照
- 6. 最小工程骨架
- 自我检验清单
- 参考资料
1. 鲲鹏路线 1 要解决的具体痛点
1.1 单机上下文缓存的三个工程缺口
Ch3 的路线 D(上下文缓存)方向是对的,但当前业内单机实现有三个具体缺口:
| 缺口 | 表现 | 真实代价 |
|---|---|---|
| 跨进程不可见 | Agent A 缓存的中间结果 Agent B 看不到 | 多 Agent 协作必须在每个 Agent 重新算一遍 |
| 跨节点不可见 | 推理服务做了 PD 解耦后,Prefill 节点的缓存 Decode 节点看不到 | KV cache / 工具结果都得跨网传 |
| 生命周期粗糙 | 单机内存压力大就直接逐出,没有”按用户/按会话”的细粒度策略 | 高频用户的会话 cache 频繁丢失 |
这三个缺口共有一个根本原因:当前上下文缓存被实现为”进程内 / 单机内”的数据结构——而 Agent Memory 真正的工作负载是跨进程、跨节点、跨会话的。
1.2 鲲鹏路线 1 的核心定位
把这件事放在系统架构图里:
┌─────────────────────────────────────┐
│ 用户 / 应用层 │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Agent Harness (LangGraph / OpenClaw 等) │
│ - 工具调度 │
│ - 上下文组装 │
└─────────┬───────────────────────┬────┘
│ │
┌─────────▼─────────┐ ┌────────▼─────────┐
│ Prefill 节点 1 │ │ Prefill 节点 2 │
│ Decode 节点 1 │ │ Decode 节点 2 │
└─────────┬─────────┘ └────────┬─────────┘
│ │
└───────────┬───────────┘
▼
┌─────────────────────────────────────┐
│ ⭐ UB 共享域内存池(鲲鹏路线 1) │
│ - 跨节点 / 跨进程共享 │
│ - 中间数据 / KV cache / 工具结果 │
│ - 微秒级随机读 │
└─────────────────────────────────────┘
🌟 核心定位:把”上下文缓存”从单机数据结构升级为跨节点共享的系统服务——这是鲲鹏路线 1 的工程原创性。
2. UB 内存池:硬件底座的关键属性
2.1 UB 是什么
UB(Unified Bus,统一总线)是华为面向数据中心场景定义的高速互联协议。从 Agent Memory 系统的视角,它的关键属性可以归纳为四条:
| 属性 | 数值范围 | 工程含义 |
|---|---|---|
| 单次访问延迟 | 微秒级(典型 1-3 µs) | 接近本地 DRAM 量级,比 SSD 快 50-100x |
| 单链路带宽 | 数十到上百 GB/s | 接近 PCIe 5.0 / DDR5 带宽 |
| 池容量 | 跨节点合池 | 单机 DIMM 装不下时仍能扩 |
| 协议特性 | load/store 语义 + RDMA 原语 | 既能像内存一样访问,也能像 RDMA 一样原子操作 |
🍎 直觉比喻:本地 DRAM 是”你的座位旁边的小抽屉”,单机 SSD 是”办公室档案柜”——UB 内存池是部门所有人共享的高架资料柜,离每个人都很近,但是不在每个人面前。
2.2 为什么 UB 适合做”上下文缓存”
把 Agent Memory 系统的访问模式画出来:
| 访问类型 | 频率 | 数据大小 | 延迟敏感度 |
|---|---|---|---|
| 工具调用结果写入 | 中(每秒数十次) | KB-MB 级 | 中 |
| 工具调用结果读取 | 中-高 | KB-MB 级 | 高 |
| KV Cache 跨节点共享 | 高(每 token) | 几十 MB | 极高 |
| 长会话历史读写 | 低-中 | KB 级 | 中 |
UB 内存池的”微秒级 + 几十 GB/s + 跨节点共享”这三个属性恰好同时命中这四类访问的最严格者(KV Cache 共享)。这是它作为”上下文缓存底座”的天然适配。
2.3 UB 不能解的事
公平地说,UB 也不是万能的:
- ❌ 比 DRAM 仍慢 30-50x——HBM / DRAM 内的数据不应该被搬到 UB 池
- ❌ 跨节点协议有头部开销——超小数据(几十字节)单次访问性价比低
- ❌ 运维复杂度高——需要专用网络、专用驱动、专用协议栈
把这些限制翻译过来:UB 适合做KB-MB 量级、跨节点必需、可批量访问的中间数据 / KV cache;不适合做高频微小元数据 / 仍需 DRAM 速度的热数据。
3. 上下文缓存的数据流与内存布局
3.1 一次”工具调用”的完整数据流
把 Agent 调用一个工具(比如 search 返回 5 篇文档)的完整数据流画出来:
Step 1: Agent 决定调用 search
│
▼
Step 2: Harness 把 search 的请求参数组装好
│
▼
Step 3: 调 search 工具
│
▼
Step 4: search 返回 5 篇文档(~15K tokens)
│
├── 4a. ⭐ Harness 把文档原文写入 UB 内存池
│ (key: ctx_42_search_step3, ttl: 1h)
│
▼
Step 5: Harness 给 LLM 上下文里只放:
│ "<search_result id='ctx_42_search_step3' summary='5 papers about...'>"
│ (~200 tokens, 不是 15K)
│
▼
Step 6: LLM 决定下一步——可能引用 step3 也可能不引用
│
▼
Step 7: 如果 LLM 决定 "summarize step3"
│
├── 7a. ⭐ Harness 从 UB 池读回原文
│ (read by id, latency ~1-3 µs)
│
├── 7b. 把原文 chunked 后传给 summarize 工具
│
▼
Step 8: summarize 输出摘要(~1K tokens)
│
├── 8a. 摘要写入 UB 池 (key: ctx_42_summary_step7)
▼
Step 9: LLM 上下文里只保留 step3 + step7 的指针 + 摘要
⭐ 关键观察:在这个流里,LLM 上下文从未直接持有 15K 原文——只持有”指针 + 短摘要”。原文只在工具间传递时被取出来用一次。
3.2 UB 内存池的内存布局
实现这个数据流,UB 内存池里至少要维护四类 region:
| Region | 数据类型 | 生命周期 |
|---|---|---|
ctx_<sess>_<step> | 工具调用中间结果 | 会话级 / 步级 |
kv_<sess>_<layer> | KV Cache 跨节点共享段 | 推理生命周期 |
hist_<user>_<turn> | 长会话历史 | 用户级(带时效衰减) |
meta_<obj_id> | 上述各类的元数据 / 摘要 / 索引 | 同源 region |
每个 region 都有:
struct UBRegion {
uint64_t id; // 全局唯一
uint32_t size; // 实际占用
uint8_t tier; // 0=hot, 1=warm, 2=cold
uint64_t created_at;
uint64_t ttl_ms;
uint32_t ref_count; // 跨节点引用计数
char content[]; // 实际数据
};
引用计数 ref_count 是关键——它让”跨节点共享”变得可显式管理:
- Prefill 节点 1 写入一段中间结果 → ref_count = 1
- Decode 节点 2 读这段时持有引用 → ref_count = 2
- 两端都释放后才被 GC
3.3 与 KV Cache 体系的耦合
不只是工具调用结果——KV Cache 本身也可以放进 UB 池。这件事在 PD(Prefill-Decode)解耦推理里特别有价值:
- Prefill 节点把 KV cache 写到 UB 池
- Decode 节点直接从 UB 池读取——不需要把 KV cache 通过网络再传一遍
这等价于把第十四模块《长记忆大模型系统》第 4 章讨论的 LMCache / AttentionStore / Mooncake 这一类工作的工业落地直接套进了同一份硬件底座。
🌟 关键判断:上下文缓存和 KV cache 跨节点共享是同一种系统能力的两个切片——它们都需要”跨节点 + 微秒级 + 大容量”的中间档介质。鲲鹏路线 1 把它们统一在同一份 UB 池里,这是它的工程经济学。
4. 典型负载下的延迟与成本分布
4.1 三类典型负载
把鲲鹏路线 1 在三种典型 Agent 负载下的关键指标对照:
| 负载 | 工具数 | 中间数据规模 | 多节点协作 |
|---|---|---|---|
| 轻负载客服 | 5-10 | 总计 ~5K tokens | 单节点 |
| 中负载工作助手 | 30-60 | 总计 ~50K tokens | 2-4 节点 |
| 重负载 BI / 多 Agent | 60+ | 总计 ~200K+ tokens | 4-8 节点 |
4.2 延迟和成本对照
| 指标 | 中负载工作助手 | 重负载多 Agent |
|---|---|---|
| 基线(单机内 LLM 上下文走全部中间数据) | ||
| 单次 prefill TTFT | ~3 秒 | ~15 秒 |
| 总 token 消耗 / 任务 | ~50K | ~250K |
| 任务总成本(GPT-4 价位) | ~$0.30 | ~$1.50 |
| + 鲲鹏路线 1(UB 上下文缓存) | ||
| 单次 prefill TTFT | ~0.8 秒 | ~3.5 秒 |
| 总 token 消耗 / 任务 | ~15K | ~70K |
| 任务总成本 | ~$0.09 | ~$0.42 |
| 改善 | TTFT ↓ 70%, 成本 ↓ 70% | TTFT ↓ 75%, 成本 ↓ 72% |
⭐ 观察:改善并不是”线性叠加”——它在重负载下的相对收益反而更大。原因是 prefill 的 ——把 prompt 砍一半,prefill 时间砍 75%。
4.3 UB 池本身的延迟开销
加入 UB 内存池一层会引入额外的访问开销:
| 操作 | 单次开销 | 触发频率 | 累计延迟 |
|---|---|---|---|
| 写中间结果到 UB | ~3-5 µs | 每工具一次 | 工具调用占大头,~µs 不显著 |
| 从 UB 读回 | ~1-3 µs | LLM 引用时 | 同上 |
| 跨节点引用建立 | ~10-20 µs | 跨节点协作时 | 显著但仍 < 1ms |
| GC / ref_count 维护 | 异步 | 周期性 | 不在关键路径 |
总额外延迟 < 1 ms 量级——相比节省的几秒级 prefill 时间,完全可以接受。
5. 与单机上下文缓存的精确对照
| 维度 | 单机上下文缓存 (路线 D) | 鲲鹏路线 1 (UB 上下文缓存) |
|---|---|---|
| 缓存位置 | 进程内 / 单机内 | 跨节点共享池 |
| 跨进程可见 | ❌ | ✅ |
| 跨节点可见 | ❌ | ✅ |
| KV cache 共享 | ❌ | ✅ |
| 写入延迟 | < 1 µs(本地内存) | ~3-5 µs |
| 读出延迟 | < 1 µs | ~1-3 µs |
| 容量 | 单机内存上限 | 池容量(多机合池) |
| 生命周期管理 | 简单 LRU | 多 tier + ref_count |
| 工程复杂度 | 低 | 中-高(需要新硬件 / 协议栈) |
| 适用场景 | 单 Agent / 单机推理 | 多 Agent 协作 / PD 解耦推理 |
🌟 关键判断:路线 D 没有错——它在单 Agent / 单机场景下完全够用。鲲鹏路线 1 是路线 D 的”超集”——它把同样的能力扩展到跨节点场景,但代价是引入新硬件和工程复杂度。这个取舍是否合算,取决于你的工作负载:
- 单机轻负载 → 路线 D 就够
- 多 Agent 协作或 PD 解耦推理 → 鲲鹏路线 1 的红利显著
6. 最小工程骨架
把鲲鹏路线 1 的核心机制用 Python 伪代码写出来,作为团队对照设计的起点:
import time
import uuid
from dataclasses import dataclass, field
from typing import Optional
# ═══════════════════════════════════════════════════════════
# UB 内存池抽象(实际实现在 C/Rust + 专用驱动)
# ═══════════════════════════════════════════════════════════
@dataclass
class UBRegion:
rid: str
content: bytes
size: int
tier: int = 0 # 0=hot, 1=warm, 2=cold
created_at: float = field(default_factory=time.time)
ttl_ms: int = 3600 * 1000
ref_count: int = 1
summary: str = "" # 给 LLM 上下文用的短摘要
class UBPool:
"""跨节点共享内存池——伪实现"""
def __init__(self):
self.regions: dict[str, UBRegion] = {}
def write(self, content: bytes, summary: str, ttl_ms: int = 3600*1000) -> str:
rid = str(uuid.uuid4())
self.regions[rid] = UBRegion(
rid=rid, content=content, size=len(content),
ttl_ms=ttl_ms, summary=summary,
)
return rid
def read(self, rid: str) -> Optional[bytes]:
r = self.regions.get(rid)
return r.content if r else None
def get_summary(self, rid: str) -> str:
r = self.regions.get(rid)
return r.summary if r else ""
def acquire(self, rid: str):
if rid in self.regions:
self.regions[rid].ref_count += 1
def release(self, rid: str):
if rid in self.regions:
self.regions[rid].ref_count -= 1
if self.regions[rid].ref_count <= 0:
del self.regions[rid]
# ═══════════════════════════════════════════════════════════
# Harness 端:把"中间数据搬出 LLM 上下文"
# ═══════════════════════════════════════════════════════════
class ContextCachingHarness:
def __init__(self, ub_pool: UBPool):
self.pool = ub_pool
def call_tool_with_caching(self, tool_func, *args, **kwargs):
"""调用一个工具,把结果写入 UB 池,返回指针 + 摘要"""
result = tool_func(*args, **kwargs)
# 1. 把完整结果序列化
payload = serialize(result)
# 2. 生成简短摘要(让 LLM 在上下文里能"看见"这件事)
summary = make_summary(result, max_tokens=100)
# 3. 写入 UB 池
rid = self.pool.write(payload, summary=summary)
# 4. 返回 LLM 上下文用的"指针 + 摘要"
return f"<cached id='{rid}' summary='{summary}'/>"
def materialize_for_step(self, rids: list[str], target_tool):
"""LLM 决定要把某些缓存的中间数据传给下一步工具时使用"""
actual_payloads = []
for rid in rids:
self.pool.acquire(rid)
actual_payloads.append(self.pool.read(rid))
try:
return target_tool(*actual_payloads)
finally:
for rid in rids:
self.pool.release(rid)
# ═══════════════════════════════════════════════════════════
# 用法示例
# ═══════════════════════════════════════════════════════════
pool = UBPool()
harness = ContextCachingHarness(pool)
# Agent 第一步:调 search
search_pointer = harness.call_tool_with_caching(
search_tool, query="best SSD for AI training")
# search_pointer 是 "<cached id='abc-123' summary='5 papers, ranked by ...'/>"
# 它进入 LLM 上下文——只占 ~50 tokens 而不是 ~15K
# LLM 决定 summarize 这条 search 结果
search_rid = parse_id(search_pointer)
summary_result = harness.materialize_for_step(
rids=[search_rid], target_tool=summarize_tool)
# summarize_tool 拿到完整原文,但 LLM 上下文里只看到指针
这份代码不到 80 行,但它体现了鲲鹏路线 1 的核心机制:
- 写入时给 LLM 留摘要 + 指针——LLM 上下文只看到
<cached id summary/> - 真正引用时由 Harness 物化——把 UB 池的内容传给下一步工具
- 引用计数 + ref_count 自动 GC——跨节点协作不会泄漏
🍎 直觉比喻:这套机制好比把”会议室的所有文件”搬到”档案柜”——会议室里只放”文件号清单”,谁要用到具体文件就去柜子里取一下。这样会议室空气清新,prefill 也快。
🎯 自我检验清单
- 单机上下文缓存(路线 D)和鲲鹏路线 1 的核心区别是什么?跨节点协作场景下哪一类性价比更高?
- UB 内存池的”微秒级 + 几十 GB/s + 跨节点共享”这三个属性各自命中哪些 Agent Memory 访问模式?
- 解释”prefill TTFT 是 “——为什么把上下文砍一半能让 TTFT 快 4 倍而不是 2 倍?
- 在第 6 节最小骨架里,
acquire / release引用计数解决的是什么具体的工程问题? - 如果你的项目目前是单机推理 + 单 Agent,路线 D 就够吗?什么时候才值得迁移到鲲鹏路线 1?
📚 参考资料
- 鲲鹏 Agent Memory 创新方案(华为鲲鹏团队 2026 公开发表)—— 路线 1 详细技术披露
- 本系列模块十三 第 6 章 RDMA verbs 实战
- 本系列模块十四 第 4 章 KV Cache 跨层级管理论文精读(LMCache / AttentionStore / Mooncake 等)
- CXL 3.0 Specification(2024)
- Mooncake(KAIST + Moonshot 2024)—— PD 解耦推理 KV cache 跨节点共享代表作
- NVIDIA NIXL(2025)—— 推理框架的 KV cache 抽象层
下一章预告:Ch5 进入鲲鹏路线 2——基于 UB 内存池的全局向量检索——用同一份共享底座装下千亿级 HNSW 整图,避开 Ch3 路线 E(HNSW 分片)的召回质量损失。