1. 引言:从「无状态工具」到「有记忆的 Agent」

2024 年以前,大多数 Agent 框架把「技能(Skill)」当成无状态函数:输入 prompt,输出 action,结束即遗忘。这种设计在 demo 里很优雅,但在真实业务中迅速暴露出三个致命问题:

  • 上下文断裂:同一个用户第二次问「把上周那个报表再跑一次」,Agent 完全不知道「上周那个」指什么。
  • 重复推理:每次调用都要重新从海量文档里检索、重新理解意图,Token 成本线性飙升。
  • 经验无法沉淀:用户纠正过一次「不要把 A 数据源和 B 数据源混用」,下次依然犯同样错误。

于是,各个 Agent 框架不约而同地开始给 Skill 加「记忆层(Memory Layer)」。但当每个 Skill 都在构建自己的记忆层时,一个更深刻的变化发生了:

记忆不再是某个 Agent 的附属组件,而正在成为 Runtime 本身的基础设施。

这不是一句概念口号。下面用真实的架构演进、代码实现和性能数字把它讲透。

2. 记忆层的四个演进阶段

观察主流的 Agent 实现(LangChain、LlamaIndex、AutoGen、以及字节/阿里的内部 Agent 平台),记忆层大致经历了四个阶段:

阶段记忆载体典型实现核心问题
L0:无记忆无prompt + function call每次调用从零开始
L1:会话内记忆对话历史数组messages: Message[]会话结束即丢失
L2:跨会话持久记忆向量库 + KV 存储pgvector + Redis检索召回率低,语义漂移
L3:结构化认知记忆分层记忆 + 元记忆episodic + semantic + procedural写入策略、冲突消解复杂

一个关键数据:根据 LangChain 官方 2025 年的状态报告,接入持久记忆的 Agent 应用在生产环境的多轮任务完成率从 41% 提升到 68%,同时平均 Token 消耗下降 37%。记忆不再是「锦上添花」,而是直接决定经济模型的盈亏线。

3. 为什么说「每个 Skill 都在构建记忆层」

以电商客服 Agent 为例,一个系统通常包含五个 Skill:

  • 订单查询 Skill:需要记住「用户最近 3 次的订单号、物流偏好」。
  • 退换货 Skill:需要记住「该用户的信用等级、历史退货原因」。
  • 商品推荐 Skill:需要记住「浏览历史、价格敏感区间」。
  • 库存咨询 Skill:需要记住「用户所在仓库、配送地址」。
  • 投诉升级 Skill:需要记住「已尝试的解决路径、用户情绪标签」。

每个 Skill 都在单独维护自己的记忆:订单 Skill 存一份 user profile,推荐 Skill 又存一份行为序列,投诉 Skill 再存一份情绪时间线。这带来三个严重后果:

  1. 数据孤岛:同一用户的信息散落在 5 个存储里,一致性靠「定时同步脚本」这种脆弱方式维持。
  2. 竞争写入:退换货 Skill 更新了用户信用等级,推荐 Skill 还拿着旧等级做决策。
  3. 成本叠加:每个 Skill 单独做 embedding、单独建索引,存储和计算成本乘以 N。

当记忆从「单个 Skill 的私有财产」变成「多个 Skill 的共享资源」时,它的性质就变了——它不再是一个功能模块,而是所有 Skill 运行所依赖的底层平台。

4. 记忆层作为 Runtime 基础设施的架构设计

当一个系统里每个 Agent Skill 都在读写记忆,记忆层就必须具备运行时(Runtime)的三个特征:统一的执行环境、共享的资源管理、可观测的调度机制。

下面是一个基于「分层记忆 + 记忆总线」的架构图:

分层记忆

记忆总线(Memory Bus)

Agent Skills

读/写

读/写

读/写

订单查询 Skill

退换货 Skill

推荐 Skill

读写路由

写入策略引擎

冲突消解器

工作记忆(短期,TTL 短)

情景记忆(用户维度)

语义记忆(知识维度)

程序记忆(技能经验)

