mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-09-05 08:06:15 +00:00
docs(quick_start): update guide for ReMe MCP usage
- Rename ExperienceMaker to ReMe - Simplify server setup instructions - Add detailed Python client examples for MCP tools - Include personal memory usage instructions - Remove unnecessary sections and clarify existing content
This commit is contained in:
parent
abb1a86aef
commit
883dab7166
7 changed files with 1443 additions and 713 deletions
287
cookbook/simple_demo/use_task_memory_mcp_demo.py
Normal file
287
cookbook/simple_demo/use_task_memory_mcp_demo.py
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Task Memory Demo for MemoryScope using MCP Client
|
||||
|
||||
This script demonstrates how to use the task memory capabilities of MemoryScope
|
||||
through the MCP client interface. It shows how to run an agent, summarize conversations,
|
||||
retrieve memories, and manage the memory workspace.
|
||||
"""
|
||||
|
||||
import json
|
||||
import time
|
||||
import asyncio
|
||||
from typing import List, Dict, Any, Optional
|
||||
|
||||
from fastmcp import Client
|
||||
from mcp.types import CallToolResult
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
# API configuration
|
||||
MCP_URL = "http://0.0.0.0:8002/sse/"
|
||||
WORKSPACE_ID = "test_workspace"
|
||||
|
||||
|
||||
async def delete_workspace(client: Client) -> None:
|
||||
"""
|
||||
Delete the current workspace from the vector store
|
||||
|
||||
Args:
|
||||
client: MCP client instance
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
try:
|
||||
result = await client.call_tool(
|
||||
"vector_store",
|
||||
arguments={
|
||||
"workspace_id": WORKSPACE_ID,
|
||||
"action": "delete",
|
||||
}
|
||||
)
|
||||
print(f"Workspace '{WORKSPACE_ID}' deleted successfully")
|
||||
except Exception as e:
|
||||
print(f"Error deleting workspace: {e}")
|
||||
|
||||
|
||||
async def run_agent(client: Client, query: str, dump_messages: bool = False) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Run the agent with a specific query
|
||||
|
||||
Args:
|
||||
client: MCP client instance
|
||||
query: The query to send to the agent
|
||||
dump_messages: Whether to save messages to a file
|
||||
|
||||
Returns:
|
||||
List of message objects from the conversation
|
||||
"""
|
||||
try:
|
||||
result = await client.call_tool(
|
||||
"react",
|
||||
arguments={"query": query}
|
||||
)
|
||||
|
||||
# Extract and display the answer
|
||||
response_data = json.loads(result.content)
|
||||
answer = response_data.get("answer", "")
|
||||
print(f"Agent response: {answer}")
|
||||
|
||||
# Get the conversation messages
|
||||
messages = response_data.get("messages", [])
|
||||
|
||||
# Optionally save messages to file
|
||||
if dump_messages and messages:
|
||||
with open("messages.jsonl", "w") as f:
|
||||
f.write(json.dumps(messages, indent=2, ensure_ascii=False))
|
||||
print(f"Messages saved to messages.jsonl")
|
||||
|
||||
return messages
|
||||
except Exception as e:
|
||||
print(f"Error running agent: {e}")
|
||||
return []
|
||||
|
||||
|
||||
async def run_summary(client: Client, messages: List[Dict[str, Any]], enable_dump_memory: bool = True) -> None:
|
||||
"""
|
||||
Generate a summary of conversation messages and create task memories
|
||||
|
||||
Args:
|
||||
client: MCP client instance
|
||||
messages: List of message objects from a conversation
|
||||
enable_dump_memory: Whether to save memory list to a file
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
if not messages:
|
||||
print("No messages to summarize")
|
||||
return
|
||||
|
||||
try:
|
||||
result = await client.call_tool(
|
||||
"summary_task_memory",
|
||||
arguments={
|
||||
"workspace_id": WORKSPACE_ID,
|
||||
"trajectories": [
|
||||
{"messages": messages, "score": 1.0}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
response_data = json.loads(result.content)
|
||||
|
||||
# Extract memory list from response
|
||||
memory_list = response_data.get("metadata", {}).get("memory_list", [])
|
||||
print(f"Memory list: {memory_list}")
|
||||
|
||||
# Optionally save memory list to file
|
||||
if enable_dump_memory and memory_list:
|
||||
with open("task_memory.jsonl", "w") as f:
|
||||
f.write(json.dumps(memory_list, indent=2, ensure_ascii=False))
|
||||
print(f"Memory saved to task_memory.jsonl")
|
||||
except Exception as e:
|
||||
print(f"Error running summary: {e}")
|
||||
|
||||
|
||||
async def run_retrieve(client: Client, query: str) -> str:
|
||||
"""
|
||||
Retrieve relevant task memories based on a query
|
||||
|
||||
Args:
|
||||
client: MCP client instance
|
||||
query: The query to retrieve relevant memories
|
||||
|
||||
Returns:
|
||||
String containing the retrieved memory answer
|
||||
"""
|
||||
try:
|
||||
result = await client.call_tool(
|
||||
"retrieve_task_memory",
|
||||
arguments={
|
||||
"workspace_id": WORKSPACE_ID,
|
||||
"query": query,
|
||||
}
|
||||
)
|
||||
|
||||
response_data = json.loads(result.content)
|
||||
|
||||
# Extract and return the answer
|
||||
answer = response_data.get("answer", "")
|
||||
print(f"Retrieved memory: {answer}")
|
||||
return answer
|
||||
except Exception as e:
|
||||
print(f"Error retrieving memory: {e}")
|
||||
return ""
|
||||
|
||||
|
||||
async def run_agent_with_memory(client: Client, query_first: str, query_second: str, enable_dump_memory: bool = True) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Run the agent with memory augmentation
|
||||
|
||||
This function demonstrates how to use task memory to enhance agent responses:
|
||||
1. First run the agent with the second query to build memory
|
||||
2. Then summarize the conversation to create memories
|
||||
3. Retrieve relevant memories for the first query
|
||||
4. Run the agent with the first query augmented with retrieved memories
|
||||
|
||||
Args:
|
||||
client: MCP client instance
|
||||
query_first: The query to run with memory augmentation
|
||||
query_second: The query to build initial memories
|
||||
enable_dump_memory: Whether to save memory list to a file
|
||||
|
||||
Returns:
|
||||
List of message objects from the final conversation
|
||||
"""
|
||||
# Run agent with second query to build initial memories
|
||||
print(f"\n--- Building memories with query: '{query_second}' ---")
|
||||
messages = await run_agent(client, query=query_second)
|
||||
|
||||
# Summarize conversation to create memories
|
||||
print("\n--- Summarizing conversation to create memories ---")
|
||||
await run_summary(client, messages, enable_dump_memory)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# Retrieve relevant memories for the first query
|
||||
print(f"\n--- Retrieving memories for query: '{query_first}' ---")
|
||||
retrieved_memory = await run_retrieve(client, query_first)
|
||||
|
||||
# Run agent with first query augmented with retrieved memories
|
||||
print(f"\n--- Running agent with memory-augmented query ---")
|
||||
augmented_query = f"{retrieved_memory}\n\nUser Question:\n{query_first}"
|
||||
print(f"Augmented query: {augmented_query}")
|
||||
messages = await run_agent(client, query=augmented_query)
|
||||
|
||||
return messages
|
||||
|
||||
|
||||
async def dump_memory(client: Client, path: str = "./") -> None:
|
||||
"""
|
||||
Dump the vector store memories to disk
|
||||
|
||||
Args:
|
||||
client: MCP client instance
|
||||
path: Directory path to save the memories
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
try:
|
||||
result = await client.call_tool(
|
||||
"vector_store",
|
||||
arguments={
|
||||
"workspace_id": WORKSPACE_ID,
|
||||
"action": "dump",
|
||||
"path": path,
|
||||
}
|
||||
)
|
||||
print(f"Memory dumped to {path}")
|
||||
except Exception as e:
|
||||
print(f"Error dumping memory: {e}")
|
||||
|
||||
|
||||
async def load_memory(client: Client, path: str = "./") -> None:
|
||||
"""
|
||||
Load memories from disk into the vector store
|
||||
|
||||
Args:
|
||||
client: MCP client instance
|
||||
path: Directory path to load the memories from
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
try:
|
||||
result = await client.call_tool(
|
||||
"vector_store",
|
||||
arguments={
|
||||
"workspace_id": WORKSPACE_ID,
|
||||
"action": "load",
|
||||
"path": path,
|
||||
}
|
||||
)
|
||||
print(f"Memory loaded from {path}")
|
||||
except Exception as e:
|
||||
print(f"Error loading memory: {e}")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""
|
||||
Main function to demonstrate task memory workflow
|
||||
"""
|
||||
# Define example queries
|
||||
query1 = "Analyze Xiaomi Corporation"
|
||||
query2 = "Analyze the company Tesla."
|
||||
|
||||
print("=== Task Memory Demo (MCP Client) ===")
|
||||
|
||||
async with Client(MCP_URL) as client:
|
||||
# Step 1: Clean up workspace
|
||||
print("\n1. Deleting workspace...")
|
||||
await delete_workspace(client)
|
||||
|
||||
# Step 2: Run agent with first query and save messages
|
||||
print("\n2. Running agent with first query...")
|
||||
await run_agent(client, query=query1, dump_messages=True)
|
||||
|
||||
# Step 3: Demonstrate memory-augmented agent
|
||||
print("\n3. Running memory-augmented agent workflow...")
|
||||
await run_agent_with_memory(client, query_first=query1, query_second=query2)
|
||||
|
||||
# Step 4: Demonstrate memory persistence
|
||||
print("\n4. Dumping memory to disk...")
|
||||
await dump_memory(client)
|
||||
|
||||
print("\n5. Loading memory from disk...")
|
||||
await load_memory(client)
|
||||
|
||||
print("\n=== Demo Complete ===")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
|
@ -1,15 +1,14 @@
|
|||
# ExperienceMaker MCP Quick Start Guide
|
||||
# MCP Quick Start Guide
|
||||
|
||||
This guide will help you get started with ExperienceMaker using the Model Context Protocol (MCP) interface for seamless
|
||||
This guide will help you get started with ReMe using the Model Context Protocol (MCP) interface for seamless
|
||||
integration with MCP-compatible clients.
|
||||
|
||||
## 🚀 What You'll Learn
|
||||
|
||||
- How to set up ExperienceMaker MCP server
|
||||
- Connect to the server using MCP clients
|
||||
- Run an agent and generate experiences via MCP
|
||||
- Retrieve and apply experiences through MCP tools
|
||||
- Build experience-enhanced agents with MCP integration
|
||||
- How to set up and configure ReMe MCP server
|
||||
- How to connect to the server using Python MCP clients
|
||||
- How to use task memory operations through MCP
|
||||
- How to build experience-enhanced agents with MCP integration
|
||||
|
||||
## 📋 Prerequisites
|
||||
|
||||
|
|
@ -23,14 +22,14 @@ integration with MCP-compatible clients.
|
|||
### Option 1: Install from PyPI (Recommended)
|
||||
|
||||
```bash
|
||||
pip install experiencemaker
|
||||
pip install reme_ai
|
||||
```
|
||||
|
||||
### Option 2: Install from Source
|
||||
|
||||
```bash
|
||||
git clone https://github.com/modelscope/ExperienceMaker.git
|
||||
cd ExperienceMaker
|
||||
git clone https://github.com/modelscope/ReMe.git
|
||||
cd ReMe
|
||||
pip install .
|
||||
```
|
||||
|
||||
|
|
@ -39,77 +38,58 @@ pip install .
|
|||
Create a `.env` file in your project directory:
|
||||
|
||||
```bash
|
||||
# Required: LLM API configuration
|
||||
LLM_API_KEY="sk-xxx"
|
||||
LLM_BASE_URL="https://xxx.com/v1"
|
||||
FLOW_EMBEDDING_API_KEY=sk-xxxx
|
||||
FLOW_EMBEDDING_BASE_URL=https://xxxx/v1
|
||||
|
||||
# Required: Embedding model configuration
|
||||
EMBEDDING_MODEL_API_KEY="sk-xxx"
|
||||
EMBEDDING_MODEL_BASE_URL="https://xxx.com/v1"
|
||||
|
||||
# Optional: Elasticsearch configuration (if using Elasticsearch backend)
|
||||
ES_HOSTS="http://localhost:9200"
|
||||
FLOW_LLM_API_KEY=sk-xxxx
|
||||
FLOW_LLM_BASE_URL=https://xxxx/v1
|
||||
```
|
||||
|
||||
## 🚀 Start the MCP Server
|
||||
## 🚀 Building an MCP Server with ReMe
|
||||
|
||||
### Option 1: STDIO Transport (Recommended for MCP clients)
|
||||
ReMe provides a flexible framework for building MCP servers that can communicate using either STDIO or SSE (Server-Sent
|
||||
Events) transport protocols.
|
||||
|
||||
### Starting the MCP Server
|
||||
|
||||
#### Option 1: STDIO Transport (Recommended for MCP clients)
|
||||
|
||||
```bash
|
||||
experiencemaker_mcp \
|
||||
mcp_transport=stdio \
|
||||
llm.default.model_name=qwen3-32b \
|
||||
reme \
|
||||
backend=mcp \
|
||||
mcp.transport=stdio \
|
||||
llm.default.model_name=qwen3-30b-a3b-thinking-2507 \
|
||||
embedding_model.default.model_name=text-embedding-v4 \
|
||||
vector_store.default.backend=local_file
|
||||
vector_store.default.backend=local
|
||||
```
|
||||
|
||||
### Option 2: SSE Transport (Server-Sent Events)
|
||||
#### Option 2: SSE Transport (Server-Sent Events)
|
||||
|
||||
```bash
|
||||
experiencemaker_mcp \
|
||||
mcp_transport=sse \
|
||||
reme \
|
||||
backend=mcp \
|
||||
mcp.transport=sse \
|
||||
http_service.port=8001 \
|
||||
llm.default.model_name=qwen3-32b \
|
||||
llm.default.model_name=qwen3-30b-a3b-thinking-2507 \
|
||||
embedding_model.default.model_name=text-embedding-v4 \
|
||||
vector_store.default.backend=local_file
|
||||
vector_store.default.backend=local
|
||||
```
|
||||
|
||||
The SSE server will start on `http://localhost:8001/sse`
|
||||
The SSE server will start on `http://localhost:8002/sse`
|
||||
|
||||
### Elasticsearch Backend
|
||||
### Configuring MCP Server for Claude Desktop
|
||||
|
||||
```bash
|
||||
experiencemaker_mcp \
|
||||
mcp_transport=stdio \
|
||||
llm.default.model_name=qwen3-32b \
|
||||
embedding_model.default.model_name=text-embedding-v4 \
|
||||
vector_store.default.backend=elasticsearch
|
||||
```
|
||||
|
||||
**Setup Elasticsearch:**
|
||||
|
||||
```bash
|
||||
export ES_HOSTS="http://localhost:9200"
|
||||
# Quick setup using Elastic's official script
|
||||
curl -fsSL https://elastic.co/start-local | sh
|
||||
```
|
||||
|
||||
📖 **Need Help?** Refer to [Vector Store Setup](vector_store_setup.md) for comprehensive deployment guidance.
|
||||
|
||||
## 🔧 Configure MCP Client
|
||||
|
||||
### Claude Desktop Configuration
|
||||
|
||||
Add to your Claude Desktop `claude_desktop_config.json`:
|
||||
To integrate with Claude Desktop, add the following configuration to your `claude_desktop_config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"experiencemaker": {
|
||||
"command": "experiencemaker_mcp",
|
||||
"reme": {
|
||||
"command": "reme",
|
||||
"args": [
|
||||
"mcp_transport=stdio",
|
||||
"llm.default.model_name=qwen3-32b",
|
||||
"backend=mcp",
|
||||
"mcp.transport=stdio",
|
||||
"llm.default.model_name=qwen3-30b-a3b-thinking-2507",
|
||||
"embedding_model.default.model_name=text-embedding-v4",
|
||||
"vector_store.default.backend=local_file"
|
||||
]
|
||||
|
|
@ -118,453 +98,311 @@ Add to your Claude Desktop `claude_desktop_config.json`:
|
|||
}
|
||||
```
|
||||
|
||||
### Custom MCP Client Configuration
|
||||
This configuration:
|
||||
|
||||
If using a custom MCP client, connect to:
|
||||
1. Registers a new MCP server named "reme"
|
||||
2. Specifies the command to launch the server (`reme`)
|
||||
3. Configures the server to use STDIO transport
|
||||
4. Sets the LLM and embedding models to use
|
||||
5. Configures the vector store backend
|
||||
|
||||
- **STDIO**: Use subprocess to communicate with the server
|
||||
- **SSE**: Connect to `http://localhost:8001/sse`
|
||||
### Advanced Server Configuration Options
|
||||
|
||||
## 📝 Using ExperienceMaker MCP Tools
|
||||
For more advanced use cases, you can configure the server with additional parameters:
|
||||
|
||||
The MCP server exposes three main tools:
|
||||
```bash
|
||||
# Full configuration example
|
||||
reme \
|
||||
backend=mcp \
|
||||
mcp.transport=stdio \
|
||||
http_service.host=0.0.0.0 \
|
||||
http_service.port=8002 \
|
||||
llm.default.model_name=qwen3-30b-a3b-thinking-2507 \
|
||||
embedding_model.default.model_name=text-embedding-v4 \
|
||||
vector_store.default.backend=elasticsearch \
|
||||
```
|
||||
|
||||
- `retriever`: Retrieve experiences from workspace
|
||||
- `summarizer`: Transform trajectories into experiences
|
||||
- `vector_store`: Manage vector store operations
|
||||
## 🔌 Using Python Client to Call MCP Services
|
||||
|
||||
Note: The `workspace_id` serves as your experience storage namespace. Experiences in different workspaces remain
|
||||
completely isolated.
|
||||
The ReMe framework provides a Python client for interacting with MCP services. This section focuses specifically on
|
||||
using the `summary_task_memory` and `retrieve_task_memory` tools.
|
||||
|
||||
### 📊 Using the Summarizer Tool
|
||||
### Setting Up the Python MCP Client
|
||||
|
||||
Transform conversation trajectories into valuable experiences using batch summarization.
|
||||
First, install the required packages:
|
||||
|
||||
**Tool Parameters:**
|
||||
```bash
|
||||
pip install fastmcp dotenv
|
||||
```
|
||||
|
||||
- `traj_list`: List of trajectories (each containing messages and score)
|
||||
- `workspace_id`: Workspace identifier (default: "default")
|
||||
- `config`: Additional configuration parameters (optional)
|
||||
|
||||
<details open>
|
||||
<summary><b>Python MCP Client Example</b></summary>
|
||||
Then, create a basic client connection:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from fastmcp import Client
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from experiencemaker.schema.message import Message, Trajectory, Role
|
||||
from experiencemaker.schema.request import SummarizerRequest
|
||||
from experiencemaker.service.mcp_client import MCPClient
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# MCP server URL (for SSE transport)
|
||||
MCP_URL = "http://0.0.0.0:8002/sse/"
|
||||
WORKSPACE_ID = "my_workspace"
|
||||
|
||||
|
||||
async def example_summarizer():
|
||||
async with MCPClient(base_url="http://0.0.0.0:8001/sse") as client:
|
||||
# Create trajectory with conversation
|
||||
trajectory = Trajectory(
|
||||
messages=[
|
||||
Message(role=Role.USER, content="Hello, how can I solve a math problem?"),
|
||||
Message(role=Role.ASSISTANT, content="I'd be happy to help! What math problem are you working on?"),
|
||||
Message(role=Role.USER, content="What is 2+2?"),
|
||||
Message(role=Role.ASSISTANT, content="2+2 equals 4.")
|
||||
],
|
||||
score=1.0 # Success score
|
||||
)
|
||||
|
||||
request = SummarizerRequest(
|
||||
workspace_id="math_workspace",
|
||||
traj_list=[trajectory]
|
||||
)
|
||||
|
||||
response = await client.call_summarizer(request)
|
||||
print("Generated experiences:")
|
||||
for experience in response.experience_list:
|
||||
print(f"- {experience.content}")
|
||||
async def main():
|
||||
async with Client(MCP_URL) as client:
|
||||
# Your MCP operations will go here
|
||||
pass
|
||||
|
||||
|
||||
# Run the example
|
||||
asyncio.run(example_summarizer())
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
</details>
|
||||
### Using the Task Memory Summarizer
|
||||
|
||||
<details>
|
||||
<summary><b>MCP Tool Call (JSON)</b></summary>
|
||||
The `summary_task_memory` tool transforms conversation trajectories into valuable task memories:
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "summarizer",
|
||||
"arguments": {
|
||||
"traj_list": [
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello, how can I solve a math problem?"
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "I'd be happy to help! What math problem are you working on?"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What is 2+2?"
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "2+2 equals 4."
|
||||
```python
|
||||
async def run_summary(client, messages):
|
||||
"""
|
||||
Generate a summary of conversation messages and create task memories
|
||||
|
||||
Args:
|
||||
client: MCP client instance
|
||||
messages: List of message objects from a conversation
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
try:
|
||||
result = await client.call_tool(
|
||||
"summary_task_memory",
|
||||
arguments={
|
||||
"workspace_id": "my_workspace",
|
||||
"trajectories": [
|
||||
{"messages": messages, "score": 1.0}
|
||||
]
|
||||
}
|
||||
],
|
||||
"score": 1.0
|
||||
)
|
||||
|
||||
# Parse the response
|
||||
import json
|
||||
response_data = json.loads(result.content)
|
||||
|
||||
# Extract memory list from response
|
||||
memory_list = response_data.get("metadata", {}).get("memory_list", [])
|
||||
print(f"Created memories: {memory_list}")
|
||||
|
||||
# Optionally save memories to file
|
||||
with open("task_memory.jsonl", "w") as f:
|
||||
f.write(json.dumps(memory_list, indent=2, ensure_ascii=False))
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error running summary: {e}")
|
||||
```
|
||||
|
||||
### Using the Task Memory Retriever
|
||||
|
||||
The `retrieve_task_memory` tool allows you to retrieve relevant memories based on a query:
|
||||
|
||||
```python
|
||||
async def run_retrieve(client, query):
|
||||
"""
|
||||
Retrieve relevant task memories based on a query
|
||||
|
||||
Args:
|
||||
client: MCP client instance
|
||||
query: The query to retrieve relevant memories
|
||||
|
||||
Returns:
|
||||
String containing the retrieved memory answer
|
||||
"""
|
||||
try:
|
||||
result = await client.call_tool(
|
||||
"retrieve_task_memory",
|
||||
arguments={
|
||||
"workspace_id": "my_workspace",
|
||||
"query": query,
|
||||
}
|
||||
)
|
||||
|
||||
# Parse the response
|
||||
import json
|
||||
response_data = json.loads(result.content)
|
||||
|
||||
# Extract and return the answer
|
||||
answer = response_data.get("answer", "")
|
||||
print(f"Retrieved memory: {answer}")
|
||||
return answer
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error retrieving memory: {e}")
|
||||
return ""
|
||||
```
|
||||
|
||||
### Complete Memory-Augmented Agent Example
|
||||
|
||||
Here's a complete example showing how to build a memory-augmented agent using the MCP client:
|
||||
|
||||
```python
|
||||
import json
|
||||
import asyncio
|
||||
from fastmcp import Client
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# API configuration
|
||||
MCP_URL = "http://0.0.0.0:8002/sse/"
|
||||
WORKSPACE_ID = "test_workspace"
|
||||
|
||||
|
||||
async def run_agent(client, query):
|
||||
"""Run the agent with a specific query"""
|
||||
result = await client.call_tool(
|
||||
"react",
|
||||
arguments={"query": query}
|
||||
)
|
||||
|
||||
response_data = json.loads(result.content)
|
||||
answer = response_data.get("answer", "")
|
||||
messages = response_data.get("messages", [])
|
||||
|
||||
return messages
|
||||
|
||||
|
||||
async def run_summary(client, messages):
|
||||
"""Generate task memories from conversation"""
|
||||
result = await client.call_tool(
|
||||
"summary_task_memory",
|
||||
arguments={
|
||||
"workspace_id": WORKSPACE_ID,
|
||||
"trajectories": [
|
||||
{"messages": messages, "score": 1.0}
|
||||
]
|
||||
}
|
||||
],
|
||||
"workspace_id": "math_workspace"
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
response_data = json.loads(result.content)
|
||||
memory_list = response_data.get("metadata", {}).get("memory_list", [])
|
||||
|
||||
return memory_list
|
||||
|
||||
|
||||
async def run_retrieve(client, query):
|
||||
"""Retrieve relevant task memories"""
|
||||
result = await client.call_tool(
|
||||
"retrieve_task_memory",
|
||||
arguments={
|
||||
"workspace_id": WORKSPACE_ID,
|
||||
"query": query,
|
||||
}
|
||||
)
|
||||
|
||||
response_data = json.loads(result.content)
|
||||
answer = response_data.get("answer", "")
|
||||
|
||||
return answer
|
||||
|
||||
|
||||
async def memory_augmented_workflow():
|
||||
"""Complete memory-augmented agent workflow"""
|
||||
query1 = "Analyze Xiaomi Corporation"
|
||||
query2 = "Analyze the company Tesla."
|
||||
|
||||
async with Client(MCP_URL) as client:
|
||||
# Step 1: Build initial memories with query2
|
||||
print(f"Building memories with: '{query2}'")
|
||||
messages = await run_agent(client, query=query2)
|
||||
|
||||
# Step 2: Summarize conversation to create memories
|
||||
print("Creating memories from conversation")
|
||||
memory_list = await run_summary(client, messages)
|
||||
print(f"Created {len(memory_list)} memories")
|
||||
|
||||
# Step 3: Retrieve relevant memories for query1
|
||||
print(f"Retrieving memories for: '{query1}'")
|
||||
retrieved_memory = await run_retrieve(client, query1)
|
||||
|
||||
# Step 4: Run agent with memory-augmented query
|
||||
print("Running memory-augmented agent")
|
||||
augmented_query = f"{retrieved_memory}\n\nUser Question:\n{query1}"
|
||||
final_messages = await run_agent(client, query=augmented_query)
|
||||
|
||||
# Extract the agent's final answer
|
||||
final_answer = ""
|
||||
for msg in final_messages:
|
||||
if msg.get("role") == "assistant" and msg.get("content"):
|
||||
final_answer = msg.get("content")
|
||||
break
|
||||
|
||||
print(f"Memory-augmented response: {final_answer}")
|
||||
|
||||
|
||||
# Run the workflow
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(memory_augmented_workflow())
|
||||
```
|
||||
|
||||
</details>
|
||||
### Managing Vector Store with MCP
|
||||
|
||||
### 🔍 Using the Retriever Tool
|
||||
|
||||
Intelligently search and retrieve the most relevant experiences from your workspace.
|
||||
|
||||
**Tool Parameters:**
|
||||
|
||||
- `query`: Search query string
|
||||
- `messages`: List of conversation messages (optional)
|
||||
- `top_k`: Number of top experiences to retrieve (default: 1)
|
||||
- `workspace_id`: Workspace identifier (default: "default")
|
||||
- `config`: Additional configuration parameters (optional)
|
||||
|
||||
<details open>
|
||||
<summary><b>Python MCP Client Example</b></summary>
|
||||
You can also manage your vector store through MCP:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from experiencemaker.service.mcp_client import MCPClient
|
||||
from experiencemaker.schema.request import RetrieverRequest
|
||||
async def manage_vector_store(client):
|
||||
# Delete a workspace
|
||||
await client.call_tool(
|
||||
"vector_store",
|
||||
arguments={
|
||||
"workspace_id": WORKSPACE_ID,
|
||||
"action": "delete",
|
||||
}
|
||||
)
|
||||
|
||||
# Dump memories to disk
|
||||
await client.call_tool(
|
||||
"vector_store",
|
||||
arguments={
|
||||
"workspace_id": WORKSPACE_ID,
|
||||
"action": "dump",
|
||||
"path": "./backups/",
|
||||
}
|
||||
)
|
||||
|
||||
async def example_retriever():
|
||||
async with MCPClient(base_url="http://0.0.0.0:8001/sse") as client:
|
||||
request = RetrieverRequest(
|
||||
workspace_id="math_workspace",
|
||||
query="How to solve basic arithmetic problems?",
|
||||
top_k=3
|
||||
)
|
||||
|
||||
response = await client.call_retriever(request)
|
||||
print(f"Retrieved experiences: {response.experience_merged}")
|
||||
print(f"Experience list:")
|
||||
for exp in response.experience_list:
|
||||
print(f"- {exp.content}")
|
||||
|
||||
|
||||
# Run the example
|
||||
asyncio.run(example_retriever())
|
||||
# Load memories from disk
|
||||
await client.call_tool(
|
||||
"vector_store",
|
||||
arguments={
|
||||
"workspace_id": WORKSPACE_ID,
|
||||
"action": "load",
|
||||
"path": "./backups/",
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>MCP Tool Call (JSON)</b></summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "retriever",
|
||||
"arguments": {
|
||||
"query": "How to solve basic arithmetic problems?",
|
||||
"top_k": 3,
|
||||
"workspace_id": "math_workspace"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### 💾 Using the Vector Store Tool
|
||||
|
||||
Manage vector store operations for workspace data.
|
||||
|
||||
**Tool Parameters:**
|
||||
|
||||
- `action`: Action to perform ("dump", "load", "delete", "copy")
|
||||
- `workspace_id`: Target workspace identifier
|
||||
- `src_workspace_id`: Source workspace (for copy operation)
|
||||
- `path`: File system path (for dump/load operations, default: "./")
|
||||
- `config`: Additional configuration parameters (optional)
|
||||
|
||||
#### Dump Experiences From Vector Store
|
||||
|
||||
<details open>
|
||||
<summary><b>Python MCP Client Example</b></summary>
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from experiencemaker.service.mcp_client import MCPClient
|
||||
from experiencemaker.schema.request import VectorStoreRequest
|
||||
|
||||
|
||||
async def example_dump():
|
||||
async with MCPClient(base_url="http://0.0.0.0:8001/sse") as client:
|
||||
request = VectorStoreRequest(
|
||||
workspace_id="math_workspace",
|
||||
action="dump",
|
||||
path="./backups/"
|
||||
)
|
||||
|
||||
response = await client.call_vector_store(request)
|
||||
print(f"Dump result: {response}")
|
||||
|
||||
|
||||
# Run the example
|
||||
asyncio.run(example_dump())
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>MCP Tool Call (JSON)</b></summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "vector_store",
|
||||
"arguments": {
|
||||
"action": "dump",
|
||||
"workspace_id": "math_workspace",
|
||||
"path": "./backups/"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Load Experiences To Vector Store
|
||||
|
||||
<details open>
|
||||
<summary><b>Python MCP Client Example</b></summary>
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from experiencemaker.service.mcp_client import MCPClient
|
||||
from experiencemaker.schema.request import VectorStoreRequest
|
||||
|
||||
|
||||
async def example_load():
|
||||
async with MCPClient(base_url="http://0.0.0.0:8001/sse") as client:
|
||||
request = VectorStoreRequest(
|
||||
workspace_id="math_workspace",
|
||||
action="load",
|
||||
path="./backups/"
|
||||
)
|
||||
|
||||
response = await client.call_vector_store(request)
|
||||
print(f"Load result: {response}")
|
||||
|
||||
|
||||
# Run the example
|
||||
asyncio.run(example_load())
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Delete Workspace
|
||||
|
||||
<details open>
|
||||
<summary><b>Python MCP Client Example</b></summary>
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from experiencemaker.service.mcp_client import MCPClient
|
||||
from experiencemaker.schema.request import VectorStoreRequest
|
||||
|
||||
|
||||
async def example_delete():
|
||||
async with MCPClient(base_url="http://0.0.0.0:8001/sse") as client:
|
||||
request = VectorStoreRequest(
|
||||
workspace_id="math_workspace",
|
||||
action="delete"
|
||||
)
|
||||
|
||||
response = await client.call_vector_store(request)
|
||||
print(f"Delete result: {response}")
|
||||
|
||||
|
||||
# Run the example
|
||||
asyncio.run(example_delete())
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Copy Workspace
|
||||
|
||||
<details open>
|
||||
<summary><b>Python MCP Client Example</b></summary>
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from experiencemaker.service.mcp_client import MCPClient
|
||||
from experiencemaker.schema.request import VectorStoreRequest
|
||||
|
||||
|
||||
async def example_copy():
|
||||
async with MCPClient(base_url="http://0.0.0.0:8001/sse") as client:
|
||||
request = VectorStoreRequest(
|
||||
workspace_id="math_workspace_copy",
|
||||
action="copy",
|
||||
src_workspace_id="math_workspace"
|
||||
)
|
||||
|
||||
response = await client.call_vector_store(request)
|
||||
print(f"Copy result: {response}")
|
||||
|
||||
|
||||
# Run the example
|
||||
asyncio.run(example_copy())
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## 🔄 Complete MCP Workflow Example
|
||||
|
||||
Here's a complete example showing the full workflow:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from experiencemaker.service.mcp_client import MCPClient
|
||||
from experiencemaker.schema.request import SummarizerRequest, RetrieverRequest
|
||||
from experiencemaker.schema.message import Message, Trajectory, Role
|
||||
|
||||
async def complete_workflow():
|
||||
async with MCPClient(base_url="http://0.0.0.0:8001/sse") as client:
|
||||
print("Available tools:", await client.list_tools())
|
||||
|
||||
# Step 1: Create experiences from trajectories
|
||||
trajectory = Trajectory(
|
||||
messages=[
|
||||
Message(role=Role.USER, content="How do I calculate compound interest?"),
|
||||
Message(role=Role.ASSISTANT,
|
||||
content="Compound interest is calculated using the formula A = P(1 + r/n)^(nt), where A is the final amount, P is the principal, r is the annual interest rate, n is the number of times interest is compounded per year, and t is the time in years."),
|
||||
Message(role=Role.USER, content="Can you give me an example?"),
|
||||
Message(role=Role.ASSISTANT,
|
||||
content="Sure! If you invest $1000 at 5% annual interest compounded monthly for 2 years: A = 1000(1 + 0.05/12)^(12*2) = $1104.94")
|
||||
],
|
||||
score=1.0
|
||||
)
|
||||
|
||||
summarizer_request = SummarizerRequest(
|
||||
workspace_id="finance_workspace",
|
||||
traj_list=[trajectory]
|
||||
)
|
||||
|
||||
summarizer_response = await client.call_summarizer(summarizer_request)
|
||||
print(f"Created {len(summarizer_response.experience_list)} experiences")
|
||||
|
||||
# Step 2: Retrieve relevant experiences
|
||||
retriever_request = RetrieverRequest(
|
||||
workspace_id="finance_workspace",
|
||||
query="How to calculate interest on investments?",
|
||||
top_k=2
|
||||
)
|
||||
|
||||
retriever_response = await client.call_retriever(retriever_request)
|
||||
print(f"Retrieved experiences: {retriever_response.experience_merged}")
|
||||
|
||||
|
||||
# Run the complete workflow
|
||||
asyncio.run(complete_workflow())
|
||||
```
|
||||
|
||||
## 🎭 Claude Desktop Integration
|
||||
|
||||
Once configured with Claude Desktop, you can directly ask Claude to use ExperienceMaker tools:
|
||||
|
||||
```
|
||||
Claude, please use the summarizer tool to create experiences from this conversation about solving math problems, then retrieve similar experiences when I ask about arithmetic.
|
||||
```
|
||||
|
||||
Claude will automatically call the appropriate MCP tools and provide contextually relevant responses based on your
|
||||
stored experiences.
|
||||
|
||||
## 🐛 Common Issues
|
||||
## 🐛 Common Issues and Troubleshooting
|
||||
|
||||
### MCP Server Won't Start
|
||||
|
||||
- Check if the required ports are available (for SSE transport)
|
||||
- Verify your API keys in `.env` file
|
||||
- Ensure Python version is 3.12+
|
||||
- Check MCP transport configuration
|
||||
|
||||
### MCP Client Connection Issues
|
||||
|
||||
- For STDIO: Ensure the command path is correct in your MCP client config
|
||||
- For SSE: Verify the server URL and port accessibility
|
||||
- Check firewall settings for SSE connections
|
||||
|
||||
### No Experiences Retrieved
|
||||
### No Memories Retrieved
|
||||
|
||||
- Make sure you've run the summarizer tool first to create experiences
|
||||
- Make sure you've run the summarizer tool first to create memories
|
||||
- Check if workspace_id matches between operations
|
||||
- Verify vector store backend is properly configured
|
||||
|
||||
### API Connection Errors
|
||||
|
||||
- Confirm LLM_BASE_URL and API keys are correct
|
||||
- Test API access independently
|
||||
- Check network connectivity
|
||||
|
||||
## 🔧 Advanced Configuration
|
||||
|
||||
### Custom MCP Client Setup
|
||||
|
||||
```python
|
||||
# For STDIO transport
|
||||
async with MCPClient(enable_sse=False) as client:
|
||||
# Your MCP operations here
|
||||
pass
|
||||
|
||||
# For SSE transport with custom URL
|
||||
async with MCPClient(base_url="http://custom-host:8001/sse") as client:
|
||||
# Your MCP operations here
|
||||
pass
|
||||
```
|
||||
|
||||
### Server Configuration Options
|
||||
|
||||
```bash
|
||||
# Full configuration example
|
||||
experiencemaker_mcp \
|
||||
mcp_transport=stdio \
|
||||
http_service.host=0.0.0.0 \
|
||||
http_service.port=8001 \
|
||||
llm.default.model_name=qwen3-32b \
|
||||
llm.default.api_key=${LLM_API_KEY} \
|
||||
llm.default.base_url=${LLM_BASE_URL} \
|
||||
embedding_model.default.model_name=text-embedding-v4 \
|
||||
embedding_model.default.api_key=${EMBEDDING_MODEL_API_KEY} \
|
||||
embedding_model.default.base_url=${EMBEDDING_MODEL_BASE_URL} \
|
||||
vector_store.default.backend=elasticsearch \
|
||||
vector_store.default.host=localhost \
|
||||
vector_store.default.port=9200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
🎯 **You're all set!** You now have a working ExperienceMaker MCP setup that can seamlessly integrate with MCP-compatible
|
||||
clients and learn from interactions to improve over time through the standardized MCP protocol.
|
||||
|
||||
## 📚 Next Steps
|
||||
|
||||
- Explore the [Configuration Guide](configuration_guide.md) for advanced customization
|
||||
- Check out [cookbook examples](../cookbook/) for practical implementations
|
||||
- Learn about [Vector Store Setup](vector_store_setup.md) for production deployments
|
||||
- Review the [Operations Documentation](operations_documentation.md) for maintenance procedures
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
# Personal Memory in reme
|
||||
|
||||
## Configuration Logic
|
||||
|
||||
reme's personal memory system consists of two main components: retrieval and summarization. The configuration for these components is defined in the default.yaml file.
|
||||
|
||||
### Retrieval Configuration (`retrieve_personal_memory`)
|
||||
|
||||
```yaml
|
||||
retrieve_personal_memory:
|
||||
flow_content: set_query_op >> (extract_time_op | (retrieve_memory_op >> semantic_rank_op)) >> fuse_rerank_op
|
||||
```
|
||||
|
||||
This flow performs the following operations:
|
||||
1. `set_query_op`: Prepares the query for memory retrieval
|
||||
2. Parallel paths:
|
||||
- `extract_time_op`: Extracts time-related information from the query
|
||||
- `retrieve_memory_op >> semantic_rank_op`: Retrieves memories and ranks them semantically
|
||||
3. `fuse_rerank_op`: Combines and reranks the results for final output
|
||||
|
||||
### Summarization Configuration (`summary_personal_memory`)
|
||||
|
||||
```yaml
|
||||
summary_personal_memory:
|
||||
flow_content: info_filter_op >> (get_observation_op | get_observation_with_time_op | load_today_memory_op) >> contra_repeat_op >> update_vector_store_op
|
||||
```
|
||||
|
||||
This flow performs the following operations:
|
||||
1. `info_filter_op`: Filters incoming information to extract relevant personal details
|
||||
2. Parallel paths for observation extraction:
|
||||
- `get_observation_op`: Extracts general observations
|
||||
- `get_observation_with_time_op`: Extracts observations with time context
|
||||
- `load_today_memory_op`: Loads memories from the current day
|
||||
3. `contra_repeat_op`: Removes contradictions and repetitions
|
||||
4. `update_vector_store_op`: Stores the processed memories in the vector database
|
||||
|
||||
## Basic Usage
|
||||
|
||||
The following example demonstrates how to use personal memory in MemoryScope:
|
||||
|
||||
### 1. Setup
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
import json
|
||||
import aiohttp
|
||||
|
||||
# API base URL (default is http://0.0.0.0:8002)
|
||||
base_url = "http://0.0.0.0:8002"
|
||||
workspace_id = "personal_memory_demo"
|
||||
```
|
||||
|
||||
### 2. Clear Existing Memories
|
||||
|
||||
```python
|
||||
async with aiohttp.ClientSession() as session:
|
||||
# Delete existing workspace memories
|
||||
async with session.post(
|
||||
f"{base_url}/vector_store",
|
||||
json={
|
||||
"action": "delete",
|
||||
"workspace_id": workspace_id,
|
||||
},
|
||||
headers={"Content-Type": "application/json"}
|
||||
) as response:
|
||||
result = await response.json()
|
||||
```
|
||||
|
||||
### 3. Create Conversation with Personal Information
|
||||
|
||||
```python
|
||||
# Example conversation with personal details
|
||||
messages = [
|
||||
{"role": "user", "content": "My name is John Smith, I'm 28 years old"},
|
||||
{"role": "assistant", "content": "Nice to meet you, John!"},
|
||||
{"role": "user", "content": "I'm a software engineer working with Python"},
|
||||
{"role": "assistant", "content": "I see, you're a Python engineer."},
|
||||
# Additional conversation messages...
|
||||
]
|
||||
```
|
||||
|
||||
### 4. Summarize Personal Memories
|
||||
|
||||
```python
|
||||
async with session.post(
|
||||
f"{base_url}/summary_personal_memory",
|
||||
json={
|
||||
"trajectories": [
|
||||
{"messages": messages, "score": 1.0}
|
||||
],
|
||||
"workspace_id": workspace_id,
|
||||
},
|
||||
headers={"Content-Type": "application/json"}
|
||||
) as response:
|
||||
result = await response.json()
|
||||
```
|
||||
|
||||
### 5. Retrieve Personal Memories
|
||||
|
||||
```python
|
||||
# Example queries to retrieve personal information
|
||||
queries = [
|
||||
"What's my name and age?",
|
||||
"What do I do for work?",
|
||||
"What are my hobbies?"
|
||||
]
|
||||
|
||||
for query in queries:
|
||||
async with session.post(
|
||||
f"{base_url}/retrieve_personal_memory",
|
||||
json={
|
||||
"query": query,
|
||||
"workspace_id": workspace_id,
|
||||
},
|
||||
headers={"Content-Type": "application/json"}
|
||||
) as response:
|
||||
result = await response.json()
|
||||
print(f"Query: {query}")
|
||||
print(f"Answer: {result.get('answer', '')}")
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
For a complete working example, refer to `/cookbook/simple_demo/use_personal_memory_demo.py` in the reme repository.
|
||||
152
doc/personal_memory/personal_retrieve_ops.md
Normal file
152
doc/personal_memory/personal_retrieve_ops.md
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
# Personal Memory Retrieve Operations
|
||||
|
||||
This document describes the operations used in the personal memory retrieval flow of MemoryScope. The retrieval flow is defined in the configuration as:
|
||||
|
||||
```yaml
|
||||
retrieve_personal_memory:
|
||||
flow_content: set_query_op >> (extract_time_op | (retrieve_memory_op >> semantic_rank_op)) >> fuse_rerank_op
|
||||
```
|
||||
|
||||
## SetQueryOp
|
||||
|
||||
### Functionality
|
||||
`SetQueryOp` prepares the query for memory retrieval by setting the query and its associated timestamp into the context. It's the first operation in the personal memory retrieval flow.
|
||||
|
||||
### Parameters
|
||||
- `op.set_query_op.params.timestamp`: (Optional) Integer timestamp to use instead of the current time. If not provided, the current timestamp will be used.
|
||||
|
||||
### Implementation Details
|
||||
The operation:
|
||||
1. Takes the query from the context (which is guaranteed to exist as a flow input requirement)
|
||||
2. Sets a timestamp (either current time or from parameters)
|
||||
3. Stores the query and timestamp as a tuple in the context for downstream operations
|
||||
|
||||
## ExtractTimeOp
|
||||
|
||||
### Functionality
|
||||
`ExtractTimeOp` identifies and extracts time-related information from the query. It uses an LLM to analyze the query text and determine any temporal references or constraints.
|
||||
|
||||
### Parameters
|
||||
- `op.extract_time_op.params.language`: Language for time extraction (defaults to "en")
|
||||
|
||||
### Implementation Details
|
||||
The operation:
|
||||
1. Checks if the query contains datetime keywords
|
||||
2. If time-related words are found, it prepares a prompt for the LLM with:
|
||||
- System instructions
|
||||
- Few-shot examples
|
||||
- The user's query and current time
|
||||
3. Parses the LLM response to extract time information (year, month, day, etc.)
|
||||
4. Stores the extracted time dictionary in the context for downstream operations
|
||||
|
||||
## RetrieveMemoryOp
|
||||
|
||||
### Functionality
|
||||
`RetrieveMemoryOp` retrieves memories from the vector store based on the query. It extends the `RecallVectorStoreOp` class to provide memory retrieval functionality.
|
||||
|
||||
### Parameters
|
||||
- `op.retrieve_memory_op.params.recall_key`: Key in the context to use as the query (default: "query")
|
||||
- `op.retrieve_memory_op.params.top_k`: Maximum number of memories to retrieve (default: 3)
|
||||
- `op.retrieve_memory_op.params.threshold_score`: (Optional) Minimum similarity score for memories (filters out memories below this threshold)
|
||||
|
||||
### Implementation Details
|
||||
The operation:
|
||||
1. Retrieves the query from the context
|
||||
2. Searches the vector store for relevant memories based on the query
|
||||
3. Removes duplicate memories
|
||||
4. Filters memories by threshold score if specified
|
||||
5. Stores the retrieved memories in the context for downstream operations
|
||||
|
||||
## SemanticRankOp
|
||||
|
||||
### Functionality
|
||||
`SemanticRankOp` ranks memories based on their semantic relevance to the query using an LLM. This improves the quality of retrieved memories by considering deeper semantic relationships beyond vector similarity.
|
||||
|
||||
### Parameters
|
||||
- `op.semantic_rank_op.params.enable_ranker`: Whether to enable semantic ranking (default: true)
|
||||
- `op.semantic_rank_op.params.output_memory_max_count`: Maximum number of memories to output (default: 10)
|
||||
|
||||
### Implementation Details
|
||||
The operation:
|
||||
1. Retrieves the memory list from the context
|
||||
2. If ranking is enabled and there are more memories than the output limit:
|
||||
- Removes duplicates based on content
|
||||
- Formats memories for LLM ranking
|
||||
- Asks the LLM to rank memories by relevance on a scale of 0.0 to 1.0
|
||||
- Parses the ranking results and applies scores to memories
|
||||
3. Sorts memories by score
|
||||
4. Stores the ranked memories in the context for downstream operations
|
||||
|
||||
## FuseRerankOp
|
||||
|
||||
### Functionality
|
||||
`FuseRerankOp` performs the final reranking of memories by combining multiple factors: semantic scores, memory types, and temporal relevance. It also formats the final output.
|
||||
|
||||
### Parameters
|
||||
- `op.fuse_rerank_op.params.fuse_score_threshold`: Minimum score threshold for memories (default: 0.1)
|
||||
- `op.fuse_rerank_op.params.fuse_ratio_dict`: Dictionary of memory type to score multiplier ratios (default: {"conversation": 0.5, "observation": 1, "obs_customized": 1.2, "insight": 2.0})
|
||||
- `op.fuse_rerank_op.params.fuse_time_ratio`: Score multiplier for time-relevant memories (default: 2.0)
|
||||
- `op.fuse_rerank_op.params.output_memory_max_count`: Maximum number of memories to output (default: 5)
|
||||
|
||||
### Implementation Details
|
||||
The operation:
|
||||
1. Retrieves extracted time information and memory list from the context
|
||||
2. For each memory:
|
||||
- Checks if the memory score is above the threshold
|
||||
- Applies a type-based adjustment factor based on the memory type
|
||||
- Determines time relevance by matching memory time metadata with extracted time
|
||||
- Calculates the final score by multiplying the original score by type and time factors
|
||||
3. Sorts memories by the reranked scores
|
||||
4. Selects the top-K memories based on the output limit
|
||||
5. Formats memories for output with timestamps if available
|
||||
6. Stores both the formatted output and the memory list in the context
|
||||
|
||||
## PrintMemoryOp
|
||||
|
||||
### Functionality
|
||||
`PrintMemoryOp` formats the retrieved memories for display to the user. It provides a clean, structured representation of the memory content.
|
||||
|
||||
### Parameters
|
||||
No specific parameters for this operation.
|
||||
|
||||
### Implementation Details
|
||||
The operation:
|
||||
1. Retrieves the memory list from the context
|
||||
2. Formats each memory with:
|
||||
- Memory index
|
||||
- When to use information
|
||||
- Content
|
||||
- Additional metadata (if available)
|
||||
3. Joins the formatted memories into a single string
|
||||
4. Stores the formatted string in the context as the response answer
|
||||
|
||||
## ReadMessageOp
|
||||
|
||||
### Functionality
|
||||
`ReadMessageOp` fetches unmemorized chat messages from the context. This is useful for retrieving recent conversations that haven't been processed into memories yet.
|
||||
|
||||
### Parameters
|
||||
- `op.read_message_op.params.contextual_msg_max_count`: Maximum number of contextual messages to retrieve (default: 10)
|
||||
|
||||
### Implementation Details
|
||||
The operation:
|
||||
1. Retrieves chat messages from the context
|
||||
2. Filters for messages that:
|
||||
- Are not marked as memorized
|
||||
- Contain the target name
|
||||
3. Flattens the messages into a single list
|
||||
4. Sorts messages by creation time if available
|
||||
5. Stores the filtered messages back in the context
|
||||
|
||||
## Complete Flow Execution
|
||||
|
||||
When the personal memory retrieval flow executes:
|
||||
|
||||
1. `SetQueryOp` prepares the query with timestamp
|
||||
2. Two parallel paths execute:
|
||||
- `ExtractTimeOp` extracts time information from the query
|
||||
- `RetrieveMemoryOp` followed by `SemanticRankOp` retrieves and ranks memories
|
||||
3. `FuseRerankOp` combines the results, considering both semantic relevance and time information
|
||||
4. The final output is a ranked list of memories relevant to the query
|
||||
|
||||
This flow provides a sophisticated memory retrieval system that considers semantic relevance, memory types, and temporal context to deliver the most appropriate memories for a given query.
|
||||
196
doc/personal_memory/personal_summary_ops.md
Normal file
196
doc/personal_memory/personal_summary_ops.md
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
# Personal Memory Summary Operations
|
||||
|
||||
This document provides a detailed overview of the operations (ops) used in the personal memory summarization flow in MemoryScope. Each operation plays a specific role in processing, filtering, and storing personal memories.
|
||||
|
||||
## Overview
|
||||
|
||||
As defined in the configuration, the personal memory summarization flow follows this sequence:
|
||||
|
||||
```yaml
|
||||
summary_personal_memory:
|
||||
flow_content: info_filter_op >> (get_observation_op | get_observation_with_time_op | load_today_memory_op) >> contra_repeat_op >> update_vector_store_op
|
||||
```
|
||||
|
||||
This document describes each operation in detail, including its purpose, parameters, and configuration options.
|
||||
|
||||
## InfoFilterOp
|
||||
|
||||
### Purpose
|
||||
Filters messages based on information content scores, retaining only those that include significant information about the user.
|
||||
|
||||
### Parameters
|
||||
- `op.info_filter_op.params.preserved_scores`: Comma-separated string of scores to preserve (default: "2,3")
|
||||
- `op.info_filter_op.params.info_filter_msg_max_size`: Maximum size of messages to process (default: 200)
|
||||
|
||||
### Description
|
||||
This operation analyzes messages to determine which ones contain valuable personal information. It uses an LLM to score each message on a scale of 0-3:
|
||||
- 0: No user information
|
||||
- 1: Hypothetical or fictional content
|
||||
- 2: General or time-sensitive information
|
||||
- 3: Clear, important information or explicitly requested records
|
||||
|
||||
Only messages with scores specified in `preserved_scores` are retained. Messages are also filtered to exclude those already memorized and to only include messages from the user.
|
||||
|
||||
## GetObservationOp
|
||||
|
||||
### Purpose
|
||||
Extracts general observations about the user from messages that don't contain time-related information.
|
||||
|
||||
### Parameters
|
||||
No specific parameters for this operation.
|
||||
|
||||
### Description
|
||||
This operation processes messages that don't contain time-related keywords. It uses an LLM to extract meaningful observations about the user from these messages. Each observation includes:
|
||||
- Content: The actual observation text
|
||||
- Keywords: Tags that indicate when this observation might be relevant
|
||||
- Source message: The original message that led to this observation
|
||||
|
||||
The operation creates `PersonalMemory` objects with observation type "personal_info" for each extracted observation.
|
||||
|
||||
## GetObservationWithTimeOp
|
||||
|
||||
### Purpose
|
||||
Extracts observations with time context from messages that contain time-related information.
|
||||
|
||||
### Parameters
|
||||
No specific parameters for this operation.
|
||||
|
||||
### Description
|
||||
This operation is the counterpart to `GetObservationOp` but focuses specifically on messages containing time-related keywords. It extracts observations while preserving the time context, which is important for memories related to schedules, appointments, or time-specific preferences.
|
||||
|
||||
The operation creates `PersonalMemory` objects with observation type "personal_info_with_time" for each extracted observation, including the time information in the metadata.
|
||||
|
||||
## LoadTodayMemoryOp
|
||||
|
||||
### Purpose
|
||||
Loads memories created today from the vector store to prevent duplication and enable updating of recent memories.
|
||||
|
||||
### Parameters
|
||||
- `op.load_today_memory_op.params.top_k`: Maximum number of memories to retrieve (default: 50)
|
||||
|
||||
### Description
|
||||
This operation retrieves memories created on the current day using vector store search with date filtering. It converts vector nodes to memory objects and makes them available for deduplication in subsequent operations. This helps ensure that new observations don't create redundant memories for information already captured earlier in the day.
|
||||
|
||||
## ContraRepeatOp
|
||||
|
||||
### Purpose
|
||||
Identifies and removes contradictory or repetitive information from the collected memories.
|
||||
|
||||
### Parameters
|
||||
- `op.contra_repeat_op.params.contra_repeat_max_count`: Maximum number of memories to process (default: 50)
|
||||
- `op.contra_repeat_op.params.enable_contra_repeat`: Whether to enable contradiction/repetition checking (default: true)
|
||||
|
||||
### Description
|
||||
This operation analyzes the combined memories from previous operations (observation_memories, observation_memories_with_time, today_memories) to identify contradictions or redundancies. It uses an LLM to evaluate each memory and mark it as:
|
||||
- "Contradiction": Contradicts other memories
|
||||
- "Contained": Redundant as the information is already contained in other memories
|
||||
- "None": Unique and should be kept
|
||||
|
||||
Memories marked as contradictory or contained are filtered out, and their IDs are tracked for deletion from the vector store.
|
||||
|
||||
## LongContraRepeatOp
|
||||
|
||||
### Purpose
|
||||
Performs more sophisticated contradiction and redundancy analysis for longer-term memory management.
|
||||
|
||||
### Parameters
|
||||
- `op.long_contra_repeat_op.params.long_contra_repeat_max_count`: Maximum number of memories to process (default: 50)
|
||||
- `op.long_contra_repeat_op.params.enable_long_contra_repeat`: Whether to enable this operation (default: true)
|
||||
|
||||
### Description
|
||||
This operation extends the basic contradiction analysis of `ContraRepeatOp` with the ability to resolve conflicts by modifying contradictory memories rather than simply removing them. It's particularly useful for managing long-term personal memories where information might evolve over time.
|
||||
|
||||
For contradictory memories, it can either:
|
||||
- Modify the content to resolve the contradiction
|
||||
- Remove the memory if it's completely invalidated
|
||||
- Keep the most accurate/recent information
|
||||
|
||||
## UpdateInsightOp
|
||||
|
||||
### Purpose
|
||||
Updates existing insight values based on new observations.
|
||||
|
||||
### Parameters
|
||||
- `op.update_insight_op.params.update_insight_threshold`: Minimum relevance score threshold (default: 0.3)
|
||||
- `op.update_insight_op.params.update_insight_max_count`: Maximum number of insights to update (default: 5)
|
||||
|
||||
### Description
|
||||
This operation integrates new observations into existing insights about the user. It:
|
||||
1. Scores insight memories based on relevance to new observations
|
||||
2. Selects the top insights that meet the relevance threshold
|
||||
3. Updates each selected insight using an LLM to incorporate the new information
|
||||
4. Creates updated insight memories with the original ID but new content
|
||||
|
||||
This helps maintain accurate and up-to-date insights as new information about the user becomes available.
|
||||
|
||||
## GetReflectionSubjectOp
|
||||
|
||||
### Purpose
|
||||
Generates reflection subjects (topics) from personal memories for insight extraction.
|
||||
|
||||
### Parameters
|
||||
- `op.get_reflection_subject_op.params.reflect_obs_cnt_threshold`: Minimum number of memories required for reflection (default: 10)
|
||||
- `op.get_reflection_subject_op.params.reflect_num_questions`: Maximum number of new subjects to generate (default: 3)
|
||||
|
||||
### Description
|
||||
This operation analyzes a collection of personal memories to identify potential topics for reflection and insight generation. It:
|
||||
1. Checks if there are sufficient memories for meaningful reflection
|
||||
2. Extracts existing insight subjects to avoid duplication
|
||||
3. Uses an LLM to generate new reflection subjects based on memory content
|
||||
4. Creates insight memory objects for these new subjects
|
||||
|
||||
The generated subjects serve as focal points for organizing and synthesizing personal information about the user.
|
||||
|
||||
## UpdateVectorStoreOp
|
||||
|
||||
### Purpose
|
||||
Stores the processed memories in the vector database and removes deleted memories.
|
||||
|
||||
### Parameters
|
||||
No specific parameters for this operation.
|
||||
|
||||
### Description
|
||||
This operation is the final step in the personal memory summarization flow. It:
|
||||
1. Deletes memories that were marked for removal (contradictory or redundant)
|
||||
2. Inserts new or updated memories into the vector store
|
||||
3. Records the number of deleted and inserted memories
|
||||
|
||||
This ensures that the vector store remains up-to-date with the latest processed memories.
|
||||
|
||||
## Configuration Example
|
||||
|
||||
Here's an example of how to configure these operations in your YAML configuration:
|
||||
|
||||
```yaml
|
||||
op:
|
||||
info_filter_op:
|
||||
params:
|
||||
preserved_scores: "2,3"
|
||||
info_filter_msg_max_size: 200
|
||||
|
||||
load_today_memory_op:
|
||||
params:
|
||||
top_k: 50
|
||||
|
||||
contra_repeat_op:
|
||||
params:
|
||||
contra_repeat_max_count: 50
|
||||
enable_contra_repeat: true
|
||||
|
||||
long_contra_repeat_op:
|
||||
params:
|
||||
long_contra_repeat_max_count: 50
|
||||
enable_long_contra_repeat: true
|
||||
|
||||
update_insight_op:
|
||||
params:
|
||||
update_insight_threshold: 0.3
|
||||
update_insight_max_count: 5
|
||||
|
||||
get_reflection_subject_op:
|
||||
params:
|
||||
reflect_obs_cnt_threshold: 10
|
||||
reflect_num_questions: 3
|
||||
```
|
||||
|
||||
This configuration can be adjusted based on your specific requirements for personal memory processing.
|
||||
|
|
@ -0,0 +1,390 @@
|
|||
# 🚀 Vector Store API Guide
|
||||
|
||||
This guide covers the vector store implementations available in flowllm, their APIs, and how to use them effectively.
|
||||
|
||||
## 📋 Overview
|
||||
|
||||
flowllm provides multiple vector store backends for different use cases:
|
||||
|
||||
- **LocalVectorStore** (`backend=local`) - 📁 Simple file-based storage for development and small datasets
|
||||
- **ChromaVectorStore** (`backend=chroma`) - 🔮 Embedded vector database for moderate scale
|
||||
- **EsVectorStore** (`backend=elasticsearch`) - 🔍 Elasticsearch-based storage for production and large scale
|
||||
|
||||
All vector stores implement the `BaseVectorStore` interface, providing a consistent API across implementations.
|
||||
|
||||
## 🔄 Common API Methods
|
||||
|
||||
All vector store implementations share these core methods:
|
||||
|
||||
### Workspace Management
|
||||
|
||||
```python
|
||||
# Check if workspace exists
|
||||
store.exist_workspace(workspace_id: str) -> bool
|
||||
|
||||
# Create a new workspace
|
||||
store.create_workspace(workspace_id: str, **kwargs)
|
||||
|
||||
# Delete a workspace
|
||||
store.delete_workspace(workspace_id: str, **kwargs)
|
||||
|
||||
# Copy a workspace
|
||||
store.copy_workspace(src_workspace_id: str, dest_workspace_id: str, **kwargs)
|
||||
```
|
||||
|
||||
### Data Operations
|
||||
|
||||
```python
|
||||
# Insert nodes (single or list)
|
||||
store.insert(nodes: VectorNode | List[VectorNode], workspace_id: str, **kwargs)
|
||||
|
||||
# Delete nodes by ID
|
||||
store.delete(node_ids: str | List[str], workspace_id: str, **kwargs)
|
||||
|
||||
# Search for similar nodes
|
||||
store.search(query: str, workspace_id: str, top_k: int = 1, **kwargs) -> List[VectorNode]
|
||||
|
||||
# Iterate through workspace nodes
|
||||
for node in store.iter_workspace_nodes(workspace_id: str, **kwargs):
|
||||
# Process each node
|
||||
```
|
||||
|
||||
### Import/Export
|
||||
|
||||
```python
|
||||
# Export workspace to file
|
||||
store.dump_workspace(workspace_id: str, path: str | Path = "", callback_fn=None, **kwargs)
|
||||
|
||||
# Import workspace from file
|
||||
store.load_workspace(workspace_id: str, path: str | Path = "", nodes: List[VectorNode] = None,
|
||||
callback_fn=None, **kwargs)
|
||||
```
|
||||
|
||||
## ⚡ Vector Store Implementations
|
||||
|
||||
### 1. 📁 LocalVectorStore (`backend=local`)
|
||||
|
||||
A simple file-based vector store that saves data to local JSONL files.
|
||||
|
||||
#### 💡 When to Use
|
||||
- **Development and testing** - No external dependencies required 🛠️
|
||||
- **Small datasets** - Suitable for datasets with < 10,000 vectors 📊
|
||||
- **Single-user applications** - Limited concurrent access support 👤
|
||||
|
||||
#### ⚙️ Configuration
|
||||
|
||||
```python
|
||||
from flowllm.storage.vector_store import LocalVectorStore
|
||||
from flowllm.embedding_model import OpenAICompatibleEmbeddingModel
|
||||
from flowllm.utils.common_utils import load_env
|
||||
|
||||
# Load environment variables (for API keys)
|
||||
load_env()
|
||||
|
||||
# Initialize embedding model
|
||||
embedding_model = OpenAICompatibleEmbeddingModel(dimensions=1024, model_name="text-embedding-v4")
|
||||
|
||||
# Initialize vector store
|
||||
vector_store = LocalVectorStore(
|
||||
embedding_model=embedding_model,
|
||||
store_dir="./file_vector_store", # Directory to store JSONL files
|
||||
batch_size=1024 # Batch size for operations
|
||||
)
|
||||
```
|
||||
|
||||
#### 💻 Example Usage
|
||||
|
||||
```python
|
||||
from flowllm.schema.vector_node import VectorNode
|
||||
|
||||
# Create workspace
|
||||
workspace_id = "my_workspace"
|
||||
vector_store.create_workspace(workspace_id)
|
||||
|
||||
# Create nodes
|
||||
nodes = [
|
||||
VectorNode(
|
||||
unique_id="node1",
|
||||
workspace_id=workspace_id,
|
||||
content="Artificial intelligence is revolutionizing technology",
|
||||
metadata={"category": "tech", "source": "article1"}
|
||||
),
|
||||
VectorNode(
|
||||
unique_id="node2",
|
||||
workspace_id=workspace_id,
|
||||
content="Machine learning enables data-driven insights",
|
||||
metadata={"category": "tech", "source": "article2"}
|
||||
)
|
||||
]
|
||||
|
||||
# Insert nodes
|
||||
vector_store.insert(nodes, workspace_id)
|
||||
|
||||
# Search
|
||||
results = vector_store.search("What is AI?", workspace_id, top_k=2)
|
||||
for result in results:
|
||||
print(f"Content: {result.content}")
|
||||
print(f"Metadata: {result.metadata}")
|
||||
print(f"Score: {result.metadata.get('score', 'N/A')}")
|
||||
```
|
||||
|
||||
### 2. 🔮 ChromaVectorStore (`backend=chroma`)
|
||||
|
||||
An embedded vector database that provides persistent storage with advanced features.
|
||||
|
||||
#### 💡 When to Use
|
||||
- **Local development** with persistence requirements 🏠
|
||||
- **Medium-scale applications** (10K - 1M vectors) 📈
|
||||
- **Applications requiring metadata filtering** 🔍
|
||||
|
||||
#### ⚙️ Configuration
|
||||
|
||||
```python
|
||||
from flowllm.storage.vector_store import ChromaVectorStore
|
||||
from flowllm.embedding_model import OpenAICompatibleEmbeddingModel
|
||||
from flowllm.utils.common_utils import load_env
|
||||
|
||||
# Load environment variables
|
||||
load_env()
|
||||
|
||||
# Initialize embedding model
|
||||
embedding_model = OpenAICompatibleEmbeddingModel(dimensions=1024, model_name="text-embedding-v4")
|
||||
|
||||
# Initialize vector store
|
||||
vector_store = ChromaVectorStore(
|
||||
embedding_model=embedding_model,
|
||||
store_dir="./chroma_vector_store", # Directory for Chroma database
|
||||
batch_size=1024 # Batch size for operations
|
||||
)
|
||||
```
|
||||
|
||||
#### 💻 Example Usage
|
||||
|
||||
```python
|
||||
from flowllm.schema.vector_node import VectorNode
|
||||
|
||||
workspace_id = "chroma_workspace"
|
||||
|
||||
# Check if workspace exists and create if needed
|
||||
if not vector_store.exist_workspace(workspace_id):
|
||||
vector_store.create_workspace(workspace_id)
|
||||
|
||||
# Create nodes with metadata
|
||||
nodes = [
|
||||
VectorNode(
|
||||
unique_id="node1",
|
||||
workspace_id=workspace_id,
|
||||
content="Deep learning models require large datasets",
|
||||
metadata={
|
||||
"category": "AI",
|
||||
"difficulty": "advanced",
|
||||
"topic": "deep_learning"
|
||||
}
|
||||
),
|
||||
VectorNode(
|
||||
unique_id="node2",
|
||||
workspace_id=workspace_id,
|
||||
content="Transformer architecture revolutionized NLP",
|
||||
metadata={
|
||||
"category": "AI",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "transformers"
|
||||
}
|
||||
)
|
||||
]
|
||||
|
||||
# Insert nodes
|
||||
vector_store.insert(nodes, workspace_id)
|
||||
|
||||
# Search
|
||||
results = vector_store.search("deep learning", workspace_id, top_k=5)
|
||||
for result in results:
|
||||
print(f"Content: {result.content}")
|
||||
print(f"Metadata: {result.metadata}")
|
||||
```
|
||||
|
||||
### 3. 🔍 EsVectorStore (`backend=elasticsearch`)
|
||||
|
||||
Production-grade vector search using Elasticsearch with advanced filtering and scaling capabilities.
|
||||
|
||||
#### 💡 When to Use
|
||||
- **Production environments** requiring high availability 🏭
|
||||
- **Large-scale applications** (1M+ vectors) 🚀
|
||||
- **Complex filtering requirements** on metadata 🎯
|
||||
|
||||
#### 🛠️ Setup Elasticsearch
|
||||
|
||||
Before using EsVectorStore, set up Elasticsearch:
|
||||
|
||||
##### Option 1: Docker Run
|
||||
```bash
|
||||
# Pull the latest Elasticsearch image
|
||||
docker pull docker.elastic.co/elasticsearch/elasticsearch-wolfi:9.0.0
|
||||
|
||||
# Run Elasticsearch container
|
||||
docker run -p 9200:9200 \
|
||||
-e "discovery.type=single-node" \
|
||||
-e "xpack.security.enabled=false" \
|
||||
-e "xpack.license.self_generated.type=trial" \
|
||||
-e "http.host=0.0.0.0" \
|
||||
docker.elastic.co/elasticsearch/elasticsearch-wolfi:9.0.0
|
||||
```
|
||||
|
||||
##### Environment Configuration
|
||||
```bash
|
||||
export FLOW_ES_HOSTS=http://localhost:9200
|
||||
```
|
||||
|
||||
#### ⚙️ Configuration
|
||||
|
||||
```python
|
||||
from flowllm.storage.vector_store import EsVectorStore
|
||||
from flowllm.embedding_model import OpenAICompatibleEmbeddingModel
|
||||
from flowllm.utils.common_utils import load_env
|
||||
import os
|
||||
|
||||
# Load environment variables
|
||||
load_env()
|
||||
|
||||
# Initialize embedding model
|
||||
embedding_model = OpenAICompatibleEmbeddingModel(dimensions=1024, model_name="text-embedding-v4")
|
||||
|
||||
# Initialize vector store
|
||||
vector_store = EsVectorStore(
|
||||
embedding_model=embedding_model,
|
||||
hosts=os.getenv("FLOW_ES_HOSTS", "http://localhost:9200"), # Elasticsearch hosts
|
||||
basic_auth=None, # ("username", "password") for auth
|
||||
batch_size=1024 # Batch size for bulk operations
|
||||
)
|
||||
```
|
||||
|
||||
#### 🎯 Advanced Filtering
|
||||
|
||||
EsVectorStore supports advanced filtering capabilities:
|
||||
|
||||
```python
|
||||
# Add term filters
|
||||
vector_store.add_term_filter("metadata.category", "technology")
|
||||
|
||||
# Add range filters
|
||||
vector_store.add_range_filter("metadata.score", gte=0.8)
|
||||
vector_store.add_range_filter("metadata.timestamp", gte="2024-01-01", lte="2024-12-31")
|
||||
|
||||
# Search with filters applied
|
||||
results = vector_store.search("machine learning", workspace_id, top_k=10)
|
||||
|
||||
# Clear filters for next search
|
||||
vector_store.clear_filter()
|
||||
```
|
||||
|
||||
#### 💻 Example Usage
|
||||
|
||||
```python
|
||||
from flowllm.schema.vector_node import VectorNode
|
||||
|
||||
# Define workspace
|
||||
workspace_id = "production_workspace"
|
||||
|
||||
# Create workspace if needed
|
||||
if not vector_store.exist_workspace(workspace_id):
|
||||
vector_store.create_workspace(workspace_id)
|
||||
|
||||
# Create nodes with rich metadata
|
||||
nodes = [
|
||||
VectorNode(
|
||||
unique_id="doc1",
|
||||
workspace_id=workspace_id,
|
||||
content="Transformer architecture revolutionized NLP",
|
||||
metadata={
|
||||
"category": "AI",
|
||||
"subcategory": "NLP",
|
||||
"author": "research_team",
|
||||
"timestamp": "2024-01-15",
|
||||
"confidence": 0.95,
|
||||
"tags": ["transformer", "nlp", "attention"]
|
||||
}
|
||||
)
|
||||
]
|
||||
|
||||
# Insert with refresh for immediate availability
|
||||
vector_store.insert(nodes, workspace_id, refresh=True)
|
||||
|
||||
# Advanced search with filters
|
||||
vector_store.add_term_filter("metadata.category", "AI")
|
||||
vector_store.add_range_filter("metadata.confidence", gte=0.9)
|
||||
|
||||
results = vector_store.search("transformer models", workspace_id, top_k=5)
|
||||
|
||||
for result in results:
|
||||
print(f"Score: {result.metadata.get('score', 'N/A')}")
|
||||
print(f"Content: {result.content}")
|
||||
print(f"Metadata: {result.metadata}")
|
||||
```
|
||||
|
||||
## 📝 Working with VectorNode
|
||||
|
||||
The `VectorNode` class is the fundamental data unit for all vector stores:
|
||||
|
||||
```python
|
||||
from flowllm.schema.vector_node import VectorNode
|
||||
|
||||
# Create a node
|
||||
node = VectorNode(
|
||||
unique_id="unique_identifier", # Unique ID for the node (required)
|
||||
workspace_id="my_workspace", # Workspace ID (required)
|
||||
content="Text content to embed", # Content to be embedded (required)
|
||||
metadata={ # Optional metadata
|
||||
"source": "document1",
|
||||
"category": "technology",
|
||||
"timestamp": "2024-08-29"
|
||||
},
|
||||
vector=None # Vector will be generated automatically if None
|
||||
)
|
||||
```
|
||||
|
||||
## 🔄 Import/Export Example
|
||||
|
||||
Export and import workspaces for backup or transfer:
|
||||
|
||||
```python
|
||||
# Export workspace to file
|
||||
vector_store.dump_workspace(
|
||||
workspace_id="my_workspace",
|
||||
path="./backup_data" # Directory to store the exported data
|
||||
)
|
||||
|
||||
# Import workspace from file
|
||||
vector_store.load_workspace(
|
||||
workspace_id="new_workspace",
|
||||
path="./backup_data" # Directory containing the exported data
|
||||
)
|
||||
|
||||
# Copy workspace within the same store
|
||||
vector_store.copy_workspace(
|
||||
src_workspace_id="original_workspace",
|
||||
dest_workspace_id="copied_workspace"
|
||||
)
|
||||
```
|
||||
|
||||
## 🧩 Integration with Embedding Models
|
||||
|
||||
All vector stores require an embedding model to function:
|
||||
|
||||
```python
|
||||
from flowllm.embedding_model import OpenAICompatibleEmbeddingModel
|
||||
|
||||
# Initialize embedding model
|
||||
embedding_model = OpenAICompatibleEmbeddingModel(
|
||||
dimensions=1024, # Embedding dimensions
|
||||
model_name="text-embedding-v4", # Model name
|
||||
batch_size=32 # Batch size for embedding generation
|
||||
)
|
||||
|
||||
# Pass to vector store
|
||||
vector_store = LocalVectorStore(
|
||||
embedding_model=embedding_model,
|
||||
store_dir="./vector_store"
|
||||
)
|
||||
```
|
||||
|
||||
🎉 This guide provides everything you need to work with vector stores in flowllm. Choose the implementation that best fits your use case and scale up as needed! ✨
|
||||
|
|
@ -1,257 +0,0 @@
|
|||
# 🚀 Vector Store Quick Start Guide
|
||||
This comprehensive guide covers all available vector store implementations in ExperienceMaker, their differences, use cases, and setup instructions.
|
||||
|
||||
## 📋 Overview
|
||||
ExperienceMaker supports multiple vector store backends for different use cases and deployment scenarios:
|
||||
- **FileVectorStore** (`backend=local_file`) - 📁 Local file-based storage for development and small datasets
|
||||
- **ChromaVectorStore** (`backend=chroma`) - 🔮 Embedded vector database for local development and moderate scale
|
||||
- **EsVectorStore** (`backend=elasticsearch`) - 🔍 Elasticsearch-based storage for production and large scale
|
||||
|
||||
## ⚡ Vector Store Implementations
|
||||
### 1. 📁 FileVectorStore (`backend=local_file`)
|
||||
A simple file-based vector store that saves data to local JSONL files. Perfect for development, testing, and small datasets.
|
||||
|
||||
#### 💡 When to Use
|
||||
- **Development and testing** - No external dependencies required 🛠️
|
||||
- **Small datasets** - Suitable for datasets with < 10,000 vectors 📊
|
||||
- **Single-user applications** - No concurrent access support 👤
|
||||
- **Prototyping** - Quick setup without infrastructure ⚡
|
||||
|
||||
#### ✨ Features
|
||||
- ✅ No external dependencies
|
||||
- ✅ Simple file-based persistence
|
||||
- ✅ Built-in cosine similarity search
|
||||
- ❌ No concurrent access support
|
||||
- ❌ Limited scalability
|
||||
- ❌ No advanced filtering
|
||||
|
||||
#### ⚙️ Configuration Parameters
|
||||
```python
|
||||
from experiencemaker.vector_store import FileVectorStore
|
||||
from experiencemaker.embedding_model.openai_compatible_embedding_model import OpenAICompatibleEmbeddingModel
|
||||
|
||||
embedding_model = OpenAICompatibleEmbeddingModel(dimensions=1024, model_name="text-embedding-v4")
|
||||
|
||||
vector_store = FileVectorStore(
|
||||
embedding_model=embedding_model,
|
||||
store_dir="./file_vector_store", # Directory to store JSONL files
|
||||
batch_size=1024 # Batch size for operations
|
||||
)
|
||||
```
|
||||
|
||||
#### 💻 Example Usage
|
||||
```python
|
||||
# Create workspace and insert data
|
||||
workspace_id = "my_workspace"
|
||||
vector_store.create_workspace(workspace_id)
|
||||
|
||||
nodes = [
|
||||
VectorNode(
|
||||
workspace_id=workspace_id,
|
||||
content="Artificial intelligence is revolutionizing technology",
|
||||
metadata={"category": "tech", "source": "article1"}
|
||||
),
|
||||
VectorNode(
|
||||
workspace_id=workspace_id,
|
||||
content="Machine learning enables data-driven insights",
|
||||
metadata={"category": "tech", "source": "article2"}
|
||||
)
|
||||
]
|
||||
|
||||
vector_store.insert(nodes, workspace_id)
|
||||
|
||||
# Search
|
||||
results = vector_store.search("What is AI?", workspace_id, top_k=2)
|
||||
```
|
||||
|
||||
### 2. 🔮 ChromaVectorStore (`backend=chroma`)
|
||||
|
||||
An embedded vector database that provides persistent storage with advanced features while remaining easy to deploy.
|
||||
|
||||
#### 💡 When to Use
|
||||
- **Local development** with persistence requirements 🏠
|
||||
- **Medium-scale applications** (10K - 1M vectors) 📈
|
||||
- **Multi-user applications** with moderate concurrency 👥
|
||||
- **Applications requiring metadata filtering** 🔍
|
||||
- **Docker deployments** without external database dependencies 🐳
|
||||
|
||||
#### ✨ Features
|
||||
- ✅ Persistent embedded database
|
||||
- ✅ Advanced metadata filtering
|
||||
- ✅ Built-in vector indexing (HNSW)
|
||||
- ✅ HTTP API support
|
||||
- ✅ Concurrent access support
|
||||
- ✅ Collection management
|
||||
- ❌ Limited horizontal scaling
|
||||
|
||||
#### ⚙️ Configuration Parameters
|
||||
```python
|
||||
from experiencemaker.vector_store import ChromaVectorStore
|
||||
from experiencemaker.embedding_model.openai_compatible_embedding_model import OpenAICompatibleEmbeddingModel
|
||||
|
||||
embedding_model = OpenAICompatibleEmbeddingModel(dimensions=1024, model_name="text-embedding-v4")
|
||||
|
||||
vector_store = ChromaVectorStore(
|
||||
embedding_model=embedding_model,
|
||||
store_dir="./chroma_vector_store", # Directory for Chroma database
|
||||
batch_size=1024 # Batch size for operations
|
||||
)
|
||||
```
|
||||
|
||||
#### 💻 Example Usage
|
||||
```python
|
||||
workspace_id = "chroma_workspace"
|
||||
|
||||
# Check if workspace exists
|
||||
if not vector_store.exist_workspace(workspace_id):
|
||||
vector_store.create_workspace(workspace_id)
|
||||
|
||||
# Insert with metadata
|
||||
nodes = [
|
||||
VectorNode(
|
||||
workspace_id=workspace_id,
|
||||
content="Deep learning models require large datasets",
|
||||
metadata={"category": "AI", "difficulty": "advanced", "topic": "deep_learning"}
|
||||
)
|
||||
]
|
||||
|
||||
vector_store.insert(nodes, workspace_id)
|
||||
|
||||
# Search with results
|
||||
results = vector_store.search("deep learning", workspace_id, top_k=5)
|
||||
for result in results:
|
||||
print(f"Content: {result.content}")
|
||||
print(f"Metadata: {result.metadata}")
|
||||
```
|
||||
|
||||
### 3. 🔍 EsVectorStore (`backend=elasticsearch`)
|
||||
|
||||
Production-grade vector search using Elasticsearch with advanced filtering, scaling, and enterprise features.
|
||||
|
||||
#### 💡 When to Use
|
||||
- **Production environments** requiring high availability 🏭
|
||||
- **Large-scale applications** (1M+ vectors) 🚀
|
||||
- **High-throughput scenarios** with many concurrent users ⚡
|
||||
- **Complex filtering requirements** on metadata 🎯
|
||||
- **Distributed deployments** across multiple nodes 🌐
|
||||
- **Enterprise environments** with existing Elasticsearch infrastructure 🏢
|
||||
|
||||
#### ✨ Features
|
||||
- ✅ Horizontal scaling
|
||||
- ✅ High availability and fault tolerance
|
||||
- ✅ Advanced filtering and aggregations
|
||||
- ✅ Real-time indexing and search
|
||||
- ✅ Cluster management
|
||||
- ✅ Enterprise security features
|
||||
- ✅ Monitoring and analytics
|
||||
- ❌ Complex setup and maintenance
|
||||
- ❌ Higher resource requirements
|
||||
|
||||
#### 🛠️ Setup Elasticsearch
|
||||
|
||||
Before using EsVectorStore, you need to set up Elasticsearch. Choose one of the following methods:
|
||||
|
||||
##### Option 1: All-in-One Script (Recommended for Development) 🎯
|
||||
```bash
|
||||
curl -fsSL https://elastic.co/start-local | sh
|
||||
```
|
||||
|
||||
##### Option 2: Docker Run with HTTP Host 🐳
|
||||
```bash
|
||||
# Pull the latest Elasticsearch image
|
||||
docker pull docker.elastic.co/elasticsearch/elasticsearch-wolfi:9.0.0
|
||||
|
||||
# Run Elasticsearch container
|
||||
docker run -p 9200:9200 \
|
||||
-e "discovery.type=single-node" \
|
||||
-e "xpack.security.enabled=false" \
|
||||
-e "xpack.license.self_generated.type=trial" \
|
||||
-e "http.host=0.0.0.0" \
|
||||
docker.elastic.co/elasticsearch/elasticsearch-wolfi:9.0.0
|
||||
```
|
||||
|
||||
##### 🔧 Environment Configuration
|
||||
Set the Elasticsearch hosts environment variable:
|
||||
```bash
|
||||
export ES_HOSTS=http://localhost:9200
|
||||
```
|
||||
|
||||
#### ⚙️ Configuration Parameters
|
||||
```python
|
||||
from experiencemaker.vector_store import EsVectorStore
|
||||
from experiencemaker.embedding_model.openai_compatible_embedding_model import OpenAICompatibleEmbeddingModel
|
||||
import os
|
||||
|
||||
embedding_model = OpenAICompatibleEmbeddingModel(dimensions=1024, model_name="text-embedding-v4")
|
||||
|
||||
vector_store = EsVectorStore(
|
||||
embedding_model=embedding_model,
|
||||
hosts=os.getenv("ES_HOSTS", "http://localhost:9200"), # Elasticsearch hosts
|
||||
basic_auth=None, # ("username", "password") for auth
|
||||
batch_size=1024, # Batch size for bulk operations
|
||||
retrieve_filters=[] # Pre-configured filters
|
||||
)
|
||||
```
|
||||
|
||||
#### 🎯 Advanced Filtering
|
||||
EsVectorStore supports advanced filtering capabilities:
|
||||
|
||||
```python
|
||||
# Add term filters
|
||||
vector_store.add_term_filter("metadata.category", "technology")
|
||||
vector_store.add_term_filter("metadata.language", "en")
|
||||
|
||||
# Add range filters
|
||||
vector_store.add_range_filter("metadata.score", gte=0.8)
|
||||
vector_store.add_range_filter("metadata.timestamp", gte="2024-01-01", lte="2024-12-31")
|
||||
|
||||
# Search with filters applied
|
||||
results = vector_store.search("machine learning", workspace_id, top_k=10)
|
||||
|
||||
# Clear filters for next search
|
||||
vector_store.clear_filter()
|
||||
```
|
||||
|
||||
#### 💻 Example Usage
|
||||
```python
|
||||
from experiencemaker.schema.vector_node import VectorNode
|
||||
|
||||
# Configure connection
|
||||
workspace_id = "production_workspace"
|
||||
|
||||
# Create workspace with custom mapping
|
||||
if not vector_store.exist_workspace(workspace_id):
|
||||
vector_store.create_workspace(workspace_id)
|
||||
|
||||
# Insert with rich metadata
|
||||
nodes = [
|
||||
VectorNode(
|
||||
workspace_id=workspace_id,
|
||||
content="Transformer architecture revolutionized NLP",
|
||||
metadata={
|
||||
"category": "AI",
|
||||
"subcategory": "NLP",
|
||||
"author": "research_team",
|
||||
"timestamp": "2024-01-15",
|
||||
"confidence": 0.95,
|
||||
"tags": ["transformer", "nlp", "attention"]
|
||||
}
|
||||
)
|
||||
]
|
||||
|
||||
# Insert with refresh for immediate availability
|
||||
vector_store.insert(nodes, workspace_id, refresh=True)
|
||||
|
||||
# Advanced search with filters
|
||||
vector_store.add_term_filter("metadata.category", "AI")
|
||||
vector_store.add_range_filter("metadata.confidence", gte=0.9)
|
||||
|
||||
results = vector_store.search("transformer models", workspace_id, top_k=5)
|
||||
|
||||
for result in results:
|
||||
print(f"Score: {result.metadata.get('_score', 'N/A')}")
|
||||
print(f"Content: {result.content}")
|
||||
print(f"Metadata: {result.metadata}")
|
||||
```
|
||||
|
||||
🎉 This guide provides everything you need to get started with vector stores in ExperienceMaker. Choose the implementation that best fits your use case and scale up as needed! ✨
|
||||
Loading…
Add table
Reference in a new issue