找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1538

积分

0

好友

193

主题
发表于 5 天前 | 查看: 10| 回复: 0

如果你已经开发过 Agent,很可能遇到过这样的窘境:Agent 一开始表现得很聪明,但运行几轮之后就开始胡言乱语,再过一会儿甚至忘了自己的核心任务。

问题的根源往往只有一个:记忆系统的设计是错误的。

书籍《Rust AI Agent Development》封面

一个残酷现实:LLM 没有“记忆”,只有上下文

我们必须认清,大型语言模型(LLM)所谓的“记忆”,其本质是:

  • 你单次请求(request)中喂给它的 token 序列。
  • 模型不会主动记住上一次请求中的任何内容。

因此,许多 Agent 项目采取了一种看似直接的“记忆方案”:将所有历史对话记录,一股脑儿地塞进后续的提示词(Prompt)里。

结果可想而知:

  • ❌ Token 消耗爆炸式增长。
  • ❌ 调用成本完全失控。
  • ❌ 上下文中的信息噪声越来越大。
  • ❌ 模型因信息过载而抓不住重点,决策质量下降。

一个真正可用、可长期运行的 Agent,其记忆系统必须是分层设计的。

Agent 的三层记忆模型(核心认知框架)

下面这张图是你需要理解的“认知地图”,它描绘了一个健壮的 Agent 记忆体系:

┌────────────────────┐
│ Working Memory     │  当前上下文(短期)
│ (Short-Term)       │
├────────────────────┤
│ Episodic Memory    │  事件 / 行为记录
│ (Long-Term Raw)    │
├────────────────────┤
│ Semantic Memory    │  可检索的知识
│ (Vector /  Index)  │
└────────────────────┘

接下来,我们逐层拆解,并用 Rust 的方式来实现它们。

Working Memory:不是“越多越好”,而是“刚刚好”

问题:为什么上下文越长,Agent 反而越蠢?

因为 LLM 本身并不具备为你做“信息优先级排序”的能力。无限制地追加历史,只会让关键信息淹没在冗余对话中。

因此,Working Memory(工作记忆)的核心原则是:

只保留当前决策真正需要的信息。

Rust 实现:滑动窗口与信息过滤

一个基础的工作记忆结构可以这样定义:

pub struct WorkingMemory {
    messages: Vec<ChatMessage>,
    max_tokens: usize,
}

当向其中添加新消息时,你可以执行以下策略来保持其精炼:

  1. 丢弃过于陈旧的工具调用输出。
  2. 保留用户的初始目标(Goal)以及最近几轮的思考链(Reasoning)。
  3. 当消息积累到一定长度时,调用 LLM 先进行摘要,再保留摘要结果

以下是“记忆压缩”的简化示例:

pub fn compact(&mut self) {
    if self.messages.len() > 20 {
        let summary = summarize_with_llm(&self.messages[..10]);
        self.messages.drain(0..10);
        self.messages.insert(0, summary);
    }
}

📌 记忆的主动压缩与提炼,是 Agent 走向成熟的重要标志。

Episodic Memory:Agent 的“行为黑匣子”

这是许多教程中缺失,但在生产环境中必不可少的部分——事件记忆。

什么是 Episodic Memory?

简单来说,它是 Agent 每一次重要行为的不可变记录。例如:

{
  "ts": "2026-01-04T01:21:00Z",
  "step": 7,
  "action": "tool_call",
  "tool": "http_get",
  "input": {"url": "..."},
  "result": "timeout"
}

Rust 实现:仅追加(Append-only)的 JSONL 文件

为什么优先选择简单的文件格式而非数据库?这基于以下几点考量:

  • 仅追加写入:确保即使在 Agent 崩溃时也不会丢失已记录的数据。
  • 人类可读:便于开发者和运维人员直接查看、分析。
  • 后期可处理:可以轻松进行离线回放、行为分析或用于微调。

实现代码可能如下:

pub struct EpisodicMemory {
    writer: tokio::fs::File,
}

impl EpisodicMemory {
    pub async fn record(&mut self, event: &Value) -> Result<()> {
        use tokio::io::AsyncWriteExt;
        self.writer.write_all(event.to_string().as_bytes()).await?;
        self.writer.write_all(b"\n").await?;
        Ok(())
    }
}