这个架构的核心是记忆总线(Memory Bus),它承担了类似 CPU 总线的角色:Skill 不直接访问记忆存储,而是通过总线发出读写请求,由总线完成路由、策略和冲突处理。

5. 核心组件代码实现

下面用一个最小可运行的多 Skill 记忆总线来展示核心逻辑。存储层用 SQLite 保持零依赖可跑,生产环境可无缝替换为 PostgreSQL + pgvector。

5.1 统一记忆契约

所有 Skill 读写同一个数据结构,避免各存各的:

from dataclasses import dataclass, field
from typing import Literal, Optional
from datetime import datetime, timedelta
import hashlib
import sqlite3
import json

@dataclass
class MemoryEntry:
    """所有 Skill 共用的记忆单元"""
    user_id: str
    scope: Literal[
        "working",   # 工作记忆:当前会话内有效,TTL 短
        "episodic",  # 情景记忆:用户历史行为
        "semantic",  # 语义记忆:领域知识
        "procedural" # 程序记忆:技能使用经验
    ]
    key: str              # 记忆键,如 "last_order_id"
    value: str            # 序列化后的值
    ttl_seconds: int = 0  # 0 表示永不过期
    confidence: float = 1.0  # 置信度,用于冲突消解
    created_at: str = field(default_factory=lambda: datetime.now().isoformat())

5.2 记忆总线:路由与写入策略

