diff --git a/README.md b/README.md index 55b45da7..9f223710 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

Python Version - PyPI Version + PyPI Version License GitHub Stars

@@ -28,7 +28,7 @@ Personal memory helps "**understand user preferences**", task memory helps agent ## 📰 Latest Updates -- **[2025-10]** 🚀 ReMe v0.1.10.5 released! Core enhancement: direct Python import support. You can now use ReMe without starting an HTTP or MCP service - simply `from reme_ai import ReMeApp` and call methods directly in your Python code. +- **[2025-10]** 🚀 ReMe v0.1.10.6 released! Core enhancement: direct Python import support. You can now use ReMe without starting an HTTP or MCP service - simply `from reme_ai import ReMeApp` and call methods directly in your Python code. - **[2025-10]** 🔧 Tool Memory support is now available! Enables data-driven tool selection and parameter optimization through historical performance tracking. Check out the [Tool Memory Guide](docs/tool_memory/tool_memory.md) and [benchmark results](docs/tool_memory/tool_bench.md). - **[2025-09]** 🎉 ReMe v0.1.9 has been officially released, adding support for asynchronous operations. It has also been integrated into the memory service of agentscope-runtime. @@ -685,8 +685,8 @@ You can find more details on reproducing the experiment in [quickstart.md](docs/ ### 🧊 [Frozenlake Experiment](docs/cookbook/frozenlake/quickstart.md) -| without ReMe | with ReMe | -|:--------------------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------------:| +| without ReMe | with ReMe | +|:----------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------:| |

GIF 1

|

GIF 2

