Rust Agent 的长期记忆设计:别再把一切都塞进 Prompt 了

  • King
  • 发布于 1天前
  • 阅读 48

如果你已经写过Agent,你一定遇到过这个阶段👇一开始很聪明跑几轮之后开始胡言乱语再跑一会儿就忘了自己是谁根因只有一个:记忆设计是错的。一个残酷现实:LLM没有“记忆”,只有上下文LLM所谓的“记忆”,本质是:你这次request里给了多少token模型不会记得

如果你已经写过 Agent,你一定遇到过这个阶段👇

一开始很聪明 跑几轮之后开始胡言乱语 再跑一会儿就忘了自己是谁

根因只有一个:记忆设计是错的。


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

LLM 所谓的“记忆”,本质是:

  • 你这次 request 里给了多少 token
  • 模型不会记得上一次请求的任何东西

所以很多 Agent 的“记忆方案”是:

👉 把所有历史对话,全塞进 prompt

结果是:

  • ❌ token 爆炸
  • ❌ 成本失控
  • ❌ 信息噪声越来越大
  • ❌ 模型反而抓不到重点

真正可用的 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,
}

你可以在 push 的时候做三件事:

  1. 丢弃太老的 tool 输出
  2. 保留 user 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

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(())
    }
}

💡 为什么不用数据库?

  • append-only:崩溃不丢数据
  • 人类可读
  • 后期可离线分析 / 回放 / 微调

Agent 没有 episodic memory,就无法 Debug。


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

这一步,Agent 才真的“像在学习”。

Semantic Memory 的作用只有一句话:

当遇到相似问题时,能想起“以前发生过什么”


Semantic Memory 的最小可用设计

我们不一上来就上 Milvus / Pinecone。

先做一个 可控、可嵌入的本地方案

数据结构

pub struct MemoryChunk {
    pub id: Uuid,
    pub embedding: Vec<f32>,
    pub text: String,
}

存储方案(极简但好用)

  • SQLite(元数据)
  • embeddings 存 BLOB
  • 余弦相似度在内存算

Embedding 是“慢操作”,一定要异步化

这是一个很多人踩过的坑。

pub async fn embed(text: &str) -> Result<Vec<f32>> {
    // 调用 embedding model
}

原则:

  • ❌ 不要在 Agent 主循环同步调用
  • ✅ 后台异步写入 semantic memory
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 的“记忆质量”,比记忆数量重要 100 倍。


Semantic Memory 如何参与 Agent 推理?

每一轮 LLM 调用之前

1. 取当前 goal
2. embedding
3. 从 semantic memory 检索 top-k
4. 作为“参考资料”拼进 system prompt

示例 Prompt 片段:

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

📌 注意: 不是当作“对话历史”,而是参考知识


记忆系统的 4 条工程铁律(强烈建议原文照抄)

你可以在文章结尾用“总结金句”:

  1. Working Memory 越短,Agent 越聪明
  2. Episodic Memory 是为了 Debug,不是为了推理
  3. Semantic Memory 只存“值得被想起的事”
  4. 记忆不是数据结构,是策略

到这里,你的 Agent 已经不再是 Demo

如果你已经跟到第三篇,你的 Agent 现在已经具备:

  • ✅ 并发执行器
  • ✅ 成本可控
  • ✅ 可 Debug
  • ✅ 可学习
  • ✅ 可长期运行

这已经远远超过 90% 开源 Agent 框架的工程深度

  • 学分: 0
  • 分类: Rust
  • 标签:
点赞 0
收藏 0
分享

0 条评论

请先 登录 后评论