第 3 章 数据模型详解


文档摘要

第 3 章 数据模型详解 memU 的一切 API 返回值都可以还原为四种持久记录 + 向量索引。本章把字段含义讲透,后面调 API 才不会「对着字典猜」。 3.1 四类核心记录 记录 | 角色 | 一句话 | 来源素材 | 「这条记忆从哪来」 | 原子记忆 | 「具体记住什么」 | 主题文件夹 | 「属于哪个话题」 | 归属边 | 「哪条记忆在哪个文件夹里」 3.2 Resource(来源层) Resource 代表一份原始输入,是溯源链的起点。

第 3 章 数据模型详解

memU 的一切 API 返回值都可以还原为四种持久记录 + 向量索引。本章把字段含义讲透,后面调 API 才不会「对着字典猜」。

3.1 四类核心记录

Resource ──产生──► MemoryItem ──归属──► MemoryCategory ▲ ▲ └──── CategoryItem ──┘ (关系边)
记录 角色 一句话
Resource 来源素材 「这条记忆从哪来」
MemoryItem 原子记忆 「具体记住什么」
MemoryCategory 主题文件夹 「属于哪个话题」
CategoryItem 归属边 「哪条记忆在哪个文件夹里」

3.2 Resource(来源层)

Resource 代表一份原始输入,是溯源链的起点。

典型字段

字段 类型 说明
id string 唯一标识
url string 原始地址(本地路径或 HTTP URL)
modality enum conversation / document / image / video / audio
local_path string 摄入后在本地的副本路径
caption string 预处理生成的文本描述(多模态关键)
embedding float[] 可选,来源级向量

示例(概念性 JSON)

{ "id": "res_01", "url": "https://storage.example.com/meeting.mp4", "modality": "video", "caption": "产品规划会议,讨论 onboarding 简化与上线风险。", "embedding": [0.012, -0.034, "..."] }

工程要点

  • 多模态内容(图片、视频)会先变成 caption 文本,再进入提取阶段
  • 检索时若 Item / Category 摘要不够,会回退到 Resource 读 caption 或原文

3.3 MemoryItem(记忆项层)

MemoryItem 是 LLM 从 Resource 中提取的原子记忆,相当于「一个文件」。

典型字段

字段 类型 说明
id string 唯一标识
memory_type enum profile / event / knowledge / behavior / skill / tool
summary string 自然语言摘要(检索主字段)
extra object 扩展元数据(时间、实体等)
happened_at datetime 事件类记忆的发生时间
embedding float[] 项级向量(RAG 模式使用)
resource_id string 关联的来源 ID

示例

{ "id": "mem_01", "memory_type": "profile", "summary": "用户偏好简洁的技术文档,不喜欢冗长铺垫。", "happened_at": null, "resource_id": "res_01" }

类型选用指南

memory_type 何时产生 retrieve 时的权重建议
profile 稳定特征、偏好 个性化回复时优先
event 决策、会议、里程碑 任务规划时优先
knowledge 客观事实、文档要点 问答、RAG 补充
behavior 使用习惯、频率模式 proactive 推荐
skill 方法论、最佳实践 编码 / 分析智能体
tool CLI、API 使用教训 工具调用智能体

3.4 MemoryCategory(主题层)

Category 是自动生成的「文件夹」,维护主题级摘要,供宽泛查询快速加载上下文。

典型字段

