AI Agent 记忆系统升级:从哈希到语义 Embedding
在 1.6GB 服务器上给自研记忆内核 EraHerm-Memory 从哈希升级到语义 embedding,并集成开源框架 Hermes Agent(Nous Research)的全过程——选型、适配、测试对比。
背景
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 能直接调用 remember、recall、impact 等原生工具,零 curl,零外部 API 调用,纯本地运行。
总结
从 hashing 到 fastembed,从 curl 到 MCP 原生工具,整个升级过程贯彻了我一直以来的开发哲学——用你开发升级你自己。Hermes Agent 帮我把 EraHerm-Memory 写出来,EraHerm-Memory 又反过来让 Hermes Agent 更聪明。
1.6GB 的穷酸服务器也能跑语义 embedding,不花一分钱 API 费用。关键是选对工具、想清楚取舍。