refactor(core): restructure tool modules and add memory operations

This commit is contained in:
jinli.yl 2026-01-06 14:31:47 +08:00
parent ed33749cf6
commit 245e2564e4
54 changed files with 1665 additions and 55 deletions

View file

@ -3,7 +3,6 @@
# pylint: disable=wrong-import-position
# flake8: noqa: F401
from . import agent
from . import config
from . import context
from . import embedding
@ -14,6 +13,5 @@ from . import op
from . import schema
from . import service
from . import token_counter
from . import tool
from . import utils
from . import vector_store

View file

@ -38,6 +38,15 @@ class Application:
Initialize the Application with configuration settings.
Args:
*args: Additional arguments passed to parser. Examples:
- "llm.default.model_name=qwen3-30b-a3b-thinking-2507"
- "llm.default.backend=openai_compatible"
- "llm.default.temperature=0.6"
- "embedding_model.default.model_name=text-embedding-v4"
- "embedding_model.default.backend=openai_compatible"
- "embedding_model.default.dimensions=1024"
- "vector_store.default.backend=memory"
- "vector_store.default.embedding_model=default"
llm_api_key: API key for LLM service
llm_api_base: Base URL for LLM service
embedding_api_key: API key for embedding service
@ -50,7 +59,8 @@ class Application:
embedding_model: Embedding model configuration dictionary
vector_store: Vector store configuration dictionary
token_counter: Token counter configuration dictionary
**kwargs: Additional configuration arguments
**kwargs: Additional keyword arguments passed to parser. Same format as args but as kwargs. Examples:
- **{"llm.default.model_name": "qwen3-30b-a3b-thinking-2507"}
"""
load_env()
@ -89,25 +99,25 @@ class Application:
C.print_logo()
@staticmethod
def _update_env(key: str, value: str | None) -> None:
def _update_env(key: str, value: str | None):
"""Update environment variable if value is provided."""
if value:
os.environ[key] = value
@staticmethod
async def start() -> None:
async def start():
"""Initialize the service context and prepare external MCP servers."""
C.initialize_service_context()
await C.prepare_mcp_servers()
@staticmethod
def start_sync() -> None:
def start_sync():
"""Synchronous version of start()."""
C.initialize_service_context()
run_coro_safely(C.prepare_mcp_servers())
@staticmethod
async def stop(wait_thread_pool: bool = True, wait_ray: bool = True) -> None:
async def stop(wait_thread_pool: bool = True, wait_ray: bool = True):
"""
Stop the application and cleanup resources.
@ -120,7 +130,7 @@ class Application:
C.shutdown_ray(wait=wait_ray)
@staticmethod
def stop_sync(wait_thread_pool: bool = True, wait_ray: bool = True) -> None:
def stop_sync(wait_thread_pool: bool = True, wait_ray: bool = True):
"""Synchronous version of stop()."""
C.close_sync()
C.shutdown_thread_pool(wait=wait_thread_pool)

View file

@ -60,6 +60,9 @@ class ServiceContext(BaseContext):
# MCP server mapping: maps server_name -> {tool_name: ToolCall}
self.mcp_server_mapping: dict[str, dict] = {}
# Initialization flag: ensures initialize_service_context is called only once
self._initialized: bool = False
def register(self, name: str, register_type: RegistryEnum):
"""Return a decorator to register a component within a specific registry category.
@ -268,7 +271,12 @@ class ServiceContext(BaseContext):
9. Service backend instance
Note: This method should be called after service_config is set.
This method can only be called once. Subsequent calls will be ignored.
"""
if self._initialized:
logger.warning("initialize_service_context has already been called. Skipping re-initialization.")
return
self.language = self.service_config.language
self.thread_pool = ThreadPoolExecutor(max_workers=self.service_config.thread_pool_max_workers)
@ -286,6 +294,9 @@ class ServiceContext(BaseContext):
self._initialize_flow()
self._initialize_service()
# Mark as initialized
self._initialized = True
def _initialize_llm(self):
"""Initialize all configured LLM instances.

View file

@ -40,7 +40,7 @@ class BaseOp:
token_counter: str | BaseTokenCounter = "default",
enable_cache: bool = False,
cache_path: str = "cache/op",
cache_expire_hours: float = 0.1,
cache_expire_hours: float | None = None,
sub_ops: dict[str, "BaseOp"] | list["BaseOp"] | Optional["BaseOp"] = None,
input_mapping: dict[str, str] | None = None,
output_mapping: dict[str, str] | None = None,

View file

@ -1,9 +0,0 @@
"""tool"""
from .mcp_tool import MCPTool
from . import search
__all__ = [
"MCPTool",
"search",
]

View file

@ -6,7 +6,7 @@ from .common_utils import run_coro_safely, execute_stream_task
from .env_utils import load_env
from .execute_tuils import exec_code, run_shell_command
from .http_client import HttpClient
from .llm_utils import extract_content, format_messages
from .llm_utils import extract_content, format_messages, deduplicate_memories
from .logger_utils import init_logger
from .logo_utils import print_logo
from .mcp_client import MCPClient
@ -27,6 +27,7 @@ __all__ = [
"HttpClient",
"extract_content",
"format_messages",
"deduplicate_memories",
"init_logger",
"print_logo",
"MCPClient",

View file

@ -2,10 +2,10 @@
from loguru import logger
from ..context import C
from ..enumeration import Role
from ..op import BaseOp
from ..schema import Message, ToolCall
from ..core.context import C
from ..core.enumeration import Role
from ..core.op import BaseOp
from ..core.schema import Message, ToolCall
@C.register_op()

View file

@ -2,10 +2,10 @@
from loguru import logger
from ..context import C
from ..enumeration import Role, ChunkEnum
from ..op import BaseOp
from ..schema import Message, ToolCall
from ..core.context import C
from ..core.enumeration import Role, ChunkEnum
from ..core.op import BaseOp
from ..core.schema import Message, ToolCall
@C.register_op()

17
reme_ai/tool/__init__.py Normal file
View file

@ -0,0 +1,17 @@
"""tool"""
from . import execute
from . import memory
from . import search
from .base_memory_tool import BaseMemoryTool
from .mcp_tool import MCPTool
from .think_tool import ThinkTool
__all__ = [
"execute",
"memory",
"search",
"BaseMemoryTool",
"MCPTool",
"ThinkTool",
]

View file

@ -0,0 +1,102 @@
"""Base class for memory tool"""
from abc import ABCMeta
from pathlib import Path
from ..core.enumeration import MemoryType
from ..core.op import BaseOp
from ..core.schema import ToolCall, MemoryNode
from ..core.utils import CacheHandler
class BaseMemoryTool(BaseOp, metaclass=ABCMeta):
"""Base class for memory tool"""
def __init__(
self,
enable_multiple: bool = True,
enable_thinking_params: bool = False,
meta_memory_path: str = "./meta_memory",
**kwargs,
):
super().__init__(**kwargs)
self.enable_multiple: bool = enable_multiple
self.enable_thinking_params: bool = enable_thinking_params
self.meta_memory_path: str = meta_memory_path
self._meta_memory: CacheHandler | None = None
def _build_parameters(self) -> dict:
return {}
def _build_multiple_parameters(self) -> dict:
return {}
def _build_tool_call(self) -> ToolCall:
if self.enable_multiple:
parameters = self._build_multiple_parameters()
else:
parameters = self._build_parameters()
if self.enable_thinking_params and "thinking" not in parameters["properties"]:
parameters["properties"] = {
"thinking": {
"type": "string",
"description": "Your thinking and reasoning about how to fill in the parameters",
},
**parameters["properties"],
}
parameters["required"] = ["thinking", *parameters["required"]]
return ToolCall(
**{
"description": self.get_prompt("tool" + ("_multiple" if self.enable_multiple else "")),
"parameters": parameters,
},
)
@property
def meta_memory(self) -> CacheHandler:
"""Get or create the meta memory cache handler."""
if self._meta_memory is None:
self._meta_memory = CacheHandler(Path(self.meta_memory_path) / self.vector_store.collection_name)
return self._meta_memory
@property
def memory_type(self) -> MemoryType:
"""Get the memory type from context."""
return MemoryType(self.context.get("memory_type"))
@property
def memory_target(self) -> str:
"""Get the memory target from context."""
return self.context.get("memory_target", "")
@property
def ref_memory_id(self) -> str:
"""Get the reference memory ID from context."""
return self.context.get("ref_memory_id", "")
@property
def author(self) -> str:
"""Get the author from context."""
return self.context.get("author", "")
def _build_memory_node(
self,
memory_content: str,
when_to_use: str = "",
metadata: dict | None = None,
) -> MemoryNode:
"""Build MemoryNode from content, when_to_use, and metadata.
This is a shared utility method for subclasses that need to create MemoryNode instances.
"""
return MemoryNode(
memory_type=self.memory_type,
memory_target=self.memory_target,
when_to_use=when_to_use or "",
content=memory_content,
ref_memory_id=self.ref_memory_id,
author=self.author,
metadata=metadata or {},
)

View file

@ -4,11 +4,11 @@ This module provides an operation that can execute Python code strings
and return the output or error messages.
"""
from ...context import C
from ...op import BaseOp
from ...schema import ToolCall
from ...core.context import C
from ...core.op import BaseOp
from ...core.schema import ToolCall
from ...utils import exec_code
from ...core.utils import exec_code
@C.register_op()