字段 类型 说明
id string 唯一标识
name string 规范化主题名(如 user_preferences
description string 主题描述
summary string 演进式摘要(随新 Item 更新)
embedding float[] 类级向量

示例

{ "id": "cat_01", "name": "product_goals", "description": "产品目标与上线相关决策", "summary": "当前优先级:简化 onboarding;下次评审前需完成风险清单。" }

自组织机制:新 Item 写入时,LLM 决定归入已有 Category 或创建新 Category,并更新 summary。无需人工打标签。

3.5 CategoryItem(关系边)

CategoryItem 只表示「哪条 MemoryItem 属于哪个 MemoryCategory」,是多对多关系。

{ "item_id": "mem_01", "category_id": "cat_01" }

检索时利用关系图:

  • 从 Category 向下展开 Item 列表
  • 从 Item 向上聚合 Category 摘要
  • 避免对同一 Resource 重复提取

3.6 memorize() 返回结构完整说明

result = await service.memorize(...) # result 结构: { "resource": Resource, # 本次摄入的来源(单条) "items": [MemoryItem, ...], # 新提取的记忆项 "categories": [MemoryCategory, ...], # 新建或更新的主题 "relations": [CategoryItem, ...], # 新建立的归属关系 }

注意:一次 memorize 可能更新已有 Category 的 summary,而不只是追加新 Category。

3.7 retrieve() 返回结构完整说明

context = await service.retrieve(...) # context 结构: { "needs_retrieval": bool, # 是否真的需要查记忆 "original_query": str, # 原始查询文本 "rewritten_query": str, # LLM 重写后的查询 "next_step_query": str | None, # 建议的下一步查询(迭代检索) "categories": [MemoryCategory, ...], "items": [MemoryItem, ...], "resources": [Resource, ...], }

字段解读

字段 作用
needs_retrieval 若为 False,可能直接回复无需查库
rewritten_query 消歧、补全后的检索 query
next_step_query 支持多轮渐进式检索
categories 主题层命中结果
items 细项层命中结果
resources 需读原文时的来源列表

3.8 作用域字段传播

当配置 UserConfig 或在 API 传入 user / where 时,作用域字段(如 user_idagent_idsession_id)会写入各层记录:

# 写入 await service.memorize( resource_url="...", modality="document", user={"user_id": "u123", "agent_id": "assistant_v2"}, ) # 读取(必须匹配) await service.retrieve( queries=[...], where={"user_id": "u123"}, )

常见错误:写入时带了 user_id,检索时忘记 where → 结果为空或串数据。

3.9 向量索引在三层中的角色

是否 embedding RAG 模式用途
Resource 可选 来源级召回
MemoryItem 通常有 细粒度语义匹配
MemoryCategory 通常有 主题级快速定位

LLM 模式(method="llm")则把格式化后的 Category / Item / Resource 文本交给 LLM 排序,不过度依赖向量。

3.10 与 Markdown 导出树的对应(预览)

memU 可将结构化存储导出为人类可读树(第 10 章):

存储记录 导出产物
全部 Resource 资源目录 + 索引页
MemoryCategory 每个主题一个 Markdown 页
聚合视图 MEMORY 总览、SKILL 技能索引

导出是只读渲染,不改变数据库中的记录。

3.11 本章小结

  • 四种记录:Resource、MemoryItem、MemoryCategory、CategoryItem。
  • memorize 产出 items + categories + relations;retrieve 分层返回 categories / items / resources。
  • 作用域字段必须写入与读取一致。
  • 向量分布在三层,RAG 与 LLM 模式利用方式不同。

动手实验

  1. 跑通第 1 章示例,把 memorize 返回的 JSON pretty-print 出来,手工标注每个字段。
  2. 对同一用户写入两条不同 modality 的数据,观察 categories 是否合并或新建。
  3. 预习第 6 章:思考 next_step_query 在什么场景下会非空。

配套可运行示例

本章讲的三层结构(resource / items / categories)可以用下面这个完整脚本的「美化打印」函数直观看到。它把 memorize 返回的字段逐层展开,并统计 memory_type 分布:

"""memorize 返回结构可视化与 memory_type 分布统计完整示例。 把 memorize() 返回的三层结构(resource / items / categories)美化打印, 并统计不同 modality 提取出哪些类型的记忆项。 依赖:pip install memu-py (需配置 LLM API Key) """ import asyncio from memu import MemoryService def print_memorize_result(result, title="memorize 返回"): """美化打印 memorize() 的返回,逐层展开三层结构。""" print(f"=== {title} ===") res = result.get("resource", {}) print(f" resource id: {res.get('id')}") print(f" modality : {res.get('modality')}") items = result.get("items", []) print(f" items : {len(items)} 条") for it in items: print(f" [{it.get('memory_type')}] {str(it.get('summary',''))[:80]}") cats = result.get("categories", []) if cats: print(f" categories : {[c.get('name') for c in cats]}") # memory_type 分布统计 type_counts = {} for it in items: t = it.get("memory_type", "unknown") type_counts[t] = type_counts.get(t, 0) + 1 print(f" 类型分布 : {type_counts}") CONVERSATION = [ {"role": "user", "content": "我是后端工程师,主攻分布式系统,用 Go 较多。"}, {"role": "assistant", "content": "了解了。"}, {"role": "user", "content": "我们团队下季度要做服务网格迁移,从 Istio 换到 Linkerd。"}, {"role": "assistant", "content": "记下了。"}, ] async def main(): service = MemoryService( llm_profiles={"default": {"api_key": "your_api_key", "chat_model": "gpt-4o-mini"}}, database_config={"metadata_store": {"provider": "inmemory"}}, retrieve_config={"method": "rag"}, ) # 实际使用时把 CONVERSATION 写成本地 JSON 文件,resource_url 指向它 import json, tempfile, os p = os.path.join(tempfile.gettempdir(), "memu_conv.json") with open(p, "w", encoding="utf-8") as f: json.dump(CONVERSATION, f, ensure_ascii=False) result = await service.memorize( resource_url=p, modality="conversation", user={"user_id": "demo_user"}, ) print_memorize_result(result) if __name__ == "__main__": asyncio.run(main())

💡 运行后观察 items 的 memory_type 分布:对话类输入通常会提取出 profile(用户画像)、event(决策/待办)、knowledge 等类型。这正是 memU 区别于「整段塞向量库」的地方——它把原始来源编译成了带类型的原子事实。

下一章:第 4 章 — MemoryService 初始化与配置。

参见:第 2 章三层架构;附录 B 字段速查。


发布者: 作者: 转发
评论区 (0)
U