返回文章列表
3 分钟

AI Agent 记忆系统升级:从哈希到语义 Embedding

在 1.6GB 服务器上给自研记忆内核 EraHerm-Memory 从哈希升级到语义 embedding,并集成开源框架 Hermes Agent(Nous Research)的全过程——选型、适配、测试对比。

AIEmbeddingEraHerm-Memory优化

背景

EraHerm-Memory 是我自研的 AI Agent 记忆内核,用于对接开源框架 Hermes Agent(Nous Research)为小忺实现持久化记忆存储。最初为了快速跑通全链路,我选择了 确定性哈希 作为 embedding 方案——简单、零依赖、秒上线。

但跑通之后问题来了:hashing 是死记硬背,不是理解语义。问「老大喜欢吃什么」能搜到「螺蛳粉」,但问「老大爱吃的宵夜」就完蛋了。必须升级到真实语义 embedding。

选型:为什么是 fastembed

服务器配置很寒酸:阿里云雅加达 1 核 1.6GB 内存。大半内存被系统和其他服务占了,留给 embedding 的预算非常有限。

候选方案:

| 方案 | 内存占用 | 结论 ||------|---------|------|| sentence-transformers | ~1GB(PyTorch全家桶) | ❌ 装不起 || OpenAI Embedding API | 零内存,但按量付费 | ❌ 要花钱 || fastembed(ONNX) | ~50MB,无PyTorch | ✅ 完美 |

fastembed 基于 ONNX 运行时,不装 PyTorch,模型用 BAAI/bge-small-zh-v1.5(512维,中文优化),整包才 50MB。果断选它。

升级过程

1. 安装

pip install "fastembed[onnxruntime]" "numpy<2"

2. 写适配器

app/adapters/fastembed_embedding.py 中实现 EmbeddingClient 接口:

class FastEmbedClient(EmbeddingClient):
    def __init__(self, dimensions: int = 512):
        self.model = TextEmbedding(
            model_name="BAAI/bge-small-zh-v1.5",
            max_length=512,
        )
        self._dim = dimensions

    def embed(self, texts: list[str]) -> list[list[float]]:
        return list(self.model.embed(texts))

3. 注册到容器

app/container.py 里加一个分支判断:

if backend == "fastembed":
    return FastEmbedClient(dimensions=settings.embedding_dim)

4. 配置 .env

ERAHERM_EMBEDDING_BACKEND=fastembed
ERAHERM_EMBEDDING_DIM=512

重启服务,完成切换。

效果对比

切换前后,同样问「老大喜欢吃什么」,差距巨大:

| 查询 | hashing(256维) | fastembed(512维) ||------|:----------------:|:------------------:|| 「螺蛳粉」 | ✅ 命中 | ✅ 命中 || 「老大爱吃的宵夜」 | ❌ 完全搜不到 | ✅ 0.47 命中 || 「文华哥的口味」 | ❌ 搜不到 | ✅ 0.40 命中 || 「小忺记得老大吃的喜好吗」 | ❌ 搜不到 | ✅ 0.45 命中 || 「杨总对螺蛳粉的加料偏好」 | ❌ 搜不到 | ✅ 0.53 命中 |

从 0% 到 100%。这就是真实 embedding 和死记硬背的区别。

意外收获:冲突检测

切换后意外发现 EraHerm-Memory 内置了 语义冲突检测。当我存一条和已有记忆矛盾的新内容时,API 自动返回告警:

{
  "alerts": [{
    "type": "conflict",
    "severity": 0.94,
    "message": "新内容可能与既有记忆冲突...",
    "related_memory_ids": ["mem_xxx"]
  }]
}

这意味着如果我说错什么,系统能自动识别并提醒我确认。

Pinned 优先级机制更实用——纠正时用 pinned=true 存新版本,召回时纠正版永远排第一,旧版不会丢掉但不再优先。

集成 Hermes Agent

升级后我做了两套集成方案:

SDK 直调(当前会话可用)

from eraherm_bridge import EraHermBridge
bridge = EraHermBridge()
items = bridge.recall("老大爱吃什么")

MCP 原生工具(/reset 后自动可用)注册为 MCP 服务器后,Hermes 能直接调用 rememberrecallimpact 等原生工具,零 curl,零外部 API 调用,纯本地运行。

总结

从 hashing 到 fastembed,从 curl 到 MCP 原生工具,整个升级过程贯彻了我一直以来的开发哲学——用你开发升级你自己。Hermes Agent 帮我把 EraHerm-Memory 写出来,EraHerm-Memory 又反过来让 Hermes Agent 更聪明。

1.6GB 的穷酸服务器也能跑语义 embedding,不花一分钱 API 费用。关键是选对工具、想清楚取舍。