Files
Hy-Memory/docs/usage.md
T
26c33ee40f refactor(sdk): internalize legacy data migration (#146)
* refactor(sdk): keep legacy migration out of client APIs

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(migration): stage SDK data before cutover

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: alvinfei <alvinfei@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-22 20:49:23 +08:00

12 KiB
Raw Blame History

Hy Memory 使用指南

本文只描述当前 HyMemoryClient 已公开的方法和返回结构。

安装与快速开始

pip install hy-memory==2.0.4

# 可选后端
pip install 'hy-memory[qdrant]==2.0.4'
pip install 'hy-memory[faiss]==2.0.4'
pip install 'hy-memory[graph]==2.0.4'     # Neo4j;Kuzu 已在核心依赖中
pip install 'hy-memory[mysql]==2.0.4'
pip install 'hy-memory[rerank]==2.0.4'    # 本地多语言 cross-encoder rerank

默认 LLM 和 Embedding 均使用 OpenAI-compatible 接口。使用 OpenAI 时可只设置:

export OPENAI_API_KEY="sk-your-key"
from hy_memory import HyMemoryClient

with HyMemoryClient(mode="pro") as client:
    client.add(
        "用户喜欢科幻电影,特别是《星际穿越》",
        user_id="user_001",
    )
    result = client.search(
        "用户喜欢什么?",
        user_ids=["user_001"],
    )
    for channel, memories in result["memories"].items():
        for memory in memories:
            print(channel, memory["score"], memory["content"])

配置

SDK 会加载项目目录中的 .env。完整配置见 env_reference.md。

MEMORY_MODE=pro
MEMORY_LLM_MODEL=deepseek-chat
MEMORY_LLM_API_KEY=sk-your-key
MEMORY_LLM_BASE_URL=https://api.deepseek.com
MEMORY_EMBEDDER_MODEL=text-embedding-v4
MEMORY_EMBEDDER_API_KEY=sk-your-key
MEMORY_EMBEDDER_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
MEMORY_EMBEDDING_DIMS=1024

也可以使用当前公开的 from_config():

client = HyMemoryClient.from_config({
    "mode": "pro",
    "llm": {
        "model": "deepseek-chat",
        "api_key": "sk-xxx",
        "base_url": "https://api.deepseek.com",
    },
    "embedder": {
        "model": "text-embedding-v4",
        "api_key": "sk-xxx",
        "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
        "embedding_dims": 1024,
    },
    "vector_store": {
        "provider": "chroma",
        "embedding_dims": 1024,
    },
})

通过 from_config() 配置时,Embedder 与 Vector Store 的维度是两个独立字段,必须保持 一致;环境变量方式则统一使用 MEMORY_EMBEDDING_DIMS。

支持的向量后端为 chroma、qdrant、faiss。Graph 后端通常使用 kuzu 或 neo4j。

Client API

同步和异步方法使用相同参数。异步方法以 async_ 为前缀。

add() / async_add()

result = client.add(
    [
        {"role": "user", "content": "推荐一部电影"},
        {"role": "assistant", "content": "推荐《星际穿越》"},
    ],
    user_id="user_001",
    agent_id="default_agent",
    session_id="default_session",
    source="openclaw",
    scene=None,
    memory_at=None,
    metadata=None,
    meta=None,
    embed_override=None,
)

metadata 与 meta 不是同一字段:

  • metadata 是兼容旧调用方保留的 SDK 处理选项,会进入内部 WriteRequest.extra,不保证持久化或随查询结果返回。
  • meta 是调用方的业务上下文,会持久化并在 search()、get() 等结果中返回。 同一记忆被多次合并时,各次 meta 会按写入顺序保存在列表中。

需要以后读回的数据必须放在 meta,不要放在 metadata。

scene 的公开取值:

  • None:普通文本直接走 chat;messages 含 tool 时执行快速场景判断。
  • chat:强制走普通记忆链路。
  • working:强制走生产力记忆链路。
  • coding:强制走软件工程变体,并维护版本演进链。

强制 working / coding 时输入必须含 tool 消息,否则回退 chat。普通 chat 写入返回 success、scene、memory_id、request_id、elapsed_ms 等字段;生产力链路返回其 双层 reconcile 操作、experience_ids、fact_ids 等字段,完整结构见 scene_coding_usage.md。

mode="lite" 不执行 LLM 抽取,写入内容直接以 L1_RAW 形式保存。lite 的 Search 是 严格的 raw-only 向量检索:无论调用方传入什么 layers、scene 或 search_mode,都只 召回 L1_RAW,不会进入 working/coding、Schema 或 Intention 链路,也不会调用场景判断 LLM。pro / ultra 仍遵循各自的层级与场景参数,默认不公开原始对话。

source 是调用方应用来源,例如 openclaw、cursor、codex。它与 agent_id 正交:前者说明“从哪个产品/入口写入”,后者仍是记忆隔离中的 agent 身份。source 会 执行 strip().lower() 后存储,并随 chat、working、coding 记忆一起返回。

普通 chat 检索:

result = client.search(
    query="用户喜欢什么?",
    user_ids=["user_001"],
    agent_ids=None,
    session_ids=None,
    include_sources=["openclaw", "codex"],
    exclude_sources=["cursor"],
    limit=10,
    min_score=0.4,
    schema_limit=10,
    schema_min_score=0.4,
    intention_limit=0,
    reader="",                  # HY_MEMORY_READER;默认 legacy
    layers=None,
    created_after=None,
    search_mode="default",
)

source 过滤语义为:

source IN include_sources AND source NOT IN exclude_sources

任一列表为空时省略对应条件;两者冲突时黑名单优先。Chroma/Qdrant/Graph 会在数据库 候选截断前执行过滤,FAISS 使用 ID selector 在索引搜索前限定候选,不采用多路召回或 扩大 top-k 后过滤。多来源证据派生出的聚合记忆标记为 mixed;开启任意 source 过滤 时默认排除 mixed,只有显式将 mixed 放入白名单才返回。

chat 成功路径返回:

{
    "request_id": "...",
    "memories": {
        "normal": [],      # L2/L3 普通记忆;L4/L5 当前未启用
        "schema": [],      # L6 Schema
        "proactive": [],   # L7 Intention;默认关闭
    },
    "rewrite": {
        "used": False,
        "original_query": "用户喜欢什么?",
        "effective_query": "用户喜欢什么?",
        "created_after": None,
        "scene": "normal",
    },
    "elapsed_ms": 12.3,
}

Reader 执行失败时返回空的 memories 三桶和 request_id / elapsed_ms;当前失败兜底不 包含 rewrite。

L0 用户画像不在 Search 中。schema_limit 只控制 L6,不会被 L0 占位。

Search 也可以路由到独立生产力记忆库:

result = client.search(
    "这个异常最后是怎么修复的?",
    user_ids=["user_001"],
    scene="coding",
    workspace_id="repo-id",
    branch="main",
)

working / coding 路径返回 scene="productivity"、facts、experiences,不使用 chat 的三桶结构;coding 还会返回 chain_block 和 chain_used 字段(默认空; 需 MEMORY_CODING_CHAIN_BLOCK_ENABLED=true 才注入 diff 块)。

search_mode

search_mode scene 行为
default None + 单个 query 固定 chat/normal,零 LLM
default None + queries=[...] 行为由 Coding 与 Query Rewrite 开关共同决定,见下表
agentic None + 单/多 query 一次 LLM 完成场景判断、改写和时间解析
任意 显式 chat/working/coding 直接路由,不调用 judge/rewrite LLM

query 与 queries 二选一;queries 最后一项是目标 query,前面的项用于上下文消解。 LLM 或解析失败时回退到 chat/normal 和原 query。

default 多-query 在总字符数不超过 MEMORY_QUERY_REWRITE_MAX_CHARS 时:

MEMORY_CODING_ENABLED MEMORY_ENABLE_QUERY_REWRITE 行为
true 任意 一次 LLM 完成 chat / working / coding 分类和 query rewrite
false true 一次 LLM 完成 rewrite 与时间解析,不做场景分类
false false 零 LLM,固定 chat/normal,使用最后一条 query

超过长度保护时不做 rewrite;Coding 开启时只执行有界场景分类,关闭时零 LLM。

get_user_profile() / async_get_user_profile()

用户画像只由 user_id 唯一确定,不接收 agent_id 或 session_id:

result = client.get_user_profile(user_id="user_001")
profile = result["profile"]
{
    "request_id": "...",
    "profile": {
        "memory_id": "...",
        "user_id": "user_001",
        "content": "...",
        "layer": "l0_basic_info",
        "basic_info": {"name": "Alice"},
        "catalog": {"attached": False},
        "supplement": {"attached": False},
        "tags": [],
        "memory_at": 1234567890,
        "gmt_created": 1234567890,
    },
    "elapsed_ms": 1.2,
}

不存在 L0 且没有可附加的 balanced catalog 时,profile 为 None。该接口不调用 Embedding 或 LLM。启用对应开关后,active catalog 和 supplement 会附加到同一画像; catalog 也可以在没有基础 L0 时形成只读的合成画像。

CRUD、列表与克隆

以下均为当前公开方法:

memory = client.get("memory_id")
# raw ID 返回 L1_RAW,fact ID 返回对应事实层。
raw_memory = client.get("raw_id")
result = client.update("memory_id", "更新后的内容")
result = client.delete("memory_id")
result = client.delete_all(user_id="user_001")

page = client.list_memories(
    user_id="user_001",
    limit=100,
    offset=0,
    order="desc",
    layers=["l1_raw"],  # 显式列出 raw;不传仍沿用默认语义层列表
    with_evolution=False,
)

result = client.clone_user("user_001", "user_002", agent_id=None)

对应异步方法为 async_get()、async_update()、async_delete()、 async_delete_all()、async_list_memories()、async_clone_user()。

get() / async_get() 是统一的 ID 精确读取:返回结果的 layer、kind 和 variant 表明实际类型。授权由调用 SDK 前的产品边界完成,不由 SDK 根据 layer 猜测。list_memories() 默认仍不包含 L1_RAW;显式传 layers=["l1_raw"] 时按同一列表合同返回 raw。

普通 list_memories() 返回 vdb 桶,并在 Graph 有结果时附带 graph;传入 workspace_id 时返回生产力记忆的 scene="productivity" 和 experiences。

evolve() / async_evolve()(Ultra)

System 2 不在 add() 中自动触发,当前也没有 get_write_status() 接口。调用方完成一批 写入后显式执行:

result = client.evolve(
    user_id="user_001",
    agent_id="default_agent",
)

evolve() 仅允许 mode="ultra";同步方法阻塞到本次认知加工完成。它读取当前 VDB 全量,生成或更新 L6 Schema,并执行配置允许的 System 2 处理。

其他当前公开方法

  • finalize_session() / async_finalize_session():在功能开关开启时归档指定 session。
  • get_user_basic_info() / async_get_user_basic_info():读取 L0 basic info 记录。
  • sweep() / async_sweep():Ultra 下显式执行 cross-domain sweeper。
  • get_metrics() / async_get_metrics():读取 SDK 指标。
  • build_memory_graph() / async_build_memory_graph():回填普通记忆关系图。
  • build_entity_store() / async_build_entity_store():回填独立 Entity Store。
  • history() / async_history()、get_recent_history() / async_get_recent_history()。

Embedding 端点覆盖

add() / async_add() / search() / async_search() 支持 per-call embed_override:

override = {
    "base_url": "http://embedding-host:8000/v1",
    "api_key": "sk-xxx",
    "extra_headers": {"X-Route": "direct"},
    "timeout": 15,
}
client.add("用户喜欢篮球", user_id="u1", embed_override=override)
client.search("兴趣爱好", user_ids=["u1"], embed_override=override)

它只覆盖接入端点,不覆盖 model、维度或 extra_body。新端点必须运行与已入库数据兼容的 Embedding 模型。

数据隔离与处理模式

普通记忆使用 user_id → agent_id → session_id 三层作用域。Search 必须传非空 user_ids;不传 agent_ids / session_ids 时可跨对应维度检索。

用户画像是例外:L0、balanced catalog 和 specific supplement 从生成到读取都只由 user_id 唯一确定。

  • lite:原文向量化,不调用提取 LLM。
  • pro:Embedding + LLM 提取 + reconcile;Graph 可通过 MEMORY_ENABLE_GRAPH 开启。
  • ultra:具备 pro 能力并强制启用 Graph;System 2 由调用方显式调用 evolve()。

Reader、Rerank 和 Search 参数详见 hy_memory_search_skill.md。