Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4374817
fix(memory): align type annotations and correct region attribute refe…
Isuzu-ren Jul 27, 2026
4696eb2
fix(integration/langgraph): stop message duplication in checkpointer …
Isuzu-ren Jul 29, 2026
acd4bfa
feat(memory): add delete_session API support
Isuzu-ren Jul 30, 2026
a501280
fix(memory): simplify delete_session logging per maintainer review
Isuzu-ren Aug 4, 2026
9d846b6
feat(runtime): add session_storage_mount_path to storage_config
jsuwuj Aug 5, 2026
fd2030f
test(runtime): add unit tests for session_storage_mount_path
jsuwuj Aug 6, 2026
a5c4d35
Merge pull request #34 from Isuzu-ren/fix/memory-align-type
Zhiyue-Wang Aug 7, 2026
58e8d1e
Merge pull request #36 from Isuzu-ren/fix/langgraph-message-duplication
Zhiyue-Wang Aug 7, 2026
eab9be1
fix(memory): resolve the error in the agent_with_memory example
gglzf4 Aug 8, 2026
0edf88c
fix(memory): resolve the error in the agent_with_memory example
gglzf4 Aug 8, 2026
3f7c396
feat(memory): add hermes memory plugin
gglzf4 Aug 8, 2026
69f0bb0
feat(memory): Adjust the sequence of environmental changes
gglzf4 Aug 8, 2026
5f9ba9e
feat(memory): Fix test case errors
gglzf4 Aug 9, 2026
e1dbcc1
feat(memory): Remove the dependency on AK and SK
gglzf4 Aug 10, 2026
e7d50e7
feat(integration): add navigation agent demo with LangGraph + AgentAr…
Isuzu-ren Jul 31, 2026
7b9b676
refactor(integration): hybrid long-term memory recall for navigation …
Isuzu-ren Aug 6, 2026
d0e510c
feat(memory): Update README
gglzf4 Aug 10, 2026
e9a1eda
refactor(example): extract prompts, unify message utils, --debug→CLI …
Isuzu-ren Aug 10, 2026
3e74f07
feat(memory): pip_dependencies
gglzf4 Aug 10, 2026
6da25cf
feat(memory): update config schema description
gglzf4 Aug 11, 2026
1c2954b
Merge pull request #37 from Isuzu-ren/feature/delete-session-api
Zhiyue-Wang Aug 11, 2026
ac4ab45
Merge pull request #38 from Isuzu-ren/feature/navigation-langgraph-me…
Zhiyue-Wang Aug 11, 2026
af3545f
Merge pull request #42 from gglzf4/fix/memory-usage-#41
Zhiyue-Wang Aug 11, 2026
659d1b4
Merge pull request #44 from gglzf4/feature/agentarts-memory-hermes-#43
Zhiyue-Wang Aug 11, 2026
192de69
refactor(sdk): change session_storage_mount_path to nested session_st…
jsuwuj Aug 12, 2026
71decfc
Merge branch 'huaweicloud:main' into feature/session-storage-mount-path
jsuwuj Aug 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ test-results/
# Docker
*.dockerignore

# Demo local data
examples/navigation_langgraph_memory/sessions.json

# Temporary files
*.tmp
*.temp
Expand Down
120 changes: 120 additions & 0 deletions agentarts-memory-plugins/agentarts-memory-hermes/README.en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# agentarts-memory-hermes

A Hermes Memory Provider plugin that uses Huawei Cloud AgentArts Memory as the long-term memory backend for Hermes Agent.

## Overview

- **Cross-session memory persistence**: Automatically writes conversation content to AgentArts Memory after each turn (non-blocking)
- **Context injection**: Automatically injects relevant memories before each LLM call (user profile / episodic / semantic + history summary)
- **Compression protection**: Re-injects relevant memories before context compression to prevent key information from being dropped
- **MEMORY.md mirroring**: Syncs Hermes built-in `MEMORY.md` writes to AgentArts
- **Active retrieval tools**:
- `ltm_search` — Search long-term memory and return entries relevant to the query
- `ltm_search_summary` — Get a list of memory summaries

## Prerequisites