View file

@ -4,11 +4,11 @@ This module provides an operation that can execute shell commands
asynchronously and return the output, error, and exit code.
"""
from ...context import C
from ...op import BaseOp
from ...schema import ToolCall
from ...core.context import C
from ...core.op import BaseOp
from ...core.schema import ToolCall
from ...utils import run_shell_command
from ...core.utils import run_shell_command
@C.register_op()

View file

@ -2,10 +2,10 @@
from typing import List
from ..context import C
from ..op import BaseOp
from ..schema import ToolCall
from ..utils import MCPClient
from ..core.context import C
from ..core.op import BaseOp
from ..core.schema import ToolCall
from ..core.utils import MCPClient
@C.register_op()

View file

@ -0,0 +1,27 @@
"""Memory tool operations."""
from .history.add_history_memory import AddHistoryMemory
from .history.read_history_memory import ReadHistoryMemory
from .identity.read_identity_memory import ReadIdentityMemory
from .identity.update_identity_memory import UpdateIdentityMemory
from .meta.add_meta_memory import AddMetaMemory
from .meta.read_meta_memory import ReadMetaMemory
from .vector.add_memory import AddMemory
from .vector.add_summary_memory import AddSummaryMemory
from .vector.delete_memory import DeleteMemory
from .vector.update_memory import UpdateMemory
from .vector.vector_retrieve_memory import VectorRetrieveMemory
__all__ = [
"AddHistoryMemory",
"ReadHistoryMemory",
"ReadIdentityMemory",
"UpdateIdentityMemory",
"AddMetaMemory",
"ReadMetaMemory",
"AddMemory",
"AddSummaryMemory",
"DeleteMemory",
"UpdateMemory",
"VectorRetrieveMemory",
]

View file

View file

@ -0,0 +1,111 @@
"""Add history memory operation."""
from loguru import logger
from ...base_memory_tool import BaseMemoryTool
from ....core.context import C
from ....core.schema import MemoryNode
@C.register_op()
class AddHistoryMemory(BaseMemoryTool):
"""Add history memory from conversation messages."""
def __init__(self, add_metadata: bool = True, **kwargs):
super().__init__(**kwargs)
self.add_metadata: bool = add_metadata
def _build_item_schema(self) -> tuple[dict, list[str]]:
properties = {
"messages": {
"type": "array",
"description": self.get_prompt("messages"),
"items": {"type": "object"},
},
}
required = ["messages"]
if self.add_metadata:
properties["metadata"] = {
"type": "object",
"description": self.get_prompt("metadata"),
}
return properties, required
def _build_parameters(self) -> dict:
properties, required = self._build_item_schema()
return {
"type": "object",
"properties": properties,
"required": required,
}
def _build_multiple_parameters(self) -> dict:
item_properties, required_fields = self._build_item_schema()
return {
"type": "object",
"properties": {
"histories": {
"type": "array",
"description": self.get_prompt("histories"),
"items": {
"type": "object",
"properties": item_properties,
"required": required_fields,
},
},
},
"required": ["histories"],
}
def _format_messages(self, messages: list) -> str:
return "\n".join([f"{msg.get('role', 'unknown')}: {msg.get('content', '')}" for msg in messages])
def _extract_history_data(self, hist_dict: dict) -> tuple[list, dict]:
messages = hist_dict.get("messages", [])
metadata = hist_dict.get("metadata", {}) if self.add_metadata else {}
return messages, metadata
async def execute(self):
memory_nodes: list[MemoryNode] = []
if self.enable_multiple:
histories: list[dict] = self.context.get("histories", [])
if not histories:
self.output = "No histories provided for addition."
return
for hist in histories:
messages, metadata = self._extract_history_data(hist)
if not messages:
logger.warning("Skipping history with empty messages")
continue
memory_content = self._format_messages(messages)
memory_nodes.append(
self._build_memory_node(memory_content, when_to_use="", metadata=metadata),
)
else:
messages, metadata = self._extract_history_data(self.context)
if not messages:
self.output = "No messages provided for addition."
return
memory_content = self._format_messages(messages)
memory_nodes.append(
self._build_memory_node(memory_content, when_to_use="", metadata=metadata),
)
if not memory_nodes:
self.output = "No valid histories provided for addition."
return
vector_nodes = [node.to_vector_node() for node in memory_nodes]
vector_ids: list[str] = [node.vector_id for node in vector_nodes]
await self.vector_store.delete(vector_ids=vector_ids)
await self.vector_store.insert(nodes=vector_nodes)
self.output = f"Successfully added {len(memory_nodes)} history memories to vector_store."
logger.info(self.output)

View file

@ -0,0 +1,14 @@
tool: |
Add history memory from conversation messages.
tool_multiple: |
Add multiple history memories in a single operation.
messages: |
List of message objects with 'role' and 'content' fields.
metadata: |
Optional metadata (time, session_id, topic, etc.).
histories: |
List of history objects, each with messages and optional metadata.

View file

@ -0,0 +1,75 @@
"""Read history memory operation."""
from loguru import logger
from ...base_memory_tool import BaseMemoryTool
from ....core.context import C
from ....core.schema import MemoryNode
@C.register_op()
class ReadHistoryMemory(BaseMemoryTool):
"""Read history memories by IDs."""
def _build_parameters(self) -> dict:
return {
"type": "object",
"properties": {
"memory_id": {
"type": "string",
"description": self.get_prompt("memory_id"),
},
},
"required": ["memory_id"],
}
def _build_multiple_parameters(self) -> dict:
return {
"type": "object",
"properties": {
"memory_ids": {
"type": "array",
"description": self.get_prompt("memory_ids"),
"items": {"type": "string"},
},
},
"required": ["memory_ids"],
}
async def execute(self):
if self.enable_multiple:
memory_ids: list[str] = self.context.get("memory_ids", [])
else:
memory_id = self.context.get("memory_id", "")
memory_ids: list[str] = [memory_id] if memory_id else []
memory_ids = [mid for mid in memory_ids if mid]
if not memory_ids:
self.output = "No valid history memory IDs provided for reading."
logger.warning(self.output)
return
nodes = await self.vector_store.search(
query="",
top_k=len(memory_ids),
filter_dict={"vector_id": memory_ids},
)
if not nodes:
self.output = "No history memories found with the provided IDs."
logger.warning(self.output)
return
memories: list[MemoryNode] = [MemoryNode.from_vector_node(n) for n in nodes]
output_lines = []
for memory in memories:
output_lines.append(f"Memory ID: {memory.vector_id}")
output_lines.append(f"Content:\n{memory.content}")
if memory.metadata:
output_lines.append(f"Metadata: {memory.metadata}")
output_lines.append("---")
self.output = "\n".join(output_lines)
logger.info(f"Successfully read {len(memories)} history memories.")

View file

@ -0,0 +1,11 @@
tool: |
Read history memory by ID.
tool_multiple: |
Read multiple history memories by IDs.
memory_id: |
Unique identifier of the history memory.
memory_ids: |
List of unique identifiers of history memories.

View file

View file

@ -0,0 +1,33 @@
"""Read identity memory operation."""
from loguru import logger
from ...base_memory_tool import BaseMemoryTool
from ....core.context import C
@C.register_op()
class ReadIdentityMemory(BaseMemoryTool):
"""Read identity memory for agent self-cognition."""
def __init__(self, **kwargs):
kwargs["enable_multiple"] = False
super().__init__(**kwargs)
def _build_parameters(self) -> dict:
return {
"type": "object",
"properties": {},
"required": [],
}
async def execute(self):
result = self.meta_memory.load("identity_memory")
identity_memory = result if result is not None else ""
if identity_memory:
self.output = f"Identity memory:\n{identity_memory}"
logger.info("Retrieved identity memory")
else:
self.output = "No identity memory found."
logger.info(self.output)

View file

@ -0,0 +1,3 @@
tool: |
Read the identity memory for the agent.
Retrieve self-cognition information such as identity, role, personality, or current state.

View file

@ -0,0 +1,39 @@
"""Update identity memory operation."""
from loguru import logger
from ...base_memory_tool import BaseMemoryTool
from ....core.context import C
@C.register_op()
class UpdateIdentityMemory(BaseMemoryTool):
"""Update identity memory for agent self-cognition."""
def __init__(self, **kwargs):
kwargs["enable_multiple"] = False
super().__init__(**kwargs)
def _build_parameters(self) -> dict:
return {
"type": "object",
"properties": {
"identity_memory": {
"type": "string",
"description": self.get_prompt("identity_memory"),
},
},
"required": ["identity_memory"],
}
async def execute(self):
identity_memory = self.context.get("identity_memory", "")
if not identity_memory:
self.output = "No valid identity memory provided for update."
logger.warning(self.output)
return
self.meta_memory.save("identity_memory", identity_memory)
self.output = "Successfully updated identity memory."
logger.info(self.output)

View file

@ -0,0 +1,7 @@
tool: |
Update the identity memory for the agent.
Store self-cognition information such as identity, role, personality, or current state.
identity_memory: |
The identity memory content to store.
Should be a clear statement capturing the agent's self-cognition or current state.

View file

View file

@ -0,0 +1,121 @@
"""Add meta memory operation for adding memory metadata."""
import json
from loguru import logger
from ...base_memory_tool import BaseMemoryTool
from ....core.context import C
from ....core.enumeration import MemoryType
@C.register_op()
class AddMetaMemory(BaseMemoryTool):
"""Add memory metadata (memory_type and memory_target) to meta storage.
Supports single/multiple addition modes via `enable_multiple` parameter.
"""
def _build_item_schema(self) -> tuple[dict, list[str]]:
"""Build shared schema properties and required fields for meta memory items.
Returns:
Tuple of (properties dict, required fields list).
"""
properties = {
"memory_type": {
"type": "string",
"description": self.get_prompt("memory_type"),
"enum": [MemoryType.PERSONAL.value, MemoryType.PROCEDURAL.value],
},
"memory_target": {
"type": "string",
"description": self.get_prompt("memory_target"),
},
}
required = ["memory_type", "memory_target"]
return properties, required
def _build_parameters(self) -> dict:
"""Build input schema for single meta memory addition."""
properties, required = self._build_item_schema()
return {
"type": "object",
"properties": properties,
"required": required,
}
def _build_multiple_parameters(self) -> dict:
"""Build input schema for multiple meta memory addition."""
item_properties, required_fields = self._build_item_schema()
return {
"type": "object",
"properties": {
"meta_memories": {
"type": "array",
"description": self.get_prompt("meta_memories"),
"items": {
"type": "object",
"properties": item_properties,
"required": required_fields,
},
},
},
"required": ["meta_memories"],
}
def _load_meta_memories(self) -> list[dict]:
"""Load existing meta memories from cache."""
result = self.meta_memory.load("meta_memories")
return result if result is not None else []
def _save_meta_memories(self, memories: list[dict]) -> bool:
"""Save meta memories to cache."""
return self.meta_memory.save("meta_memories", memories)
async def execute(self):
"""Execute addition: load existing, merge with new, and save.
Duplicates (same memory_type and memory_target) are skipped.
"""
existing_memories: list[dict] = self._load_meta_memories()
existing_set = {(m["memory_type"], m["memory_target"]) for m in existing_memories}
# Build new memories to add based on mode
new_memories: list[dict] = []
if self.enable_multiple:
meta_memories: list[dict] = self.context.get("meta_memories", [])
for mem in meta_memories:
memory_type = mem.get("memory_type", "")
memory_target = mem.get("memory_target", "")
if memory_type and (memory_type, memory_target) not in existing_set:
new_memories.append(
{
"memory_type": memory_type,
"memory_target": memory_target,
},
)
existing_set.add((memory_type, memory_target))
else:
memory_type = self.context.get("memory_type", "")
memory_target = self.context.get("memory_target", "")
if memory_type and (memory_type, memory_target) not in existing_set:
new_memories.append(
{
"memory_type": memory_type,
"memory_target": memory_target,
},
)
if not new_memories:
self.output = "No new meta memories to add (all entries already exist or invalid)."
return
# Merge and save
all_memories = existing_memories + new_memories
self._save_meta_memories(all_memories)
# Format output
added_str = json.dumps(new_memories, ensure_ascii=False)
self.output = f"Successfully added {len(new_memories)} meta memory entries: {added_str}"
logger.info(self.output)

View file

@ -0,0 +1,24 @@
tool: |
Add a memory metadata entry to register a new memory type and target.
Use this tool to define what types of memories should be tracked, such as:
- Personal memories: "John", "Alice" (person-specific preferences and context)
- Procedural memories: "deployment_process", "code_review_steps" (how-to knowledge)
tool_multiple: |
Add multiple memory metadata entries to register multiple memory types and targets at once.
Use this tool to define multiple memory tracking categories in a single operation.
Each entry specifies a memory_type and memory_target for organizing different memory domains.
meta_memories: |
A list of memory metadata entries to add. Each entry contains memory_type and memory_target.
memory_type: |
The type of memory to register. Valid values are: personal, procedural.
- personal: Person-specific memory storing preferences and context about specific individuals
- procedural: Procedural memory storing how-to knowledge and step-by-step processes
memory_target: |
The target identifier for this memory category.
Examples:
- For personal memory: person's name (e.g., "John", "Alice")
- For procedural memory: process name (e.g., "deployment", "code_review")

View file

@ -0,0 +1,110 @@
"""Read meta memory operation for retrieving memory metadata."""
from loguru import logger
from ...base_memory_tool import BaseMemoryTool
from ....core.context import C
from ....core.enumeration import MemoryType
@C.register_op()
class ReadMetaMemory(BaseMemoryTool):
"""Read memory metadata (memory_type and memory_target) from meta storage.
This operation reads stored memory metadata and optionally includes
TOOL and IDENTITY type memories.
"""
def __init__(
self,
enable_tool_memory: bool = False,
enable_identity_memory: bool = False,
**kwargs,
):
"""Initialize ReadMetaMemory.
Args:
enable_tool_memory: Include TOOL type meta memory. Defaults to False.
enable_identity_memory: Include IDENTITY type meta memory. Defaults to False.
**kwargs: Additional arguments for BaseMemoryTool.
"""
kwargs["enable_multiple"] = False
super().__init__(**kwargs)
self.enable_tool_memory = enable_tool_memory
self.enable_identity_memory = enable_identity_memory
def _build_parameters(self) -> dict:
"""Build input schema for reading meta memory.
No input parameters required for reading.
"""
return {
"type": "object",
"properties": {},
"required": [],
}
def _load_meta_memories(self) -> list[dict[str, str]]:
"""Load meta memories from cache and apply filters."""
result = self.meta_memory.load("meta_memories")
all_memories = result if result is not None else []
filtered_memories = []
for m in all_memories:
memory_type = MemoryType(m.get("memory_type"))
if memory_type in (MemoryType.PERSONAL, MemoryType.PROCEDURAL):
filtered_memories.append(m)
if self.enable_tool_memory:
filtered_memories.append(
{
"memory_type": MemoryType.TOOL.value,
"memory_target": "tool_guidelines",
},
)
if self.enable_identity_memory:
filtered_memories.append(
{
"memory_type": MemoryType.IDENTITY.value,
"memory_target": "self",
},
)
return filtered_memories
def _format_memory_metadata(self, memories: list[dict[str, str]]) -> str:
"""Format memory metadata into a readable string.
Args:
memories: List of memory metadata entries.
Returns:
str: Formatted memory metadata string.
"""
if not memories:
return ""
lines = []
for memory in memories:
memory_type = memory["memory_type"]
memory_target = memory["memory_target"]
description = self.get_prompt(f"type_{memory_type}")
lines.append(f"- {memory_type}({memory_target}): {description}")
return "\n".join(lines)
async def execute(self):
"""Execute the read meta memory operation.
Reads memory metadata from cache storage and formats output.
"""
memories = self._load_meta_memories()
if memories:
formatted = self._format_memory_metadata(memories)
self.output = formatted
logger.info(f"Retrieved {len(memories)} meta memory entries")
else:
self.output = "No memory metadata found."
logger.info(self.output)

View file

@ -0,0 +1,16 @@
tool: |
Read the memory metadata registry to see what types of memories are being tracked.
Use this tool to retrieve all registered memory types and their targets.
This helps understand what memory categories are available for storing and retrieving information.
type_identity: |
Self-cognition memory storing agent's identity, personality, and current state.
type_personal: |
Person-specific memory storing preferences and context about specific individuals.
type_procedural: |
Procedural memory storing how-to knowledge and step-by-step processes.
type_tool: |
Tool memory storing tool usage patterns, success rates, token consumption, and latency.

View file

View file

@ -0,0 +1,144 @@
"""Add memory operation for vector store."""
from loguru import logger
from ...base_memory_tool import BaseMemoryTool
from ....core.context import C
from ....core.schema import MemoryNode
@C.register_op()
class AddMemory(BaseMemoryTool):
"""Add memories to vector store with optional when_to_use and metadata.
Supports single/multiple addition modes via `enable_multiple` parameter.
"""
def __init__(self, add_when_to_use: bool = False, add_metadata: bool = True, **kwargs):
"""Initialize AddMemory.
Args:
add_when_to_use: Include when_to_use field for better retrieval.
add_metadata: Include metadata field for additional info.
**kwargs: Additional arguments for BaseMemoryTool.
"""
super().__init__(**kwargs)
self.add_when_to_use: bool = add_when_to_use
self.add_metadata: bool = add_metadata
def _build_item_schema(self) -> tuple[dict, list[str]]:
"""Build shared schema properties and required fields for memory items.
Returns:
Tuple of (properties dict, required fields list).
"""
properties = {}
required = []
if self.add_when_to_use:
properties["when_to_use"] = {
"type": "string",
"description": self.get_prompt("when_to_use"),
}
required.append("when_to_use")
properties["memory_content"] = {
"type": "string",
"description": self.get_prompt("memory_content"),
}
required.append("memory_content")
if self.add_metadata:
properties["metadata"] = {
"type": "object",
"description": self.get_prompt("metadata"),
}
return properties, required
def _build_parameters(self) -> dict:
"""Build input schema for single memory addition."""
properties, required = self._build_item_schema()
return {
"type": "object",
"properties": properties,
"required": required,
}
def _build_multiple_parameters(self) -> dict:
"""Build input schema for multiple memory addition."""
item_properties, required_fields = self._build_item_schema()
return {
"type": "object",
"properties": {
"memories": {
"type": "array",
"description": self.get_prompt("memories"),
"items": {
"type": "object",
"properties": item_properties,
"required": required_fields,
},
},
},
"required": ["memories"],
}
def _extract_memory_data(self, mem_dict: dict) -> tuple[str, str, dict]:
"""Extract memory data from a dictionary with proper defaults.
Args:
mem_dict: Dictionary containing memory fields.
Returns:
Tuple of (memory_content, when_to_use, metadata).
"""
memory_content = mem_dict.get("memory_content", "")
when_to_use = mem_dict.get("when_to_use", "") if self.add_when_to_use else ""
metadata = mem_dict.get("metadata", {}) if self.add_metadata else {}
return memory_content, when_to_use, metadata
async def execute(self):
"""Execute addition: delete existing IDs (upsert), then insert new memories."""
memory_nodes: list[MemoryNode] = []
if self.enable_multiple:
memories: list[dict] = self.context.get("memories", [])
if not memories:
self.output = "No memories provided for addition."
return
for mem in memories:
memory_content, when_to_use, metadata = self._extract_memory_data(mem)
if not memory_content:
logger.warning("Skipping memory with empty content")
continue
memory_nodes.append(
self._build_memory_node(memory_content, when_to_use, metadata),
)
else:
memory_content, when_to_use, metadata = self._extract_memory_data(self.context)
if not memory_content:
self.output = "No memory content provided for addition."
return
memory_nodes.append(
self._build_memory_node(memory_content, when_to_use, metadata),
)
if not memory_nodes:
self.output = "No valid memories provided for addition."
return
# Convert to VectorNodes and collect IDs
vector_nodes = [node.to_vector_node() for node in memory_nodes]
vector_ids: list[str] = [node.vector_id for node in vector_nodes]
# Delete existing IDs (upsert behavior), then insert
await self.vector_store.delete(vector_ids=vector_ids)
await self.vector_store.insert(nodes=vector_nodes)
self.output = f"Successfully added {len(memory_nodes)} memories to vector_store."
logger.info(self.output)

View file

@ -0,0 +1,37 @@
tool: |
Add a memory to the vector store for future retrieval.
Use this tool to store important information that should be remembered, such as:
- Meta information: "I am very happy"
- Personal preferences: "John prefers dark mode", "Alice works in PST timezone"
- Procedural knowledge: "To deploy, run build then push", "Always validate input before processing"
- Tool usage tips: "search_tool works best with short queries", "Use cache tool for frequently accessed data"
tool_multiple: |
Add multiple memories to the vector store for future retrieval.
Use this tool to store multiple pieces of important information in a single operation.
Each memory can include when_to_use conditions and metadata for better organization and retrieval.
Examples: storing multiple user preferences, multiple procedural steps, or multiple tool usage tips.
when_to_use: |
Optional condition description for when to retrieve this memory.
This field is used for vector embedding to improve retrieval accuracy by providing contextual information.
Examples:
- "when user asks about authentication"
- "when deploying to production"
- "when using search_tool"
- "when handling error cases"
memory_content: |
The content of the memory to store.
Should be a clear, concise statement that captures the information to remember.
Keep it focused on a single piece of information for better retrieval accuracy.
metadata: |
Optional metadata for the memory, providing additional context. Can include:
- time: The timestamp or date associated with the memory (e.g., "2025-01-06 10:30:00")
- source: Where this information came from (e.g., "user_input", "documentation", "observation")
- tags: List of tags for categorization (e.g., ["authentication", "security"])
- Any other custom key-value pairs relevant to the memory
memories: |
A list of memory objects to store.

View file

@ -0,0 +1,67 @@
"""Add summary memory operation for vector store."""
from loguru import logger
from .add_memory import AddMemory
from ....core.context import C
@C.register_op()
class AddSummaryMemory(AddMemory):
"""Add LLM-summarized memories to vector store.
Differences from AddMemory:
- Single memory mode only (enable_multiple=False)
- Uses 'summary_memory' parameter instead of 'memory_content'
- No when_to_use field (add_when_to_use=False)
"""
def __init__(
self,
add_metadata: bool = True,
**kwargs,
):
"""Initialize AddSummaryMemory.
Args:
add_metadata: Include metadata field for additional info.
**kwargs: Additional arguments for AddMemory.
"""
# Force single mode and disable when_to_use
kwargs["enable_multiple"] = False
kwargs["add_when_to_use"] = False
super().__init__(add_metadata=add_metadata, **kwargs)
def _build_parameters(self) -> dict:
"""Build input schema for summary memory addition."""
properties = {
"summary_memory": {
"type": "string",
"description": self.get_prompt("summary_memory"),
},
}
required = ["summary_memory"]
if self.add_metadata:
properties["metadata"] = {
"type": "object",
"description": self.get_prompt("metadata"),
}
return {
"type": "object",
"properties": properties,
"required": required,
}
async def execute(self):
"""Execute addition: map summary_memory to memory_content and call parent."""
# Map summary_memory to memory_content
summary_memory = self.context.get("summary_memory", "")
if not summary_memory:
self.output = "No summary memory content provided for addition."
logger.warning(self.output)
return
self.context["memory_content"] = summary_memory
await super().execute()

View file

@ -0,0 +1,27 @@
tool: |
Add a summary memory to the vector store for future retrieval.
Use this tool to store a summarized version of the provided context.
The LLM should first summarize the context, then call this tool with the summarized content.
This tool is specifically designed for storing summaries of conversations, events, or information
that has been condensed from a larger context. Examples:
- Summarizing a long conversation: "User discussed project requirements for a web app with authentication"
- Summarizing a decision: "Team decided to use PostgreSQL for the database after evaluating options"
- Summarizing an event: "Successfully deployed version 2.0 to production with new features"
summary_memory: |
The summarized content to store as memory.
Should be a clear, concise summary that captures the key information from the context.
Keep it focused and informative - aim for 1-3 sentences that convey the essential points.
Examples:
- "User prefers Python for backend development and has experience with FastAPI framework"
- "Project deadline is January 15th, requires authentication, payment integration, and admin dashboard"
- "Bug in user registration was caused by missing email validation, fixed by adding regex check"
metadata: |
Optional metadata for the memory, providing additional context. Can include:
- time: The timestamp or date associated with the memory (e.g., "2025-01-06 10:30:00")
- source: Where this information came from (e.g., "conversation", "meeting", "observation")
- tags: List of tags for categorization (e.g., ["project", "decision"])
- summary_type: Type of summary (e.g., "conversation", "decision", "event", "task")
- Any other custom key-value pairs relevant to the memory

View file

@ -0,0 +1,60 @@
"""Delete memory operation for vector store."""
from loguru import logger
from ...base_memory_tool import BaseMemoryTool
from ....core.context import C
@C.register_op()
class DeleteMemory(BaseMemoryTool):
"""Delete memories from vector store by IDs.
Supports single/multiple deletion modes via `enable_multiple` parameter.
"""
def _build_parameters(self) -> dict:
"""Build input schema for single memory deletion."""
return {
"type": "object",
"properties": {
"memory_id": {
"type": "string",
"description": self.get_prompt("memory_id"),
},
},
"required": ["memory_id"],
}
def _build_multiple_parameters(self) -> dict:
"""Build input schema for multiple memory deletion."""
return {
"type": "object",
"properties": {
"memory_ids": {
"type": "array",
"description": self.get_prompt("memory_ids"),
"items": {"type": "string"},
},
},
"required": ["memory_ids"],
}
async def execute(self):
"""Execute deletion: remove memories from vector store by IDs."""
if self.enable_multiple:
memory_ids = self.context.get("memory_ids", [])
else:
single_id = self.context.get("memory_id", "")
memory_ids = [single_id] if single_id else []
# Filter out empty IDs
memory_ids = [mid for mid in memory_ids if mid]
if not memory_ids:
self.output = "No valid memory IDs provided for deletion."
return
await self.vector_store.delete(vector_ids=memory_ids)
self.output = f"Successfully deleted {len(memory_ids)} memories from vector_store."
logger.info(self.output)

View file

@ -0,0 +1,25 @@
tool: |
Delete a memory from the vector store using its unique ID.
Use this tool when:
- The user explicitly requests to remove or forget information
- A memory is identified as outdated, incorrect, or no longer relevant
- Information needs to be removed for privacy or compliance reasons
- Duplicate or conflicting memories need to be cleaned up
Memory ID can be obtained from previous memory retrieval results.
tool_multiple: |
Delete multiple memories from the vector store using their unique IDs.
Use this tool for batch deletion when:
- The user explicitly requests to remove or forget multiple pieces of information
- Multiple memories are identified as outdated, incorrect, or no longer relevant
- Bulk cleanup of information is needed for privacy or compliance reasons
- Multiple duplicate or conflicting memories need to be removed
Memory IDs can be obtained from previous memory retrieval results.
memory_id: |
The unique identifier (memory_id) of the memory to delete.
This ID is returned when memories are retrieved or added.
memory_ids: |
A list of unique identifiers (memory_ids) of the memories to delete.
Each ID should be a valid memory_id obtained from previous operations.

View file

@ -0,0 +1,153 @@
"""Update memory operation for vector store."""
from loguru import logger
from ...base_memory_tool import BaseMemoryTool
from ....core.context import C
from ....core.schema import MemoryNode
@C.register_op()
class UpdateMemory(BaseMemoryTool):
"""Update memories by deleting old ones and inserting new ones.
Supports single/multiple update modes via `enable_multiple` parameter.
"""
def __init__(
self,
add_when_to_use: bool = False,
add_metadata: bool = True,
**kwargs,
):
"""Initialize UpdateMemory.
Args:
add_when_to_use: Include when_to_use field for better retrieval.
add_metadata: Include metadata field for additional info.
**kwargs: Additional arguments for BaseMemoryTool.
"""
super().__init__(**kwargs)
self.add_when_to_use: bool = add_when_to_use
self.add_metadata: bool = add_metadata
def _build_item_schema(self) -> tuple[dict, list[str]]:
"""Build shared schema properties and required fields for memory items.
Returns:
Tuple of (properties dict, required fields list).
"""
properties = {
"memory_id": {
"type": "string",
"description": self.get_prompt("memory_id"),
},
}
required = ["memory_id"]
if self.add_when_to_use:
properties["when_to_use"] = {
"type": "string",
"description": self.get_prompt("when_to_use"),
}
properties["memory_content"] = {
"type": "string",
"description": self.get_prompt("memory_content"),
}
required.append("memory_content")
if self.add_metadata:
properties["metadata"] = {
"type": "object",
"description": self.get_prompt("metadata"),
}
return properties, required
def _build_parameters(self) -> dict:
"""Build input schema for single memory update."""
properties, required = self._build_item_schema()
return {
"type": "object",
"properties": properties,
"required": required,
}
def _build_multiple_parameters(self) -> dict:
"""Build input schema for multiple memory update."""
item_properties, required_fields = self._build_item_schema()
return {
"type": "object",
"properties": {
"memories": {
"type": "array",
"description": self.get_prompt("memories"),
"items": {
"type": "object",
"properties": item_properties,
"required": required_fields,
},
},
},
"required": ["memories"],
}
def _extract_memory_data(self, mem_dict: dict) -> tuple[str, str, str, dict]:
"""Extract memory update data from a dictionary with proper defaults.
Args:
mem_dict: Dictionary containing memory fields.
Returns:
Tuple of (memory_id, memory_content, when_to_use, metadata).
"""
memory_id = mem_dict.get("memory_id", "")
memory_content = mem_dict.get("memory_content", "")
when_to_use = mem_dict.get("when_to_use", "") if self.add_when_to_use else ""
metadata = mem_dict.get("metadata", {}) if self.add_metadata else {}
return memory_id, memory_content, when_to_use, metadata
async def execute(self):
"""Execute update: delete old memories by ID, insert new ones with updated content."""
# Collect old IDs to delete and new nodes to insert
old_memory_ids: list[str] = []
new_memory_nodes: list[MemoryNode] = []
if self.enable_multiple:
memories: list[dict] = self.context.get("memories", [])
if not memories:
self.output = "No memories provided for update."
return
for mem in memories:
memory_id, memory_content, when_to_use, metadata = self._extract_memory_data(mem)
if not memory_id or not memory_content:
logger.warning(f"Skipping memory with missing id or content: {mem}")
continue
old_memory_ids.append(memory_id)
new_memory_nodes.append(self._build_memory_node(memory_content, when_to_use, metadata))
else:
memory_id, memory_content, when_to_use, metadata = self._extract_memory_data(self.context)
if not memory_id or not memory_content:
self.output = "No memory ID or content provided for update."
return
old_memory_ids.append(memory_id)
new_memory_nodes.append(self._build_memory_node(memory_content, when_to_use, metadata))
if not old_memory_ids or not new_memory_nodes:
self.output = "No valid memories provided for update."
return
# Convert to VectorNodes and collect IDs
vector_nodes = [node.to_vector_node() for node in new_memory_nodes]
new_vector_ids = [node.vector_id for node in vector_nodes]
# Delete old and duplicate new IDs (upsert behavior)
all_ids_to_delete = list(set(old_memory_ids + new_vector_ids))
await self.vector_store.delete(vector_ids=all_ids_to_delete)
await self.vector_store.insert(nodes=vector_nodes)
self.output = f"Update: deleted {len(old_memory_ids)} old memories, added {len(new_memory_nodes)} new memories."
logger.info(self.output)

View file

@ -0,0 +1,45 @@
tool: |
Update a memory in the vector store by replacing the old memory with new content.
Use this tool when:
- The user wants to modify or correct existing information
- A memory needs to be updated with new details while keeping its relevance
- Information has changed and the old memory is no longer accurate
- You need to refine or improve the clarity of stored information
Memory ID can be obtained from previous memory retrieval results.
tool_multiple: |
Update multiple memories in the vector store by replacing old memories with new content.
Use this tool for batch updates when:
- The user wants to modify or correct multiple pieces of existing information
- Multiple memories need to be updated with new details while keeping their relevance
- Information has changed across multiple memories
- You need to refine or improve multiple stored memories at once
Memory IDs can be obtained from previous memory retrieval results.
memory_id: |
The unique identifier (memory_id) of the old memory to be replaced.
This ID is returned when memories are retrieved or added.
when_to_use: |
Optional condition description for when to retrieve this memory.
This field is used for vector embedding to improve retrieval accuracy by providing contextual information.
Examples:
- "when user asks about authentication"
- "when deploying to production"
- "when using search_tool"
- "when handling error cases"
memory_content: |
The new content of the memory to store.
Should be a clear, concise statement that captures the updated information to remember.
Keep it focused on a single piece of information for better retrieval accuracy.
metadata: |
Optional metadata for the new memory, providing additional context. Can include:
- time: The timestamp or date associated with the memory (e.g., "2025-01-06 10:30:00")
- source: Where this information came from (e.g., "user_input", "documentation", "observation")
- tags: List of tags for categorization (e.g., ["authentication", "security"])
- Any other custom key-value pairs relevant to the memory
memories: |
A list of memory update objects.

View file

@ -0,0 +1,212 @@
"""Vector-based memory retrieval using semantic similarity search."""
from loguru import logger
from ...base_memory_tool import BaseMemoryTool
from ....core.context import C
from ....core.enumeration import MemoryType
from ....core.schema import MemoryNode, VectorNode
from ....core.utils import deduplicate_memories
@C.register_op()
class VectorRetrieveMemory(BaseMemoryTool):
"""Retrieve memories using vector similarity search.
Supports single/multiple query modes via `enable_multiple` parameter.
When `add_memory_type_target` is False, memory_type/memory_target are from context.
"""
def __init__(
self,
enable_summary_memory: bool = False,
add_memory_type_target: bool = False,
top_k: int = 10,
**kwargs,
):
"""Initialize VectorRetrieveMemory.
Args:
enable_summary_memory: Include summary memories in results.
add_memory_type_target: Include memory_type/memory_target in schema (else from context).
top_k: Max memories to retrieve per query.
**kwargs: Additional args for BaseMemoryTool.
"""
super().__init__(**kwargs)
self.enable_summary_memory: bool = enable_summary_memory
self.add_memory_type_target: bool = add_memory_type_target
self.top_k: int = top_k
def _build_query_schema(self) -> tuple[dict, list[str]]:
"""Build shared schema properties and required fields for query items.
Returns:
Tuple of (properties dict, required fields list).
"""
properties = {}
required = []
if self.add_memory_type_target:
properties["memory_type"] = {
"type": "string",
"description": self.get_prompt("memory_type"),
"enum": [
MemoryType.IDENTITY.value,
MemoryType.PERSONAL.value,
MemoryType.PROCEDURAL.value,
MemoryType.TOOL.value,
],
}
properties["memory_target"] = {
"type": "string",
"description": self.get_prompt("memory_target"),
}
required.extend(["memory_type", "memory_target"])
properties["query"] = {
"type": "string",
"description": self.get_prompt("query"),
}
required.append("query")
return properties, required
def _build_parameters(self) -> dict:
"""Build input schema for single query mode.
Returns:
Schema with memory_type/memory_target/query (if add_memory_type_target) or query only.
"""
properties, required = self._build_query_schema()
return {
"type": "object",
"properties": properties,
"required": required,
}
def _build_multiple_parameters(self) -> dict:
"""Build input schema for multiple query mode.
Returns:
Schema with query_items array. Each item has memory_type/memory_target/query
(if add_memory_type_target) or query only.
"""
item_properties, item_required = self._build_query_schema()
return {
"type": "object",
"properties": {
"query_items": {
"type": "array",
"description": self.get_prompt("query_items"),
"items": {
"type": "object",
"properties": item_properties,
"required": item_required,
},
},
},
"required": ["query_items"],
}
async def _retrieve_by_query(
self,
memory_type: str,
memory_target: str,
query: str,
) -> list[MemoryNode]:
"""Retrieve memories by query using vector similarity search.
Args:
memory_type: Memory type to search.
memory_target: Memory target to search.
query: Query string for similarity search.
Returns:
List of matching memories.
"""
memory_type_list = [MemoryType(memory_type)]
if self.enable_summary_memory:
memory_type_list.append(MemoryType.SUMMARY)
filter_dict = {
"memory_type": [mt.value for mt in memory_type_list],
"memory_target": [memory_target],
}
nodes: list[VectorNode] = await self.vector_store.search(
query=query,
top_k=self.top_k,
filter_dict=filter_dict,
)
memory_nodes: list[MemoryNode] = [MemoryNode.from_vector_node(n) for n in nodes]
# Filter TOOL memories: keep only if when_to_use matches query (tool name)
filtered_memory_nodes = [
m for m in memory_nodes if not (m.memory_type == MemoryType.TOOL and m.when_to_use != query)
]
return filtered_memory_nodes
async def execute(self):
"""Execute memory retrieval based on query texts.
Handles single/multiple query modes. When add_memory_type_target is False,
memory_type/memory_target are from context. Outputs formatted results or error message.
"""
default_memory_type: str = self.context.get("memory_type", "")
default_memory_target: str = self.context.get("memory_target", "")
# Normalize to list of query items
if self.enable_multiple:
query_items: list[dict] = self.context.get("query_items", [])
if not query_items:
self.output = "No query items provided for retrieval."
return
else:
query = self.context.get("query", "")
if not query:
self.output = "No query provided for retrieval."
return
query_items = [
{
"memory_type": default_memory_type,
"memory_target": default_memory_target,
"query": query,
},
]
# Filter out items without query text
query_items = [item for item in query_items if item.get("query")]
if not query_items:
self.output = "No valid query texts provided for retrieval."
return
# Retrieve memories for all queries
memories: list[MemoryNode] = []
for item in query_items:
memory_type = item.get("memory_type") or default_memory_type
memory_target = item.get("memory_target") or default_memory_target
if not memory_type or not memory_target:
logger.warning(f"Skipping query with missing memory_type or memory_target: {item}")
continue
retrieved = await self._retrieve_by_query(
memory_type=memory_type,
memory_target=memory_target,
query=item["query"],
)
memories.extend(retrieved)
# Deduplicate and format output
memories = deduplicate_memories(memories)
if not memories:
self.output = "No memories found matching the query."
else:
self.output = "\n".join([m.format_memory() for m in memories])
logger.info(f"Retrieved {len(memories)} memories")

View file

@ -0,0 +1,31 @@
tool: |
Retrieve memories from the memory store using vector similarity search.
Use this tool to find relevant memories based on semantic similarity to the query.
The search returns the most relevant memories ranked by similarity score.
tool_multiple: |
Retrieve memories from the memory store using multiple queries with vector similarity search.
Use this tool to find relevant memories based on semantic similarity to multiple queries.
This is useful when you need to search for different types of information in a single operation.
The search returns the most relevant memories ranked by similarity score for each query.
memory_type: |
The type of memory to search for. Must be one of:
- "identity": Information about the AI agent's identity, role, or characteristics
- "personal": Information about users, their preferences, or personal details
- "procedural": Step-by-step instructions, workflows, or how-to knowledge
- "tool": Tool usage tips, examples, and best practices
memory_target: |
The target of the memory to search within.
- For "personal" memory: the person's name or identifier (e.g., "john", "alice")
- For "procedural" memory: the process or task name (e.g., "deployment", "authentication")
- For "tool" memory: the tool name (e.g., "search_tool", "calculator")
- For "identity" memory: typically "self" or the agent's identifier
query: |
The query text for vector similarity search.
Use descriptive queries that capture the semantic meaning of what you're looking for.
query_items: |
A list of query items for vector similarity search.

View file

@ -6,6 +6,6 @@ from .tavily_search import TavilySearch
__all__ = [
"DashscopeSearch",
"TavilySearch",
"MockSearch",
"TavilySearch",
]

View file

@ -9,9 +9,9 @@ from typing import Literal
from loguru import logger
from ...context import C
from ...op import BaseOp
from ...schema import ToolCall
from ...core.context import C
from ...core.op import BaseOp
from ...core.schema import ToolCall
@C.register_op()

View file

@ -9,11 +9,11 @@ import random
from loguru import logger
from ...context import C
from ...enumeration import Role
from ...op import BaseOp
from ...schema import ToolCall, Message
from ...utils import extract_content
from ...core.context import C
from ...core.enumeration import Role
from ...core.op import BaseOp
from ...core.schema import ToolCall, Message
from ...core.utils import extract_content
@C.register_op()

View file

@ -9,9 +9,9 @@ import os
from loguru import logger
from ...context import C
from ...op import BaseOp
from ...schema import ToolCall
from ...core.context import C
from ...core.op import BaseOp
from ...core.schema import ToolCall
@C.register_op()

View file

@ -0,0 +1,56 @@
"""Think tool for agent reflection and planning.
This module provides a tool that prompts the model for explicit reflection
before taking actions, helping agents reason about their next steps.
"""
from ..core.context import C
from ..core.op import BaseOp
from ..core.schema import ToolCall
@C.register_op()
class ThinkTool(BaseOp):
"""Utility that prompts the model for explicit reflection text.
This tool provides a thinking mechanism for agents to reflect on:
1. Whether current context is sufficient to answer
2. What information is missing
3. Which tool and parameters to use next
"""
def __init__(self, add_output_reflection: bool = False, **kwargs):
"""Initialize the think tool tool.
Args:
add_output_reflection: If True, outputs the reflection content;
if False, outputs a confirmation message
**kwargs: Additional arguments passed to BaseOp
"""
super().__init__(**kwargs)
self.add_output_reflection: bool = add_output_reflection
def _build_tool_call(self) -> ToolCall:
"""Build the tool call schema for think tool."""
return ToolCall(
**{
"description": self.get_prompt("tool"),
"parameters": {
"type": "object",
"properties": {
"reflection": {
"type": "string",
"description": self.get_prompt("reflection"),
},
},
"required": ["reflection"],
},
},
)
async def execute(self):
"""Execute the think tool by processing reflection input."""
if self.add_output_reflection:
self.output = self.context["reflection"]
else:
self.output = self.get_prompt("reflection_output")

View file

@ -0,0 +1,32 @@
tool: |
Before calling any external tool or when rethinking and planning is needed, you must invoke this tool for brief reflection.
The output must cover:
1. Whether the current context is enough to answer the user directly, plus reasoning.
2. If not, what information or validation is missing.
3. A strategy to close the gap: which tool to call next, why, and key parameters or query terms.
Keep the reasoning tightly scoped to the current turn, avoid unrelated background,
and do not execute tools from here—only produce clear, actionable thoughts.
reflection: |
1) Can I answer now? Why?
2) What is missing?
3) Which tool + params next?
reflection_output: |
Reflection has been recorded.
tool_zh: |
每次准备调用任何外部工具之前或者需要重新思考规划,都必须先调用本工具进行简短思考。
输出需覆盖以下要点:
1. 评估当前上下文是否足以直接回答用户问题,并解释理由。
2. 若不能回答,明确缺失的信息或验证步骤。
3. 针对缺口设计下一步策略:列出计划使用的工具、调用目的、关键参数或查询关键词。
思考要紧扣当前轮对话内容,避免复述无关背景,不要直接执行工具,只输出清晰推理。
reflection_zh: |
1) 能直接回答吗?为什么?
2) 缺什么信息?
3) 下一步用哪个工具+参数?
reflection_output_zh: |
已经记录反思

View file

@ -19,7 +19,7 @@ def test_search():
Tests DashscopeSearch, MockSearch, and TavilySearch operations
with a sample query to verify they work correctly.
"""
from reme_ai.core.tool.search import DashscopeSearch, MockSearch, TavilySearch
from reme_ai.tool.search import DashscopeSearch, MockSearch, TavilySearch
query = "今天杭州的天气如何?"
@ -43,7 +43,7 @@ def test_execute():
including successful execution, syntax errors, runtime errors, and
invalid commands to verify error handling.
"""
from reme_ai.core.tool.execute import ExecuteCode, ExecuteShell
from reme_ai.tool.execute import ExecuteCode, ExecuteShell
# Test ExecuteCode
print("\n" + "=" * 60)
@ -155,7 +155,7 @@ def test_simple_chat():
Tests the SimpleChat agent with a basic query to verify
it can process and respond to user input.
"""
from reme_ai.core.agent import SimpleChat
from reme_ai.mem_agent import SimpleChat
op = SimpleChat()
asyncio.run(op.call(query="你好"))
@ -168,7 +168,7 @@ async def test_stream_chat():
Tests the StreamChat agent with a query to verify it can
process and stream responses in real-time using async operations.
"""
from reme_ai.core.agent import StreamChat
from reme_ai.mem_agent import StreamChat
from reme_ai.core.utils import execute_stream_task
from reme_ai.core.context import RuntimeContext
from asyncio import Queue