Files
Hy-Memory/docs/usage.md
T
alvinfeiandClaude Opus 5.5 16da11f788 feat(sdk): prompts in 11 languages and an explicit language option
Move every LLM prompt that had a zh variant into one registry,
hy_memory/prompts/, with one module per language (en, zh-Hans, zh-Hant,
ja, es, de, pt, fr, ko, ru, id). Call sites look prompts up with
get_prompt(name, language), so a new language needs only a new module.
The en and zh-Hans strings are moved verbatim, a test pins their sha256,
and the old names (prompts_zh.*_ZH, extractor.EXTRACT_SYSTEM_PROMPT, ...)
still import.

Add an optional language: MemoryConfig.language (env MEMORY_LANGUAGE)
as the client default, plus a per-call language= on add / async_add,
add_extracted, finalize_session, evolve and sweep. It goes through
extract and basic profile, reconcile and search query, the coding
extractor and both coding reconcilers with the chain summary, the
episode and topic summaries, and System 2. Codes are validated, with
aliases (zh, zh-CN, zh-TW, zh-HK, pt-BR); an unknown code raises
ValueError. With an explicit language, memories are written in that
language. Without one, the zh/en auto-detection is unchanged. The
server accepts language on /add and /add_extracted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-01 21:29:59 +08:00

15 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,
    language=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 记忆一起返回。

提示词语言(language)

SDK 的 LLM 提示词提供 11 种语言,按语言码注册在 hy_memory/prompts/ (每种语言一个模块,get_prompt(name, language) 统一取用):

语言码 语言 别名
en English en-US、en-GB
zh-Hans 简体中文 zh、zh-CN、zh-SG
zh-Hant 繁體中文 zh-TW、zh-HK、zh-MO
ja 日本語
es Español
de Deutsch
pt Português pt-BR、pt-PT
fr Français
ko 한국어
ru Русский
id Bahasa Indonesia

语言码大小写不敏感,_ 与 - 等价;未知语言码抛 ValueError 并列出支持的语言码。

# client 默认值(也可用环境变量 MEMORY_LANGUAGE=ja)
client = HyMemoryClient.from_config({"mode": "pro", "language": "ja"})

# per-call 覆盖 client 默认值
client.add(messages, user_id="u1", language="de")
client.add_extracted(["Der Nutzer trinkt keinen Kaffee."], user_id="u1", language="de")
client.finalize_session(user_id="u1", session_id="s1", language="de")
client.evolve(user_id="u1", language="de")   # ultra
client.sweep(user_ids=["u1"], language="de")  # ultra

优先级:per-call language > MemoryConfig.language(MEMORY_LANGUAGE)> 自动检测。

  • 未指定语言(默认):行为与以前完全一致。SDK 按输入做 zh/en 自动检测,中文输入用 简体中文提示词,其余用英文提示词;自动检测不会选到其他语言。提示词里的「输出语言与 输入语言一致」规则仍然生效,所以记忆正文保持对话的原始语言。
  • 显式指定语言:本次请求的全部 LLM 调用(抽取、基础画像、reconcile、working / coding 链路的抽取与两个 reconciler 及演变总结、episode / topic 摘要、System 2)都使用 该语言的提示词,并在提示词末尾追加该语言的「输出语言」规则:记忆正文、摘要等自然语言 字段一律用该语言书写,即使对话是另一种语言。JSON 键、字段名、枚举值、代码、路径、 报错信息和专有名词保持原样;tags 仍是英文小写关键词。
  • 只有英文版的提示词(session / dynamic episode、cross-domain sweeper)在显式指定语言时 同样追加该输出规则;搜索侧的场景判断 / query rewrite 只有英文版且保持 query 原语言, 因此 search() 不接受 language。
  • 自定义 extract_system_prompt_zh / extract_system_prompt_en 只覆盖 zh-Hans / en。

普通 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。