1. Install Hermes (installation guide: https://hermes-agent.nousresearch.com/docs/getting-started/installation)
2. Create a Huawei Cloud AgentArts memory space, and obtain the region (`HUAWEICLOUD_SDK_REGION`), memory space ID (`AGENTARTS_MEMORY_SPACE_ID`), and API Key (`HUAWEICLOUD_SDK_MEMORY_API_KEY`)

| Parameter | Description |
|---------------------------------|------------------------------------|
| `AGENTARTS_MEMORY_SPACE_ID` | AgentArts memory space ID |
| `HUAWEICLOUD_SDK_MEMORY_API_KEY`| AgentArts memory space API Key |
| `HUAWEICLOUD_SDK_REGION` | Region (default `cn-southwest-2`) |

## Installation

There are two installation methods — choose either one.

### Option 1: Install as a memory provider

Copy the plugin directory to Hermes' memory provider plugin path:

```bash
cp -r agentarts-memory-hermes ~/.hermes/hermes-agent/plugins/memory/
```

Configure interactively via `hermes memory setup`, or manually set the environment variables above. Follow the prompts to select `agentarts_memory` and complete configuration.

### Option 2: Install as a general plugin

Copy the plugin directory to Hermes' general plugin path and register via the `hermes plugins` command:

```bash
cp -r agentarts-memory-hermes ~/.hermes/plugins/
```

Configure interactively via `hermes plugins`, or manually set the environment variables above. Follow the prompts to select `agentarts_memory` and complete configuration.

## Configuration

During configuration, you will be prompted to enter the API Key, Space ID, etc. Sensitive fields (API Key) are written to `.env`, while non-sensitive config (`space_id`, `region`) is written to `$HERMES_HOME/agentarts.json`.

## Tools

### ltm_search

Search AgentArts long-term memory and return entries relevant to the query.

| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `query` | string | Yes | — | Search query string |
| `top_k` | integer | No | 5 | Return top K results |

### ltm_search_summary

Get a list of AgentArts memory summaries.

| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `limit` | integer | No | 10 | Number of entries to return |

## CLI Commands

After the plugin is registered, the following CLI subcommands are available (only when the provider is active):

| Command | Description |
|---|---|
| `hermes agentarts_memory status` | Show provider status and environment variable configuration |
| `hermes agentarts_memory config` | Show saved non-sensitive configuration |
| `hermes agentarts_memory test` | Test provider connectivity |

## Architecture

- **Client mode**: Uses `MemoryClient` (Client mode), not Session mode
- **Non-blocking sync**: `sync_turn` executes `add_messages` in a daemon thread, not blocking the Hermes main loop
- **Profile isolation**: All paths use the `hermes_home` kwarg from `initialize()`
- **Thread safety**: `_lock` protects concurrent read/write to `_client`

## Lifecycle Hooks

| Hook | When | Responsibility |
|---|---|---|
| `system_prompt_block` | System prompt assembly | Inject memory capability description |
| `prefetch` | Before each LLM call | Search and inject relevant memories |
| `sync_turn` | After each conversation turn | Non-blocking write of conversation content |
| `on_pre_compress` | Before context compression | Re-inject relevant memories |
| `on_memory_write` | On built-in memory write | Mirror MEMORY.md to AgentArts |
| `on_session_end` | On session end | No-op (turns already persisted per-turn) |
| `shutdown` | On process exit | Clean up connections |

## FAQ

### No search results?

AgentArts Memory takes time to generate memories from conversation messages (about 30 seconds). Content written by `sync_turn` is not immediately searchable; `prefetch` searches memories generated from earlier turns.

### Authentication failure?

Check:
1. Whether the API Key is valid
2. Whether the region configuration is correct
3. Whether the Space status is `running`

## Development

```bash
pip install -e ".[dev]"
pytest tests/unit/ -v
```
120 changes: 120 additions & 0 deletions agentarts-memory-plugins/agentarts-memory-hermes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# agentarts-memory-hermes

Hermes Memory Provider 插件,将华为云 AgentArts Memory 作为 Hermes Agent 的长期记忆后端。

## 功能概述

- **跨会话记忆持久化**:每轮对话后自动写入 AgentArts Memory(非阻塞)
- **上下文注入**:LLM 调用前自动注入相关记忆(用户画像 / 情景 / 语义 + 历史摘要)
- **压缩保护**:上下文压缩前重新注入相关记忆,避免关键信息被裁掉
- **MEMORY.md 镜像**:将 Hermes 内置 `MEMORY.md` 的写入同步到 AgentArts
- **主动检索工具**:
- `ltm_search` — 搜索长期记忆,返回与查询相关的记忆条目
- `ltm_search_summary` — 获取记忆摘要列表

## 前置条件

1. 安装Hermes(安装教程:https://hermes-agent.nousresearch.com/docs/getting-started/installation)
2. 创建华为云AgentArts记忆库,获取到区域(`HUAWEICLOUD_SDK_REGION`)、记忆库 ID(`AGENTARTS_MEMORY_SPACE_ID`)、API Key(`HUAWEICLOUD_SDK_MEMORY_API_KEY`)

| 参数 | 说明 |
|----------------------------------|----------------------------|
| `AGENTARTS_MEMORY_SPACE_ID` | AgentArts 记忆库ID |
| `HUAWEICLOUD_SDK_MEMORY_API_KEY` | AgentArts 记忆库API Key |
| `HUAWEICLOUD_SDK_REGION` | 区域(默认 `cn-southwest-2`)|


## 安装

有两种安装方式,任选其一。

### 方式一:作为 memory provider 安装

将插件目录复制到 Hermes 的 memory provider 插件路径:

```bash
cp -r agentarts-memory-hermes ~/.hermes/hermes-agent/plugins/memory/
```
通过 `hermes memory setup` 交互式配置,或手动设置上述环境变量。 按提示选择 `agentarts_memory` 并完成配置。

### 方式二:作为通用插件安装

将插件目录复制到 Hermes 的通用插件路径,通过 `hermes plugins` 命令注册:

```bash
cp -r agentarts-memory-hermes ~/.hermes/plugins/
```

通过 `hermes plugins` 交互式配置,或手动设置上述环境变量。 按提示选择 `agentarts_memory` 并完成配置。

## 配置

配置过程中会提示输入 API Key、Space ID 等。敏感字段(API Key)写入 `.env`,非敏感配置(`space_id`、`region`)写入 `$HERMES_HOME/agentarts.json`。

## 工具说明

### ltm_search

搜索 AgentArts 长期记忆,返回与查询相关的记忆条目。

| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| `query` | string | 是 | — | 搜索查询字符串 |
| `top_k` | integer | 否 | 5 | 返回前 K 个结果 |

### ltm_search_summary

获取 AgentArts 记忆摘要列表。

| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| `limit` | integer | 否 | 10 | 返回条目数量 |

## CLI 命令

插件注册后提供以下 CLI 子命令(仅在 provider 活跃时可用):

| 命令 | 说明 |
|---|---|
| `hermes agentarts_memory status` | 显示 provider 状态和环境变量配置 |
| `hermes agentarts_memory config` | 显示已保存的非敏感配置 |
| `hermes agentarts_memory test` | 测试 provider 连通性 |

## 架构说明

- **Client 模式**:使用 `MemoryClient`(Client 模式),不依赖 Session 模式
- **非阻塞 sync**:`sync_turn` 在守护线程中执行 `add_messages`,不阻塞 Hermes 主循环
- **Profile 隔离**:所有路径使用 `initialize()` 中的 `hermes_home` kwarg
- **线程安全**:`_lock` 保护 `_client` 的并发读写

## 生命周期钩子

| 钩子 | 调用时机 | 职责 |
|---|---|---|
| `system_prompt_block` | 系统 prompt 组装时 | 注入记忆能力说明 |
| `prefetch` | 每次 LLM 调用前 | 搜索并注入相关记忆 |
| `sync_turn` | 每轮对话后 | 非阻塞写入对话内容 |
| `on_pre_compress` | 上下文压缩前 | 重新注入相关记忆 |
| `on_memory_write` | 内置 memory 写入时 | 镜像 MEMORY.md 到 AgentArts |
| `on_session_end` | 对话结束时 | no-op(逐轮已落库) |
| `shutdown` | 进程退出时 | 清理连接 |

## 常见问题

### 记忆搜索无结果?

AgentArts Memory 从对话消息生成记忆需要时间(约 30 秒)。`sync_turn` 写入后不会立即可搜索,`prefetch` 搜索的是此前轮次已生成的记忆。

### 认证失败?

检查:
1. API Key 是否有效
2. 区域配置是否正确
3. Space 状态是否为 `running`

## 开发

```bash
pip install -e ".[dev]"
pytest tests/unit/ -v
```
8 changes: 8 additions & 0 deletions agentarts-memory-plugins/agentarts-memory-hermes/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""AgentArts Memory Provider plugin for Hermes Agent."""

try:
from .provider import AgentArtsMemoryProvider, register
except ImportError: # imported as a top-level module (no parent package)
from provider import AgentArtsMemoryProvider, register

__all__ = ["AgentArtsMemoryProvider", "register"]
132 changes: 132 additions & 0 deletions agentarts-memory-plugins/agentarts-memory-hermes/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
"""CLI subcommands for the agentarts_memory provider.

This module registers ``hermes agentarts_memory <subcommand>`` commands via
the ``register_cli(subparser)`` convention. The commands are only available
when this provider is the active ``memory.provider`` in Hermes configuration.
"""

import json
import os

from provider import (
ENV_API_KEY,
ENV_REGION,
ENV_SPACE_ID,
AgentArtsMemoryProvider,
)


def _status(args) -> None:
"""Show provider status: availability, env vars, and active config."""
provider = AgentArtsMemoryProvider()
available = provider.is_available()

print("AgentArts Memory Provider — Status")
print(f" Provider name : {provider.name}")
print(f" Available : {'yes' if available else 'no'}")
print()
print("Environment variables:")

env_info = [
("HUAWEICLOUD_SDK_MEMORY_API_KEY", ENV_API_KEY, False),
("AGENTARTS_MEMORY_SPACE_ID", ENV_SPACE_ID, False),
("HUAWEICLOUD_SDK_REGION", ENV_REGION, True),
]

all_set = True
for display_name, env_var, optional in env_info:
value = os.getenv(env_var)
if value:
if optional:
print(f" {display_name} = {value}")
else:
print(f" {display_name} = {'*' * min(len(value), 8)}…({len(value)} chars)")
else:
label = "(optional)" if optional else "(MISSING)"
print(f" {display_name} = <not set> {label}")
if not optional:
all_set = False

if not all_set:
print()
print("Warning: Some required environment variables are not set.")
print("Run 'hermes memory setup' to configure the provider.")


def _config(args) -> None:
"""Show the non-secret configuration saved to agentarts.json."""
hermes_home = os.environ.get("HERMES_HOME", "")
if not hermes_home:
print("HERMES_HOME is not set. Cannot locate configuration file.")
print("Run this command from within a Hermes session context.")
return

config_path = os.path.join(hermes_home, "agentarts.json")
if not os.path.exists(config_path):
print(f"No configuration file found at: {config_path}")
print("Run 'hermes memory setup' to configure the provider.")
return

with open(config_path, encoding="utf-8") as f:
data = json.load(f)

print("AgentArts Memory Provider — Configuration")
print(f" Config file: {config_path}")
print()
if data:
for key, value in data.items():
print(f" {key} = {value}")
else:
print(" (empty — no non-secret values saved)")


def _test_connection(args) -> None:
"""Test connectivity by initializing the provider and checking the client."""
provider = AgentArtsMemoryProvider()

if not provider.is_available():
print("AgentArts Memory Provider — Connection Test: FAILED")
print("Required environment variables are not set.")
print("Run 'hermes memory setup' to configure the provider.")
return

print("AgentArts Memory Provider — Connection Test")
print(" Environment variables: OK")
print(" Initializing MemoryClient...")

try:
provider.initialize("cli-test-session", hermes_home=os.environ.get("HERMES_HOME", ""))
print(" MemoryClient initialized: OK")
print(" Memory session created: OK")
print()
print("Connection test: PASSED")
provider.shutdown()
except Exception as e:
print(f" Error: {e}")
print()
print("Connection test: FAILED")


def _handle_command(args) -> None:
"""Dispatch to the appropriate subcommand handler."""
sub = getattr(args, "agentarts_memory_command", None)
if sub == "status":
_status(args)
elif sub == "config":
_config(args)
elif sub == "test":
_test_connection(args)
else:
print("Usage: hermes agentarts_memory <status|config|test>")


def register_cli(subparser) -> None:
"""Build the argparse tree for 'hermes agentarts_memory' commands.

Called by discover_plugin_cli_commands() during argparse initialization.
"""
subs = subparser.add_subparsers(dest="agentarts_memory_command")
subs.add_parser("status", help="Show provider status and environment variables")
subs.add_parser("config", help="Show saved non-secret configuration")
subs.add_parser("test", help="Test provider connectivity")
subparser.set_defaults(func=_handle_command)
Loading
Loading