Tencent Open-Sourced Hy Memory 2.0, and It Stops Treating Agent Memory as One Chat Log
On October 16 Tencent Hunyuan open-sourced the whole Hy Memory 2.0 stack: a 30B-A3B memory model with FP8 and GGUF builds, a Python SDK called hy-memory, and MemHub, a local-first desktop app. Apache 2.0 throughout. I have read enough memory frameworks this year to expect another vector store with a summariser taped to it. Then I got to the scene split, and it stopped being an incremental thing. Chat, working and coding are three separate pipelines with different schemas, not one pool with a scene column.
Three layers open-sourced at once: model, framework, app. Missing one keeps memory at demo stage.
What is actually in the box
Three layers. The model: Hy-Memory-30B-A3B on Hugging Face, with a technical report on arXiv. The framework: hy-memory==2.0.4 on PyPI. The app: MemHub v1.0.4 plus memhub-cli 1.0.5, including a signed macOS build. Default storage is ChromaDB plus Kuzu plus SQLite, with Qdrant, FAISS, Neo4j and MySQL available as extras.
The good news is that nothing is welded to Tencent's own API. You supply an LLM endpoint and an embedding endpoint, both OpenAI-compatible. I ran it with DeepSeek for the LLM and a different vendor for embeddings without touching anything Tencent-specific.
The caveat worth stating early: memory writes call an LLM. Ingestion has a cost and a failure mode, which is a different budget shape from the vector-store-and-upsert pattern most of us are used to.
# Install is one line. The interesting part is what the package expects of you.
# Apache 2.0, current release 2.0.4. Default storage is ChromaDB + Kuzu + SQLite.
pip install hy-memory==2.0.4
# Optional backends, if you already run them:
# 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 instead of Kuzu
# pip install "hy-memory[mysql]==2.0.4"
# pip install "hy-memory[rerank]==2.0.4"
# You need an LLM endpoint and an embedding endpoint, both OpenAI-compatible.
# Any provider works; nothing is hard-wired to Tencent's own API.
export MEMORY_LLM_API_KEY="sk-..."
export MEMORY_LLM_BASE_URL="https://api.deepseek.com"
export MEMORY_LLM_MODEL="deepseek-chat"
export MEMORY_EMBEDDER_API_KEY="sk-..."
export MEMORY_EMBEDDER_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
export MEMORY_EMBEDDER_MODEL="text-embedding-v3"
export MEMORY_EMBEDDING_DIMS=1024
# One caveat before you point this at production: memory writes call an LLM.
# That means ingestion has a cost and a failure mode, which is the opposite of
# the vector-store-plus-upsert story most teams are used to budget for.Three modes, and the wrong one is the usual mistake
lite does raw embedding writes and searches with zero LLM calls, and it ignores both scene and layers. pro adds LLM extraction and conflict reconciliation. ultra adds System 2 schema induction and a cross-domain sweeper triggered by evolve().
Start on lite to prove the plumbing, then climb when recall quality actually annoys you. Beginning on ultra because it sounds stronger usually means discovering your embedding bill first and your retrieval latency second.
One detail that reveals the design intent: search does not return a flat list. It returns three channels — profile (who this user is), normal (relevant items) and proactive (what you did not ask for but should know). That shape is a decision, not an accident of the return type.
# Three modes, and picking the wrong one is the most common mistake.
#
# lite -> raw embedding only, zero LLM calls, scene and layers ignored
# pro -> adds LLM extraction and conflict reconciliation
# ultra -> adds System 2 schema induction across domains (use evolve())
#
# Start with lite to prove the plumbing, then move up when the recall quality
# actually annoys you. Starting on ultra because it sounds better is how people
# discover their embedding bill before they discover their retrieval latency.
from hy_memory import HyMemoryClient
client = HyMemoryClient(mode="pro")
# Write: a plain string, or OpenAI-format messages. Both are supported.
client.add("I love sci-fi movies, especially Interstellar", user_id="user_1")
client.add([
{"role": "user", "content": "Recommend a movie"},
{"role": "assistant", "content": "Try Interstellar — a sci-fi masterpiece by Nolan"},
], user_id="user_1")
# Search returns three channels, not one flat list. That shape is the point:
# results["memories"]["profile"] - who this user is
# results["memories"]["normal"] - relevant items
# results["memories"]["proactive"] - things you did not ask for but should see
results = client.search("What movies does the user like?", user_ids=["user_1"], limit=10)
for mem in results["memories"]["normal"]:
print(f" [{mem['score']:.2f}] {mem['content']}")
client.close()Inside the eight layers, immutability is the load-bearing wall
L0 basic info, L1 raw conversation (append-only), L2 atomic facts (versioned, immutable), L3 session summaries, L6 schemas as a graph layer, L7 prospective intentions with an expiry that demotes them to ordinary facts. L4 and L5 are reserved.
Here is why L2 matters more than the rest. Almost no memory bug is "the vector search missed it". The bug is "the store returned a stale fact as if it were current". An immutable, versioned fact plus a supersede chain is the standard fix: when a user moves cities, the old fact is superseded, not overwritten, and the chain survives.
That is also why conflict reconciliation exists instead of last-write-wins. If all you have is upsert, you do not have facts. You have the most recent write.
# The claim worth testing is the eight-layer hierarchy, L0 through L7.
# Not marketing: each layer is a different table with different write rules.
#
# L0 BASIC_INFO basic information
# L1 RAW raw conversation, append-only
# L2 FACT atomic facts, versioned and immutable
# L3 SUMMARY session summaries
# L4 IDENTITY reserved
# L5 KNOWLEDGE reserved
# L6 SCHEMA mental models (graph layer)
# L7 INTENTION prospective intentions, with an expiry
#
# The design consequence: L2 facts are immutable and versioned, so "the user
# lives in Chengdu" does not get overwritten when they move, it gets superseded.
# That is why conflict reconciliation exists instead of last-write-wins.
#
# Why this matters for retrieval quality: most memory bugs are not "the vector
# search missed it". They are "the store returned a stale fact as current".
# Immutability plus a supersede chain is the standard fix, and it is baked in
# rather than bolted on by hand.
from hy_memory import HyMemoryClient
client = HyMemoryClient(mode="pro")
client.add("My address changed: I moved from Chengdu to Hangzhou last month",
user_id="user_1")
# A well-built memory layer should answer the current state, not concatenate
# both facts into a paragraph and hope the reader sorts it out.
hits = client.search("Where does the user live now?", user_ids=["user_1"], limit=5)
for m in hits["memories"]["normal"]:
print(f"[{m['score']:.2f}] {m['content']}")
client.close()Chat, working and coding are three pipelines, not one vector pool with a scene label.
Working memory and coding memory are two layers, not one
Both only activate in pro or ultra, and writes must carry a tool-call trace via tool_calls or role=tool messages. That is a hard requirement: in lite, the scene argument is ignored and everything lands as raw memory.
The two-layer design is the part I would steal. Facts record what this session did — the task, the approach, the result — exactly one per session, no threshold. Experiences record approaches worth reusing next time, zero to many, gated by a value threshold. The fact layer guarantees the work is remembered. The experience layer makes what was learned reusable. They cross-link.
Coding adds a version-evolution chain, and the reason is subtle. Fact extraction deliberately refuses to paste code — the budget goes to coordinates like file, function and branch. So a version that was later reverted never appears in a fact, and it is not in the working tree or at HEAD either. The chain records the real diff of every version with zero LLM calls, filling exactly the hole a summary inevitably leaves.
# Working and coding memory only activate in pro or ultra mode, and writes
# must include a tool-call trace. That is a hard requirement, not a style note:
# lite mode ignores the scene argument entirely and stores everything raw.
#
# The three scenes, and why one pipeline would not cover them:
#
# chat small talk, preferences, personal info -> L0-L7 hierarchy
# working docs, spreadsheets, research, analysis -> facts + experiences
# coding code, debugging, tests, builds, SQL -> same two layers
# + a diff chain
#
# The two-layer design is the part I would copy. Facts record what the session
# did (exactly one per session, no threshold). Experiences record what is
# reusable next time (zero to many, gated by a value threshold). One guarantees
# the work is remembered; the other makes what was learned reusable.
from hy_memory import HyMemoryClient
client = HyMemoryClient(mode="pro")
messages = [
{"role": "user", "content": "Fix the timeout in the search client."},
{"role": "assistant", "content": "", "tool_calls": [{
"id": "call_1", "type": "function",
"function": {"name": "apply_patch", "arguments": '{"path":"src/search.py"}'},
}]},
{"role": "tool", "tool_call_id": "call_1", "name": "apply_patch",
"content": "Patch applied successfully"},
]
client.add(
messages,
scene="coding", # or "working"
user_id="user_123",
workspace_id="github.com/example/service", # isolate per repo
branch="fix/search-timeout", # needs workspace_id
)
result = client.search(
"Which timeout implementation was finally kept?",
scene="coding",
user_ids=["user_123"],
workspace_id="github.com/example/service",
branch="fix/search-timeout",
limit=5,
)
result["facts"] # fact layer
result["experiences"] # experience layer
result["chain_block"] # coding only, needs MEMORY_CODING_CHAIN_BLOCK_ENABLED
client.close()MemHub is the part most people will actually touch
It is a local-first desktop app plus CLI that collects history from AI tools you have already authorised and keeps it in one local directory. The official docs list Claude Code, Cursor, Codex, OpenCode and Qwen Code.
Three checks before you let it index anything.
Scope. A tool that reads coding traces reads source code, and sometimes secrets pasted into prompts. Decide which repositories are in scope before the first index, not after.
Isolation. The framework separates memories by source and filters with include_sources / exclude_sources. Use it, instead of one mixed pool where your Cursor habits surface while you are asking about a Codex session.
Cost. Indexing is not free in pro mode because writes call an LLM. Index a week, measure, then decide. Do not index two years of history on the first afternoon and check the bill afterwards.
The honest framing: this is a memory runtime, not a memory format. If your team cannot agree on what counts as a fact, the pipeline will faithfully store your disagreement in eight carefully normalised tables.
# MemHub is the piece most people will actually touch day to day.
# It is a local-first desktop app plus CLI that pulls history from AI tools you
# already authorised and keeps it in one local directory.
#
# From the official docs, MemHub covers Claude Code, Cursor, Codex, OpenCode and
# Qwen Code. Current release is MemHub v1.0.4 with memhub-cli 1.0.5, and there
# is a signed macOS app plus verified macOS/Linux runtimes.
npx [email protected] --help
# then, per the MemHub README, point it at the tool sources you consent to
# and let it index into the local data directory
# Three practical checks before you let it index anything:
#
# 1. Scope. A memory tool that reads coding traces reads source code and
# sometimes secrets pasted into prompts. Decide which repos are in scope
# BEFORE the first index, not after.
#
# 2. Isolation. The framework separates memories by source (source="cursor",
# "codex", "openclaw") and filters with include_sources/exclude_sources.
# Use that instead of one mixed pool, or your Cursor habits will surface
# while you are asking about a Codex session.
#
# 3. Cost. Indexing is not free in pro mode because writes call an LLM.
# Index a week, measure, then decide. Do not index two years of history
# on the first afternoon and check the bill afterwards.
# The honest framing: this is a memory *runtime*, not a memory *format*.
# If your team cannot agree on what a "fact" is, the pipeline will faithfully
# store your disagreement in eight well-normalised tables.Immutable facts with a supersede chain fix the most common memory bug there is.
📌 Frequently Asked Questions
When was Hy Memory 2.0 open-sourced and under what licence?
October 16, 2026, under Apache 2.0. Three layers shipped together: Hy-Memory-30B-A3B model weights on Hugging Face (with FP8 and GGUF builds), the hy-memory Python SDK at version 2.0.4 on PyPI, and the MemHub desktop/CLI application.
Do I have to use Tencent models or APIs?
No. You configure an OpenAI-compatible LLM endpoint and an embedding endpoint via MEMORY_LLM_* and MEMORY_EMBEDDER_* variables. Running DeepSeek for the LLM and a separate provider for embeddings is a documented, ordinary configuration.
How do I choose between lite, pro and ultra?
lite makes zero LLM calls and does raw vector work, so use it to validate plumbing. pro adds extraction and reconciliation and is the sane default. ultra adds the System 2 graph layer and cross-domain induction via evolve(). Climb the modes; do not start at the top.
What is different about coding memory?
Coding reuses the same two-layer facts-plus-experiences schema as working, but adds a version-evolution chain that records how many times a piece of code changed and what each change was, storing real diffs without LLM calls. It exists because fact extraction deliberately omits code bodies, so reverted versions would otherwise be unrecoverable.
Which tools does MemHub support and is data local?
The official docs list Claude Code, Cursor, Codex, OpenCode and Qwen Code. MemHub is described as local-first, with data in a local directory. Current builds are MemHub v1.0.4 and memhub-cli 1.0.5, with a signed macOS app and verified macOS/Linux runtimes. Read the MemHub README for the precise privacy boundary.
🔧 Recommended Tools
JSON Formatter
Read the three-channel search shape and tool traces.
AI Token Counter
pro mode writes call an LLM, so estimate the bill first.
GraphRAG Builder
Understand the L6 graph layer and cross-domain schemas.
Agent Interoperability Guide
See the interface boundary when unifying multi-tool history.
Spec-Driven Development
Define what counts as a fact before building the pipeline.