Skip to content

fix(hermes-plugin): fall back to agent_identity when no gateway user_id#53

Closed
YOMXXX wants to merge 1 commit into
Tencent:mainfrom
YOMXXX:fix/python-user-id-fallback
Closed

fix(hermes-plugin): fall back to agent_identity when no gateway user_id#53
YOMXXX wants to merge 1 commit into
Tencent:mainfrom
YOMXXX:fix/python-user-id-fallback

Conversation

@YOMXXX
Copy link
Copy Markdown
Contributor

@YOMXXX YOMXXX commented May 18, 2026

Summary | 摘要

修复 #15 报告的 Hermes --profile 用户共享单一记忆池的问题。memory_tencentdb 之前忽略 Hermes 传过来的 agent_identity kwarg,把所有非 gateway session 都 scope 到 \"default\",导致 hermes --profile work--profile personal 的 Persona / L1 / L2 数据互相污染。

Fix #15 — memory_tencentdb ignored Hermes' `agent_identity` kwarg and hardcoded the scope to `"default"` for all non-gateway sessions, so `hermes --profile work` and `--profile personal` silently shared one memory pool. Now falls back to `agent_identity` when `user_id` is absent.

Root cause

Hermes `run_agent.py` 把当前 profile 名通过 kwargs 传给 memory provider:

```python

run_agent.py L1803-1808

_init_kwargs["agent_identity"] = _profile
_init_kwargs["agent_workspace"] = "hermes"
```

但 memory_tencentdb 的 `initialize()` 只读 `user_id`:

```python

hermes-plugin/memory/memory_tencentdb/init.py:705 (before)

self._user_id = kwargs.get("user_id", "default")
```

→ 所有 CLI profile 的 `user_id` 都是 `"default"` → 一个记忆池。

Fix

新的解析优先级(first non-empty wins,否则 `"default"`):

  1. `user_id` — gateway 集成(Telegram / Discord / …)传,保持现有多用户隔离。
  2. `agent_identity` — Hermes CLI profile,新加的 fallback 路径
  3. `"default"` — bare CLI 调用的历史 fallback。

空字符串视为未设置(misconfigured gateway 发 `user_id=""` 不再静默盖住 profile scope)。

Compatibility | 兼容性

场景 之前 修复后
Bare CLI(无 profile,无 gateway) `"default"` `"default"`(不变)
`hermes --profile work` `"default"`(污染!) `"work"`(隔离)
`hermes --profile personal` `"default"`(污染!) `"personal"`(隔离)
Gateway(Telegram `user_id=alice`) `"alice"` `"alice"`(不变)
Gateway + profile(`user_id=alice` + `agent_identity=work`) `"alice"` `"alice"`(multi-user 优先)

已有 `default` 数据保留,新 profile 从空白开始 —— 与 issue 报告者预期一致。

Refactor

把 user_id resolution 抽到独立模块 `hermes-plugin/memory/memory_tencentdb/_identity.py`(纯函数 `resolve_user_id(kwargs) -> str`),让它可以脱离 `init.py` 中的 `agent.memory_provider` import 独立单测 —— `agent` 模块只在 hermes-agent 仓库里存在,plugin standalone 测试需要绕开它。

`hermes-plugin/conftest.py` 提供 stub for `agent.memory_provider`(仅当真模块不可用时启用,hermes-agent 环境下被 sys.path 上的真模块 shadow,不影响集成测试)。

Tests

新建 `hermes-plugin/memory/memory_tencentdb/tests/test_resolve_user_id.py` — 6 cases:

# 场景 期望
1 空 kwargs `"default"`
2 仅 `user_id=alice` `"alice"`
3 仅 `agent_identity=work` `"work"`
4 同时 `user_id` + `agent_identity` `"alice"`(gateway 优先)
5 `user_id=""`(空字符串)+ `agent_identity=work` `"work"`(fall-through)
6 无关 kwargs(`agent_workspace` 等) 不影响解析

```
✓ pytest hermes-plugin/memory/memory_tencentdb/tests/test_resolve_user_id.py → 6/6 passed
```

DCO

Commit 带 `Signed-off-by: 李冠辰 liguanchen@xiaomi.com`。

Closes #15.

Hermes passes ``agent_identity`` (the active --profile name) to memory
providers via kwargs during ``initialize()``:

    # run_agent.py L1803-1808
    _init_kwargs["agent_identity"] = _profile
    _init_kwargs["agent_workspace"] = "hermes"

But memory_tencentdb ignored ``agent_identity`` and hardcoded the scope
to ``"default"`` for non-gateway sessions:

    # __init__.py L705 (before)
    self._user_id = kwargs.get("user_id", "default")

The effect: every Hermes CLI profile (``hermes --profile work`` /
``--profile personal`` / …) silently shared the same memory pool —
Persona, L1 memories, and L2 scene blocks bled across profiles that
were intended to be isolated contexts. Reported in Tencent#15 by @ljnsysu2005
with a complete patch suggestion.

Fix:

  Priority chain (first non-empty wins, otherwise ``"default"``):
    1. ``user_id``        — gateway integrations (Telegram, Discord, …),
                            preserves existing multi-user isolation.
    2. ``agent_identity`` — Hermes CLI profile, new fallback path.
    3. ``"default"``      — historical fallback for bare CLI invocation.

  Empty strings fall through (a misconfigured gateway sending
  ``user_id=""`` no longer silently masks the profile scope).

  Existing data under ``"default"`` is preserved; new profiles start
  fresh.

The resolution logic is factored into a standalone module
(``_identity.py``) so it can be unit-tested without pulling in
``agent.memory_provider`` — that module only exists inside a hermes-agent
checkout, and the new plugin-side tests must run standalone.

Tests: new hermes-plugin/memory/memory_tencentdb/tests/
test_resolve_user_id.py — 6 cases covering empty-kwargs default, gateway
user_id priority, agent_identity fallback, both-set priority order,
empty-string fall-through, and unrelated-kwargs isolation.

A plugin-level conftest.py at hermes-plugin/conftest.py stubs
``agent.memory_provider`` when it's not importable (plugin standalone
test runs), without affecting hermes-agent integration test runs where
the real ``MemoryProvider`` shadows the stub via sys.path.

Closes Tencent#15.

Signed-off-by: 李冠辰 <liguanchen@xiaomi.com>
@Maxwell-Code07
Copy link
Copy Markdown
Collaborator

Hi @YOMXXX,

We've received your PR regarding multi-profile memory isolation in Hermes. Thanks for the contribution! We'll review and follow up.

@noFloat
Copy link
Copy Markdown
Collaborator

noFloat commented May 21, 2026

感谢你的 PR,也感谢你针对 #15 中提到的问题提供修复方案和测试用例。

我们认可 #15 中描述的问题:Hermes 在多 profile 场景下不应该意外共享同一个 memory scope;在没有 gateway user_id 的情况下,确实需要考虑 agent_identity,以避免不同 profile 之间的记忆互相污染。

不过,这个 PR 当前我们倾向于不直接合入。

原因是我们正在进行一轮更整体的架构迭代:会将相关记忆服务能力后移到后端,并在 API 层显式引入 Agent ID 和 user ID,用于统一处理不同 Agent、不同用户之间的记忆隔离。

再次感谢你的贡献。

@noFloat noFloat closed this May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: support per-profile memory isolation via agent_identity kwarg

3 participants