一个没有事件记忆的 Agent,将几乎无法进行有效的调试和问题追溯。

真正拉开差距的:Semantic Memory(语义/向量记忆)

到了这一层,Agent 才真正开始“像人类一样学习”。它的核心作用可以用一句话概括:

当遇到与过去相似的问题或情境时,能够回忆起“之前发生了什么以及如何解决的”。

最小可用设计(从简开始)

我们不必一开始就引入复杂的向量数据库(如 Milvus, Pinecone)。一个可控、可嵌入的轻量级方案更能帮助理解本质。

数据结构

pub struct MemoryChunk {
    pub id: Uuid,
    pub embedding: Vec<f32>, // 向量嵌入
    pub text: String,        // 原始文本
}

存储方案(极简但有效)

  • 使用 SQLite 存储元数据(如 ID、文本、时间戳)。
  • 将向量嵌入(embeddings)作为 BLOB 存入 SQLite 或单独的文件。
  • 检索时,将向量加载到内存中计算余弦相似度。这种方案在记忆条数不多时非常高效,也是理解 数据库 如何与 AI 结合的好起点。

Embedding 是“慢操作”,务必异步化

同步调用 Embedding 模型会严重阻塞 Agent 的主循环,这是一个常见的性能陷阱。

pub async fn embed(text: &str) -> Result<Vec<f32>> {
    // 调用外部 Embedding API 或本地模型
}

核心原则:

  • ❌ 禁止在 Agent 的主决策循环中同步调用 Embedding。
  • ✅ 将需要记忆的内容发送到后台任务进行异步处理。
tokio::spawn(async move {
    let emb = embed(&text).await?;
    semantic_memory.insert(text, emb).await?;
});

检索策略:不是“最相似”,而是“最有用”

天真的做法是直接返回余弦相似度最高的几条记忆:

top_k_by_cosine_similarity(query_embedding)

但更有效的策略是设计一个综合评分函数,它可能考虑:

最终评分 = 语义相似度 × 时间衰减因子 × 历史成功率权重

score = similarity * freshness * success_rate

这提醒我们一个关键点:

Agent 的“记忆质量”(相关性、时效性、有效性),远比记忆的“数量”重要得多。

Semantic Memory 如何参与 Agent 推理?

语义记忆并非用作对话历史,而是在每轮 LLM 调用前,作为“参考资料”注入。基本流程如下:

1. 提取当前任务目标(Goal)或问题。
2. 将其转换为向量(Embedding)。
3. 从语义记忆中检索出最相关的 Top-K 条记录。
4. 将这些记录格式化后,拼接到 System Prompt 中。

示例 Prompt 片段:

Relevant past experiences:
- Previously, HTTP timeout was solved by lowering concurrency.
- Similar error occurred when RPC rate limit was exceeded.

📌 关键: 这些记忆是作为参考知识(Contextual Knowledge)提供,而不是原始对话记录,这能大幅提升 LLM 利用历史经验的能力。

记忆系统的四条工程铁律

  1. Working Memory 越精炼,Agent 的决策越聪明。
  2. Episodic Memory 是为了调试与审计,而不是为了直接参与推理。
  3. Semantic Memory 只应存储那些“值得被未来想起”的经验。
  4. 记忆系统本质上是策略设计,而不仅仅是数据结构的选择。

结语

当你为 Agent 系统完整地实现了这三层记忆模型,它便已经脱离了简单的 Demo 阶段。此时的 Agent 具备了:

  • ✅ 可控的执行与并发能力
  • ✅ 可预测、可管理的成本
  • ✅ 可追溯、可调试的运行日志
  • ✅ 能从经验中学习的潜力
  • ✅ 可长期稳定运行的基础架构

这整套设计所体现的工程深度,已远超目前绝大多数开源 Agent 框架的简易实现。

如果你对构建此类可落地的 人工智能 应用感兴趣,欢迎在 云栈社区 交流更多实践细节。




上一篇:K8S容器云平台工程师:容器云平台核心技能精讲 企业级K8S部署、CICD、监控与运维实战
下一篇:深入解析MyBatis映射器模块:从接口定义到动态代理的执行原理
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-10 08:51 , Processed in 0.191175 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表