| We tested on 100 random frozenlake maps using qwen3-8b: @@ -711,10 +711,10 @@ We tested ReMe on BFCL-V3 multi-turn-base (randomly split 50train/150val) using We evaluated Tool Memory effectiveness using a controlled benchmark with three mock search tools using Qwen3-30B-Instruct: -| Scenario | Avg Score | Improvement | -|-----------------------|-----------|--------------------| -| Train (No Memory) | 0.650 | - | -| Test (No Memory) | 0.672 | Baseline | +| Scenario | Avg Score | Improvement | +|------------------------|-----------|-------------| +| Train (No Memory) | 0.650 | - | +| Test (No Memory) | 0.672 | Baseline | | **Test (With Memory)** | **0.772** | **+14.88%** | **Key Findings:** diff --git a/cookbook/simple_demo/import_usage_demo.py b/cookbook/simple_demo/import_usage_demo.py index 0eead058..2781b804 100644 --- a/cookbook/simple_demo/import_usage_demo.py +++ b/cookbook/simple_demo/import_usage_demo.py @@ -7,7 +7,7 @@ from reme_ai import ReMeApp # Task Memory Management Examples # ============================================ -async def summary_task_memory(): +async def summary_task_memory(app: ReMeApp): """ Experience Summarizer: Learn from execution trajectories @@ -20,28 +20,23 @@ async def summary_task_memory(): ] }' """ - async with ReMeApp( - "llm.default.model_name=qwen3-30b-a3b-thinking-2507", - "embedding_model.default.model_name=text-embedding-v4", - "vector_store.default.backend=memory" - ) as app: - result = await app.async_execute( - name="summary_task_memory", - workspace_id="task_workspace", - trajectories=[ - { - "messages": [ - {"role": "user", "content": "Help me create a project plan"} - ], - "score": 1.0 - } - ] - ) - print("Summary Task Memory Result:") - print(result) + result = await app.async_execute( + name="summary_task_memory", + workspace_id="task_workspace", + trajectories=[ + { + "messages": [ + {"role": "user", "content": "Help me create a project plan"} + ], + "score": 1.0 + } + ] + ) + print("Summary Task Memory Result:") + print(result["answer"]) -async def retrieve_task_memory(): +async def retrieve_task_memory(app: ReMeApp): """ Retriever: Get relevant memories @@ -53,26 +48,21 @@ async def retrieve_task_memory(): "top_k": 1 }' """ - async with ReMeApp( - "llm.default.model_name=qwen3-30b-a3b-thinking-2507", - "embedding_model.default.model_name=text-embedding-v4", - "vector_store.default.backend=memory" - ) as app: - result = await app.async_execute( - name="retrieve_task_memory", - workspace_id="task_workspace", - query="How to efficiently manage project progress?", - top_k=1 - ) - print("Retrieve Task Memory Result:") - print(result) + result = await app.async_execute( + name="retrieve_task_memory", + workspace_id="task_workspace", + query="How to efficiently manage project progress?", + top_k=1 + ) + print("Retrieve Task Memory Result:") + print(result["answer"]) # ============================================ # Personal Memory Management Examples # ============================================ -async def summary_personal_memory(): +async def summary_personal_memory(app: ReMeApp): """ Memory Integration: Learn from user interactions @@ -88,29 +78,24 @@ async def summary_personal_memory(): ] }' """ - async with ReMeApp( - "llm.default.model_name=qwen3-30b-a3b-thinking-2507", - "embedding_model.default.model_name=text-embedding-v4", - "vector_store.default.backend=memory" - ) as app: - result = await app.async_execute( - name="summary_personal_memory", - workspace_id="task_workspace", - trajectories=[ - { - "messages": [ - {"role": "user", "content": "I like to drink coffee while working in the morning"}, - {"role": "assistant", - "content": "I understand, you prefer to start your workday with coffee to stay energized"} - ] - } - ] - ) - print("Summary Personal Memory Result:") - print(result) + result = await app.async_execute( + name="summary_personal_memory", + workspace_id="task_workspace", + trajectories=[ + { + "messages": [ + {"role": "user", "content": "I like to drink coffee while working in the morning"}, + {"role": "assistant", + "content": "I understand, you prefer to start your workday with coffee to stay energized"} + ] + } + ] + ) + print("Summary Personal Memory Result:") + print(result["answer"]) -async def retrieve_personal_memory(): +async def retrieve_personal_memory(app: ReMeApp): """ Memory Retrieval: Get personal memory fragments @@ -122,26 +107,21 @@ async def retrieve_personal_memory(): "top_k": 5 }' """ - async with ReMeApp( - "llm.default.model_name=qwen3-30b-a3b-thinking-2507", - "embedding_model.default.model_name=text-embedding-v4", - "vector_store.default.backend=memory" - ) as app: - result = await app.async_execute( - name="retrieve_personal_memory", - workspace_id="task_workspace", - query="What are the user's work habits?", - top_k=5 - ) - print("Retrieve Personal Memory Result:") - print(result) + result = await app.async_execute( + name="retrieve_personal_memory", + workspace_id="task_workspace", + query="What are the user's work habits?", + top_k=5 + ) + print("Retrieve Personal Memory Result:") + print(result["answer"]) # ============================================ # Tool Memory Management Examples # ============================================ -async def add_tool_call_result(): +async def add_tool_call_result(app: ReMeApp): """ Record tool execution results @@ -162,31 +142,26 @@ async def add_tool_call_result(): ] }' """ - async with ReMeApp( - "llm.default.model_name=qwen3-30b-a3b-thinking-2507", - "embedding_model.default.model_name=text-embedding-v4", - "vector_store.default.backend=memory" - ) as app: - result = await app.async_execute( - name="add_tool_call_result", - workspace_id="tool_workspace", - tool_call_results=[ - { - "create_time": "2025-10-21 10:30:00", - "tool_name": "web_search", - "input": {"query": "Python asyncio tutorial", "max_results": 10}, - "output": "Found 10 relevant results...", - "token_cost": 150, - "success": True, - "time_cost": 2.3 - } - ] - ) - print("Add Tool Call Result:") - print(result) + result = await app.async_execute( + name="add_tool_call_result", + workspace_id="tool_workspace", + tool_call_results=[ + { + "create_time": "2025-10-21 10:30:00", + "tool_name": "web_search", + "input": {"query": "Python asyncio tutorial", "max_results": 10}, + "output": "Found 10 relevant results...", + "token_cost": 150, + "success": True, + "time_cost": 2.3 + } + ] + ) + print("Add Tool Call Result:") + print(result["answer"]) -async def summary_tool_memory(): +async def summary_tool_memory(app: ReMeApp): """ Generate usage guidelines from history @@ -197,21 +172,16 @@ async def summary_tool_memory(): "tool_names": "web_search" }' """ - async with ReMeApp( - "llm.default.model_name=qwen3-30b-a3b-thinking-2507", - "embedding_model.default.model_name=text-embedding-v4", - "vector_store.default.backend=memory" - ) as app: - result = await app.async_execute( - name="summary_tool_memory", - workspace_id="tool_workspace", - tool_names="web_search" - ) - print("Summary Tool Memory Result:") - print(result) + result = await app.async_execute( + name="summary_tool_memory", + workspace_id="tool_workspace", + tool_names="web_search" + ) + print("Summary Tool Memory Result:") + print(result["answer"]) -async def retrieve_tool_memory(): +async def retrieve_tool_memory(app: ReMeApp): """ Retrieve tool guidelines before use @@ -222,25 +192,20 @@ async def retrieve_tool_memory(): "tool_names": "web_search" }' """ - async with ReMeApp( - "llm.default.model_name=qwen3-30b-a3b-thinking-2507", - "embedding_model.default.model_name=text-embedding-v4", - "vector_store.default.backend=memory" - ) as app: - result = await app.async_execute( - name="retrieve_tool_memory", - workspace_id="tool_workspace", - tool_names="web_search" - ) - print("Retrieve Tool Memory Result:") - print(result) + result = await app.async_execute( + name="retrieve_tool_memory", + workspace_id="tool_workspace", + tool_names="web_search" + ) + print("Retrieve Tool Memory Result:") + print(result["answer"]) # ============================================ # Vector Store Management Example # ============================================ -async def load_vector_store(): +async def load_vector_store(app: ReMeApp): """ Load pre-built memories @@ -252,19 +217,14 @@ async def load_vector_store(): "path": "./docs/library/" }' """ - async with ReMeApp( - "llm.default.model_name=qwen3-30b-a3b-thinking-2507", - "embedding_model.default.model_name=text-embedding-v4", - "vector_store.default.backend=memory" - ) as app: - result = await app.async_execute( - name="vector_store", - workspace_id="appworld", - action="load", - path="./docs/library/" - ) - print("Load Vector Store Result:") - print(result) + result = await app.async_execute( + name="vector_store", + workspace_id="appworld", + action="load", + path="./docs/library/" + ) + print("Load Vector Store Result:") + print(result["answer"]) # ============================================ @@ -273,33 +233,33 @@ async def load_vector_store(): async def main(): """Run all examples""" - print("=" * 60) - print("Task Memory Examples") - print("=" * 60) - await summary_task_memory() - print("\n") - await retrieve_task_memory() - - print("\n" + "=" * 60) - print("Personal Memory Examples") - print("=" * 60) - await summary_personal_memory() - print("\n") - await retrieve_personal_memory() - - print("\n" + "=" * 60) - print("Tool Memory Examples") - print("=" * 60) - await add_tool_call_result() - print("\n") - await summary_tool_memory() - print("\n") - await retrieve_tool_memory() - - print("\n" + "=" * 60) - print("Vector Store Examples") - print("=" * 60) - await load_vector_store() + async with ReMeApp( + "llm.default.model_name=qwen3-30b-a3b-thinking-2507", + "embedding_model.default.model_name=text-embedding-v4", + "vector_store.default.backend=memory" + ) as app: + print("=" * 60) + print("Task Memory Examples") + print("=" * 60) + await summary_task_memory(app) + print("\n") + await retrieve_task_memory(app) + + print("\n" + "=" * 60) + print("Personal Memory Examples") + print("=" * 60) + await summary_personal_memory(app) + print("\n") + await retrieve_personal_memory(app) + + print("\n" + "=" * 60) + print("Tool Memory Examples") + print("=" * 60) + await add_tool_call_result(app) + print("\n") + await summary_tool_memory(app) + print("\n") + await retrieve_tool_memory(app) if __name__ == "__main__": diff --git a/docs/index.md b/docs/index.md index de898700..c56177a1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -17,7 +17,7 @@ kernelspec:
Python Version - PyPI Version + PyPI Version License GitHub Stars
diff --git a/pyproject.toml b/pyproject.toml index 4bafc5b5..1d6e58c1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "reme_ai" -version = "0.1.10.5" +version = "0.1.10.6" description = "Remember me" authors = [ { name = "jinli.yl", email = "jinli.yl@alibaba-inc.com" }, diff --git a/reme_ai/__init__.py b/reme_ai/__init__.py index f7ea22f2..1fc69579 100644 --- a/reme_ai/__init__.py +++ b/reme_ai/__init__.py @@ -2,7 +2,7 @@ import os os.environ["FLOW_APP_NAME"] = "ReMe" -__version__ = "0.1.10.5" +__version__ = "0.1.10.6" from reme_ai.app import ReMeApp from . import agent diff --git a/reme_ai/retrieve/tool/retrieve_tool_memory_op.py b/reme_ai/retrieve/tool/retrieve_tool_memory_op.py index 1b909a01..558284a9 100644 --- a/reme_ai/retrieve/tool/retrieve_tool_memory_op.py +++ b/reme_ai/retrieve/tool/retrieve_tool_memory_op.py @@ -14,6 +14,20 @@ class RetrieveToolMemoryOp(BaseAsyncOp): def __init__(self, **kwargs): super().__init__(**kwargs) + def _format_tool_memories(self, memories: List[ToolMemory]) -> str: + """Format tool memories into a structured document format""" + lines = [] + lines.append(f"Retrieved {len(memories)} tool memory(ies):\n") + + for idx, memory in enumerate(memories, 1): + lines.append(f"Tool: {memory.when_to_use}") + lines.append(memory.content) + + if idx < len(memories): + lines.append("\n---\n") + + return "\n".join(lines) + async def async_execute(self): tool_names: str = self.context.get("tool_names", "") workspace_id: str = self.context.workspace_id @@ -59,8 +73,11 @@ class RetrieveToolMemoryOp(BaseAsyncOp): self.context.response.success = False return + # Format tool memories as document + formatted_answer = self._format_tool_memories(matched_tool_memories) + # Set response - self.context.response.answer = f"Successfully retrieved {len(matched_tool_memories)} tool memories" + self.context.response.answer = formatted_answer self.context.response.success = True self.context.response.metadata["memory_list"] = matched_tool_memories diff --git a/reme_ai/summary/tool/parse_tool_call_result_op.py b/reme_ai/summary/tool/parse_tool_call_result_op.py index e40c2812..566f367a 100644 --- a/reme_ai/summary/tool/parse_tool_call_result_op.py +++ b/reme_ai/summary/tool/parse_tool_call_result_op.py @@ -24,6 +24,37 @@ class ParseToolCallResultOp(BaseAsyncOp): self.max_history_tool_call_cnt: int = max_history_tool_call_cnt self.evaluation_sleep_interval: float = evaluation_sleep_interval + def _format_tool_memories_summary(self, memory_list: List[ToolMemory], deleted_memory_ids: List[str]) -> str: + """Format tool memories update summary""" + lines = [] + + # 统计信息 + total_tools = len(memory_list) + updated_tools = len(deleted_memory_ids) + new_tools = total_tools - updated_tools + + lines.append(f"Processed {total_tools} tool(s): {updated_tools} updated, {new_tools} newly created\n") + + # 详细信息 + for idx, memory in enumerate(memory_list, 1): + is_updated = memory.memory_id in deleted_memory_ids + status = "Updated" if is_updated else "New" + + lines.append(f"[{status}] {memory.when_to_use}") + lines.append(f" Total calls: {len(memory.tool_call_results)}") + + # 显示最近添加的调用结果统计 + if memory.tool_call_results: + recent_results = memory.tool_call_results[-3:] + success_count = sum(1 for r in recent_results if r.success) + avg_score = sum(r.score for r in recent_results) / len(recent_results) + lines.append(f" Recent calls: {success_count}/{len(recent_results)} successful, avg score: {avg_score:.2f}") + + if idx < len(memory_list): + lines.append("") + + return "\n".join(lines) + async def _evaluate_single_tool_call(self, tool_call_result: ToolCallResult, index: int) -> ToolCallResult: await asyncio.sleep(self.evaluation_sleep_interval * index) @@ -127,7 +158,12 @@ class ParseToolCallResultOp(BaseAsyncOp): all_memory_list.append(tool_memory) + # 格式化结果信息 + formatted_answer = self._format_tool_memories_summary(all_memory_list, all_deleted_memory_ids) + # 设置返回结果 + self.context.response.answer = formatted_answer + self.context.response.success = True self.context.response.metadata["deleted_memory_ids"] = all_deleted_memory_ids self.context.response.metadata["memory_list"] = all_memory_list diff --git a/reme_ai/summary/tool/summary_tool_memory_op.py b/reme_ai/summary/tool/summary_tool_memory_op.py index 893337ea..5da760cb 100644 --- a/reme_ai/summary/tool/summary_tool_memory_op.py +++ b/reme_ai/summary/tool/summary_tool_memory_op.py @@ -23,6 +23,25 @@ class SummaryToolMemoryOp(BaseAsyncOp): self.recent_call_count: int = recent_call_count self.summary_sleep_interval: float = summary_sleep_interval + def _format_summary_result(self, summarized_memories: List[ToolMemory], skipped_memories: List[ToolMemory]) -> str: + """Format tool memory summary result""" + lines = [] + + # 统计信息 + total_tools = len(summarized_memories) + len(skipped_memories) + lines.append(f"Processed {total_tools} tool(s): {len(summarized_memories)} summarized, {len(skipped_memories)} skipped\n") + + # 显示已总结的工具详细信息 + if summarized_memories: + for idx, memory in enumerate(summarized_memories, 1): + lines.append(f"Tool: {memory.when_to_use}") + lines.append(memory.content) + + if idx < len(summarized_memories): + lines.append("\n---\n") + + return "\n".join(lines) + @staticmethod def _format_call_summaries_markdown(recent_calls: List) -> str: """Format tool call summaries as markdown.""" @@ -180,10 +199,11 @@ class SummaryToolMemoryOp(BaseAsyncOp): # Combine summarized and skipped memories all_memories = valid_summarized_memories + tools_skipped + # Format summary result + formatted_answer = self._format_summary_result(valid_summarized_memories, tools_skipped) + # Set response - self.context.response.answer = (f"Successfully processed {len(all_memories)} tool memories: " - f"{len(valid_summarized_memories)} summarized, " - f"{len(tools_skipped)} skipped (already up-to-date)") + self.context.response.answer = formatted_answer self.context.response.success = True self.context.response.metadata["memory_list"] = all_memories self.context.response.metadata["deleted_memory_ids"] = [m.memory_id for m in all_memories]