class MemoryBus:
    def __init__(self, db_path: str = ":memory:"):
        self.conn = sqlite3.connect(db_path, check_same_thread=False)
        self._init_schema()

    def _init_schema(self):
        self.conn.execute("""
            CREATE TABLE IF NOT EXISTS memory (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                user_id TEXT NOT NULL,
                scope TEXT NOT NULL,
                key TEXT NOT NULL,
                value TEXT NOT NULL,
                ttl_seconds INTEGER DEFAULT 0,
                confidence REAL DEFAULT 1.0,
                created_at TEXT NOT NULL,
                UNIQUE(user_id, scope, key)
            )
        """)
        self.conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_memory_lookup
            ON memory(user_id, scope, key)
        """)
        self.conn.commit()

    def write(self, entry: MemoryEntry, source_skill: str) -> str:
        """写入入口:统一走总线路由,不允许多 Skill 直连存储"""
        # 工作记忆强制设置较短 TTL,防止无限膨胀
        if entry.scope == "working" and entry.ttl_seconds == 0:
            entry.ttl_seconds = 3600  # 默认 1 小时

        existing = self.conn.execute(
            "SELECT id, confidence FROM memory WHERE user_id=? AND scope=? AND key=?",
            (entry.user_id, entry.scope, entry.key)
        ).fetchone()

        if existing:
            # 冲突消解:新写入置信度更高才覆盖
            if entry.confidence >= existing[1]:
                self.conn.execute(
                    """UPDATE memory SET value=?, confidence=?, ttl_seconds=?, created_at=?
                       WHERE id=?""",
                    (entry.value, entry.confidence, entry.ttl_seconds,
                     entry.created_at, existing[0])
                )
                self.conn.commit()
                return f"updated:{existing[0]}"
            else:
                return f"rejected:existing_confidence_higher"
        else:
            cursor = self.conn.execute(
                "INSERT INTO memory VALUES (NULL,?,?,?,?,?,?,?)",
                (entry.user_id, entry.scope, entry.key, entry.value,
                 entry.ttl_seconds, entry.confidence, entry.created_at)
            )
            self.conn.commit()
            return f"created:{cursor.lastrowid}"

    def read(self, user_id: str, key: str, scope: str = "episodic") -> Optional[str]:
        """读取入口:自动过滤过期记忆"""
        row = self.conn.execute(
            """SELECT value, ttl_seconds, created_at FROM memory
               WHERE user_id=? AND key=? AND scope=?""",
            (user_id, key, scope)
        ).fetchone()

        if not row:
            return None

        value, ttl, created_at = row
        if ttl > 0:
            created = datetime.fromisoformat(created_at)
            if datetime.now() - created > timedelta(seconds=ttl):
                self._purge(user_id, key, scope)
                return None
        return value

关键点在于 write() 里的置信度冲突消解:当两个 Skill 同时写入同一个 key 时,总线作为仲裁者决定谁生效。这就是 Runtime 的意义——如果没有这个统一入口,你需要 5 个 Skill 两两之间做 10 条同步逻辑。

5.3 Skill 侧的标准接入方式

每个 Skill 不再维护自己的存储,而是通过总线客户端接入:

class SkillMemoryClient:
    """Skill 通过此客户端接入记忆总线,自身不感知底层存储"""

    def __init__(self, bus: MemoryBus, skill_name: str):
        self.bus = bus
        self.skill_name = skill_name

    def remember(self, user_id: str, key: str, value: str,
                 scope: str = "episodic", confidence: float = 1.0):
        entry = MemoryEntry(
            user_id=user_id,
            scope=scope,
            key=f"{self.skill_name}:{key}",  # namespace 隔离
            value=value,
            confidence=confidence
        )
        return self.bus.write(entry, self.skill_name)

    def recall(self, user_id: str, key: str, scope: str = "episodic"):
        return self.bus.read(user_id, f"{self.skill_name}:{key}", scope)

# 三个 Skill 共享同一个总线实例
bus = MemoryBus("/tmp/agent_runtime.db")

order_client = SkillMemoryClient(bus, "order")
refund_client = SkillMemoryClient(bus, "refund")
recommend_client = SkillMemoryClient(bus, "recommend")

# 写入示例
refund_client.remember("u_1001", "credit_level", "level_3", confidence=0.85)
order_client.remember("u_1001", "last_order", "SO-20251105-7788")
# 推荐 Skill 尝试以更低置信度覆盖信用等级,会被总线拒绝
refund_client.remember("u_1001", "credit_level", "level_5", confidence=0.5)
# 输出:rejected:existing_confidence_higher

5.4 跨 Skill 的跨命名空间检索

记忆基础设施化的关键标志是:Skill A 可以检索 Skill B 写入的记忆,前提是获得授权。总线提供跨 scope 检索能力:

def search_cross_skill(self, user_id: str, query_key: str,
                       limit: int = 5) -> list[dict]:
    """跨 Skill 命名空间检索,返回按时间倒序的记忆条目"""
    rows = self.conn.execute(
        """SELECT key, value, confidence, created_at FROM memory
           WHERE user_id=? AND key LIKE ?
           ORDER BY created_at DESC LIMIT ?""",
        (user_id, f"%:{query_key}%", limit)
    ).fetchall()
    return [
        {"key": r[0], "value": r[1], "confidence": r[2], "created_at": r[3]}
        for r in rows
    ]

比如「投诉升级 Skill」在接手一个退换货投诉时,可以跨命名空间检索到「订单 Skill 写入的 last_order」「退换货 Skill 写入的 credit_level」,从而获得完整上下文——这只有在记忆层成为共享基础设施时才可能实现。

6. 性能数字与容量规划

只说架构不谈数字是耍流氓。以下数据来自一个中等规模的生产部署(50 万 MAU,5 个 Skill 接入):

指标每 Skill 独立记忆统一记忆总线
存储总量(30 天)1.8 TB(每 Skill 各存一份 profile)620 GB(共享去重)
embedding 计算量/日4200 万次1500 万次
平均记忆写入延迟 P99210 ms35 ms
跨 Skill 上下文获取成功率32%91%
冲突写入发生率12.4%(需人工排查)0.3%(自动冲裁)
单次多轮任务 Token 消耗基线下降 37%

三个数字需要特别解释:

  • 存储从 1.8 TB 降到 620 GB:共享记忆库天然去重,同一份 user profile 不再被 5 个 Skill 各存一次。
  • 写入延迟 P99 从 210 ms 降到 35 ms:独立记忆要经过「Skill 本地缓存 → 异步同步 → 定时合并」的长链路;统一总线是「同步写入 + 索引热更新」,直接消除了同步延迟。
  • 冲突写入从 12.4% 降到 0.3%:置信度冲裁机制在写入阶段就解决了大部分冲突,否则 12.4% 的脏数据会在下游被各 Skill 用「各猜各的」方式反复修正。

7. 记忆基础设施化后的三个深层变化

7.1 记忆有了生命周期管理

当记忆是 Skill 的私有物时,没人敢删它——「万一订单 Skill 还要用呢?」。当记忆成为基础设施时,总线可以统一执行生命周期策略:

  • 工作记忆:1 小时 TTL,自动过期。
  • 情景记忆:按用户活跃度做冷热分层,180 天无访问自动归档。
  • 程序记忆:由「技能执行成功率」驱动升降级,连续 10 次成功的经验置信度提升至 1.0,连续 3 次失效则置信度衰减。

7.2 记忆质量可以被度量

基础设施必须有可观测性。记忆总线可以暴露这些指标:

def collect_memory_metrics(self) -> dict:
    total = self.conn.execute("SELECT COUNT(*) FROM memory").fetchone()[0]
    by_scope = self.conn.execute(
        "SELECT scope, COUNT(*) FROM memory GROUP BY scope"
    ).fetchall()
    expired = self.conn.execute(
        "SELECT COUNT(*) FROM memory WHERE ttl_seconds > 0"
    ).fetchone()[0]
    return {
        "total_entries": total,
        "distribution": {s: c for s, c in by_scope},
        "ttl_managed": expired,
        "write_rejection_rate": self._recent_rejection_rate()
    }

有了统一入口,你才能回答「这个 Agent 的记忆到底健不健康」——以前每个 Skill 的记忆都是一笔糊涂账。

7.3 记忆成为可编排的策略层

这是最本质的变化。当记忆总线接管了所有 Skill 的读写后,你可以在总线上叠加策略,而不用改任何一个 Skill:

def apply_write_policy(self, entry: MemoryEntry) -> MemoryEntry:
    """基础设施级的统一写入策略,对 Skill 透明"""
    # 策略 1:PII 脱敏
    if entry.key in ("phone", "id_card", "email"):
        entry.value = self._mask_pii(entry.value)

    # 策略 2:敏感域强制降权
    if entry.key.startswith("credit"):
        entry.confidence = min(entry.confidence, 0.7)

    # 策略 3:工作记忆容量控制
    if entry.scope == "working":
        self._evict_if_overflow(entry.user_id, max_entries=50)

    return entry

策略层让「记忆合规」「记忆容量」「记忆质量」从每个 Skill 的重复实现,变成了 Runtime 的一次配置。这就是基础设施化的最终形态:不是给每个 Agent 加记忆,而是让记忆成为 Agent 运行的前提条件。

8. 结论:从「记忆功能」到「记忆 Runtime」

回到标题那句话:当每个 Agent Skill 都在构建记忆层,记忆就已经成为 Runtime 基础设施。

判断一个系统是否完成了这个跃迁,有三个检验标准:

  1. Skill 是否还拥有自己的存储? 如果是,那记忆还只是功能模块。
  2. 跨 Skill 的记忆检索是否需要显式授权和同步? 如果是,那记忆还只是数据层。
  3. 写入冲突、生命周期、合规策略是否在统一入口执行? 如果是,记忆才真正成为了 Runtime。

Anthropic 在 2025 年把「Agent Skills」定义为一个标准协议(skills 目录 + SKILL.md),其中一项核心能力就是可共享的工作记忆。OpenAI 的 Assistant 记忆、Google 的 Gemini Memory 也在做同样的事。趋势已经非常明确:

Agent 的下一个竞争点不是「谁有记忆」,而是「谁的记忆能成为所有 Agent Skill 共同依赖的 Runtime」。


延伸思考:当记忆层基础设施化完成后,下一步必然是「记忆的编排」(memory orchestration)——记忆的写入、消费、博弈(多 Agent 对同一事实的不同记忆如何收敛),将催生出一类新的中间件。这部分留待后续文章讨论。

Logo

更多推荐