# Hy Memory: a 3B-active memory writer that ties Opus 5, and four launch numbers that belong to the engine

> Satyajit Ghana — Head of Engineering @ Inkers Technology
> canonical: https://ai.thesatyajit.com/articles/hy-memory
> date: 2026-10-16
> tags: agent-memory, agents, reinforcement-learning, grpo, mixture-of-experts, benchmarks

On 16 October Tencent Hunyuan [posted](https://x.com/TencentAI_News/status/2111024931941294158) that "a 3B active model beats Opus 5 at memory", and open-sourced three things under the name Hy Memory. **MemHub** is a desktop and CLI app that pulls conversation history out of Claude Code, Cursor, Codex and seven other tools into one local store. **Hy Memory** is the engine and Python SDK underneath it. **Hy-Memory-30B-A3B** is a model trained for one job: deciding what goes into that store.

The post makes six claims. The model is first on all four benchmarks run, leads MemoryArena by 10.2 points, and cuts memory volume by 51.8% and recall tokens by 39.3%. It still wins when dropped into Mem0, MemOS or EverMemOS, "even on the one system it never trained on". And in Tencent's products one assistant's multi-hop recall went from 50% to 90%.

I cloned the [repo](https://github.com/Tencent-Hunyuan/Hy-Memory), read every safetensors header of the [weights](https://huggingface.co/tencent/Hy-Memory-30B-A3B) by range request, read the [technical report](https://arxiv.org/abs/2610.22196) and the project's website, installed the engine in a scratch venv, ran its test suite, and drove it through two conversation turns with a stub model on localhost. I did not run the 30B model. Numbers below are labelled **measured** (I computed it from a file or a run), **reported** (Tencent's figure, not re-run) or **reasoned** (my arithmetic on the other two).

The short version: the model is real and the training idea is good. The headline is thinner than the post says, and four of its numbers come from a different system.

## What a memory model does that a chat model doesn't

An external memory system has three parts. A **writer** turns an interaction into changes to a persistent store. A **retriever** picks records for a later question. A **reader** answers from what was retrieved. The report's point is that the writer is the hard one: "a powerful downstream reader cannot reliably infer a fact that was never stored" (reported, paper §1). Store everything instead and you pay for it in tokens and in retrieval noise.

So a memory writer is a policy acting on external state. At chunk $t$ it sees the bank $M_t$ (or the slice the framework shows it) and the raw chunk $x_t$, emits operations $a_t$, and the framework applies them to make $M_{t+1}$. Each call may start from a fresh prompt, but the decisions are sequential because the bank carries their consequences. In practice that is three jobs a chat model is not trained for:

- **Extract.** Pull durable facts out of a turn, as self-contained third-person sentences, and drop the greetings, the encouragement and the assistant's 2,000-word essay.
- **Reconcile.** Compare those facts with what is already stored and choose an operation: add a new record, merge into an old one, or mark the old one superseded.
- **Emit valid JSON** under a framework's schema, every time, because a malformed reply is a lost write.

Mem0, MemOS, EverMemOS and Hy Memory all put an ordinary LLM behind a long prompt to do this. Hy-Memory-30B-A3B is trained to be that LLM.

## One write, traced through the engine

The engine is a large Python package: 75,578 lines under `hy_memory/` at commit `fba347a` (measured). Its own diagram is a fair map.

<Figure
  src="https://media.thesatyajit.com/ai/articles/hy-memory/fig1.png?v=934da077d83d5d8f"
  alt="Diagram titled HY Memory Architecture. add() or search() enters a scene router that sends chat to a left panel and working or coding to a right panel. The chat panel has a write row (Embed plus raw L1_RAW, LLM extraction of facts summaries and intentions, Reconcile with a supersedes chain), a store row (vector store for L0 to L4 and L7, optional graph store for L6 schemas), a search row (recall by vector BM25 and graph, fusion with chain expansion, response with profile normal and proactive channels) and a System 2 row (evolve, schema induction, cross-domain sweeper). The working and coding panel has extraction of fact plus experiences, reconcile with ADD UPDATE SUPERSEDE, diff capture with zero LLM, stores for facts and experiences and a version chain, and a search row that attaches revisions to recalled facts."
  caption="The engine: a scene router in front of a chat pipeline with a layered memory and a working/coding pipeline with facts, reusable experiences and, for code, a chain of real diffs. Model calls happen only on the write side (Hy Memory repository, docs/images/architecture.svg)."
/>

In `pro` mode a chat write goes like this, and the steps map onto files I could read:

1. **Store the raw turn.** `pipelines/writer.py` embeds the messages and stores them verbatim as layer `L1_RAW`. Once extraction succeeds, that node is demoted to `SHADOW`, kept but never recalled.
2. **Extract.** `agent/extractor.py` sends a system prompt that lists what to keep (preferences, projects, plans, decisions, constraints) and what to drop (small talk, generic emotions, "assistant reminders of old memories"), plus the last few messages for resolving "her" and "that project". The reply is JSON with three buckets: `memory`, `intentions` with a `valid_until`, and `basic_info`.
3. **Find candidates.** `agent/reconciler.py` searches each new fact against stored facts with vector and BM25 search, fuses the scores, and keeps candidates at `min_score=0.4` or above. If nothing passes, every fact is written as `ADD` and the reconcile call is skipped.
4. **Reconcile.** One more model call lists the candidates with their ids and the new facts, and asks for operations. The prompt defines three: `ADD` for new information, `UPDATE` to merge a compatible fact on the same topic, and `SUPERSEDE` only when "both cannot be true at the same time". Each superseded chain is shown by its head only, so the model cannot supersede a record that is already history.
5. **Apply.** In the code (not the docstring at the top of the file, which still describes an older ADD and DELETE scheme), `UPDATE` rewrites the old node in place and keeps its original date. `SUPERSEDE` marks the old node `superseded`, sets `is_latest` false, links it with `superseded_by`, and writes the new fact as the head of an evolution chain.
6. **Recall.** `search()` with the default mode makes no model call at all: one embedding, vector and keyword search, and a response in three channels (`profile`, `normal`, `proactive`). A chain comes back as its current state plus its history.

Chat memory sits in layers L0 to L7, from basic info through raw turns, facts and summaries to schemas and expiring intentions. Working and coding scenes use two layers instead, one fact per session plus reusable "experiences", and coding also keeps every revision's real diff.

### What happened when I ran it

The task allowed installing the engine and running it offline. `hy-memory` 2.0.1 installed from the clone into a fresh venv of 872 MB (measured). For the model and the embedder I wrote a stub OpenAI-compatible server on localhost: hashed bag-of-words embeddings, and rule-based replies to the extraction and reconcile prompts. The engine talks to its model only through `MEMORY_LLM_BASE_URL`, the slot you would point at a vLLM server running Hy-Memory-30B-A3B. Below, the decisions are my stub's. The calls, the prompts, the candidate search, the store and the recall are the engine's own (measured, shared 4-core cloud VM, CPU only).

<TurnStepper />

Turn one ("I live in Pune. Black coffee every morning, and I'm training for a half marathon in December.") cost one model call. Extraction produced three facts, the store was empty, so all three went in as `ADD` with no reconcile call. Turn two ("I moved to Bengaluru last week, and I've switched from coffee to green tea.") cost two. Extraction produced two facts; the Bengaluru fact found the Pune fact as a candidate; reconcile returned `SUPERSEDE` for it and `ADD` for the tea.

Measured with the model's own `tokenizer.json`, the extraction prompts were 2,453 and 2,480 tokens and the reconcile prompt 1,620, against replies of 106, 90 and 133 tokens. That is about 20 tokens read for every token written (reasoned, from those six counts). A memory writer is mostly a reader of long fixed prompts, which is why the cost of the model in that slot matters, and why a 3B-active one is attractive.

The interesting part is what did not happen. The tea fact's best candidate fused to 0.19, under the 0.4 gate, so the coffee fact was never shown to the model. Afterwards the store held "drinks black coffee every morning" as active next to "switched from coffee to green tea", and the query "coffee tea morning" returned both, the stale one first (0.516 against 0.486, measured). The Pune fact, which did reach the reconciler, came back as a 62-token block reading "Current: … Bengaluru … History: … Pune".

My hashed embedder is far cruder than a real one, so a real deployment would pair these more often. The mechanism holds: **the writer can only reconcile what retrieval hands it**, and that gate is in the engine, not the model. A reply under the launch post asked what happens "when two stored memories contradict each other". The answer from the code is: they are superseded if the candidate search pairs them, and both stay active if it doesn't.

## The model: a Hy3 30B-A3B with 128 experts

The model card names no base, but the paper does: the student is the "Hy3-30B-A3B instruction-tuned checkpoint", taught by the flagship [Hy3](/articles/hunyuan-hy3). The files agree. `config.json` says `model_type: hy_v3`, the same family as the 295B flagship and the same 120,832-row vocabulary.

Summing all 18,673 tensors in the 51 shard headers gives **30,064,725,888 parameters**, all BF16 except 6,016 F32 router biases (measured). That is exactly the parameter count of [Hy-MT2-30B-A3B](https://huggingface.co/tencent/Hy-MT2-30B-A3B), Tencent's translation model from May, and the two configs differ only in cosmetic fields (measured). So both are fine-tunes of the same unreleased Hy3 30B-A3B base (reasoned).

The layout, from the config and the tensor shapes (measured):

- 48 layers, hidden size 2,048, 32 query heads and 4 KV heads of width 128, QK-norm.
- Layer 0 is dense (MLP width 6,912). Layers 1 to 47 are MoE: 128 routed experts of width 768 plus one shared expert, top-8 routing with a sigmoid router and a learned expert bias.
- Routed experts hold 28,387,049,472 parameters, 94.4% of the model.
- Active per token: everything that is not a routed expert, plus 8 experts in each of 47 layers, is **3,451,867,008** including both embedding tables, or 3.20B without the input embedding. "A3B" is honest.

One config field is off: `moe_intermediate_size` says 1,536, but the expert tensors are 768 wide, which is what `expert_hidden_dim` says and what Hy-MT2's config says (measured). Loaders that read the wrong field would mis-size the experts.

The launch thread says "24GB+ VRAM recommended". The only weights published are BF16, 60.13 GB on disk (measured). They do not fit in 24 GB. You would need to quantize them yourself, roughly 15 GB at 4 bits before KV cache (reasoned), or offload experts to CPU memory. Tencent publishes FP8 and GGUF builds of Hy-MT2 but none of this model, as of today.

## How it was trained: rewards for what the bank can answer

<Figure
  src="https://media.thesatyajit.com/ai/articles/hy-memory/fig2.png?v=a22c53fac8b0b78c"
  alt="Two-panel diagram. Top strip: deployed harnesses Mem0, MemOS, Hy-Memory Chat, Working and Coding, unmodified and shared by both stages. Stage 1, Memory on-policy distillation: a parent bank feeds the harness write path where the student Hy3-30B-A3B samples every LLM call; a frozen Hy3 flagship teacher supplies top-50 reverse KL on the sampled tokens; the candidate bank is promoted as parent of the next chunk. Stage 2, Attributed memory RL, initialised at step 375: eight rollouts from a parent bank write chunks 1 to 5, shown as a grid of cells shaded by chunk score, with a dashed column marking one chunk-level group; each bank is examined by retrieving a memory block, a frozen answer model and a rubric judge; score equals PRM plus 2 ORM minus 0.3 Penalty; the best final bank is carried to the next window."
  caption="Both stages run inside the unmodified frameworks. Distillation teaches each framework's write protocol on the student's own states; RL scores each chunk's writes by what the resulting bank can answer, and carries the best bank forward (Hy-Memory-30B-A3B technical report, Figure 1)."
/>

Training has two stages, both inside the real frameworks with every framework LLM call routed to the student (reported, paper §4).

**On-policy distillation** teaches protocol. The student writes, and the frozen Hy3 flagship scores the student's own tokens (a top-50 reverse KL). Later prompts therefore contain the student's own earlier mistakes, as in deployment.

**Memory RL** teaches usefulness. A window is up to five chunks of about ten turns. Eight rollouts write the window from copies of the same parent bank, then each bank is examined: retrieve, answer with a frozen answer model, grade with a frozen rubric judge. Each chunk $t$ of rollout $i$ gets a score

$$
s_i[t] = \mathrm{prm}[t] + 2\,\mathrm{orm}[t] - 0.3\,\mathrm{pen}[t]
$$

where $\mathrm{prm}$ is the mean credit on probes written for that chunk (did an exact name, number or version survive the write?) and on **traps** (did the assistant's generic encouragement stay out?), $\mathrm{orm}$ is credit from cross-chunk questions attributed back to the chunks whose gold facts the answer recovered, and $\mathrm{pen}$ rises steeply once bank-token growth per input token passes a per-framework threshold. Advantages are normalised per chunk position across the eight rollouts, not per trajectory, so a rollout can be rewarded at chunk 2 and penalised at chunk 4. The best final bank seeds the next window.

The reward set has 164,576 probes, 8,599 traps and about 60.8k outcome questions over 57,616 chunks, and RL ran on 128 H20 GPUs (reported, §5 and §7). There is no ablation: chunk-level GRPO, traps, the growth penalty and the distillation start are all listed as "future work" (reported, §8.4). The method is well argued, and no single part of it is shown to matter.

## Checking the claims

### "Beats Opus 5": 55.49 to 54.62, carried by one metric

The evaluation swaps only the writer. Framework, prompts, retrieval, answer model and judge stay fixed, each writer builds its bank from empty, and each framework's score is the mean of six metrics: LoCoMo, LongMemEval, PersonaMem-v2, Travel SPS, Shopping PS and CMB, Tencent's own coding-memory benchmark. Every benchmark is cut to a fixed 20% subsample (reported, §8.1).

<Figure
  src="https://media.thesatyajit.com/ai/articles/hy-memory/fig3.png?v=f23fe67d2f46d1ad"
  alt="Grouped bar chart of macro-average score in percent for Hy-Memory-30B-A3B, Claude Opus 5, DeepSeek V4.1 Flash and Qwen3.6-35B-A3B. Overall 55.49, 54.62, 53.64, 50.37. Hy-Memory harness 60.55, 60.53, 60.20, 55.87. Mem0 54.02, 53.43, 49.73, 47.51. MemOS 50.51, 50.50, 49.17, 47.36. EverMemOS, marked held out, 56.89, 54.04, 55.47, 50.75. The y axis is broken and starts at 30."
  caption="The headline chart. Note the y axis starts at 30, which makes 0.01-point gaps look like bars of equal height and 2.86-point gaps look large (Hy-Memory-30B-A3B technical report, Figure 3)."
/>

Across the four frameworks it averages 55.49 against Opus 5's 54.62 (reported, Table 6), and I recompute both from the per-metric tables to rounding (measured). The per-framework margins over Opus 5 are 0.60 on Mem0, 0.02 on Hy Memory, 0.01 on MemOS and 2.86 on EverMemOS. The paper says it itself: the 0.01 to 0.02 margins "indicate parity with Opus 5 … not a statistically resolved difference", and there are no repeated seeds (reported, §8.2 and §10).

Go one level down and it is closer still. Of the 24 framework-metric cells, Hy-Memory-30B-A3B beats Opus 5 in 11, loses 12 and ties 1 (reasoned, from Tables 7 to 10). Opus 5 leads on LoCoMo in every framework where they are not tied. The lead comes from Travel SPS, where the model averages 48.22 against 40.59 across frameworks (reasoned). Remove Travel and recompute, and **Opus 5 leads, 57.43 to 56.95** (reasoned). Try it:

<MetricToggle />

The report never says which Opus 5 setting was used (thinking or not, at what effort), does not name the answer model or the judge, and ran every baseline itself. None of that makes the numbers wrong. It does mean "beats Opus 5" is a self-run tie on a 20% sample, decided by one metric, which is still a remarkable result for a model with 3.45B active parameters. The fairer headline is the one in the same table: memory training lifted the base checkpoint from 47.66 to 55.49 (reported).

### "Even on the one system it never trained on": yes, EverMemOS

The paper trains on five configurations from three frameworks (Mem0, MemOS, and Hy Memory's chat, working and coding scenes) and holds EverMemOS out entirely (reported, Table 2 and §8.3). On EverMemOS it scores 56.89 against 55.47 for DeepSeek V4.1 Flash and 54.04 for Opus 5. That is the cleanest evidence in the report that the model learned memory writing rather than three prompt formats. It is also one held-out framework, and the gain there is mostly Travel (+10.65) and CMB (+13.66) against Opus 5 (reported).

### The four benchmarks, MemoryArena, 51.8% and 39.3% belong to the engine

None of "MemoryArena", "10.2", "51.8%", "39.3%" or "50% to 90%" appears in the paper or the model card. They are on the [project website](https://memory.hunyuan.tencent.com/), and its own fine print says what they measure: the **Hy Memory framework** against Mem0, MemOS, EverOS, OpenViking, RepoMemory and a plain RAG baseline, with **DeepSeek V4.1 Flash as the memory model** and GPT-5.6-sol as answer model and judge (reported). The "four benchmarks" there are LoCoMo for chat, MemoryArena for working memory, and CodeMemoryBench and SWE-ContextBench for code. The paper's "first on all four" is about four frameworks for the model. The post folded the two into one sentence.

Taking the engine numbers on their own terms:

- **51.8% less memory volume.** The site's chat chart plots "storage" as bank tokens divided by input tokens: Hy Memory at 46.1%, EverOS at 95.6%. One minus 46.1 over 95.6 is 51.8% (reasoned). So it is a LoCoMo-only comparison against the heaviest competitor. Mem0 stores less than Hy Memory, 37.3%, at a much lower score of 55.1 against 72.6 (reported).
- **39.3% fewer recall tokens.** Also LoCoMo, also against EverOS: 1,159 tokens per question down to 703 (reported). One minus 703 over 1,159 is 39.3% (reasoned).
- **MemoryArena by 10.2 points.** The site gives 55.31 for Hy Memory, the mean of 59.04 on Group Travel Planner and 51.57 on Bundled Shopping, which checks (reasoned), and says it leads EverOS by 10.2. But the same page's working-memory chart puts EverOS at 31.90, Mem0 at 38.78 and plain RAG at 41.22, gaps of 14.09 to 23.41 points (reasoned). I cannot find the system that sits 10.2 points behind, so I cannot confirm that number.

For the model alone, the paper's matrix does contain MemoryArena's two tasks. Averaged across frameworks, Hy-Memory-30B-A3B leads Opus 5 there by 3.21 points (reasoned, from the site's per-metric averages 48.22 and 19.90 against 40.59 and 21.11).

### "Multi-hop recall went from 50% to 90%": unverifiable, and not this model

The website attributes this to the AI assistant in Tencent's stock app: an internal evaluation of A-share research memory, where multi-hop recall rose from 50 to 90 after switching from the previous memory system to Hy Memory. Its footnote says the memory model was DeepSeek V4 Flash (reported). So it is an engine result, from a private evaluation nobody outside Tencent can run. I cannot verify it, and it says nothing about Hy-Memory-30B-A3B.

## What I ran, exactly

| What | Result |
|---|---|
| `pip install` from the clone, plus `faiss-cpu` and `qdrant-client` for the optional backends | 872 MB venv |
| `pytest tests` (unit tests; `integration` deselected by the repo's own config) | 1,273 passed, 1 failed, 26 skipped, 19 deselected in 60 s; peak RSS 1.69 GB |
| The one failure | `test_reader_selection.py` compares a class `__dict__` against a fixed set, and Python 3.13 adds `__static_attributes__` and `__firstlineno__`. A test artefact, not an engine bug |
| Two-turn drive of `HyMemoryClient(mode="pro")` against my localhost stub | 3 model calls, 1 `SUPERSEDE`, 4 `ADD`, one stale fact left active, as traced above |
| Hy-Memory-30B-A3B | Not run. 60.13 GB of BF16 does not fit this machine |

All measured on a shared 4-core cloud VM, CPU only, with no API keys and no hosted endpoints.

## Where it breaks

- **Retrieval decides what the writer sees.** A contradiction that scores under the fusion gate is never reconciled, and both facts stay live. A better writer does not fix that.
- **The launch mixes systems.** Engine-with-DeepSeek numbers and model numbers share one paragraph of the post. Read the website's footnotes before quoting any of them.
- **No ablations, no leakage audit, no named judge**, and a VRAM line the shipped weights do not meet.

What I like is the reward design: probes and traps per chunk, outcome credit traced to the chunks that supplied the evidence, and a growth penalty are a careful answer to the question every memory system ducks, which is whether a particular write was useful. The engine is real software with a passing test suite and a clean model slot. Point `MEMORY_LLM_BASE_URL` at a vLLM server running this model and you have the whole stack on your own hardware, once you have the 60 GB for it.

<ModelCard repo="tencent/Hy-Memory-30B-A3B" />

<RepoCard repo="Tencent-Hunyuan/Hy-Memory" />

## Related on this site

- [Hindsight](/articles/hindsight-agent-memory) lifts a 20B backbone from 39.0% to 83.6% on LongMemEval by changing only the memory layer: the engine side of the same argument.
- [TencentDB Agent Memory](/articles/tencentdb-agent-memory) is another Tencent memory design, organised as a four-tier hierarchy around cache stability.
- [Agent Memory Repo](/articles/agent-memory-repo) shows Devin's memory as a git repo of one-line bullets: the opposite end from a trained writer.
- [MemHarness](/articles/memharness) trains a 7B policy to rewrite memories at read time, where Hy Memory trains the write.
- [VoiceMem](/articles/voicemem) is a voice-agent memory paper whose schema-routed candidate pool is a different answer to the same retrieval gate.
- [Hunyuan Hy3](/articles/hunyuan-hy3) is the 295B-A21B flagship that taught this model, and the family its 30B-A3B base comes from.
