From e55f5a9b13058381b7f47b5f7aec8da0df656ea1 Mon Sep 17 00:00:00 2001 From: raychen <815315825@qq.com> Date: Fri, 8 May 2026 10:17:15 +0800 Subject: [PATCH] =?UTF-8?q?feature:=20=E6=94=AF=E6=8C=81=20MemPalace=20?= =?UTF-8?q?=E8=AE=B0=E5=BF=86=E9=9B=86=E6=88=90=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=20Agent=20=E8=BF=90=E8=A1=8C=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 MemPalaceMemoryService 和 mempalace_tool,支持 drawer、diary、KG 工具能力。 - 新增 MemPalace 示例、文档和单测,并完善中英文 memory 文档。 - 调整 LlmAgent 循环控制,支持通过 metadata 控制运行状态。 - 优化 A2A 长运行/鉴权事件转换和取消事件处理。 - 规范 memory/tool 输出、examples 环境变量模板,并修复相关 lint 问题。 --- README.md | 2 +- README.zh_CN.md | 2 +- docs/mkdocs/en/memory.md | 592 ++++++++- docs/mkdocs/zh/memory.md | 1058 ++++++++++++----- examples/dsl/classifier_mcp/.env | 5 + examples/llmagent/.env | 4 + examples/llmagent_with_branch_filtering/.env | 3 + examples/llmagent_with_cancel/.env | 3 + examples/llmagent_with_custom_agent/.env | 3 + examples/llmagent_with_custom_prompt/.env | 3 + examples/llmagent_with_human_in_the_loop/.env | 3 + .../llmagent_with_max_history_messages/.env | 3 + examples/llmagent_with_model_create_fn/.env | 3 + examples/llmagent_with_parallal_tools/.env | 3 + examples/llmagent_with_schema/.env | 3 + .../llmagent_with_streaming_tool_complex/.env | 4 + .../llmagent_with_streaming_tool_simple/.env | 4 + examples/llmagent_with_thinking/.env | 4 + .../llmagent_with_timeline_filtering/.env | 4 + examples/llmagent_with_tool_prompt/.env | 4 + examples/llmagent_with_user_history/.env | 4 + examples/mcp_tools/.env | 4 + examples/mem0_tools/.env | 4 + examples/{mem_0 => mem0_tools}/README.md | 14 +- .../{mem_0 => mem0_tools}/agent/__init__.py | 0 examples/{mem_0 => mem0_tools}/agent/agent.py | 4 +- .../{mem_0 => mem0_tools}/agent/config.py | 0 .../{mem_0 => mem0_tools}/agent/prompts.py | 0 examples/{mem_0 => mem0_tools}/agent/tools.py | 0 .../{mem_0 => mem0_tools}/images/mem0_ai.png | Bin .../images/mem0_plat.png | Bin .../images/mem0_result.png | Bin .../images/qdrant_dashboard.png | Bin .../images/qdrant_mem.png | Bin examples/{mem_0 => mem0_tools}/run_agent.py | 6 +- examples/mem_0/.env | 1 - examples/memory_service_with_in_memory/.env | 4 + examples/memory_service_with_mem0/.env | 5 +- examples/memory_service_with_mempalace/.env | 9 + .../memory_service_with_mempalace/README.md | 253 ++++ .../agent/__init__.py | 5 + .../agent/agent.py | 47 + .../agent/config.py | 61 + .../agent/prompts.py | 8 + .../agent/tools.py | 30 + .../run_agent.py | 133 +++ examples/memory_service_with_redis/.env | 5 +- examples/memory_service_with_sql/.env | 5 +- examples/mempalace_tools/.env | 13 + examples/mempalace_tools/README.md | 180 +++ examples/mempalace_tools/agent/__init__.py | 5 + examples/mempalace_tools/agent/agent.py | 60 + examples/mempalace_tools/agent/config.py | 29 + examples/mempalace_tools/agent/prompts.py | 26 + examples/mempalace_tools/agent/tools.py | 6 + examples/mempalace_tools/out.txt | 319 +++++ examples/mempalace_tools/run_agent.py | 183 +++ examples/multi_agent_chain/.env | 5 +- examples/multi_agent_compose/.env | 5 +- examples/multi_agent_cycle/.env | 5 +- examples/multi_agent_parallel/.env | 5 +- examples/multi_agent_start_from_last/.env | 5 +- examples/multi_agent_subagent/.env | 5 +- examples/quickstart/.env | 5 +- examples/session_service_with_in_memory/.env | 5 +- examples/session_service_with_redis/.env | 5 +- examples/session_service_with_sql/.env | 5 +- examples/session_state/.env | 5 +- examples/session_summarizer/.env | 5 +- examples/skills/.env | 5 +- examples/skills_with_container/.env | 5 +- examples/skills_with_dynamic_tools/.env | 5 +- examples/streaming_tools/.env | 5 +- examples/team/.env | 5 +- examples/team_as_sub_agent/.env | 5 +- examples/team_human_in_the_loop/.env | 5 +- examples/team_member_agent_claude/.env | 5 +- examples/team_member_agent_langgraph/.env | 5 +- examples/team_member_agent_team/.env | 5 +- examples/team_member_message_filter/.env | 5 +- examples/team_parallel_execution/.env | 5 +- examples/team_with_cancel/.env | 5 +- examples/team_with_skill/.env | 5 +- examples/tools/.env | 5 +- examples/toolsets/.env | 5 +- examples/transfer_agent/.env | 5 +- examples/webfetch_tool/.env | 6 +- examples/websearch_tool/.env | 6 +- pyproject.toml | 5 + requirements-test.txt | 1 + requirements.txt | 1 + tests/memory/test_mem0_memory_service.py | 9 +- tests/memory/test_mempalace_memory_service.py | 161 +++ tests/tools/test_mempalace_tool.py | 390 ++++++ trpc_agent_sdk/agents/_constants.py | 3 + trpc_agent_sdk/agents/_llm_agent.py | 6 +- trpc_agent_sdk/events/_long_running_event.py | 3 +- trpc_agent_sdk/filter/_registry.py | 1 - .../memory/_in_memory_memory_service.py | 2 + .../memory/_redis_memory_service.py | 2 +- trpc_agent_sdk/memory/_sql_memory_service.py | 2 + trpc_agent_sdk/memory/_utils.py | 16 + trpc_agent_sdk/memory/mem0_memory_service.py | 30 +- .../memory/mempalace_memory_service.py | 444 +++++++ .../planners/_plan_re_act_planner.py | 34 +- trpc_agent_sdk/server/a2a/_constants.py | 1 + .../server/a2a/converters/_event_converter.py | 14 +- .../a2a/executor/_a2a_agent_executor.py | 2 +- trpc_agent_sdk/tools/__init__.py | 16 + trpc_agent_sdk/tools/_agent_tool.py | 16 +- trpc_agent_sdk/tools/_load_memory_tool.py | 2 +- trpc_agent_sdk/tools/mempalace_tool.py | 527 ++++++++ trpc_agent_sdk/types/__init__.py | 2 +- 113 files changed, 4577 insertions(+), 403 deletions(-) create mode 100644 examples/mem0_tools/.env rename examples/{mem_0 => mem0_tools}/README.md (97%) rename examples/{mem_0 => mem0_tools}/agent/__init__.py (100%) rename examples/{mem_0 => mem0_tools}/agent/agent.py (93%) rename examples/{mem_0 => mem0_tools}/agent/config.py (100%) rename examples/{mem_0 => mem0_tools}/agent/prompts.py (100%) rename examples/{mem_0 => mem0_tools}/agent/tools.py (100%) rename examples/{mem_0 => mem0_tools}/images/mem0_ai.png (100%) rename examples/{mem_0 => mem0_tools}/images/mem0_plat.png (100%) rename examples/{mem_0 => mem0_tools}/images/mem0_result.png (100%) rename examples/{mem_0 => mem0_tools}/images/qdrant_dashboard.png (100%) rename examples/{mem_0 => mem0_tools}/images/qdrant_mem.png (100%) rename examples/{mem_0 => mem0_tools}/run_agent.py (96%) delete mode 100644 examples/mem_0/.env create mode 100644 examples/memory_service_with_mempalace/.env create mode 100644 examples/memory_service_with_mempalace/README.md create mode 100644 examples/memory_service_with_mempalace/agent/__init__.py create mode 100644 examples/memory_service_with_mempalace/agent/agent.py create mode 100644 examples/memory_service_with_mempalace/agent/config.py create mode 100644 examples/memory_service_with_mempalace/agent/prompts.py create mode 100644 examples/memory_service_with_mempalace/agent/tools.py create mode 100644 examples/memory_service_with_mempalace/run_agent.py create mode 100644 examples/mempalace_tools/.env create mode 100644 examples/mempalace_tools/README.md create mode 100644 examples/mempalace_tools/agent/__init__.py create mode 100644 examples/mempalace_tools/agent/agent.py create mode 100644 examples/mempalace_tools/agent/config.py create mode 100644 examples/mempalace_tools/agent/prompts.py create mode 100644 examples/mempalace_tools/agent/tools.py create mode 100644 examples/mempalace_tools/out.txt create mode 100644 examples/mempalace_tools/run_agent.py create mode 100644 tests/memory/test_mempalace_memory_service.py create mode 100644 tests/tools/test_mempalace_tool.py create mode 100644 trpc_agent_sdk/memory/mempalace_memory_service.py create mode 100644 trpc_agent_sdk/tools/mempalace_tool.py diff --git a/README.md b/README.md index b7b3cf1..5ffcbfe 100644 --- a/README.md +++ b/README.md @@ -446,7 +446,7 @@ This group helps you: Recommended first: - Session: [examples/session_service_with_in_memory](./examples/session_service_with_in_memory/README.md) / [examples/session_service_with_redis](./examples/session_service_with_redis/README.md) / [examples/session_service_with_sql](./examples/session_service_with_sql/README.md) / [examples/session_summarizer](./examples/session_summarizer/README.md) / [examples/session_state](./examples/session_state/README.md) -- Memory: [examples/memory_service_with_in_memory](./examples/memory_service_with_in_memory/README.md) / [examples/memory_service_with_redis](./examples/memory_service_with_redis/README.md) / [examples/memory_service_with_sql](./examples/memory_service_with_sql/README.md) / [examples/memory_service_with_mem0](./examples/memory_service_with_mem0/README.md) / [examples/mem_0](./examples/mem_0/README.md) +- Memory: [examples/memory_service_with_in_memory](./examples/memory_service_with_in_memory/README.md) / [examples/memory_service_with_redis](./examples/memory_service_with_redis/README.md) / [examples/memory_service_with_sql](./examples/memory_service_with_sql/README.md) / [examples/memory_service_with_mem0](./examples/memory_service_with_mem0/README.md) / [examples/memory_service_with_mempalace](./examples/memory_service_with_mempalace/README.md) - Knowledge: [examples/knowledge_with_documentloader](./examples/knowledge_with_documentloader/README.md) / [examples/knowledge_with_vectorstore](./examples/knowledge_with_vectorstore/README.md) / [examples/knowledge_with_rag_agent](./examples/knowledge_with_rag_agent/README.md) / [examples/knowledge_with_searchtool_rag_agent](./examples/knowledge_with_searchtool_rag_agent/README.md) / [examples/knowledge_with_prompt_template](./examples/knowledge_with_prompt_template/README.md) / [examples/knowledge_with_custom_components](./examples/knowledge_with_custom_components/README.md) Related docs: diff --git a/README.zh_CN.md b/README.zh_CN.md index 3a9e094..a4cf0e6 100644 --- a/README.zh_CN.md +++ b/README.zh_CN.md @@ -447,7 +447,7 @@ skill_tool_set = SkillToolSet(repository=repository, run_tool_kwargs=tool_kwargs 建议先看: - Session:[examples/session_service_with_in_memory](./examples/session_service_with_in_memory/README.md) / [examples/session_service_with_redis](./examples/session_service_with_redis/README.md) / [examples/session_service_with_sql](./examples/session_service_with_sql/README.md) / [examples/session_summarizer](./examples/session_summarizer/README.md) / [examples/session_state](./examples/session_state/README.md) -- Memory:[examples/memory_service_with_in_memory](./examples/memory_service_with_in_memory/README.md) / [examples/memory_service_with_redis](./examples/memory_service_with_redis/README.md) / [examples/memory_service_with_sql](./examples/memory_service_with_sql/README.md) / [examples/memory_service_with_mem0](./examples/memory_service_with_mem0/README.md) / [examples/mem_0](./examples/mem_0/README.md) +- Memory: [examples/memory_service_with_in_memory](./examples/memory_service_with_in_memory/README.md) / [examples/memory_service_with_redis](./examples/memory_service_with_redis/README.md) / [examples/memory_service_with_sql](./examples/memory_service_with_sql/README.md) / [examples/memory_service_with_mem0](./examples/memory_service_with_mem0/README.md) / [examples/memory_service_with_mempalace](./examples/memory_service_with_mempalace/README.md) - Knowledge:[examples/knowledge_with_documentloader](./examples/knowledge_with_documentloader/README.md) / [examples/knowledge_with_vectorstore](./examples/knowledge_with_vectorstore/README.md) / [examples/knowledge_with_rag_agent](./examples/knowledge_with_rag_agent/README.md) / [examples/knowledge_with_searchtool_rag_agent](./examples/knowledge_with_searchtool_rag_agent/README.md) / [examples/knowledge_with_prompt_template](./examples/knowledge_with_prompt_template/README.md) / [examples/knowledge_with_custom_components](./examples/knowledge_with_custom_components/README.md) 相关文档: diff --git a/docs/mkdocs/en/memory.md b/docs/mkdocs/en/memory.md index 61cd5fb..0c64168 100644 --- a/docs/mkdocs/en/memory.md +++ b/docs/mkdocs/en/memory.md @@ -28,6 +28,7 @@ Based on the implementation in [trpc_agent_sdk/memory/](../../../trpc_agent_sdk/ - **InMemoryMemoryService**: Stored in an in-process memory dictionary - **RedisMemoryService**: Stored in a Redis List (JSON format) - **SqlMemoryService**: Stored in the `mem_events` table in MySQL/PostgreSQL +- **MempalaceMemoryService**: Stored as MemPalace drawers in a local ChromaDB-backed palace **Code Example**: ```python @@ -54,7 +55,7 @@ async def store_session(self, session: Session, agent_context: Optional[AgentCon **Function**: Searches for related historical memories based on query keywords. -**Search Method**: **Keyword matching** (not semantic search) +**Search Method**: Built-in InMemory/Redis/SQL services use **keyword matching**; semantic memory services such as MemPalace and Mem0 use vector / semantic retrieval. **Implementation Logic** (using `InMemoryMemoryService` as an example): ```python @@ -117,6 +118,7 @@ for memory in response.memories: - **InMemoryMemoryService**: Background periodic cleanup task (`_cleanup_loop`) - **RedisMemoryService**: Redis native `EXPIRE` mechanism (automatic expiration) - **SqlMemoryService**: Background periodic cleanup task (batch SQL DELETE) +- **MempalaceMemoryService**: Background periodic cleanup task (batch drawer deletion by metadata timestamp) **TTL Configuration**: ```python @@ -135,6 +137,7 @@ memory_service_config = MemoryServiceConfig( **TTL Refresh Mechanism**: - **Refresh on access**: TTL is refreshed for matched events during `search_memory` - **Refresh on storage**: TTL is set for new events during `store_session` +- **Persistent semantic services**: Some services, such as MemPalace, delete expired drawers by stored event timestamp rather than refreshing TTL on every search. --- @@ -165,7 +168,7 @@ _session_events = { ## MemoryService Implementations -trpc-agent provides three `MemoryService` implementations, allowing you to choose the appropriate storage backend based on your scenario: +trpc-agent provides multiple `MemoryService` implementations, allowing you to choose the appropriate storage backend based on your scenario: ### InMemoryMemoryService @@ -1092,7 +1095,7 @@ await memory.delete(memory_id="memory-id") await memory.delete_all(user_id="alice") ``` -**More Advanced Usage:** [Advanced Usage Documentation](../../../examples/mem_0/README.md#高级用法) +**More Advanced Usage:** [Advanced Usage Documentation](../../../examples/mem0_tools/README.md#高级用法) --- @@ -1135,7 +1138,7 @@ ConnectionError: Cannot connect to Qdrant at localhost:6333 |---|---|---| | `Mem0MemoryService` complete example | [examples/memory_service_with_mem0/](../../../examples/memory_service_with_mem0/README.md) | Includes execution result analysis, FAQ | | `Mem0MemoryService` source code | [mem0_memory_service.py](../../../trpc_agent_sdk/memory/mem0_memory_service.py) | Service implementation | -| Tool-based integration source code | [mem0_tool.py](../../../trpc_agent_sdk/tools/mem0_tool.py) | `SearchMemoryTool` / `SaveMemoryTool` tool classes | +| Tool-based integration source code | [mem0_tools.py](../../../trpc_agent_sdk/tools/mem0_tools.py) | `SearchMemoryTool` / `SaveMemoryTool` tool classes | | infer parameter details | [README.md#infer-参数详解](../../../examples/memory_service_with_mem0/README.md#infer-参数详解) | True vs False comparison | | FAQ | [README.md#常见问题-qa](../../../examples/memory_service_with_mem0/README.md#常见问题-qa) | Error analysis and answers | @@ -1157,6 +1160,569 @@ ConnectionError: Cannot connect to Qdrant at localhost:6333 --- +## Integrating MemPalace + +### What is MemPalace? + +MemPalace is a local-first memory system for storing verbatim memories and retrieving historical context with semantic search. Its core storage hierarchy can be understood as: + +```text +Palace + └── Wing + └── Room + └── Drawer +``` + +In `MempalaceMemoryService`, each storable framework event is filed as a drawer. The drawer contains the original text and metadata such as `wing`, `room`, `session_id`, `event_id`, `author`, and `timestamp`. + +**Core Capabilities:** +- Local persistent storage in a MemPalace palace directory +- Semantic search through MemPalace / ChromaDB +- `wing` and `room` filters for memory isolation +- CLI inspection through `mempalace search` +- TTL cleanup managed by the framework memory service + +--- + +### tRPC-Agent Integration Methods + +The recommended integration path is the framework-level memory service: + +| Method | Class / Tool | Applicable Scenario | +|---|---|---| +| **Framework-level memory service** (recommended) | `MempalaceMemoryService` | The framework automatically writes cross-session memories; the Agent retrieves them through `load_memory` | +| **MemPalace tools** | `mempalace_search` / `mempalace_add_drawer`, etc. | The Agent needs direct access to MemPalace drawers, diary, KG, or other advanced capabilities | + +`MempalaceMemoryService` is the standard MemoryService integration for this project. The framework calls `store_session()` automatically after each turn to persist memory, while the Agent calls `load_memory` during response generation to retrieve historical memories through `search_memory()`. + +--- + +### MempalaceMemoryService (Recommended) + +`MempalaceMemoryService` is a framework-level memory service. The framework stores session memories automatically after each turn, while the Agent retrieves related memories through the built-in `load_memory` tool. + +**How It Works**: Stores memory data as MemPalace drawers in a local-first memory palace backed by ChromaDB. + +**Implementation Details** (based on `mempalace_memory_service.py`): +- **Data Structure**: MemPalace `Palace -> Wing -> Room -> Drawer` +- **Storage Location**: Local MemPalace palace directory, usually `~/.mempalace/palace` +- **Search Method**: MemPalace hybrid semantic search (`search_memories`) with `wing` / `room` filters +- **TTL Mechanism**: Background periodic cleanup task; expired drawers are deleted by metadata timestamp +- **Write Mode**: Incremental background writes; events already scheduled or stored in the current process are skipped +- **Cross-session sharing**: `session.save_key`, usually `{app_name}/{user_id}`, is used as the cross-session memory dimension + +**Persistence**: ✅ **Yes**. Data is persisted in the MemPalace palace directory and can be recovered after application restart. + +**Applicable Scenarios**: +- ✅ Local-first semantic memory +- ✅ Cross-session user profile and preference memory +- ✅ Development or private deployments that should keep memory data on local disk +- ✅ Scenarios where CLI inspection with `mempalace search` is useful + +#### Quick Integration + +**Step 1: Install dependencies** + +```bash +# Install through the trpc-agent extra +pip install -e ".[mempalace]" + +# Or install MemPalace directly +pip install mempalace +``` + +**Step 2: Create `MempalaceMemoryService`** + +```python +from trpc_agent_sdk.memory import MemoryServiceConfig +from trpc_agent_sdk.memory.mempalace_memory_service import MempalaceMemoryService + +memory_service = MempalaceMemoryService( + memory_service_config=MemoryServiceConfig( + enabled=True, + ttl=MemoryServiceConfig.create_ttl_config( + enable=True, + ttl_seconds=86400, + cleanup_interval_seconds=3600, + ), + ), + wing="my_app_user", + room="conversations", + store_only_model_visible=True, +) +``` + +**Step 3: Pass `memory_service` to `Runner`** + +```python +from trpc_agent_sdk.runners import Runner +from trpc_agent_sdk.sessions import InMemorySessionService +from trpc_agent_sdk.tools import load_memory_tool + +agent = LlmAgent( + name="assistant", + model=your_model, + tools=[load_memory_tool], + instruction="Use load_memory to recall relevant past conversations before answering.", +) + +runner = Runner( + app_name="my_app", + agent=agent, + session_service=InMemorySessionService(), + memory_service=memory_service, +) +``` + +**Step 4: Run the Agent; memories are persisted across sessions automatically** + +```python +# First conversation round (session_1) +async for event in runner.run_async(user_id="alice", session_id="session_1", new_message=...): + ... +# After the turn finishes, the framework calls store_session and writes storable events to MemPalace. + +# Second conversation round (session_2) — a new session can still retrieve memories from session_1. +async for event in runner.run_async(user_id="alice", session_id="session_2", new_message=...): + ... +``` + +**Complete Runnable Example:** [examples/memory_service_with_mempalace/run_agent.py](../../../examples/memory_service_with_mempalace/run_agent.py) + +--- + +#### MemPalace Hierarchy Mapping + +```text +session.save_key = "{app_name}/{user_id}" -> wing (when wing is not explicitly configured) +room -> room, defaults to conversations +Event -> drawer +session.id / event.id / author / timestamp -> drawer metadata +``` + +If `wing="trpc-agent"` is configured explicitly, all memories are written into that wing. If `wing` is omitted, the service derives the wing from `save_key`, which is usually the more natural isolation strategy for app/user-scoped long-term memory. + +--- + +#### Path and CLI Search + +MemPalace stores data under `MempalaceConfig().palace_path`. The default path is usually: + +```text +~/.mempalace/palace +``` + +You can configure a custom path through an environment variable: + +```bash +export MEMPALACE_PALACE_PATH=/path/to/palace +``` + +Or through `~/.mempalace/config.json`: + +```json +{ + "palace_path": "/path/to/palace", + "collection_name": "mempalace_drawers" +} +``` + +If the application is configured to use a custom palace path, CLI search must use the same path: + +```bash +mempalace --palace /path/to/palace search "user name" +``` + +Filter by `wing` and `room`: + +```bash +mempalace --palace /path/to/palace search "user name" \ + --wing my_app_user \ + --room conversations +``` + +If no custom path is configured, MemPalace uses its default config, and CLI search can omit `--palace`: + +```bash +mempalace search "user name" --wing my_app_user --room conversations +``` + +> `/path/to/palace` is the MemPalace data directory that contains `chroma.sqlite3`, not a single database file. + +--- + +#### TTL Configuration (Optional) + +```python +memory_service_config = MemoryServiceConfig( + enabled=True, + ttl=MemoryServiceConfig.create_ttl_config( + enable=True, + ttl_seconds=86400, # Keep memories for 24 hours + cleanup_interval_seconds=3600, # Run cleanup every hour + ), +) +``` + +Important notes: + +- MemPalace itself does not delete memories automatically just because they have not been used for a long time. +- `MempalaceMemoryService` implements TTL cleanup at the framework layer. +- Cleanup scans drawers written by this service and deletes expired records based on the `timestamp` metadata. +- This TTL policy is based on the original event timestamp; it is not an "extend expiration on access" policy. + +--- + +#### Direct Memory Management + +The service provides a helper to delete all drawers in a wing, or only drawers in a specific room: + +```python +await memory_service.delete_memory(wing="my_app_user") +await memory_service.delete_memory(wing="my_app_user", room="conversations") +``` + +> MemPalace CLI currently does not provide a direct command to delete all memories by `wing` / `room`; use the service helper or call the underlying collection `delete(where=...)`. + +--- + +#### Storage Content Policy + +In general, only ordinary text events with long-term value should be written to MemPalace. Intermediate tool calls, tool responses, and code execution results are usually poor long-term memories because they can cause: + +- `load_memory` results to be written back into memory again +- nested historical memory JSON inside newly stored memories +- tool logs polluting long-term memory and reducing retrieval quality + +`MempalaceMemoryService` is better suited for memories such as: + +```text +User: My name is Alice. +User: My favorite color is blue. +Assistant: Confirmed the user's name or preference. +``` + +Rather than: + +```text +[tool_call] load_memory: ... +[tool_response] load_memory: {"memories": [...]} +``` + +--- + +#### Typical Workflow + +```text +1. User: Do you remember my name? + ↓ + Agent calls: load_memory(query="user name") + ↓ + Result: {"memories": []} + ↓ + Agent: I don't know your name yet. + +2. User: My name is Alice + ↓ + After the turn, the framework automatically calls MempalaceMemoryService.store_session() + ↓ + The user message is written as a drawer under the configured wing/room + +3. User starts a new session: Do you remember my name? + ↓ + Agent calls: load_memory(query="user name") + ↓ + MemPalace returns a historical memory containing "My name is Alice" + ↓ + Agent: Yes, your name is Alice. +``` + +**Complete Demo Output (MempalaceMemoryService):** [examples/memory_service_with_mempalace/README.md](../../../examples/memory_service_with_mempalace/README.md) + +--- + +### Tool-based Integration (mempalace_tool) + +`mempalace_tool` is another way to integrate with MemPalace. It is not the recommended standard MemoryService path. Instead, it exposes MemPalace capabilities as Agent-callable tools, allowing the Agent to decide when to search, write drawers, read or write diary entries, or maintain KG facts. + +The difference from `MempalaceMemoryService` is: + +| Method | Write Timing | Retrieval Method | Applicable Scenario | +|---|---|---|---| +| `MempalaceMemoryService` | The framework writes automatically after each turn | `load_memory` indirectly calls `search_memory()` | Standard cross-session long-term memory | +| `mempalace_tool` | The Agent explicitly calls tools to write | The Agent explicitly calls `mempalace_search` | Fine-grained control over MemPalace drawers, diary, KG, or manual memory management | + +#### Available Tools + +| Tool Class | Tool Name | Function | Use Case | +|---|---|---|---| +| `MempalaceSearchTool` | `mempalace_search` | Semantically search saved drawer content | The Agent needs to recall user profiles, preferences, or historical facts | +| `MempalaceAddDrawerTool` | `mempalace_add_drawer` | Write a verbatim drawer under a specified `wing/room` | The user explicitly asks the Agent to remember long-term information | +| `MempalaceDiaryWriteTool` | `mempalace_diary_write` | Write an agent diary entry | Record runtime observations, task progress, or interim summaries | +| `MempalaceDiaryReadTool` | `mempalace_diary_read` | Read recent diary entries for an agent | The Agent needs to review previous task notes | +| `MempalaceKGAddTool` | `mempalace_kg_add` | Write a knowledge-graph triple fact | Structured facts such as `subject -> predicate -> object` | +| `MempalaceKGQueryTool` | `mempalace_kg_query` | Query relationships for a knowledge-graph entity | Query facts about Alice, project dependencies, or entity relationships | +| `MempalaceKGTimelineTool` | `mempalace_kg_timeline` | Read knowledge-graph facts as a timeline | Inspect how an entity's relationships change over time | +| `MempalaceKGInvalidateTool` | `mempalace_kg_invalidate` | Mark a current fact as no longer valid | Represent fact changes while keeping historical records | + +> **Note**: Like `mem0_tool`, `mempalace_tool` exposes tools to the Agent and lets the model decide when to call them. Unlike Mem0's two search/save tools, MemPalace tools also cover diary and KG operations. See the complete example at [examples/mempalace_tools/README.md](../../../examples/mempalace_tools/README.md), and the tool source at [mempalace_tool.py](../../../trpc_agent_sdk/tools/mempalace_tool.py). + +#### Integration Architecture + +``` +┌──────────────────────┐ +│ User Input │ +└──────────┬───────────┘ + │ + ▼ +┌──────────────────────┐ +│ tRPC-Agent │◄────────────────┐ +│ LlmAgent │ │ +└──────────┬───────────┘ │ + │ │ returns tool results + │ calls tools │ + ▼ │ +┌──────────────────────┐ │ +│ MemPalace Tools │─────────────────┘ +│ - mempalace_search │ +│ - add_drawer │ +│ - diary read/write │ +│ - KG tools │ +└──────────┬───────────┘ + │ + ▼ +┌──────────────────────┐ +│ MemPalace Backend │ +│ - Palace / ChromaDB │ +│ - KG SQLite │ +└──────────────────────┘ +``` + +#### Quick Integration + +```python +from trpc_agent_sdk.agents import LlmAgent +from trpc_agent_sdk.tools.mempalace_tool import MempalaceAddDrawerTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceDiaryReadTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceDiaryWriteTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceKGAddTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceKGInvalidateTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceKGQueryTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceKGTimelineTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceSearchTool + +palace_path = "/tmp/trpc-agent-mempalace-demo" +kg_path = "/tmp/trpc-agent-mempalace-demo/knowledge_graph.sqlite3" + +tools = [ + MempalaceSearchTool(palace_path=palace_path), + MempalaceAddDrawerTool(palace_path=palace_path), + MempalaceDiaryWriteTool(palace_path=palace_path), + MempalaceDiaryReadTool(palace_path=palace_path), + MempalaceKGAddTool(palace_path=palace_path, kg_path=kg_path), + MempalaceKGQueryTool(palace_path=palace_path, kg_path=kg_path), + MempalaceKGTimelineTool(palace_path=palace_path, kg_path=kg_path), + MempalaceKGInvalidateTool(palace_path=palace_path, kg_path=kg_path), +] + +agent = LlmAgent( + name="memory_assistant", + model=your_model, + instruction=""" + You are a helpful assistant with MemPalace tools. + - Use mempalace_search before answering questions that may require past memory. + - Use mempalace_add_drawer when the user explicitly asks you to remember stable facts. + - Use diary tools for agent diary entries. + - Use KG tools for structured facts such as Alice -> likes -> Italian food. + """, + tools=tools, +) +``` + +#### Specify the MemPalace Path + +Tool classes accept `palace_path`. If it is omitted, they use `MempalaceConfig().palace_path`. KG tools also accept `kg_path`; if `kg_path` is omitted and `palace_path` is provided, they default to `palace_path/knowledge_graph.sqlite3`: + +```python +mempalace_search_tool = MempalaceSearchTool(palace_path="/path/to/palace") +mempalace_add_drawer_tool = MempalaceAddDrawerTool(palace_path="/path/to/palace") +mempalace_kg_query_tool = MempalaceKGQueryTool( + palace_path="/path/to/palace", + kg_path="/path/to/palace/knowledge_graph.sqlite3", +) +``` + +Use the same path when inspecting memories from the CLI: + +```bash +mempalace --palace /path/to/palace search "user name" +``` + +The example manages paths through `.env`: + +```bash +MEMPALACE_PALACE_PATH=/tmp/trpc-agent-mempalace-demo +MEMPALACE_KG_PATH=/tmp/trpc-agent-mempalace-demo/knowledge_graph.sqlite3 +MEMPALACE_WING=personal_assistant_alice +MEMPALACE_ROOM=user_profile +``` + +#### Tool-based Workflow + +```text +1. User: Use mempalace_search to check whether you remember my name. + ↓ + Agent calls: mempalace_search( + query="name", + wing="personal_assistant_alice", + room="user_profile" + ) + ↓ + Result: No palace found or empty results + ↓ + Agent: I do not know your name yet. + +2. User: Use mempalace_add_drawer to remember that my name is Alice. + ↓ + Agent calls: mempalace_add_drawer( + wing="personal_assistant_alice", + room="user_profile", + content="User's name is Alice." + ) + ↓ + MemPalace writes the drawer + +3. User starts a new session: Use mempalace_search to recall my name. + ↓ + Agent calls: mempalace_search(query="name", wing="personal_assistant_alice", room="user_profile") + ↓ + MemPalace returns "User's name is Alice." + ↓ + Agent: Your name is Alice. + +4. User: Use mempalace_kg_add to add this fact: Alice likes Italian food. + ↓ + Agent calls: mempalace_kg_add(subject="Alice", predicate="likes", object="Italian food") + ↓ + KG writes the triple fact: Alice -> likes -> Italian food + +5. User: Use mempalace_kg_invalidate to mark the fact Alice likes Italian food as ended today. + ↓ + Agent calls: mempalace_kg_invalidate(subject="Alice", predicate="likes", object="Italian food") + ↓ + KG keeps the historical fact but marks current as false +``` + +**Complete tool-based demo and result analysis:** [examples/mempalace_tools/README.md](../../../examples/mempalace_tools/README.md) + +--- + +#### MempalaceSearchTool + +Semantically searches drawer content saved in MemPalace. + +**Constructor:** +```python +MempalaceSearchTool( + palace_path: str | None = None, + filters_name: list[str] | None = None, + filters: list[Any] | None = None, +) +``` + +**Agent Tool Parameters (callable by LLM):** +- `query` (string, required): Search query +- `limit` (integer, optional): Maximum number of results, defaults to 5 +- `wing` (string, optional): Filter by wing +- `room` (string, optional): Filter by room + +**Return Value Example:** +```python +{ + "query": "name favorite food", + "filters": {"wing": "personal_assistant_alice", "room": "user_profile"}, + "results": [ + {"text": "User's name is Alice.", "wing": "personal_assistant_alice", "room": "user_profile"}, + {"text": "My favorite food is Italian food.", "wing": "personal_assistant_alice", "room": "user_profile"}, + ], +} +``` + +--- + +#### MempalaceAddDrawerTool + +Writes a verbatim drawer under a specified `wing/room`. It is suitable for long-term facts that the user explicitly asks the Agent to remember. + +**Agent Tool Parameters (callable by LLM):** +- `wing` (string, required): Storage scope, for example `personal_assistant_alice` +- `room` (string, required): Memory topic, for example `user_profile` +- `content` (string, required): Verbatim content to save +- `source_file` (string, optional): Source identifier + +**Return Value Example:** +```python +{ + "success": True, + "drawer_id": "drawer_personal_assistant_alice_user_profile_xxx", + "wing": "personal_assistant_alice", + "room": "user_profile", +} +``` + +--- + +#### Diary Tools + +`MempalaceDiaryWriteTool` and `MempalaceDiaryReadTool` record and read agent diary entries. They are useful for "what happened in this task, what was observed, and what to watch next" style runtime notes, and should not replace user-profile memories. + +| Tool | Key Parameters | Return Highlights | +|---|---|---| +| `mempalace_diary_write` | `entry`, `agent_name`, `topic`, `wing` | `success`, `entry_id`, `agent`, `topic` | +| `mempalace_diary_read` | `agent_name`, `last_n`, `wing` | `entries`, `total`, `showing` | + +In the example output, after writing `Alice tested the MemPalace tools example today.`, a later new session can still read that diary entry, showing that diary data is persisted. + +--- + +#### KG Tools + +KG tools maintain structured facts. A fact is usually represented as a triple: + +```text +subject -> predicate -> object +Alice -> likes -> Italian food +``` + +| Tool | Key Parameters | Semantics | +|---|---|---| +| `mempalace_kg_add` | `subject`, `predicate`, `object`, `valid_from`, `valid_to`, `confidence` | Write a structured fact | +| `mempalace_kg_query` | `entity`, `as_of`, `direction` | Query facts related to an entity | +| `mempalace_kg_timeline` | `entity` | Inspect an entity's fact timeline | +| `mempalace_kg_invalidate` | `subject`, `predicate`, `object`, `ended` | Mark a fact as no longer valid | + +`mempalace_kg_invalidate` does not delete the historical fact. It sets `valid_to` and makes `current=False`. The example therefore runs invalidation after the second-phase persistence verification, so it does not alter the query result used to validate persistence. + +#### Recommendations + +- If you only need standard cross-session long-term memory, prefer `MempalaceMemoryService`. +- Use `mempalace_tool` when the Agent needs direct control over what to write, how to classify it, diary operations, or KG maintenance. +- For user profiles and preferences, write to a stable `wing/room`, such as `personal_assistant_alice/user_profile`. +- For KG fact changes, prefer `mempalace_kg_invalidate` to express "no longer true" instead of deleting history. +- Do not let the Agent write `load_memory` tool results, code execution outputs, or other intermediate traces directly into drawers, as this can pollute long-term memory. + +--- + +### MemPalace Resources + +| Resource | Path | Description | +|---|---|---| +| `MempalaceMemoryService` complete example | [examples/memory_service_with_mempalace/](../../../examples/memory_service_with_mempalace/README.md) | Installation, path configuration, CLI search, and execution result analysis | +| `MempalaceMemoryService` source code | [mempalace_memory_service.py](../../../trpc_agent_sdk/memory/mempalace_memory_service.py) | Recommended framework-level memory service implementation | +| MemPalace tools source code | [mempalace_tool.py](../../../trpc_agent_sdk/tools/mempalace_tool.py) | Optional tool-based integration: `mempalace_search`, `mempalace_add_drawer`, diary, KG tools | + +--- + ## Core Feature Summary ### 1. Cross-session Memory Sharing @@ -1165,17 +1731,19 @@ ConnectionError: Cannot connect to Qdrant at localhost:6333 - ✅ Uses `save_key` (`app_name/user_id`) as the memory key - ✅ Suitable for storing user profiles, long-term preferences, and other cross-session information -### 2. Keyword Search +### 2. Keyword or Semantic Search - ✅ Supports keyword extraction and matching for both Chinese and English - ✅ Uses `extract_words_lower` to extract English words and Chinese characters - ✅ Matching logic: returns on any query word match +- ✅ Semantic memory services such as `MempalaceMemoryService` and `Mem0MemoryService` use vector / semantic retrieval instead of simple keyword matching ### 3. TTL Cache Eviction - ✅ Automatically cleans up expired memories, preventing unlimited storage growth - ✅ Refreshes TTL on access (during `search_memory`) - ✅ Different implementations use different cleanup mechanisms +- ⚠️ Some persistent semantic services may use fixed event timestamps for TTL cleanup rather than refreshing TTL on every search ### 4. Automatic Storage @@ -1188,6 +1756,7 @@ ConnectionError: Cannot connect to Qdrant at localhost:6333 - ✅ Supports multiple implementations: In-Memory, Redis, SQL, Mem0, etc. - ✅ Supports TRPC Redis integration - ✅ Supports Mem0 semantic memory integration (vector search + LLM extraction) +- ✅ Supports MemPalace local-first semantic memory integration (ChromaDB-backed palace) - ✅ Choose the appropriate implementation based on your scenario --- @@ -1201,21 +1770,23 @@ ConnectionError: Cannot connect to Qdrant at localhost:6333 ### 2. Keyword Search Limitations -- The current implementation uses **keyword (token) matching**, not semantic search +- The built-in InMemory/Redis/SQL implementations use **keyword (token) matching**, not semantic search - After `extract_words_lower` (whole English words, individual Chinese characters), **any** query token that appears in the event's token set counts as a match (this is not full-sentence semantic similarity) - Suitable for rapid prototyping, not suitable for complex semantic retrieval requirements +- For semantic retrieval, use `MempalaceMemoryService` or `Mem0MemoryService` ### 3. TTL Configuration - `ttl_seconds`: Memory expiration time (in seconds) -- `cleanup_interval_seconds`: Cleanup interval (InMemory/SQL only; Redis handles expiration automatically) -- TTL is automatically refreshed on access, extending the memory's validity period +- `cleanup_interval_seconds`: Cleanup interval (InMemory/SQL/MemPalace; Redis handles expiration automatically) +- InMemory/Redis refresh TTL on access; persistent semantic services may use stored timestamps for expiration ### 4. Concurrency Safety - `InMemoryMemoryService`: Thread-safe within a single process - `RedisMemoryService`: Supports multi-process/multi-server concurrency - `SqlMemoryService`: Supports multi-process/multi-server concurrency (using database transactions) +- `MempalaceMemoryService`: Local-first storage; avoid multiple processes writing to the same palace unless the underlying MemPalace/ChromaDB deployment is managed carefully --- @@ -1225,9 +1796,9 @@ MemoryService provides powerful long-term memory management capabilities: - ✅ **Cross-session sharing**: Different sessions can access shared memories - ✅ **Automatic storage**: Automatically stores Session events when `enabled=True` -- ✅ **Keyword search**: Supports Chinese and English keyword matching +- ✅ **Search**: Supports keyword matching and semantic memory retrieval depending on implementation - ✅ **TTL eviction**: Automatically cleans up expired memories -- ✅ **Multiple implementations**: In-Memory, Redis, SQL, TRPC Redis, Mem0 +- ✅ **Multiple implementations**: In-Memory, Redis, SQL, TRPC Redis, Mem0, MemPalace Through proper use of MemoryService, you can achieve: - User profile construction @@ -1241,3 +1812,4 @@ For more detailed usage examples, please refer to the related examples in the [e - [examples/memory_service_with_redis/run_agent.py](../../../examples/memory_service_with_redis/run_agent.py) - [examples/memory_service_with_sql/run_agent.py](../../../examples/memory_service_with_sql/run_agent.py) - [examples/memory_service_with_mem0/run_agent.py](../../../examples/memory_service_with_mem0/run_agent.py) +- [examples/memory_service_with_mempalace/run_agent.py](../../../examples/memory_service_with_mempalace/run_agent.py) diff --git a/docs/mkdocs/zh/memory.md b/docs/mkdocs/zh/memory.md index b076453..13597d8 100644 --- a/docs/mkdocs/zh/memory.md +++ b/docs/mkdocs/zh/memory.md @@ -163,9 +163,9 @@ _session_events = { --- -## MemoryService 实现 +## 基本 MemoryService 实现 -trpc-agent 提供了三种 `MemoryService` 实现,方便根据场景选择合适的存储后端: +trpc-agent 内置提供了三种 `MemoryService` 实现,方便根据场景选择合适的存储后端: ### InMemoryMemoryService @@ -385,7 +385,7 @@ async def _cleanup_expired_async(self) -> None: --- -## 三种实现对比 +### 三种实现对比 | 特性 | InMemoryMemoryService | RedisMemoryService | SqlMemoryService | |-----|----------------------|-------------------|------------------| @@ -407,9 +407,9 @@ async def _cleanup_expired_async(self) -> None: --- -## 使用示例 +### 使用示例 -### 基本使用流程 +#### 基本使用流程 ```python from trpc_agent_sdk.sessions import InMemorySessionService @@ -452,7 +452,9 @@ async for event in runner.run_async( # Agent 会自动调用 memory_service.search_memory() ``` -### 手动存储和搜索 +--- + +#### 手动存储和搜索 ```python # 手动存储会话到 Memory @@ -478,59 +480,9 @@ for memory in response.memories: --- -## 集成 SessionService 和 MemoryService - -在实际应用中,通常需要同时使用 `SessionService` 和 `MemoryService`: - -```python -from trpc_agent_sdk.sessions import InMemorySessionService -from trpc_agent_sdk.memory import MemoryServiceConfig -from trpc_agent_sdk.memory import InMemoryMemoryService -from trpc_agent_sdk.runners import Runner - -# 创建服务实例 -session_service = InMemorySessionService() -memory_service_config = MemoryServiceConfig( - enabled=True, - ttl=MemoryServiceConfig.create_ttl_config( - enable=True, - ttl_seconds=86400, - ), -) -memory_service = InMemoryMemoryService(memory_service_config=memory_service_config) - -# 创建 Runner 并配置服务 -runner = Runner( - app_name="my_app", - agent=my_agent, - session_service=session_service, - memory_service=memory_service # 可选:配置 MemoryService -) - -# 运行 Agent -async for event in runner.run_async( - user_id=user_id, - session_id=session_id, - new_message=user_message -): - # 处理事件... - pass -``` - -**工作流程**: - -1. **SessionService** 管理当前会话的上下文(对话历史、状态等) -2. **MemoryService** 自动存储 Session 事件到长期记忆(如果 `enabled=True`) -3. **load_memory 工具** 调用 `memory_service.search_memory()` 检索相关记忆 -4. Agent 可以同时访问当前会话上下文和历史记忆,提供更连贯的对话体验 - ---- - -## 相关示例 +#### 相关实例 -以下示例展示了不同 MemoryService 实现的使用方式: - -### InMemoryMemoryService +##### InMemoryMemoryService 📁 **示例路径**:[`examples/memory_service_with_in_memory/run_agent.py`](../../../examples/memory_service_with_in_memory/run_agent.py) @@ -548,7 +500,7 @@ python3 run_agent.py --- -### RedisMemoryService +##### RedisMemoryService 📁 **示例路径**:[`examples/memory_service_with_redis/run_agent.py`](../../../examples/memory_service_with_redis/run_agent.py) @@ -566,7 +518,7 @@ python3 run_agent.py --- -### SqlMemoryService +##### SqlMemoryService 📁 **示例路径**:[`examples/memory_service_with_sql/run_agent.py`](../../../examples/memory_service_with_sql/run_agent.py) @@ -584,79 +536,98 @@ python3 run_agent.py --- -## 集成 Mem0 -### 什么是 Mem0? +--- -Mem0 是为 LLM 提供的智能、自我改进的记忆层,能够跨对话持久化和检索用户信息,实现更加个性化和连贯一致的用户体验。 +## 扩展 MemoryService 实现 -**核心能力:** -- 🧠 智能记忆提取和存储 -- 🔍 语义搜索历史对话 -- 🔄 自动记忆更新和去重 -- 🎯 用户级别的记忆隔离 +### 集成 Mempalace -**官方资源:** -- 官方文档:[https://docs.mem0.ai/introduction](https://docs.mem0.ai/introduction) -- GitHub:[https://github.com/mem0ai/mem0](https://github.com/mem0ai/mem0) +#### 什么是 Mempalace? + +Mempalace 是一个本地优先的记忆系统,用于保存原文记忆并通过语义搜索召回历史内容。它的核心存储层级可以理解为: + +```text +Palace + └── Wing + └── Room + └── Drawer +``` + +在 `MempalaceMemoryService` 中,每条可存储的框架事件会被写成一个 drawer,drawer 中包含原始文本和 metadata,例如 `wing`、`room`、`session_id`、`event_id`、`author`、`timestamp` 等。 + +**核心能力:** +- 本地持久化存储,默认数据目录通常是 `~/.mempalace/palace` +- 基于 Mempalace / ChromaDB 的语义检索 +- 通过 `wing` / `room` 做记忆空间和类别隔离 +- 支持使用 `mempalace search` 在命令行排查记忆 +- 支持框架侧 TTL 后台清理过期 drawer --- -### tRPC-Agent 集成方式 +#### tRPC-Agent 集成方式 -tRPC-Agent 提供两种集成 Mem0 的方式: +当前推荐使用框架级记忆服务: | 方式 | 类 / 工具 | 适用场景 | |---|---|---| -| **框架级记忆服务**(推荐) | `Mem0MemoryService` | 由框架自动完成跨会话记忆的存储与检索,Agent 无感知 | -| **工具式记忆** | `SearchMemoryTool` / `SaveMemoryTool` | Agent 通过工具主动调用 Mem0,灵活控制存取时机 | +| **框架级记忆服务**(推荐) | `MempalaceMemoryService` | 由框架自动完成跨会话记忆写入,Agent 通过 `load_memory` 检索 | +| **Mempalace 工具** | `mempalace_search` / `mempalace_add_drawer` 等 | Agent 需要直接操作 Mempalace 的 drawer、diary、KG 等能力 | + +`MempalaceMemoryService` 更适合作为本项目的标准 MemoryService 接入方式。它会在每轮对话结束后由框架自动调用 `store_session()` 写入记忆,Agent 在响应时通过 `load_memory` 工具调用 `search_memory()` 检索历史记忆。 --- -### Mem0MemoryService(推荐方式) +#### MempalaceMemoryService(推荐方式) -`Mem0MemoryService` 是 tRPC-Agent 的**框架级记忆服务**,由框架在每轮对话结束后自动调用 `store_session` 存储会话记忆,Agent 在响应时通过 `load_memory` 工具主动检索相关记忆,无需手动管理存取时机。 +##### 核心设计 -#### 核心设计 +- **跨会话共享**:MemoryService 使用 `session.save_key` 作为跨 session 的用户记忆维度,`save_key` 通常是 `{app_name}/{user_id}`。 +- **Mempalace 映射**:`save_key` 默认映射为 `wing`,`room` 默认是 `conversations`,单条 `Event` 映射为 drawer。 +- **增量后台写入**:只写入当前进程尚未调度或尚未存储的 drawer,避免每轮全量重写 session。 +- **语义检索**:查询时调用 Mempalace 的 `search_memories()`,并可通过 `wing` / `room` 过滤范围。 +- **TTL 自动清理**:后台定期扫描本服务写入的 drawer,根据 metadata 中的事件时间删除过期记忆。 -- **两级 Key 策略**:`session.save_key` → Mem0 `user_id`(用户维度);`session.id` → `run_id`(会话维度) -- **跨会话共享**:同一用户的不同 session 共享同一份记忆 -- **TTL 自动过期**:后台定期清理超时记忆 +##### 快速接入 -#### 快速接入 +**步骤 1:安装依赖** -**步骤 1:创建 `Mem0MemoryService`** +```bash +# 通过 trpc-agent extra 安装 +pip install -e ".[mempalace]" -```python -from mem0 import AsyncMemory, AsyncMemoryClient -from trpc_agent_sdk.memory import MemoryServiceConfig -from trpc_agent_sdk.memory.mem0_memory_service import Mem0MemoryService +# 或者只安装 Mempalace +pip install mempalace +``` -# 自托管模式(AsyncMemory + Qdrant) -from mem0.configs.base import MemoryConfig -mem0_client = AsyncMemory(config=MemoryConfig(**{ - "vector_store": {"provider": "qdrant", "config": {"host": "localhost", "port": 6333}}, # 向量数据库声明 - "llm": {"provider": "deepseek", "config": {"model": "...", "api_key": "..."}}, # 用于记忆摘要提炼(infer=True 时使用) - "embedder": {"provider": "huggingface", "config": {"model": "multi-qa-MiniLM-L6-cos-v1"}}, # 开源嵌入模型 -})) +**步骤 2:创建 `MempalaceMemoryService`** -# 或者:远端平台模式(AsyncMemoryClient),无需自建基础设施 -mem0_client = AsyncMemoryClient(api_key="your_mem0_api_key", host="https://api.mem0.ai") +```python +from trpc_agent_sdk.memory import MemoryServiceConfig +from trpc_agent_sdk.memory.mempalace_memory_service import MempalaceMemoryService -memory_service = Mem0MemoryService( - mem0_client=mem0_client, - memory_service_config=MemoryServiceConfig( - enabled=True, - ttl=MemoryServiceConfig.create_ttl_config(enable=False), # 不启用 TTL,记忆永久保留 +memory_service_config = MemoryServiceConfig( + enabled=True, + ttl=MemoryServiceConfig.create_ttl_config( + enable=True, + ttl_seconds=86400, # 记忆保留 24 小时 + cleanup_interval_seconds=3600, # 每小时清理一次 ), - infer=False, # False=原文存储(稳定),True=语义抽取(智能) +) + +memory_service = MempalaceMemoryService( + memory_service_config=memory_service_config, + wing="my_app_user", # 可选:记忆命名空间;不传则默认由 save_key 推导 + room="conversations", # 可选:记忆类别;默认 conversations + store_only_model_visible=True, ) ``` -**步骤 2:将 `memory_service` 传入 `Runner`** +**步骤 3:将 `memory_service` 传入 `Runner`** ```python from trpc_agent_sdk.runners import Runner +from trpc_agent_sdk.sessions import InMemorySessionService from trpc_agent_sdk.tools import load_memory_tool agent = LlmAgent( @@ -674,58 +645,203 @@ runner = Runner( ) ``` -**步骤 3:运行,记忆自动跨会话持久化** +**步骤 4:运行,记忆自动跨会话持久化** ```python # 第一轮对话(session_1) async for event in runner.run_async(user_id="alice", session_id="session_1", new_message=...): ... -# 框架在对话结束后自动调用 store_session,将本轮消息存入 Mem0 +# 框架在对话结束后自动调用 store_session,将本轮可存储事件写入 Mempalace -# 第二轮对话(session_2)——新会话,但能检索到 session_1 的记忆 +# 第二轮对话(session_2)——新会话,但能通过 load_memory 检索到 session_1 的记忆 async for event in runner.run_async(user_id="alice", session_id="session_2", new_message=...): ... ``` -#### `infer` 参数选择 +**完整可运行示例:** [examples/memory_service_with_mempalace/run_agent.py](../../../examples/memory_service_with_mempalace/run_agent.py) -| | `infer=False`(推荐) | `infer=True` | -|---|---|---| -| 存储内容 | 对话原文 | LLM 提炼后的语义事实 | -| 稳定性 | 高,每条必存 | 中,LLM 判断 NONE 时不存 | -| token 消耗 | 低(无 LLM 调用) | 高(每次写入调用 LLM) | -| 冲突消解 | 不做 | 自动(新事实覆盖旧事实) | -| 推荐场景 | 完整历史归档、生产环境 | 长期用户画像、偏好提炼 | +--- + +##### Mempalace 层级映射 + +```text +session.save_key = "{app_name}/{user_id}" -> wing(未显式配置 wing 时) +room -> room,默认 conversations +Event -> drawer +session.id / event.id / author / timestamp -> drawer metadata +``` + +如果显式传入 `wing="trpc-agent"`,则所有记忆会写入该 wing;如果不显式传入,框架会使用 `save_key` 推导 wing,从而更自然地按 app/user 隔离长期记忆。 + +--- + +##### 指定存储路径与命令行查询 + +Mempalace 的数据路径来自 `MempalaceConfig().palace_path`,默认通常是: + +```text +~/.mempalace/palace +``` + +可以通过环境变量指定: + +```bash +export MEMPALACE_PALACE_PATH=/path/to/palace +``` + +也可以通过 `~/.mempalace/config.json` 指定: + +```json +{ + "palace_path": "/path/to/palace", + "collection_name": "mempalace_drawers" +} +``` + +如果代码中指定或配置了自定义 palace 路径,命令行查询时也必须使用同一个路径: + +```bash +mempalace --palace /path/to/palace search "user name" +``` + +按 `wing` / `room` 过滤: + +```bash +mempalace --palace /path/to/palace search "user name" \ + --wing my_app_user \ + --room conversations +``` + +如果使用默认路径,可以省略 `--palace`: + +```bash +mempalace search "user name" --wing my_app_user --room conversations +``` + +> `/path/to/palace` 是包含 `chroma.sqlite3` 的 Mempalace 数据目录,不是某个单独文件。 + +--- -#### TTL 配置(可选) +##### TTL 配置(可选) ```python memory_service_config = MemoryServiceConfig( enabled=True, ttl=MemoryServiceConfig.create_ttl_config( enable=True, - ttl_seconds=86400, # 记忆保留 24 小时 + ttl_seconds=86400, # 记忆保留 24 小时 cleanup_interval_seconds=3600, # 每小时清理一次 ), ) ``` -> 详细说明、运行结果分析和常见问题解答:[examples/memory_service_with_mem0/README.md](../../../examples/memory_service_with_mem0/README.md) +需要注意: + +- Mempalace 本身没有“长时间不用自动删除”的默认 TTL 机制。 +- `MempalaceMemoryService` 的 TTL 是框架侧实现的后台清理。 +- 清理时会扫描本服务写入的 drawer,并根据 metadata 中的 `timestamp` 判断是否过期。 +- 该 TTL 不是“访问后刷新过期时间”的语义,而是基于原始事件时间进行过期删除。 --- -### 工具式集成(mem0_tool) +##### 删除记忆 -tRPC-Agent 通过 **工具(Tools)** 的方式集成 Mem0,为 Agent 提供记忆能力。框架提供了两个核心工具类: +Mempalace CLI 当前没有直接提供按 `wing` 或 `wing + room` 批量删除的命令。框架中提供了便捷方法: + +```python +# 删除整个 wing +await memory_service.delete_memory(wing="my_app_user") + +# 删除 wing 下指定 room +await memory_service.delete_memory(wing="my_app_user", room="conversations") +``` + +这会删除 drawer collection 中匹配 metadata 的记录,并清理当前进程内的去重缓存,避免删除后无法重新写入。 + +--- + +##### 存储内容策略 + +当前建议只把有长期价值的普通文本事件写入 Mempalace。工具调用、工具结果、代码执行结果等中间过程通常不适合作为长期记忆,否则容易出现: + +- `load_memory` 的工具结果再次被写入记忆 +- 记忆中嵌套旧的 memory JSON +- 长期记忆被工具日志污染,检索质量下降 + +因此 `MempalaceMemoryService` 更适合存储类似下面的信息: + +```text +用户:My name is Alice. +用户:My favorite color is blue. +助手:已确认用户姓名或偏好。 +``` + +而不是存储: + +```text +[tool_call] load_memory: ... +[tool_response] load_memory: {"memories": [...]} +``` + +--- + +##### 典型工作流 + +``` +1. 用户:Do you remember my name? + ↓ + Agent 调用: load_memory(query="user name") + ↓ + 结果:{"memories": []} + ↓ + Agent:I don't know your name yet. + +2. 用户:My name is Alice + ↓ + 本轮结束后,框架自动调用 MempalaceMemoryService.store_session() + ↓ + 用户消息被写入 wing/room 下的 drawer + +3. 用户开启新 session:Do you remember my name? + ↓ + Agent 调用: load_memory(query="user name") + ↓ + Mempalace 返回包含 "My name is Alice" 的历史记忆 + ↓ + Agent:Yes, your name is Alice. +``` + +**查看完整演示输出(MempalaceMemoryService):** [examples/memory_service_with_mempalace/README.md](../../../examples/memory_service_with_mempalace/README.md) + +--- + +#### 工具式集成(mempalace_tool) + +`mempalace_tool` 是另一种与 Mempalace 集成的方式。它不是推荐的标准 MemoryService 路径,而是把 Mempalace 的能力作为 Agent 可调用工具暴露出来,让 Agent 主动决定什么时候查询、写入、读取 diary 或维护 KG。 + +它与 `MempalaceMemoryService` 的区别是: + +| 方式 | 写入时机 | 检索方式 | 适用场景 | +|---|---|---|---| +| `MempalaceMemoryService` | 框架在每轮结束后自动写入 | `load_memory` 间接调用 `search_memory()` | 标准跨会话长期记忆 | +| `mempalace_tool` | Agent 主动调用工具写入 | Agent 主动调用 `mempalace_search` | 需要精细控制 Mempalace 功能、diary、KG 或手动 drawer 管理 | + +##### 可用工具 | 工具类 | 工具名 | 功能 | 使用场景 | -|--------|--------|------|---------| -| `SearchMemoryTool` | `search_memory` | 搜索历史记忆 | Agent 需要回忆过去的对话内容 | -| `SaveMemoryTool` | `save_memory` | 保存重要信息 | Agent 判断需要记住的用户信息 | +|---|---|---|---| +| `MempalaceSearchTool` | `mempalace_search` | 按语义搜索已保存的 drawer 内容 | Agent 需要回忆用户画像、偏好或历史事实 | +| `MempalaceAddDrawerTool` | `mempalace_add_drawer` | 向指定 `wing/room` 写入一条原文 drawer | 用户明确要求记住某个长期信息 | +| `MempalaceDiaryWriteTool` | `mempalace_diary_write` | 写入 agent diary | 记录运行观察、任务过程或阶段性总结 | +| `MempalaceDiaryReadTool` | `mempalace_diary_read` | 读取指定 agent 最近的 diary | Agent 需要回顾历史任务记录 | +| `MempalaceKGAddTool` | `mempalace_kg_add` | 写入知识图谱三元组事实 | 需要结构化表达 `subject -> predicate -> object` | +| `MempalaceKGQueryTool` | `mempalace_kg_query` | 查询某个实体的知识图谱关系 | 查询 Alice 相关事实、项目依赖、实体关系等 | +| `MempalaceKGTimelineTool` | `mempalace_kg_timeline` | 按时间线读取知识图谱事实 | 查看某个实体关系如何随时间变化 | +| `MempalaceKGInvalidateTool` | `mempalace_kg_invalidate` | 将一条当前事实标记为失效 | 表达事实变化,保留历史但不再视为当前事实 | -> **注意**:两个工具类需要在实例化时传入 Mem0 客户端,`user_id` 由框架通过 `InvocationContext` 自动注入,无需在工具参数中显式传递。 +> **注意**:`mempalace_tool` 与 `mem0_tool` 类似,都是通过 Tools 暴露给 Agent,由模型决定是否调用;但 MemPalace 工具不只覆盖“搜索/保存”两个动作,还覆盖 diary 和 KG。完整示例见 [examples/mempalace_tools/README.md](../../../examples/mempalace_tools/README.md),工具源码见 [mempalace_tool.py](../../../trpc_agent_sdk/tools/mempalace_tool.py)。 -#### 集成架构 +##### 集成架构 ``` ┌──────────────────────┐ @@ -734,153 +850,436 @@ tRPC-Agent 通过 **工具(Tools)** 的方式集成 Mem0,为 Agent 提供 │ ▼ ┌──────────────────────┐ -│ tRPC-Agent │◄─────────┐ -│ LlmAgent │ │ -└──────────┬───────────┘ │ - │ │ - │ 调用工具 │ 返回记忆 - │ │ - ▼ │ -┌──────────────────────┐ │ -│ Mem0 Tools │──────────┘ -│ - SearchMemoryTool │ -│ - SaveMemoryTool │ -└──────────┬───────────┘ - │ - ▼ -┌──────────────────────┐ -│ Mem0 Client │ -│ (AsyncMemory / │ -│ AsyncMemoryClient) │ +│ tRPC-Agent │◄────────────────┐ +│ LlmAgent │ │ +└──────────┬───────────┘ │ + │ │ 返回工具结果 + │ 调用工具 │ + ▼ │ +┌──────────────────────┐ │ +│ MemPalace Tools │─────────────────┘ +│ - mempalace_search │ +│ - add_drawer │ +│ - diary read/write │ +│ - KG tools │ └──────────┬───────────┘ │ ▼ ┌──────────────────────┐ -│ Storage │ -│ - Qdrant │ -│ - Mem0 Cloud │ +│ MemPalace Backend │ +│ - Palace / ChromaDB │ +│ - KG SQLite │ └──────────────────────┘ ``` ---- +##### 快速接入 -### 部署模式 +```python +from trpc_agent_sdk.agents import LlmAgent +from trpc_agent_sdk.tools.mempalace_tool import MempalaceAddDrawerTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceDiaryReadTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceDiaryWriteTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceKGAddTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceKGInvalidateTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceKGQueryTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceKGTimelineTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceSearchTool + +palace_path = "/tmp/trpc-agent-mempalace-demo" +kg_path = "/tmp/trpc-agent-mempalace-demo/knowledge_graph.sqlite3" + +tools = [ + MempalaceSearchTool(palace_path=palace_path), + MempalaceAddDrawerTool(palace_path=palace_path), + MempalaceDiaryWriteTool(palace_path=palace_path), + MempalaceDiaryReadTool(palace_path=palace_path), + MempalaceKGAddTool(palace_path=palace_path, kg_path=kg_path), + MempalaceKGQueryTool(palace_path=palace_path, kg_path=kg_path), + MempalaceKGTimelineTool(palace_path=palace_path, kg_path=kg_path), + MempalaceKGInvalidateTool(palace_path=palace_path, kg_path=kg_path), +] -tRPC-Agent 支持 Mem0 的两种部署模式:自托管模式和平台模式 +agent = LlmAgent( + name="memory_assistant", + model=your_model, + instruction=""" + You are a helpful assistant with MemPalace tools. + - Use mempalace_search before answering questions that may require past memory. + - Use mempalace_add_drawer when the user explicitly asks you to remember stable facts. + - Use diary tools for agent diary entries. + - Use KG tools for structured facts such as Alice -> likes -> Italian food. + """, + tools=tools, +) +``` -#### 模式对比 +##### 指定 Mempalace 路径 -| 特性 | 自托管模式 | 平台模式 | -|------|-----------|---------| -| **客户端类型** | `AsyncMemory` | `AsyncMemoryClient` | -| **存储位置** | 本地向量数据库(如 Qdrant) | Mem0 云端 | -| **依赖组件** | 向量数据库 + 嵌入模型 + LLM | 仅需 API Key | -| **数据控制** | 完全控制 | 托管服务 | -| **适用场景** | 开发测试、数据敏感、本地部署 | 生产环境、快速部署 | +工具类支持传入 `palace_path`。如果不传,则使用 `MempalaceConfig().palace_path`。KG 工具额外支持 `kg_path`;如果不传 `kg_path` 且传入了 `palace_path`,默认使用 `palace_path/knowledge_graph.sqlite3`: -#### 模式一:自托管(AsyncMemory) +```python +mempalace_search_tool = MempalaceSearchTool(palace_path="/path/to/palace") +mempalace_add_drawer_tool = MempalaceAddDrawerTool(palace_path="/path/to/palace") +mempalace_kg_query_tool = MempalaceKGQueryTool( + palace_path="/path/to/palace", + kg_path="/path/to/palace/knowledge_graph.sqlite3", +) +``` -适合需要完全控制数据和基础设施的场景。 +命令行排查时也要使用相同路径: -**核心组件:** -- **向量存储**:支持多种后端(见下方完整清单) -- **LLM**:用于生成记忆摘要(OpenAI / DeepSeek / Gemini 等) -- **嵌入模型**:用于向量化(HuggingFace / OpenAI 等) +```bash +mempalace --palace /path/to/palace search "user name" +``` -**自托管支持的向量存储(完整清单):** -- `azure_ai_search` -- `azure_mysql` -- `baidu` -- `cassandra` -- `chroma` -- `databricks` -- `elasticsearch` -- `faiss` -- `langchain` -- `milvus` -- `mongodb` -- `neptune_analytics` -- `opensearch` -- `pgvector` -- `pinecone` -- `qdrant` -- `redis` -- `s3_vectors` -- `supabase` -- `turbopuffer` -- `upstash_vector` -- `valkey` -- `vertex_ai_vector_search` -- `weaviate` +示例目录中通过 `.env` 管理路径: -> 官方向量存储实现列表(以 mem0 仓库为准):[mem0/vector_stores](https://github.com/mem0ai/mem0/tree/main/mem0/vector_stores) +```bash +MEMPALACE_PALACE_PATH=/tmp/trpc-agent-mempalace-demo +MEMPALACE_KG_PATH=/tmp/trpc-agent-mempalace-demo/knowledge_graph.sqlite3 +MEMPALACE_WING=personal_assistant_alice +MEMPALACE_ROOM=user_profile +``` -**示例代码:** +##### 工具式工作流 + +```text +1. 用户:Use mempalace_search to check whether you remember my name. + ↓ + Agent 调用: mempalace_search( + query="name", + wing="personal_assistant_alice", + room="user_profile" + ) + ↓ + 结果:No palace found 或空结果 + ↓ + Agent:暂时没有记住你的名字 + +2. 用户:Use mempalace_add_drawer to remember that my name is Alice. + ↓ + Agent 调用: mempalace_add_drawer( + wing="personal_assistant_alice", + room="user_profile", + content="User's name is Alice." + ) + ↓ + Mempalace 写入 drawer + +3. 用户开启新的 session:Use mempalace_search to recall my name. + ↓ + Agent 调用: mempalace_search(query="name", wing="personal_assistant_alice", room="user_profile") + ↓ + Mempalace 返回 "User's name is Alice." + ↓ + Agent:你的名字是 Alice + +4. 用户:Use mempalace_kg_add to add this fact: Alice likes Italian food. + ↓ + Agent 调用: mempalace_kg_add(subject="Alice", predicate="likes", object="Italian food") + ↓ + KG 写入三元组事实:Alice -> likes -> Italian food + +5. 用户:Use mempalace_kg_invalidate to mark the fact Alice likes Italian food as ended today. + ↓ + Agent 调用: mempalace_kg_invalidate(subject="Alice", predicate="likes", object="Italian food") + ↓ + KG 保留历史事实,但将 current 标记为 false +``` + +**查看完整工具式演示和运行结果分析:** [examples/mempalace_tools/README.md](../../../examples/mempalace_tools/README.md) + +--- + +##### MempalaceSearchTool 介绍 + +按语义搜索 MemPalace 中已保存的 drawer 内容。 + +**构造函数:** ```python -from mem0 import AsyncMemory -from trpc_agent_sdk.server.tools.mem0_tool import SearchMemoryTool, SaveMemoryTool +MempalaceSearchTool( + palace_path: str | None = None, + filters_name: list[str] | None = None, + filters: list[Any] | None = None, +) +``` -# 配置自定义组件 -config = { - "vector_store": {"provider": "qdrant", "config": {...}}, - "llm": {"provider": "deepseek", "config": {...}}, - "embedder": {"provider": "huggingface", "config": {...}} +**Agent 工具参数(LLM 可调用):** +- `query`(string,必填):搜索查询内容 +- `limit`(integer,可选):最多返回多少条结果,默认 5 +- `wing`(string,可选):按 wing 过滤 +- `room`(string,可选):按 room 过滤 + +**返回值示例:** +```python +{ + "query": "name favorite food", + "filters": {"wing": "personal_assistant_alice", "room": "user_profile"}, + "results": [ + {"text": "User's name is Alice.", "wing": "personal_assistant_alice", "room": "user_profile"}, + {"text": "My favorite food is Italian food.", "wing": "personal_assistant_alice", "room": "user_profile"}, + ], } +``` -# 创建 Mem0 客户端 -memory = await AsyncMemory.from_config(config) +--- -# 用客户端实例化工具 -search_memory_tool = SearchMemoryTool(client=memory) -save_memory_tool = SaveMemoryTool(client=memory) +##### MempalaceAddDrawerTool 介绍 + +向指定 `wing/room` 写入一条原文 drawer,适合保存用户明确要求记住的长期事实。 + +**Agent 工具参数(LLM 可调用):** +- `wing`(string,必填):存储作用域,例如 `personal_assistant_alice` +- `room`(string,必填):记忆主题,例如 `user_profile` +- `content`(string,必填):要保存的原文内容 +- `source_file`(string,可选):来源标识 + +**返回值示例:** +```python +{ + "success": True, + "drawer_id": "drawer_personal_assistant_alice_user_profile_xxx", + "wing": "personal_assistant_alice", + "room": "user_profile", +} ``` -**详细配置:** 参见 [完整示例 - 自托管模式](../../../examples/memory_service_with_mem0/README.md#自托管模式asyncmemory--qdrant) +--- -#### 模式二:平台(AsyncMemoryClient) +##### Diary 工具介绍 -适合快速部署和生产环境使用。 +`MempalaceDiaryWriteTool` 和 `MempalaceDiaryReadTool` 用于记录和读取 agent diary。它们适合保存“本次任务做了什么、观察到什么、后续要注意什么”这类运行记录,不建议替代用户画像记忆。 -**前置条件:** -- 注册 [Mem0 平台账号](https://app.mem0.ai/dashboard) -- 获取 API Key +| 工具 | 关键参数 | 返回重点 | +|---|---|---| +| `mempalace_diary_write` | `entry`、`agent_name`、`topic`、`wing` | `success`、`entry_id`、`agent`、`topic` | +| `mempalace_diary_read` | `agent_name`、`last_n`、`wing` | `entries`、`total`、`showing` | + +示例输出中,写入 `Alice tested the MemPalace tools example today.` 后,后续新的 session 仍能读取到这条 diary,说明 diary 数据也已经持久化。 + +--- + +##### KG 工具介绍 + +KG 工具用于维护结构化事实。这里的“事实”通常是一个三元组: + +```text +subject -> predicate -> object +Alice -> likes -> Italian food +``` + +| 工具 | 关键参数 | 语义 | +|---|---|---| +| `mempalace_kg_add` | `subject`、`predicate`、`object`、`valid_from`、`valid_to`、`confidence` | 写入一条结构化事实 | +| `mempalace_kg_query` | `entity`、`as_of`、`direction` | 查询实体相关事实 | +| `mempalace_kg_timeline` | `entity` | 查看实体事实时间线 | +| `mempalace_kg_invalidate` | `subject`、`predicate`、`object`、`ended` | 将事实标记为失效 | + +`mempalace_kg_invalidate` 不会直接删除历史事实,而是设置 `valid_to` 并让 `current=False`。因此示例中把 invalidate 放在第二阶段持久化读取验证之后执行,避免提前改变第二阶段查询结果。 + +##### 使用建议 + +- 如果只是需要标准的跨 session 长期记忆,优先使用 `MempalaceMemoryService`。 +- 如果希望 Agent 主动控制写入内容、写入分类、diary 或 KG,再使用 `mempalace_tool`。 +- 对用户画像、偏好等长期信息,建议写入稳定的 `wing/room`,例如 `personal_assistant_alice/user_profile`。 +- 对 KG 事实变化,优先用 `mempalace_kg_invalidate` 表达“已不再成立”,而不是直接删除历史。 +- 不建议让 Agent 把 `load_memory` 的工具返回、代码执行结果等中间过程直接写入 drawer,否则容易污染长期记忆。 + +--- + +#### Mempalace 资料 + +| 资源 | 路径 | 说明 | +|---|---|---| +| `MempalaceMemoryService` 完整示例 | [examples/memory_service_with_mempalace/](../../../examples/memory_service_with_mempalace/README.md) | 含安装、路径配置、CLI 查询和运行结果分析 | +| `MempalaceMemoryService` 源码 | [mempalace_memory_service.py](../../../trpc_agent_sdk/memory/mempalace_memory_service.py) | 推荐方式,框架级记忆服务实现 | +| Mempalace 工具源码 | [mempalace_tool.py](../../../trpc_agent_sdk/tools/mempalace_tool.py) | 可选方式,`mempalace_search` / `mempalace_add_drawer` / diary / KG 等工具 | + +--- + +### 集成 Mem0 + +#### 什么是 Mem0? + +Mem0 是为 LLM 提供的智能、自我改进的记忆层,能够跨对话持久化和检索用户信息,实现更加个性化和连贯一致的用户体验。 + +**核心能力:** +- 🧠 智能记忆提取和存储 +- 🔍 语义搜索历史对话 +- 🔄 自动记忆更新和去重 +- 🎯 用户级别的记忆隔离 + +**官方资源:** +- 官方文档:[https://docs.mem0.ai/introduction](https://docs.mem0.ai/introduction) +- GitHub:[https://github.com/mem0ai/mem0](https://github.com/mem0ai/mem0) + +--- + +#### tRPC-Agent 集成方式 + +tRPC-Agent 提供两种集成 Mem0 的方式: + +| 方式 | 类 / 工具 | 适用场景 | +|---|---|---| +| **框架级记忆服务**(推荐) | `Mem0MemoryService` | 由框架自动完成跨会话记忆的存储与检索,Agent 无感知 | +| **工具式记忆** | `SearchMemoryTool` / `SaveMemoryTool` | Agent 通过工具主动调用 Mem0,灵活控制存取时机 | + +--- + +#### Mem0MemoryService(推荐方式) + +`Mem0MemoryService` 是 tRPC-Agent 的**框架级记忆服务**,由框架在每轮对话结束后自动调用 `store_session` 存储会话记忆,Agent 在响应时通过 `load_memory` 工具主动检索相关记忆,无需手动管理存取时机。 + +##### 核心设计 + +- **两级 Key 策略**:`session.save_key` → Mem0 `user_id`(用户维度);`session.id` → `run_id`(会话维度) +- **跨会话共享**:同一用户的不同 session 共享同一份记忆 +- **TTL 自动过期**:后台定期清理超时记忆 + +##### 快速接入 + +**步骤 1:创建 `Mem0MemoryService`** -**示例代码:** ```python -from mem0 import AsyncMemoryClient -from trpc_agent_sdk.server.tools.mem0_tool import SearchMemoryTool, SaveMemoryTool +from mem0 import AsyncMemory, AsyncMemoryClient +from trpc_agent_sdk.memory import MemoryServiceConfig +from trpc_agent_sdk.memory.mem0_memory_service import Mem0MemoryService -# 创建平台客户端 -client = AsyncMemoryClient( - api_key="m0-your-api-key", - host="https://api.mem0.ai" +# 自托管模式(AsyncMemory + Qdrant) +from mem0.configs.base import MemoryConfig +mem0_client = AsyncMemory(config=MemoryConfig(**{ + "vector_store": {"provider": "qdrant", "config": {"host": "localhost", "port": 6333}}, # 向量数据库声明 + "llm": {"provider": "deepseek", "config": {"model": "...", "api_key": "..."}}, # 用于记忆摘要提炼(infer=True 时使用) + "embedder": {"provider": "huggingface", "config": {"model": "multi-qa-MiniLM-L6-cos-v1"}}, # 开源嵌入模型 +})) + +# 或者:远端平台模式(AsyncMemoryClient),无需自建基础设施 +mem0_client = AsyncMemoryClient(api_key="your_mem0_api_key", host="https://api.mem0.ai") + +memory_service = Mem0MemoryService( + mem0_client=mem0_client, + memory_service_config=MemoryServiceConfig( + enabled=True, + ttl=MemoryServiceConfig.create_ttl_config(enable=False), # 不启用 TTL,记忆永久保留 + ), + infer=False, # False=原文存储(稳定),True=语义抽取(智能) ) +``` -# 用客户端实例化工具 -search_memory_tool = SearchMemoryTool(client=client) -save_memory_tool = SaveMemoryTool(client=client) +**步骤 2:将 `memory_service` 传入 `Runner`** + +```python +from trpc_agent_sdk.runners import Runner +from trpc_agent_sdk.tools import load_memory_tool + +agent = LlmAgent( + name="assistant", + model=your_model, + tools=[load_memory_tool], # Agent 通过此工具主动检索记忆 + instruction="Use load_memory to recall relevant past conversations before answering.", +) + +runner = Runner( + app_name="my_app", + agent=agent, + session_service=InMemorySessionService(), + memory_service=memory_service, # 框架自动负责存储 +) ``` -**详细配置:** 参见 [完整示例 - 平台模式](../../../examples/memory_service_with_mem0/README.md#远端平台模式asyncmemoryclient) +**步骤 3:运行,记忆自动跨会话持久化** + +```python +# 第一轮对话(session_1) +async for event in runner.run_async(user_id="alice", session_id="session_1", new_message=...): + ... +# 框架在对话结束后自动调用 store_session,将本轮消息存入 Mem0 + +# 第二轮对话(session_2)——新会话,但能检索到 session_1 的记忆 +async for event in runner.run_async(user_id="alice", session_id="session_2", new_message=...): + ... +``` + +**完整可运行示例:** [examples/memory_service_with_mem0/run_agent.py](../../../examples/memory_service_with_mem0/run_agent.py) + +##### `infer` 参数选择 + +| | `infer=False`(推荐) | `infer=True` | +|---|---|---| +| 存储内容 | 对话原文 | LLM 提炼后的语义事实 | +| 稳定性 | 高,每条必存 | 中,LLM 判断 NONE 时不存 | +| token 消耗 | 低(无 LLM 调用) | 高(每次写入调用 LLM) | +| 冲突消解 | 不做 | 自动(新事实覆盖旧事实) | +| 推荐场景 | 完整历史归档、生产环境 | 长期用户画像、偏好提炼 | + +##### TTL 配置(可选) + +```python +memory_service_config = MemoryServiceConfig( + enabled=True, + ttl=MemoryServiceConfig.create_ttl_config( + enable=True, + ttl_seconds=86400, # 记忆保留 24 小时 + cleanup_interval_seconds=3600, # 每小时清理一次 + ), +) +``` + +> 详细说明、运行结果分析和常见问题解答:[examples/memory_service_with_mem0/README.md](../../../examples/memory_service_with_mem0/README.md) --- -### Mem0 快速开始 +#### 工具式集成(mem0_tool) -#### 1. 安装依赖 +tRPC-Agent 通过 **工具(Tools)** 的方式集成 Mem0,为 Agent 提供记忆能力。框架提供了两个核心工具类: -```bash -# 安装 Mem0 核心包 -pip install mem0ai +| 工具类 | 工具名 | 功能 | 使用场景 | +|--------|--------|------|---------| +| `SearchMemoryTool` | `search_memory` | 搜索历史记忆 | Agent 需要回忆过去的对话内容 | +| `SaveMemoryTool` | `save_memory` | 保存重要信息 | Agent 判断需要记住的用户信息 | -# 自托管模式额外依赖 -pip install sentence-transformers qdrant-client +> **注意**:两个工具类需要在实例化时传入 Mem0 客户端,`user_id` 由框架通过 `InvocationContext` 自动注入,无需在工具参数中显式传递。 + +##### 集成架构 -# 或使用 trpc-agent 扩展安装 -pip install -e ".[mem0]" +``` +┌──────────────────────┐ +│ User Input │ +└──────────┬───────────┘ + │ + ▼ +┌──────────────────────┐ +│ tRPC-Agent │◄─────────┐ +│ LlmAgent │ │ +└──────────┬───────────┘ │ + │ │ + │ 调用工具 │ 返回记忆 + │ │ + ▼ │ +┌──────────────────────┐ │ +│ Mem0 Tools │──────────┘ +│ - SearchMemoryTool │ +│ - SaveMemoryTool │ +└──────────┬───────────┘ + │ + ▼ +┌──────────────────────┐ +│ Mem0 Client │ +│ (AsyncMemory / │ +│ AsyncMemoryClient) │ +└──────────┬───────────┘ + │ + ▼ +┌──────────────────────┐ +│ Storage │ +│ - Qdrant │ +│ - Mem0 Cloud │ +└──────────────────────┘ ``` -#### 2. 创建 Agent +--- + +##### 快速接入 ```python from trpc_agent_sdk.agents import LlmAgent @@ -905,34 +1304,46 @@ agent = LlmAgent( ) ``` -#### 3. 运行 Agent +--- -```python -from trpc_agent_sdk.runners import Runner +##### 典型工作流 -runner = Runner( - app_name="memory_app", - agent=agent, - session_service=your_session_service -) +- 场景:个人助理记住用户偏好 -# 与 Agent 交互,自动使用记忆功能 -async for event in runner.run_async( - user_id="alice", - session_id="session_1", - new_message=user_input -): - # 处理响应 - pass ``` +1. 用户:Do you remember my name? + ↓ + Agent 调用: search_memory(query="user's name") + 框架自动注入 user_id="alice" + ↓ + 结果:no_memories + ↓ + Agent:I don't have your name. Could you tell me? -**完整可运行示例:** [examples/memory_service_with_mem0/run_agent.py](../../../examples/memory_service_with_mem0/run_agent.py) +2. 用户:My name is Alice + ↓ + Agent 调用: save_memory(content="User's name is Alice") + 框架自动注入 user_id="alice" + ↓ + 结果:success + ↓ + Agent:Thank you, Alice! I'll remember that. ---- +3. 用户:Do you remember my name? + ↓ + Agent 调用: search_memory(query="user's name") + 框架自动注入 user_id="alice" + ↓ + 结果:success, memories="- Name is Alice" + ↓ + Agent:Yes, your name is Alice! +``` + +**查看完整演示输出(Mem0MemoryService):** [运行结果分析](../../../examples/memory_service_with_mem0/README.md#运行结果分析) -### 工具 API +--- -#### SearchMemoryTool +##### SearchMemoryTool 介绍 搜索用户的历史记忆。 @@ -967,7 +1378,9 @@ SearchMemoryTool( } ``` -#### SaveMemoryTool +--- + +##### SaveMemoryTool 介绍 保存重要信息到用户记忆。 @@ -1011,46 +1424,113 @@ SaveMemoryTool( --- -### 典型工作流(工具式) -#### 场景:个人助理记住用户偏好 +#### 部署模式 + +tRPC-Agent 支持 Mem0 的两种部署模式:自托管模式和平台模式 + +##### 模式对比 + +| 特性 | 自托管模式 | 平台模式 | +|------|-----------|---------| +| **客户端类型** | `AsyncMemory` | `AsyncMemoryClient` | +| **存储位置** | 本地向量数据库(如 Qdrant) | Mem0 云端 | +| **依赖组件** | 向量数据库 + 嵌入模型 + LLM | 仅需 API Key | +| **数据控制** | 完全控制 | 托管服务 | +| **适用场景** | 开发测试、数据敏感、本地部署 | 生产环境、快速部署 | + +##### 模式一:自托管(AsyncMemory) + +适合需要完全控制数据和基础设施的场景。 + +**核心组件:** +- **向量存储**:支持多种后端(见下方完整清单) +- **LLM**:用于生成记忆摘要(OpenAI / DeepSeek / Gemini 等) +- **嵌入模型**:用于向量化(HuggingFace / OpenAI 等) +**自托管支持的向量存储(完整清单):** +- `azure_ai_search` +- `azure_mysql` +- `baidu` +- `cassandra` +- `chroma` +- `databricks` +- `elasticsearch` +- `faiss` +- `langchain` +- `milvus` +- `mongodb` +- `neptune_analytics` +- `opensearch` +- `pgvector` +- `pinecone` +- `qdrant` +- `redis` +- `s3_vectors` +- `supabase` +- `turbopuffer` +- `upstash_vector` +- `valkey` +- `vertex_ai_vector_search` +- `weaviate` + +> 官方向量存储实现列表(以 mem0 仓库为准):[mem0/vector_stores](https://github.com/mem0ai/mem0/tree/main/mem0/vector_stores) + +**示例代码:** +```python +from mem0 import AsyncMemory +from trpc_agent_sdk.server.tools.mem0_tool import SearchMemoryTool, SaveMemoryTool + +# 配置自定义组件 +config = { + "vector_store": {"provider": "qdrant", "config": {...}}, + "llm": {"provider": "deepseek", "config": {...}}, + "embedder": {"provider": "huggingface", "config": {...}} +} + +# 创建 Mem0 客户端 +memory = await AsyncMemory.from_config(config) + +# 用客户端实例化工具 +search_memory_tool = SearchMemoryTool(client=memory) +save_memory_tool = SaveMemoryTool(client=memory) ``` -1. 用户:Do you remember my name? - ↓ - Agent 调用: search_memory(query="user's name") - 框架自动注入 user_id="alice" - ↓ - 结果:no_memories - ↓ - Agent:I don't have your name. Could you tell me? -2. 用户:My name is Alice - ↓ - Agent 调用: save_memory(content="User's name is Alice") - 框架自动注入 user_id="alice" - ↓ - 结果:success - ↓ - Agent:Thank you, Alice! I'll remember that. +**详细配置:** 参见 [完整示例 - 自托管模式](../../../examples/memory_service_with_mem0/README.md#自托管模式asyncmemory--qdrant) -3. 用户:Do you remember my name? - ↓ - Agent 调用: search_memory(query="user's name") - 框架自动注入 user_id="alice" - ↓ - 结果:success, memories="- Name is Alice" - ↓ - Agent:Yes, your name is Alice! +##### 模式二:平台(AsyncMemoryClient) + +适合快速部署和生产环境使用。 + +**前置条件:** +- 注册 [Mem0 平台账号](https://app.mem0.ai/dashboard) +- 获取 API Key + +**示例代码:** +```python +from mem0 import AsyncMemoryClient +from trpc_agent_sdk.server.tools.mem0_tool import SearchMemoryTool, SaveMemoryTool + +# 创建平台客户端 +client = AsyncMemoryClient( + api_key="m0-your-api-key", + host="https://api.mem0.ai" +) + +# 用客户端实例化工具 +search_memory_tool = SearchMemoryTool(client=client) +save_memory_tool = SaveMemoryTool(client=client) ``` -**查看完整演示输出(Mem0MemoryService):** [运行结果分析](../../../examples/memory_service_with_mem0/README.md#运行结果分析) +**详细配置:** 参见 [完整示例 - 平台模式](../../../examples/memory_service_with_mem0/README.md#远端平台模式asyncmemoryclient) --- -### 高级特性 -#### 多用户记忆隔离 + +#### 高级特性 + +##### 多用户记忆隔离 通过 `user_id` 参数实现用户级别的记忆隔离: @@ -1062,7 +1542,7 @@ await runner.run_async(user_id="user_a", ...) await runner.run_async(user_id="user_b", ...) ``` -#### 记忆过滤和搜索 +##### 记忆过滤和搜索 通过 `filters` 参数可以对记忆进行精细化检索,支持按用户、类别等维度过滤,避免跨用户或无关记忆的干扰: @@ -1077,7 +1557,7 @@ memories = await mem0_client.search( ) ``` -#### 直接记忆管理 +##### 直接记忆管理 除了通过 Agent 工具间接操作外,也可以直接调用 Mem0 客户端 API 对记忆进行增删查管理: @@ -1092,13 +1572,13 @@ await memory.delete(memory_id="memory-id") await memory.delete_all(user_id="alice") ``` -**更多高级用法:** [高级用法文档](../../../examples/mem_0/README.md#高级用法) +**更多高级用法:** [高级用法文档](../../../examples/mem0_tools/README.md#高级用法) --- -### Mem0 常见问题 +#### Mem0 常见问题 -#### 如何选择部署模式? +##### 如何选择部署模式? | 考虑因素 | 自托管 | 平台 | |---------|-------|------| @@ -1108,7 +1588,7 @@ await memory.delete_all(user_id="alice") | 生产环境高可用 | ❌ | ✅ | | 成本敏感(小规模) | ✅ | ❌ | -#### 自托管模式常见错误 +##### 自托管模式常见错误 **向量维度不匹配:** ``` @@ -1127,19 +1607,19 @@ ConnectionError: Cannot connect to Qdrant at localhost:6333 --- -### Mem0 参考资料 +#### Mem0 参考资料 -#### 框架资源 +##### 框架资源 | 资源 | 路径 | 说明 | |---|---|---| | `Mem0MemoryService` 完整示例 | [examples/memory_service_with_mem0/](../../../examples/memory_service_with_mem0/README.md) | 含运行结果分析、QA | | `Mem0MemoryService` 源码 | [mem0_memory_service.py](../../../trpc_agent_sdk/memory/mem0_memory_service.py) | 服务实现 | -| 工具式集成源码 | [mem0_tool.py](../../../trpc_agent_sdk/tools/mem0_tool.py) | `SearchMemoryTool` / `SaveMemoryTool` 工具类 | +| 工具式集成源码 | [mem0_tools.py](../../../trpc_agent_sdk/tools/mem0_tools.py) | `SearchMemoryTool` / `SaveMemoryTool` 工具类 | | infer 参数详解 | [README.md#infer-参数详解](../../../examples/memory_service_with_mem0/README.md#infer-参数详解) | True vs False 对比 | | 常见问题 QA | [README.md#常见问题-qa](../../../examples/memory_service_with_mem0/README.md#常见问题-qa) | 错误分析与解答 | -#### Mem0 官方资源 +##### Mem0 官方资源 - **官方文档:** [https://docs.mem0.ai/introduction](https://docs.mem0.ai/introduction) - **GitHub:** [https://github.com/mem0ai/mem0](https://github.com/mem0ai/mem0) - **示例代码:** [https://github.com/mem0ai/mem0/tree/main/examples](https://github.com/mem0ai/mem0/tree/main/examples) @@ -1147,7 +1627,7 @@ ConnectionError: Cannot connect to Qdrant at localhost:6333 --- -### 下一步 +##### 其他资料 1. **快速上手(推荐):** 查看 [Mem0MemoryService 完整示例](../../../examples/memory_service_with_mem0/) 并运行 `run_agent.py` 2. **选择部署模式:** 参考 [自托管 vs 远端平台对比](../../../examples/memory_service_with_mem0/README.md#两种部署模式详解) diff --git a/examples/dsl/classifier_mcp/.env b/examples/dsl/classifier_mcp/.env index 23a7cf9..12626e6 100644 --- a/examples/dsl/classifier_mcp/.env +++ b/examples/dsl/classifier_mcp/.env @@ -1,3 +1,8 @@ +# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name + # Generated environment variables for this workflow. # Update the values as needed. MODEL1_NAME="your-model-name-1" diff --git a/examples/llmagent/.env b/examples/llmagent/.env index d47b675..0a57a17 100644 --- a/examples/llmagent/.env +++ b/examples/llmagent/.env @@ -1 +1,5 @@ # Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name + diff --git a/examples/llmagent_with_branch_filtering/.env b/examples/llmagent_with_branch_filtering/.env index d47b675..dc79139 100644 --- a/examples/llmagent_with_branch_filtering/.env +++ b/examples/llmagent_with_branch_filtering/.env @@ -1 +1,4 @@ # Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/llmagent_with_cancel/.env b/examples/llmagent_with_cancel/.env index d47b675..dc79139 100644 --- a/examples/llmagent_with_cancel/.env +++ b/examples/llmagent_with_cancel/.env @@ -1 +1,4 @@ # Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/llmagent_with_custom_agent/.env b/examples/llmagent_with_custom_agent/.env index d47b675..dc79139 100644 --- a/examples/llmagent_with_custom_agent/.env +++ b/examples/llmagent_with_custom_agent/.env @@ -1 +1,4 @@ # Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/llmagent_with_custom_prompt/.env b/examples/llmagent_with_custom_prompt/.env index d47b675..dc79139 100644 --- a/examples/llmagent_with_custom_prompt/.env +++ b/examples/llmagent_with_custom_prompt/.env @@ -1 +1,4 @@ # Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/llmagent_with_human_in_the_loop/.env b/examples/llmagent_with_human_in_the_loop/.env index d47b675..dc79139 100644 --- a/examples/llmagent_with_human_in_the_loop/.env +++ b/examples/llmagent_with_human_in_the_loop/.env @@ -1 +1,4 @@ # Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/llmagent_with_max_history_messages/.env b/examples/llmagent_with_max_history_messages/.env index d47b675..dc79139 100644 --- a/examples/llmagent_with_max_history_messages/.env +++ b/examples/llmagent_with_max_history_messages/.env @@ -1 +1,4 @@ # Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/llmagent_with_model_create_fn/.env b/examples/llmagent_with_model_create_fn/.env index d47b675..dc79139 100644 --- a/examples/llmagent_with_model_create_fn/.env +++ b/examples/llmagent_with_model_create_fn/.env @@ -1 +1,4 @@ # Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/llmagent_with_parallal_tools/.env b/examples/llmagent_with_parallal_tools/.env index d47b675..dc79139 100644 --- a/examples/llmagent_with_parallal_tools/.env +++ b/examples/llmagent_with_parallal_tools/.env @@ -1 +1,4 @@ # Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/llmagent_with_schema/.env b/examples/llmagent_with_schema/.env index 65bab9c..0a57a17 100644 --- a/examples/llmagent_with_schema/.env +++ b/examples/llmagent_with_schema/.env @@ -1,2 +1,5 @@ # Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/llmagent_with_streaming_tool_complex/.env b/examples/llmagent_with_streaming_tool_complex/.env index 8b13789..0a57a17 100644 --- a/examples/llmagent_with_streaming_tool_complex/.env +++ b/examples/llmagent_with_streaming_tool_complex/.env @@ -1 +1,5 @@ +# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/llmagent_with_streaming_tool_simple/.env b/examples/llmagent_with_streaming_tool_simple/.env index 8b13789..0a57a17 100644 --- a/examples/llmagent_with_streaming_tool_simple/.env +++ b/examples/llmagent_with_streaming_tool_simple/.env @@ -1 +1,5 @@ +# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/llmagent_with_thinking/.env b/examples/llmagent_with_thinking/.env index 8b13789..0a57a17 100644 --- a/examples/llmagent_with_thinking/.env +++ b/examples/llmagent_with_thinking/.env @@ -1 +1,5 @@ +# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/llmagent_with_timeline_filtering/.env b/examples/llmagent_with_timeline_filtering/.env index 8b13789..0a57a17 100644 --- a/examples/llmagent_with_timeline_filtering/.env +++ b/examples/llmagent_with_timeline_filtering/.env @@ -1 +1,5 @@ +# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/llmagent_with_tool_prompt/.env b/examples/llmagent_with_tool_prompt/.env index 8b13789..0a57a17 100644 --- a/examples/llmagent_with_tool_prompt/.env +++ b/examples/llmagent_with_tool_prompt/.env @@ -1 +1,5 @@ +# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/llmagent_with_user_history/.env b/examples/llmagent_with_user_history/.env index 8b13789..0a57a17 100644 --- a/examples/llmagent_with_user_history/.env +++ b/examples/llmagent_with_user_history/.env @@ -1 +1,5 @@ +# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/mcp_tools/.env b/examples/mcp_tools/.env index 8b13789..0a57a17 100644 --- a/examples/mcp_tools/.env +++ b/examples/mcp_tools/.env @@ -1 +1,5 @@ +# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/mem0_tools/.env b/examples/mem0_tools/.env new file mode 100644 index 0000000..dc79139 --- /dev/null +++ b/examples/mem0_tools/.env @@ -0,0 +1,4 @@ +# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/mem_0/README.md b/examples/mem0_tools/README.md similarity index 97% rename from examples/mem_0/README.md rename to examples/mem0_tools/README.md index afac9e4..8eef052 100644 --- a/examples/mem_0/README.md +++ b/examples/mem0_tools/README.md @@ -25,10 +25,10 @@ personal_assistant (LlmAgent) 关键文件: -- [examples/mem_0/agent/agent.py](./agent/agent.py) -- [examples/mem_0/agent/config.py](./agent/config.py) -- [examples/mem_0/run_agent.py](./run_agent.py) -- `trpc_agent_sdk/tools/mem0_tool.py` +- [examples/mem0_tools/agent/agent.py](./agent/agent.py) +- [examples/mem0_tools/agent/config.py](./agent/config.py) +- [examples/mem0_tools/run_agent.py](./run_agent.py) +- `trpc_agent_sdk/tools/mem0_tools.py` ## 关键代码解释 @@ -79,7 +79,7 @@ pip3 install sentence-transformers qdrant-client ### 环境变量要求 -在 [examples/mem_0/.env](./.env) 中配置(或通过 `export`): +在 [examples/mem0_tools/.env](./.env) 中配置(或通过 `export`): - `TRPC_AGENT_API_KEY` - `TRPC_AGENT_BASE_URL` @@ -91,7 +91,7 @@ pip3 install sentence-transformers qdrant-client ### 运行命令 ```bash -cd examples/mem_0 +cd examples/mem0_tools python3 run_agent.py ``` @@ -369,4 +369,4 @@ AsyncMemoryClient 平台客户端参数 - [Mem0 Docs](https://docs.mem0.ai/introduction) - [Mem0 Examples](https://github.com/mem0ai/mem0/tree/main/examples) -- [tRPC-Agent Mem0 Tool](../../trpc_agent_ecosystem/tools/mem0_tool.py) +- [tRPC-Agent Mem0 Tool](../../trpc_agent_ecosystem/tools/mem0_tools.py) diff --git a/examples/mem_0/agent/__init__.py b/examples/mem0_tools/agent/__init__.py similarity index 100% rename from examples/mem_0/agent/__init__.py rename to examples/mem0_tools/agent/__init__.py diff --git a/examples/mem_0/agent/agent.py b/examples/mem0_tools/agent/agent.py similarity index 93% rename from examples/mem_0/agent/agent.py rename to examples/mem0_tools/agent/agent.py index 3457f44..af69d34 100644 --- a/examples/mem_0/agent/agent.py +++ b/examples/mem0_tools/agent/agent.py @@ -10,8 +10,8 @@ from trpc_agent_sdk.agents import LlmAgent from trpc_agent_sdk.models import LLMModel from trpc_agent_sdk.models import OpenAIModel -from trpc_agent_sdk.tools.mem0_tool import SaveMemoryTool -from trpc_agent_sdk.tools.mem0_tool import SearchMemoryTool +from trpc_agent_sdk.tools.mem0_tools import SaveMemoryTool +from trpc_agent_sdk.tools.mem0_tools import SearchMemoryTool from .config import get_mem0_platform_config from .config import get_memory_config diff --git a/examples/mem_0/agent/config.py b/examples/mem0_tools/agent/config.py similarity index 100% rename from examples/mem_0/agent/config.py rename to examples/mem0_tools/agent/config.py diff --git a/examples/mem_0/agent/prompts.py b/examples/mem0_tools/agent/prompts.py similarity index 100% rename from examples/mem_0/agent/prompts.py rename to examples/mem0_tools/agent/prompts.py diff --git a/examples/mem_0/agent/tools.py b/examples/mem0_tools/agent/tools.py similarity index 100% rename from examples/mem_0/agent/tools.py rename to examples/mem0_tools/agent/tools.py diff --git a/examples/mem_0/images/mem0_ai.png b/examples/mem0_tools/images/mem0_ai.png similarity index 100% rename from examples/mem_0/images/mem0_ai.png rename to examples/mem0_tools/images/mem0_ai.png diff --git a/examples/mem_0/images/mem0_plat.png b/examples/mem0_tools/images/mem0_plat.png similarity index 100% rename from examples/mem_0/images/mem0_plat.png rename to examples/mem0_tools/images/mem0_plat.png diff --git a/examples/mem_0/images/mem0_result.png b/examples/mem0_tools/images/mem0_result.png similarity index 100% rename from examples/mem_0/images/mem0_result.png rename to examples/mem0_tools/images/mem0_result.png diff --git a/examples/mem_0/images/qdrant_dashboard.png b/examples/mem0_tools/images/qdrant_dashboard.png similarity index 100% rename from examples/mem_0/images/qdrant_dashboard.png rename to examples/mem0_tools/images/qdrant_dashboard.png diff --git a/examples/mem_0/images/qdrant_mem.png b/examples/mem0_tools/images/qdrant_mem.png similarity index 100% rename from examples/mem_0/images/qdrant_mem.png rename to examples/mem0_tools/images/qdrant_mem.png diff --git a/examples/mem_0/run_agent.py b/examples/mem0_tools/run_agent.py similarity index 96% rename from examples/mem_0/run_agent.py rename to examples/mem0_tools/run_agent.py index 49b554f..afe9b15 100644 --- a/examples/mem_0/run_agent.py +++ b/examples/mem0_tools/run_agent.py @@ -17,7 +17,7 @@ load_dotenv() -async def run_mem_zero_agent(): +async def run_mem0_agent(): """Run the mem zero agent demo""" app_name = "memory_assistant" @@ -73,12 +73,12 @@ async def run_mem_zero_agent(): async def main(): - await run_mem_zero_agent() + await run_mem0_agent() # Sleep for 10 seconds, wait for user input print("Press Enter to continue...") await asyncio.sleep(10) print("Sleeping for 10 seconds...") - await run_mem_zero_agent() + await run_mem0_agent() if __name__ == "__main__": diff --git a/examples/mem_0/.env b/examples/mem_0/.env deleted file mode 100644 index 8b13789..0000000 --- a/examples/mem_0/.env +++ /dev/null @@ -1 +0,0 @@ - diff --git a/examples/memory_service_with_in_memory/.env b/examples/memory_service_with_in_memory/.env index 8b13789..0a57a17 100644 --- a/examples/memory_service_with_in_memory/.env +++ b/examples/memory_service_with_in_memory/.env @@ -1 +1,5 @@ +# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/memory_service_with_mem0/.env b/examples/memory_service_with_mem0/.env index 8b13789..dc79139 100644 --- a/examples/memory_service_with_mem0/.env +++ b/examples/memory_service_with_mem0/.env @@ -1 +1,4 @@ - +# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/memory_service_with_mempalace/.env b/examples/memory_service_with_mempalace/.env new file mode 100644 index 0000000..7abfc4d --- /dev/null +++ b/examples/memory_service_with_mempalace/.env @@ -0,0 +1,9 @@ +# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name + + +# If you want to use the default MemPalace path, you can comment out the following two lines. +#MEMPALACE_PALACE_PATH=~/.mempalace +#MEMPALACE_KG_PATH=~/.mempalace/knowledge_graph.sqlite3 diff --git a/examples/memory_service_with_mempalace/README.md b/examples/memory_service_with_mempalace/README.md new file mode 100644 index 0000000..1318fec --- /dev/null +++ b/examples/memory_service_with_mempalace/README.md @@ -0,0 +1,253 @@ +# MemPalace Memory Service 使用指南 + +本示例演示如何在 `trpc-agent` 中使用 `MempalaceMemoryService` 实现跨 session 的长期记忆存储与检索。 + +MemPalace 是一个本地优先的记忆系统,底层使用 ChromaDB 存储 drawer 文本、metadata 和向量索引。当前示例会把会话中的可见文本事件写入 MemPalace,并通过 `load_memory` 工具在后续 session 中检索出来。 + +## 关键特性 + +- 使用 `MempalaceMemoryService` 接入本地 MemPalace。 +- 通过 `wing` 和 `room` 组织记忆。 +- 默认按 `save_key = {app}/{user}` 维度实现跨 session 检索。 +- 支持配置 MemPalace 存储路径。 +- 支持 TTL 后台定时清理过期 drawer。 +- 示例输出中会截断过长工具结果,避免 memory JSON 刷屏。 + +## 安装依赖 + +使用前需要安装本项目依赖和 MemPalace 可选依赖。 + +在项目根目录执行: + +```bash +git clone https://github.com/trpc-group/trpc-agent-python.git +cd trpc-agent-python +python3 -m venv .venv +source .venv/bin/activate +pip install -e ".[mempalace]" +``` + +如果你使用虚拟环境,请确保运行示例和执行 `mempalace search` 时使用的是同一个环境。 + +## 运行示例 + +在项目根目录执行: + +```bash +cd examples/memory_service_with_mempalace +python3 run_agent.py +``` + +示例会连续运行三轮,每轮包含多个不同 session: + +- 先询问是否记得姓名和喜欢的颜色。 +- 再告诉 agent:姓名是 Alice,喜欢的颜色是 blue。 +- 后续 session 再次询问时,agent 会通过 `load_memory` 检索长期记忆。 + +## 关键代码 + +`run_agent.py` 中创建 Memory Service: + +```python +memory_service_config = MemoryServiceConfig( + ttl=MemoryServiceConfig.create_ttl_config( + enable=True, + ttl_seconds=20, + cleanup_interval_seconds=20, + ), + enabled=True, +) + +memory_service = MempalaceMemoryService( + memory_service_config=memory_service_config, + wing="trpc-agent", + room="conversations", + store_only_model_visible=True, +) +``` + +这里的含义是: + +- `wing="trpc-agent"`:把示例记忆固定写入 `trpc-agent` 这个 wing。 +- `room="conversations"`:把普通对话记忆写入 `conversations` room。 +- `store_only_model_visible=True`:只存模型可见的事件。 +- `ttl_seconds=20`:超过 20 秒的记忆会被后台 cleanup 删除。 +- `cleanup_interval_seconds=20`:每 20 秒执行一次清理。 + +## MemPalace 层级映射 + +MemPalace 的主要存储层级是: + +```text +Palace + └── Wing + └── Room + └── Drawer +``` + +在当前示例中: + +```text +Palace = MempalaceConfig.palace_path +Wing = trpc-agent +Room = conversations +Drawer = 单条 Event 文本 +``` + +如果没有显式传入 `wing`,`MempalaceMemoryService` 会默认用 `session.save_key` 解析 wing。框架里的 `save_key` 通常是: + +```text +{app}/{user} +``` + +这样可以做到同一个 app/user 下跨 session 查询记忆。 + +## 指定存储路径 + +MemPalace 的默认存储路径来自 `MempalaceConfig().palace_path`,通常是: + +```text +~/.mempalace/palace +``` + +如果要指定路径,可以使用 MemPalace 自己支持的配置方式,例如环境变量: + +```bash +export MEMPALACE_PALACE_PATH=/path/to/palace +``` + +也可以使用 MemPalace 配置文件 `~/.mempalace/config.json` 指定: + +```json +{ + "palace_path": "/path/to/palace", + "collection_name": "mempalace_drawers" +} +``` + +注意:`/path/to/palace` 指的是 MemPalace 数据目录,也就是包含 `chroma.sqlite3` 的目录,不是某个单独文件。 + +## 使用 CLI 查询指定路径 + +如果代码里指定或配置了自定义 palace 路径,使用 `mempalace search` 查询时也必须指定同一个路径: + +```bash +mempalace --palace /path/to/palace search "user name" +``` + +如果还要限制到当前示例的 wing: + +```bash +mempalace --palace /path/to/palace search "user name" \ + --wing trpc-agent +``` + +如果还要限制到 room: + +```bash +mempalace --palace /path/to/palace search "user name" \ + --wing trpc-agent \ + --room conversations +``` + +如果没有指定自定义路径,也可以直接查询默认 palace: + +```bash +mempalace search "user name" --wing trpc-agent --room conversations +``` + +## 删除记忆 + +MemPalace CLI 当前没有直接提供按 `wing` 或 `wing + room` 删除的命令。框架里已经在 `MempalaceMemoryService` 提供了删除方法: + +```python +await memory_service.delete_memory(wing="trpc-agent") +await memory_service.delete_memory(wing="trpc-agent", room="conversations") +``` + +如果需要用命令行删除,可以写一个小脚本直接调用 MemPalace collection 的 `delete(where=...)`。 + +## 运行结果分析 + +### 1. 首次查询没有记忆 + +第一次运行开始时: + +```text +load_memory({'query': 'user name'}) +Tool Result: {"memories": []} +``` + +说明开始时 MemPalace 中没有可召回的姓名记忆,agent 正确回答“不知道用户姓名”。 + +### 2. 写入姓名后可以跨 session 召回 + +当用户输入: + +```text +Hello! My name is Alice. Please remember my name. +``` + +后续再问: + +```text +Now, do you still remember my name? +``` + +工具结果中出现: + +```text +[2026-05-07T20:19:27.141759] user: +Hello! My name is Alice. Please remember my name. +``` + +agent 随后回答能记得姓名是 Alice。说明 MemPalace 已经把前一个 session 的用户消息写入,并在后续 session 中成功检索。 + +### 3. favorite color 也可以被召回 + +当用户输入: + +```text +Hello! My favorite color is blue. Please remember my favorite color. +``` + +后续查询 `favorite color` 时,工具结果能召回对应文本,agent 回答喜欢的颜色是 blue。说明语义检索和跨 session 记忆对这个场景有效。 + +### 4. TTL 清理生效 + +输出中可以看到多次 cleanup 日志: + +```text +MemPalace cleanup: deleted 195 expired memories +MemPalace cleanup: deleted 13 expired memories +MemPalace cleanup: deleted 5 expired memories +``` + +这说明示例中配置的 TTL 清理任务已经运行,并删除了超过 `ttl_seconds=20` 的过期记忆。 + +第三轮开始时: + +```text +load_memory({'query': 'user name'}) +Tool Result: {"memories": []} +``` + +这是符合预期的,因为 `main()` 在第二轮后等待了 30 秒,而 TTL 只有 20 秒,旧记忆已经被后台清理。 + +### 5. 结果中的现象说明 + +输出里有时 agent 会说“我没有主动保存记忆的工具”。这是模型对工具能力的表述不够准确。实际框架是在每轮结束后由 `Runner` 调用 `memory_service.store_session()` 自动写入记忆,并不是通过一个显式的 `save_memory` 工具保存。 + +因此判断是否符合要求时,应看后续 `load_memory` 是否能召回历史内容,而不是看模型是否声称自己调用了保存工具。 + +## 结论 + +`out.txt` 体现了本示例的核心目标: + +- 初始无记忆时,查询返回空。 +- 用户提供姓名或偏好后,后续 session 可以通过 MemPalace 召回。 +- 记忆按 `wing=trpc-agent`、`room=conversations` 写入。 +- TTL 到期后,旧记忆会被定时清理。 +- CLI 查询自定义路径时,需要使用 `mempalace --palace /path/to/palace search "query"`。 + +所以该运行结果符合 MemPalace memory service 示例的预期。 diff --git a/examples/memory_service_with_mempalace/agent/__init__.py b/examples/memory_service_with_mempalace/agent/__init__.py new file mode 100644 index 0000000..bc6e483 --- /dev/null +++ b/examples/memory_service_with_mempalace/agent/__init__.py @@ -0,0 +1,5 @@ +# Tencent is pleased to support the open source community by making tRPC-Agent-Python available. +# +# Copyright (C) 2026 Tencent. All rights reserved. +# +# tRPC-Agent-Python is licensed under Apache-2.0. diff --git a/examples/memory_service_with_mempalace/agent/agent.py b/examples/memory_service_with_mempalace/agent/agent.py new file mode 100644 index 0000000..7990c93 --- /dev/null +++ b/examples/memory_service_with_mempalace/agent/agent.py @@ -0,0 +1,47 @@ +# Tencent is pleased to support the open source community by making tRPC-Agent-Python available. +# +# Copyright (C) 2026 Tencent. All rights reserved. +# +# tRPC-Agent-Python is licensed under Apache-2.0. +""" Agent module""" + +from trpc_agent_sdk.agents import LlmAgent +from trpc_agent_sdk.models import LLMModel +from trpc_agent_sdk.models import OpenAIModel +from trpc_agent_sdk.tools import FunctionTool +from trpc_agent_sdk.tools import load_memory_tool +from trpc_agent_sdk.types import GenerateContentConfig +from trpc_agent_sdk.types import HttpOptions + +from .config import get_model_config +from .prompts import INSTRUCTION +from .tools import get_weather_report + + +def _create_model() -> LLMModel: + """ Create a model""" + api_key, url, model_name = get_model_config() + model = OpenAIModel(model_name=model_name, api_key=api_key, base_url=url) + return model + + + +def create_agent() -> LlmAgent: + """ Create an agent""" + generate_content_config = GenerateContentConfig( + http_options=HttpOptions(extra_body={"chat_template_kwargs": { + "enable_thinking": False + }}), + ) + agent = LlmAgent( + name="assistant", + description="A helpful assistant for conversation", + model=_create_model(), # You can change this to your preferred model + instruction=INSTRUCTION, + tools=[FunctionTool(get_weather_report), load_memory_tool], + generate_content_config=generate_content_config, + ) + return agent + + +root_agent = create_agent() diff --git a/examples/memory_service_with_mempalace/agent/config.py b/examples/memory_service_with_mempalace/agent/config.py new file mode 100644 index 0000000..0a165f5 --- /dev/null +++ b/examples/memory_service_with_mempalace/agent/config.py @@ -0,0 +1,61 @@ +# Tencent is pleased to support the open source community by making tRPC-Agent-Python available. +# +# Copyright (C) 2026 Tencent. All rights reserved. +# +# tRPC-Agent-Python is licensed under Apache-2.0. +""" Agent config module""" + +import os + +from mem0.configs.base import MemoryConfig + + +def get_model_config() -> tuple[str, str, str]: + """Get model config from environment variables""" + api_key = os.getenv('TRPC_AGENT_API_KEY', '') + url = os.getenv('TRPC_AGENT_BASE_URL', '') + model_name = os.getenv('TRPC_AGENT_MODEL_NAME', '') + if not api_key or not url or not model_name: + raise ValueError('''TRPC_AGENT_API_KEY, TRPC_AGENT_BASE_URL, + and TRPC_AGENT_MODEL_NAME must be set in environment variables''') + return api_key, url, model_name + + +def get_memory_config() -> MemoryConfig: + """Get memory config from environment variables""" + memory_config = { + "vector_store": { + "provider": "qdrant", + "config": { + "host": "localhost", + "port": 6333, + "collection_name": "mem0", + } + }, + "llm": { + "provider": "deepseek", + "config": { + "model": os.getenv('TRPC_AGENT_MODEL_NAME', ''), + "api_key": os.getenv('TRPC_AGENT_API_KEY', ''), + "deepseek_base_url": os.getenv('TRPC_AGENT_BASE_URL', ''), + "temperature": 0.2, + "max_tokens": 2000, + } + }, + "embedder": { + "provider": "huggingface", + "config": { + "model": "multi-qa-MiniLM-L6-cos-v1" # Runs locally; no API key required + # "model": "text-embedding-3-small" # Requires API key + } + } + } + return MemoryConfig(**memory_config) + + +def get_mem0_platform_config() -> dict: + """Get mem0 platform config from environment variables""" + return { + "api_key": os.getenv('MEM0_API_KEY', ''), + "host": os.getenv('MEM0_BASE_URL', ''), + } diff --git a/examples/memory_service_with_mempalace/agent/prompts.py b/examples/memory_service_with_mempalace/agent/prompts.py new file mode 100644 index 0000000..ed0cdcc --- /dev/null +++ b/examples/memory_service_with_mempalace/agent/prompts.py @@ -0,0 +1,8 @@ +# Tencent is pleased to support the open source community by making tRPC-Agent-Python available. +# +# Copyright (C) 2026 Tencent. All rights reserved. +# +# tRPC-Agent-Python is licensed under Apache-2.0. +""" prompts for agent""" + +INSTRUCTION = "You are a helpful assistant for intelligence conversation." diff --git a/examples/memory_service_with_mempalace/agent/tools.py b/examples/memory_service_with_mempalace/agent/tools.py new file mode 100644 index 0000000..e469629 --- /dev/null +++ b/examples/memory_service_with_mempalace/agent/tools.py @@ -0,0 +1,30 @@ +# Tencent is pleased to support the open source community by making tRPC-Agent-Python available. +# +# Copyright (C) 2026 Tencent. All rights reserved. +# +# tRPC-Agent-Python is licensed under Apache-2.0. +""" Tools for the agent. """ + + +def get_weather_report(city: str) -> dict: + """Retrieves the current weather report for a specified city. + + Returns: + dict: A dictionary containing the weather information with a 'status' key ('success' or 'error') + and a 'report' key with the weather details if successful, or an 'error_message' if + an error occurred. + """ + if city.lower() == "london": + return { + "status": + "success", + "report": ("The current weather in London is cloudy with a temperature of " + "18 degrees Celsius and a chance of rain."), + } + elif city.lower() == "paris": + return { + "status": "success", + "report": "The weather in Paris is sunny with a temperature of 25 degrees Celsius.", + } + else: + return {"status": "error", "error_message": f"Weather information for '{city}' is not available."} diff --git a/examples/memory_service_with_mempalace/run_agent.py b/examples/memory_service_with_mempalace/run_agent.py new file mode 100644 index 0000000..6dbe3cc --- /dev/null +++ b/examples/memory_service_with_mempalace/run_agent.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 + +# Tencent is pleased to support the open source community by making tRPC-Agent-Python available. +# +# Copyright (C) 2026 Tencent. All rights reserved. +# +# tRPC-Agent-Python is licensed under Apache-2.0. +"""Run the weather query agent demo""" +import asyncio +import sys +from pathlib import Path + +from dotenv import load_dotenv +from trpc_agent_sdk.context import AgentContext +from trpc_agent_sdk.memory import MemoryServiceConfig +from trpc_agent_sdk.memory.mempalace_memory_service import MempalaceMemoryService +from trpc_agent_sdk.runners import Runner +from trpc_agent_sdk.sessions import InMemorySessionService +from trpc_agent_sdk.types import Content +from trpc_agent_sdk.types import Part + +# Load environment variables from the .env file +load_dotenv() + +sys.path.append(str(Path(__file__).parent)) + + +def _truncate_tool_response(response, max_length: int = 256) -> str: + """Limit verbose tool responses in demo output.""" + text = str(response) + if len(text) <= max_length: + return text + return text[:max_length] + + +def create_memory_service(): + """Create session service""" + + memory_service_config = MemoryServiceConfig( + ttl=MemoryServiceConfig.create_ttl_config(enable=True, ttl_seconds=20, cleanup_interval_seconds=20), + enabled=True, + ) + memory_service = MempalaceMemoryService( + memory_service_config=memory_service_config, + wing="trpc-agent", + room="conversations", + store_only_model_visible=True, + ) + return memory_service + + +async def run_weather_agent(memory_service: MempalaceMemoryService): + """Run the weather query agent demo""" + + app_name = "weather_agent_demo" + + from agent.agent import root_agent + session_service = InMemorySessionService() + runner = Runner(app_name=app_name, agent=root_agent, session_service=session_service, memory_service=memory_service) + + user_id = "mempalace_memory_user" + current_session_id = "mempalace_memory_session" + + # Demo query list + demo_queries = [ + "Do you remember my name?", + "Do you remember my favorite color?", + "What is the weather like in paris?", + "Hello! My name is Alice. Please remember my name.", + "Now, do you still remember my name?", + "Hello! My favorite color is blue. Please remember my favorite color.", + "Now, do you still remember my favorite color?", + ] + + for index, query in enumerate(demo_queries): + # Use a new session for each query + + user_content = Content(parts=[Part.from_text(text=query)]) + + print("🤖 Assistant: ", end="", flush=True) + agent_context = AgentContext() + session_id = f"{current_session_id}_{index}" + # set_mem0_filters(agent_context, {"session_id": session_id}) + async for event in runner.run_async(agent_context=agent_context, + user_id=user_id, + session_id=session_id, + new_message=user_content): + # Check if event.content exists + if not event.content or not event.content.parts: + continue + + if event.partial: + for part in event.content.parts: + if part.text: + print(part.text, end="", flush=True) + continue + + for part in event.content.parts: + # Skip the reasoning part; the output is already generated when partial=True + if part.thought: + continue + if part.function_call: + print(f"\n🔧 [Invoke Tool: {part.function_call.name}({part.function_call.args})]") + elif part.function_response: + print(f"📊 [Tool Result: {_truncate_tool_response(part.function_response.response)}]") + # Uncomment to get the full text output of the LLM + # elif part.text: + # print(f"\n✅ {part.text}") + + print("\n" + "-" * 40) + + +async def main(): + memory_service = create_memory_service() + print("=" * 60) + print("First run") + print("=" * 60) + await run_weather_agent(memory_service) + await asyncio.sleep(2) + print("=" * 60) + print("Second run") + print("=" * 60) + await run_weather_agent(memory_service) + await asyncio.sleep(30) + print("=" * 60) + print("Third run") + print("=" * 60) + await run_weather_agent(memory_service) + # await memory_service.delete_memory(wing="trpc-agent", room="conversations") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/memory_service_with_redis/.env b/examples/memory_service_with_redis/.env index 8b13789..dc79139 100644 --- a/examples/memory_service_with_redis/.env +++ b/examples/memory_service_with_redis/.env @@ -1 +1,4 @@ - +# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/memory_service_with_sql/.env b/examples/memory_service_with_sql/.env index 8b13789..dc79139 100644 --- a/examples/memory_service_with_sql/.env +++ b/examples/memory_service_with_sql/.env @@ -1 +1,4 @@ - +# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name diff --git a/examples/mempalace_tools/.env b/examples/mempalace_tools/.env new file mode 100644 index 0000000..f360195 --- /dev/null +++ b/examples/mempalace_tools/.env @@ -0,0 +1,13 @@ +# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=your-base-url +TRPC_AGENT_MODEL_NAME=your-model-name + +# MemPalace config, you can change the wing and room to your own. +MEMPALACE_WING=personal_assistant_alice # default is personal_assistant_alice +MEMPALACE_ROOM=user_profile # default is user_profile +# Optional. Use the same path with `mempalace --palace ... search`. +# If you want to use the default MemPalace path, you can comment out the following two lines. +MEMPALACE_PALACE_PATH=~/.mempalace +MEMPALACE_KG_PATH=~/.mempalace/knowledge_graph.sqlite3 + diff --git a/examples/mempalace_tools/README.md b/examples/mempalace_tools/README.md new file mode 100644 index 0000000..6b04f9d --- /dev/null +++ b/examples/mempalace_tools/README.md @@ -0,0 +1,180 @@ +# MemPalace 工具集成示例 + +本示例演示如何把 `trpc_agent_sdk.tools.mempalace_tool` 中的工具直接注入 `LlmAgent`,让模型在对话中主动调用 MemPalace 完成长期记忆检索、写入、日记和知识图谱操作。 + +> 如果希望框架自动保存会话记忆,推荐使用 `MempalaceMemoryService`。本目录展示的是“工具式集成”:是否搜索、写入什么内容,都由模型通过工具调用完成。 + +## 关键特性 + +- **完整工具覆盖**:示例注入 `mempalace_tool.py` 中的 search、add drawer、diary、KG 全部工具。 +- **本地持久化**:MemPalace 默认使用本地 palace 目录和 ChromaDB 保存向量与原文。 +- **可指定存储路径**:通过 `MEMPALACE_PALACE_PATH` 指定 palace 目录,便于和 CLI 查询同一份数据。 +- **跨 Session 记忆**:示例每轮使用不同 `session_id`,但 MemPalace 数据仍可被后续会话检索。 +- **自动清理测试数据**:运行前后会删除示例 `wing/room` 下的数据,避免影响下一次测试。 + +## Agent 结构 + +```text +personal_assistant (LlmAgent) +├── model: OpenAIModel (config from .env) +└── tools: + ├── MempalaceSearchTool (mempalace_search) + ├── MempalaceAddDrawerTool (mempalace_add_drawer) + ├── MempalaceDiaryWriteTool (mempalace_diary_write) + ├── MempalaceDiaryReadTool (mempalace_diary_read) + ├── MempalaceKGAddTool (mempalace_kg_add) + ├── MempalaceKGQueryTool (mempalace_kg_query) + ├── MempalaceKGInvalidateTool (mempalace_kg_invalidate) + └── MempalaceKGTimelineTool (mempalace_kg_timeline) + └── backend: + └── MemPalace / ChromaDB local palace +``` + +关键文件: + +- [agent/agent.py](./agent/agent.py) +- [agent/config.py](./agent/config.py) +- [agent/prompts.py](./agent/prompts.py) +- [run_agent.py](./run_agent.py) +- `trpc_agent_sdk/tools/mempalace_tool.py` + +## 工具说明 + +| 工具类 | 工具名 | 作用 | 关键参数 | 示例触发场景 | +|---|---|---|---|---| +| `MempalaceSearchTool` | `mempalace_search` | 从 MemPalace 中按语义检索已保存的 drawer 内容。 | `query`、`limit`、`wing`、`room` | 用户问“你还记得我的名字吗?”时,先搜索 `user name`。 | +| `MempalaceAddDrawerTool` | `mempalace_add_drawer` | 将原文内容写入指定 `wing/room`,作为可检索的长期记忆。 | `wing`、`room`、`content`、`source_file` | 用户说“请记住我的名字是 Alice”时,写入用户画像房间。 | +| `MempalaceDiaryWriteTool` | `mempalace_diary_write` | 写入一条 agent 日记,适合记录运行观察、任务过程或阶段性总结。 | `entry`、`agent_name`、`topic`、`wing` | 用户要求“写一条今天测试工具的日记”。 | +| `MempalaceDiaryReadTool` | `mempalace_diary_read` | 读取指定 agent 最近的日记记录。 | `agent_name`、`last_n`、`wing` | 用户要求“读取最近几条日记”。 | +| `MempalaceKGAddTool` | `mempalace_kg_add` | 向知识图谱写入一条三元组事实,并可带有效期、置信度和来源。 | `subject`、`predicate`、`object`、`valid_from`、`valid_to`、`confidence` | 用户要求记录“Alice likes Italian food”。 | +| `MempalaceKGQueryTool` | `mempalace_kg_query` | 查询某个实体的知识图谱关系,支持按日期和方向过滤。 | `entity`、`as_of`、`direction` | 用户要求“查询 Alice 相关事实”。 | +| `MempalaceKGTimelineTool` | `mempalace_kg_timeline` | 按时间线读取知识图谱事实,可限定某个实体。 | `entity` | 用户要求“展示 Alice 的知识图谱时间线”。 | +| `MempalaceKGInvalidateTool` | `mempalace_kg_invalidate` | 将一条当前事实标记为失效,用于表达事实变化,而不是直接删除历史。 | `subject`、`predicate`、`object`、`ended` | 用户要求“把 Alice likes Italian food 标记为今天结束”。 | + +## 安装 + +```bash +git clone https://github.com/trpc-group/trpc-agent-python.git +cd trpc-agent-python +python3 -m venv .venv +source .venv/bin/activate + +pip3 install -e . +pip3 install mempalace +``` + +如果你的 MemPalace 安装需要额外向量依赖,请按 MemPalace 官方说明补装对应 embedding 或 Chroma 依赖。 + +## 环境变量 + +在 `examples/mempalace_tools/.env` 中配置,或通过 `export` 设置: + +```bash +TRPC_AGENT_API_KEY=your-api-key +TRPC_AGENT_BASE_URL=https://api.example.com/v1 +TRPC_AGENT_MODEL_NAME=your-model-name + +# Optional. If omitted, MemPalace uses its default palace path. +MEMPALACE_PALACE_PATH=/tmp/trpc-agent-mempalace-demo +MEMPALACE_KG_PATH= +MEMPALACE_WING=personal_assistant_alice +MEMPALACE_ROOM=user_profile +``` + +`wing` 和 `room` 是本示例给 MemPalace 的固定存储范围: + +- `wing`:建议映射到应用或用户级作用域,例如 `app/user`、`personal_assistant_alice`。 +- `room`:建议映射到记忆主题,例如 `user_profile`、`preferences`、`work_notes`。 + +## 运行 + +```bash +cd examples/mempalace_tools +python3 run_agent.py +``` + +示例分三个阶段执行。每条消息都会使用新的 `session_id`,用于验证不同 session 之间仍能通过 MemPalace 读到之前写入的数据。 + +第一阶段写入数据并立即用新 session 查询: + +```text +Use mempalace_search to check whether you remember my name. +Use mempalace_add_drawer to remember that my name is Alice. +Use mempalace_add_drawer to remember that my favorite food is Italian food. +Use mempalace_search to recall my name and favorite food. +Use mempalace_diary_write to write a diary entry... +Use mempalace_diary_read to read the latest diary entries. +Use mempalace_kg_add to add this fact: Alice likes Italian food. +Use mempalace_kg_query to query facts about Alice. +Use mempalace_kg_timeline to show Alice's knowledge graph timeline. +``` + +第二阶段只读取,不再写入,用于验证数据已经落盘,并且新的 session 仍能读到上一阶段的数据: + +```text +Use mempalace_search to recall my name and favorite food from the previous sessions. +Use mempalace_diary_read to read the latest diary entries from the previous sessions. +Use mempalace_kg_query to query facts about Alice from the previous sessions. +Use mempalace_kg_timeline to show Alice's knowledge graph timeline from the previous sessions. +``` + +第三阶段单独测试知识图谱失效能力。`mempalace_kg_invalidate` 会改变事实的当前状态,所以放在持久化读取验证之后执行,避免影响第二阶段判断: + +```text +Use mempalace_kg_invalidate to mark the fact Alice likes Italian food as ended today. +Use mempalace_kg_query to query facts about Alice again after invalidation. +``` + +运行时可以从日志看到: + +- 查询类问题会触发 `mempalace_search`。 +- 用户要求记住稳定信息时会触发 `mempalace_add_drawer`。 +- 日记类问题会触发 `mempalace_diary_write` / `mempalace_diary_read`。 +- 知识图谱类问题会触发 `mempalace_kg_add` / `mempalace_kg_query` / `mempalace_kg_timeline` / `mempalace_kg_invalidate`。 +- 每条消息都会换新的 `session_id`,但仍能从同一 MemPalace palace 中检索到之前写入的内容。 +- 第二阶段只读不写,用于验证 MemPalace 数据已经落盘。 +- 脚本开始和结束都会清理示例数据:drawer/diary 按 `MEMPALACE_WING`、`MEMPALACE_ROOM` 删除;KG 文件只会在设置了 `MEMPALACE_KG_PATH` 或 `MEMPALACE_PALACE_PATH` 时清理。 + +## 运行结果分析 + +以下分析基于 [out.txt](./out.txt) 中的实际输出。 + +| 阶段 | 验证目标 | 实际结果 | 是否符合预期 | +|---|---|---|---| +| 启动清理 | 运行前清理历史测试数据,避免影响本次结果。 | 首行出现 `Failed to clean MemPalace demo data: ~/.mempalace`,原因是首次运行时 palace 目录还不存在。 | 符合预期。首次运行没有历史数据可清理,不影响后续写入和查询。 | +| 第一阶段:首次搜索 | 测试 `mempalace_search` 在无记忆时的行为。 | 搜索 `name` 返回 `No palace found`,说明还没有已初始化/已写入的数据。 | 符合预期。此时尚未写入任何 drawer。 | +| 第一阶段:写入 drawer | 测试 `mempalace_add_drawer` 能写入用户画像。 | 分别写入 `User's name is Alice.` 和 `My favorite food is Italian food.`,工具返回 `success=True` 和对应 `drawer_id`。 | 符合预期。两个长期记忆都写入到 `personal_assistant_alice/user_profile`。 | +| 第一阶段:搜索 drawer | 测试不同 session 下能立即检索刚写入的 drawer。 | 搜索 `name favorite food` 返回 2 条结果,包含姓名和喜欢的食物。 | 符合预期。说明 drawer 写入后可以被语义检索命中。 | +| 第一阶段:写入日记 | 测试 `mempalace_diary_write`。 | 写入 `Alice tested the MemPalace tools example today.`,返回 `success=True` 和 `entry_id`。 | 符合预期。日记写入成功,并使用了配置的 `wing`。 | +| 第一阶段:读取日记 | 测试 `mempalace_diary_read`。 | 读取到 1 条日记,内容与刚写入的 entry 一致。 | 符合预期。说明 diary 写入和读取链路正常。 | +| 第一阶段:写入 KG 事实 | 测试 `mempalace_kg_add`。 | 写入 `Alice -> likes -> Italian food`,返回 `success=True` 和 `triple_id`。 | 符合预期。知识图谱三元组事实写入成功。 | +| 第一阶段:查询 KG 事实 | 测试 `mempalace_kg_query`。 | 查询 `Alice` 返回 1 条 outgoing fact:`Alice likes Italian food`,`current=True`。 | 符合预期。说明 KG 查询能按实体查到刚写入的事实。 | +| 第一阶段:KG 时间线 | 测试 `mempalace_kg_timeline`。 | 查询 `Alice` 的 timeline 返回同一条事实,`current=True`。 | 符合预期。说明时间线能展示实体相关事实。 | +| 第二阶段:跨 session 搜索 drawer | 验证只读阶段能读到上一阶段写入的数据。 | 使用新的 `session_id` 搜索,仍返回姓名和喜欢的食物 2 条结果。 | 符合预期。说明数据不依赖当前 session 内存,而是已经落到 MemPalace。 | +| 第二阶段:跨 session 读取日记 | 验证 diary 数据可跨 session 读取。 | 仍能读取到上一阶段写入的 1 条日记。 | 符合预期。说明 diary 数据持久化成功。 | +| 第二阶段:跨 session 查询 KG | 验证 KG 数据可跨 session 读取。 | 查询 `Alice` 仍返回 `Alice likes Italian food`,且 `current=True`。 | 符合预期。说明 KG 数据持久化成功,且在 invalidate 前仍是当前事实。 | +| 第三阶段:失效 KG 事实 | 测试 `mempalace_kg_invalidate` 的语义。 | 对 `Alice -> likes -> Italian food` 执行 invalidate,返回 `success=True`,`ended=2026-05-09`。 | 符合预期。invalidate 不删除事实,而是设置失效日期。 | +| 第三阶段:失效后查询 | 验证失效后的事实状态。 | 再次查询 `Alice`,事实仍存在,但 `valid_to=2026-05-09`,`current=False`。 | 符合预期。说明 KG 保留历史事实,同时标记其不再是当前事实。 | +| 结束清理 | 验证测试数据不会影响下次运行。 | 输出 `Cleaned MemPalace demo drawers: 3`,并删除 `knowledge_graph.sqlite3` 及 `-wal/-shm` 文件。 | 符合预期。drawer、diary 和 KG 文件都被清理。 | + +整体结论:`out.txt` 的结果符合本示例预期。它验证了每条消息使用不同 `session_id` 时,MemPalace 仍能从本地持久化数据中检索 drawer、diary 和 KG;同时也验证了 KG invalidate 的行为是“保留历史记录但标记为非当前事实”。 + +## 使用 CLI 查询 + +如果指定了 `MEMPALACE_PALACE_PATH`,需要用同一个路径查询: + +```bash +mempalace --palace /tmp/trpc-agent-mempalace-demo search "user name" +mempalace --palace /tmp/trpc-agent-mempalace-demo search "favorite food" +``` + +如果没有指定路径,CLI 需要使用 MemPalace 默认 palace 路径,或者先确认当前代码实际写入的路径。 + +## 和 MemoryService 的区别 + +| 方式 | 触发时机 | 适合场景 | +|---|---|---| +| `MempalaceMemoryService` | 框架自动在会话结束/记忆加载阶段处理 | 推荐用于稳定的长期记忆能力 | +| `mempalace_tool` | 模型主动调用工具搜索或写入 | 适合让模型显式控制“查什么、存什么” | + +本示例属于第二种方式,因此 prompt 中明确要求模型在需要回忆时调用 `mempalace_search`,在需要保存稳定事实时调用 `mempalace_add_drawer`,在需要日记或知识图谱能力时调用对应的 MemPalace 工具。 diff --git a/examples/mempalace_tools/agent/__init__.py b/examples/mempalace_tools/agent/__init__.py new file mode 100644 index 0000000..bc6e483 --- /dev/null +++ b/examples/mempalace_tools/agent/__init__.py @@ -0,0 +1,5 @@ +# Tencent is pleased to support the open source community by making tRPC-Agent-Python available. +# +# Copyright (C) 2026 Tencent. All rights reserved. +# +# tRPC-Agent-Python is licensed under Apache-2.0. diff --git a/examples/mempalace_tools/agent/agent.py b/examples/mempalace_tools/agent/agent.py new file mode 100644 index 0000000..5a7c1ed --- /dev/null +++ b/examples/mempalace_tools/agent/agent.py @@ -0,0 +1,60 @@ +# Tencent is pleased to support the open source community by making tRPC-Agent-Python available. +# +# Copyright (C) 2026 Tencent. All rights reserved. +# +# tRPC-Agent-Python is licensed under Apache-2.0. +""" Agent module""" + +from trpc_agent_sdk.agents import LlmAgent +from trpc_agent_sdk.models import LLMModel +from trpc_agent_sdk.models import OpenAIModel +from trpc_agent_sdk.tools.mempalace_tool import MempalaceAddDrawerTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceDiaryReadTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceDiaryWriteTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceKGAddTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceKGInvalidateTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceKGQueryTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceKGTimelineTool +from trpc_agent_sdk.tools.mempalace_tool import MempalaceSearchTool + +from .config import get_mempalace_config +from .config import get_model_config +from .prompts import build_instruction + + +def _create_model() -> LLMModel: + """ Create a model""" + api_key, url, model_name = get_model_config() + model = OpenAIModel(model_name=model_name, api_key=api_key, base_url=url) + return model + + +def create_agent() -> LlmAgent: + """Create an agent with MemPalace tools.""" + mempalace_config = get_mempalace_config() + palace_path = mempalace_config["palace_path"] + kg_path = mempalace_config["kg_path"] + tools = [ + MempalaceSearchTool(palace_path=palace_path), + MempalaceAddDrawerTool(palace_path=palace_path), + MempalaceDiaryWriteTool(palace_path=palace_path), + MempalaceDiaryReadTool(palace_path=palace_path), + MempalaceKGQueryTool(palace_path=palace_path, kg_path=kg_path), + MempalaceKGAddTool(palace_path=palace_path, kg_path=kg_path), + MempalaceKGInvalidateTool(palace_path=palace_path, kg_path=kg_path), + MempalaceKGTimelineTool(palace_path=palace_path, kg_path=kg_path), + ] + + return LlmAgent( + name="personal_assistant", + description="A personal assistant that remembers user preferences and past interactions", + model=_create_model(), + instruction=build_instruction( + wing=mempalace_config["wing"], + room=mempalace_config["room"], + ), + tools=tools, + ) + + +root_agent = create_agent() diff --git a/examples/mempalace_tools/agent/config.py b/examples/mempalace_tools/agent/config.py new file mode 100644 index 0000000..b741eab --- /dev/null +++ b/examples/mempalace_tools/agent/config.py @@ -0,0 +1,29 @@ +# Tencent is pleased to support the open source community by making tRPC-Agent-Python available. +# +# Copyright (C) 2026 Tencent. All rights reserved. +# +# tRPC-Agent-Python is licensed under Apache-2.0. +""" Agent config module""" + +import os + + +def get_model_config() -> tuple[str, str, str]: + """Get model config from environment variables""" + api_key = os.getenv('TRPC_AGENT_API_KEY', '') + url = os.getenv('TRPC_AGENT_BASE_URL', '') + model_name = os.getenv('TRPC_AGENT_MODEL_NAME', '') + if not api_key or not url or not model_name: + raise ValueError('''TRPC_AGENT_API_KEY, TRPC_AGENT_BASE_URL, + and TRPC_AGENT_MODEL_NAME must be set in environment variables''') + return api_key, url, model_name + + +def get_mempalace_config() -> dict: + """Get MemPalace tool configuration from environment variables.""" + return { + "palace_path": os.getenv("MEMPALACE_PALACE_PATH") or None, + "kg_path": os.getenv("MEMPALACE_KG_PATH") or None, + "wing": os.getenv("MEMPALACE_WING", "personal_assistant_alice"), + "room": os.getenv("MEMPALACE_ROOM", "user_profile"), + } diff --git a/examples/mempalace_tools/agent/prompts.py b/examples/mempalace_tools/agent/prompts.py new file mode 100644 index 0000000..737dcea --- /dev/null +++ b/examples/mempalace_tools/agent/prompts.py @@ -0,0 +1,26 @@ +# Tencent is pleased to support the open source community by making tRPC-Agent-Python available. +# +# Copyright (C) 2026 Tencent. All rights reserved. +# +# tRPC-Agent-Python is licensed under Apache-2.0. +""" prompts for agent""" + +def build_instruction(*, wing: str, room: str) -> str: + """Build the agent instruction with the configured MemPalace scope.""" + return f""" +You are a helpful personal assistant with MemPalace memory capabilities. + +Use MemPalace with this fixed scope: +- wing: {wing} +- room: {room} + +Memory policy: +- Before answering questions about remembered user information, call mempalace_search with a concise query. +- When the user tells you stable personal information or asks you to remember something, call mempalace_add_drawer. +- When the user asks you to write or read an agent diary, call mempalace_diary_write or mempalace_diary_read with the configured wing above. +- When the user asks you to add, query, invalidate, or list knowledge graph facts, call the matching KG tool: + mempalace_kg_add, mempalace_kg_query, mempalace_kg_invalidate, or mempalace_kg_timeline. +- Store only useful long-term facts. Do not store temporary tool results or implementation details. +- For mempalace_add_drawer, use the configured wing and room above, and write concise verbatim content. +- Personalize your final answer using the memory you retrieved or stored. +""" diff --git a/examples/mempalace_tools/agent/tools.py b/examples/mempalace_tools/agent/tools.py new file mode 100644 index 0000000..5fea90d --- /dev/null +++ b/examples/mempalace_tools/agent/tools.py @@ -0,0 +1,6 @@ +# Tencent is pleased to support the open source community by making tRPC-Agent-Python available. +# +# Copyright (C) 2026 Tencent. All rights reserved. +# +# tRPC-Agent-Python is licensed under Apache-2.0. +""" Tools for the agent. """ diff --git a/examples/mempalace_tools/out.txt b/examples/mempalace_tools/out.txt new file mode 100644 index 0000000..b97e85f --- /dev/null +++ b/examples/mempalace_tools/out.txt @@ -0,0 +1,319 @@ +(venv) [root@VM-186-194-centos trpc-agent-python]# python3 examples/mempalace_tools/run_agent.py +⚠️ Failed to clean MemPalace demo data: ~/.mempalace +============================================================ +First phase: write memories and verify cross-session reads +============================================================ +🆔 Session ID: d9118bff... +📝 User: Use mempalace_search to check whether you remember my name. +🤖 Assistant: The user is asking me to check if I remember their name using mempalace_search. I should search for their name in the memory system. I'll use a concise query to search for name-related information. + +I need to use mempalace_search with: +- query: something related to "name" +- I should use the configured wing: personal_assistant_alice +- room: user_profile (though this is optional in the search function) + +Let me search for "name" to see if I have any stored information about the user's name. +🔧 [Invoke Tool: mempalace_search({'query': 'name', 'wing': 'personal_assistant_alice', 'room': 'user_profile'})] +No palace found at ~/.mempalace: ~/.mempalace +📊 [Tool Result: {'error': 'No palace found', 'hint': 'Run: mempalace init