All Products
Search
Document Center

PolarDB:Build Long-Term and Short-Term Memory for AI Agents with PolarSearch Memory Container

Last Updated:Mar 18, 2026

When AI agents engage in multi-turn conversations or execute sequential tasks, they often fail to retain user preferences, past instructions, or contextual information. This lack of effective memory disrupts the interaction experience and reduces task efficiency. The PolarSearch Memory Container solves this problem. It is a long-term and short-term memory management system built into PolarDB for PostgreSQL. With this feature, agents automatically structure and persist conversation data. Later, they retrieve relevant memories quickly using semantic search—delivering truly continuous, personalized, and efficient service.

Note

The PolarSearch Memory Container feature is in canary release. To request access, submit a ticket.

How it works

The PolarSearch Memory Container uses an automated processing flow to convert unstructured conversation data into searchable and usable structured memory. It is built on a three-tier storage architecture and an intelligent memory processing pipeline.

Three-tier storage architecture

The Memory Container uses a layered design to balance storage and retrieval efficiency for memories with different lifespans.

Memory type

Index type

Primary purpose

Short-term memory (Working Memory)

Inverted index

Stores raw, unprocessed short-term conversation data. Uses inverted indexing for fast retrieval. Maintains context for the current conversation.

Long-term memory (Long-Term Memory)

Vector index + inverted index

Uses a large language model (LLM) to extract key facts from conversations, then stores them as vectors. Supports KNN semantic search for deep knowledge and experience retrieval.

Memory history (Memory History)

Inverted index

Records every memory operation (such as add or update), forming a complete audit log. Ensures memory evolution is traceable.

Memory processing flow

When an agent receives new input, the Memory Container triggers a series of actions to automatically ingest, persist, and retrieve memory.

image
  1. Memory ingestion:

    • Input and preprocessing: Receives agent conversation input using the Elasticsearch protocol. You can optionally call a large language model (LLM) to generate summaries, classify intent, or extract tags.

    • Routing decision: The Memory Router analyzes the input and compares it with existing memory to decide whether to add (ADD), update (UPDATE), or delete (DELETE) a memory.

  2. Memory persistence:

    • Short-term memory: Stores raw conversation content.

    • Long-term memory: Stores structured facts that are extracted by the LLM and vectorized by an embedding model.

    • Memory history: Writes all operations to the memory history to create an audit trail.

  3. Memory retrieval:

    • Input parsing: Accepts search criteria. Parses multimodal search requests using the Elasticsearch protocol. Calls the permission validation module to confirm data access boundaries.

    • Hybrid index search: Performs a hybrid search (ANN vector similarity search and inverted index filtering) in long-term memory. Performs keyword matching in short-term memory. Adjusts weights based on memory timestamps.

    • Result integration: Uses a rerank model to optimize the result ordering. Resolves conflicts using timestamps and other metadata. Returns the most relevant memory to the agent.

Key benefits

Capability dimension

Traditional approach

PolarSearch solution

Memory utilization

Less than 30% of the historical data is retained, which hinders its effective use.

Extracts facts using an LLM and combines them with semantic indexing to increase effective information utilization to over 85%.

Search efficiency

Relies on keyword matching, with an accuracy of 60% or less.

Uses hybrid vector and inverted indexing, plus reranking. This achieves a semantic search accuracy of over 95%.

Enterprise compliance

Multi-tenant isolation and permission control are complex to implement.

Provides native multi-tenant isolation, role-based access control (RBAC) permission control, and operation audit to meet enterprise security requirements.

System scalability

Single-point storage capacity is capped at the terabyte level.

Built on a cloud-native distributed storage architecture. Storage can scale to the petabyte level.

Development cost

Requires custom development and has a long implementation time.

Provides out-of-the-box APIs and SDKs to simplify development and enable rapid integration.

Scope

Getting Started

This guide walks you through the entire process, from environment setup to storing and retrieving a memory.

Overall workflow: Prepare environment → Enable plugin → Register models → Create memory container → Store and verify memory.

Step 1: Configure access credentials and environment variables

Before you start, prepare the following information and set it as environment variables. This simplifies subsequent curl commands and avoids repeated edits. Centralizing all configurations and credentials makes them easy to copy and execute.

Variable name

Description

Example value

POLARSEARCH_HOST_PORT

The connection address and port of the PolarSearch node.

pc-xxx.polardbsearch.rds.aliyuncs.com:3001

USER_PASSWORD

The administrator account for the PolarSearch node.

polarsearch_user:your_password

YOUR_API_KEY

Your API key for Alibaba Cloud Model Studio.

sk-xxxxxxxxxxxxxxxxxxxxxxxx

Action: Run the following commands in your terminal. Replace the example values with your actual values.

# Set the PolarSearch host and port
export POLARSEARCH_HOST_PORT="pc-xxx.polardbsearch.rds.aliyuncs.com:3001"

# Set the PolarSearch administrator password
export USER_PASSWORD="polarsearch_user:your_password"

# Set your Qwen API key
export YOUR_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxx"

Step 2: Enable the Memory Container plugin

Run the following command to enable the Memory Container feature in your PolarDB cluster.

Command line

curl -XPUT "http://${POLARSEARCH_HOST_PORT}/_cluster/settings" \
--user "${USER_PASSWORD}" \
-H 'Content-Type: application/json' \
-d '{
  "persistent": {
    "plugins.ml_commons.agentic_memory_enabled": true
  }
}'

Dashboard

PUT _cluster/settings
{
  "persistent": {
    "plugins.ml_commons.agentic_memory_enabled": true
   }
}

Step 3: Register models

You must register an LLM for fact extraction and an embedding model for semantic processing with PolarSearch.

Register an embedding model

Create a connector that points to the embedding model service in Alibaba Cloud Model Studio and register it as a callable model in PolarSearch.

  1. Set a trusted endpoint: Add your model’s API endpoint to the trusted list so that PolarSearch can call it.

    Command line

    curl -XPUT "http://${POLARSEARCH_HOST_PORT}/_cluster/settings" \
    --user "${USER_PASSWORD}" \
    -H 'Content-Type: application/json' \
    -d '{
      "persistent": {
        "plugins.ml_commons.trusted_connector_endpoints_regex": [
          "^https://dashscope.aliyuncs.com/compatible-mode/v1/.*$"
        ]
      }
    }'
    

    Dashboard

    PUT _cluster/settings
    {
      "persistent": {
        "plugins.ml_commons.trusted_connector_endpoints_regex": [
          "^https://dashscope.aliyuncs.com/compatible-mode/v1/.*$"
        ]
      }
    }
  2. Create a model connector: Configure parameters to connect to the text-embedding-v4 model.

    Note

    The pre_process_function and post_process_function parameters adapt request and response formats for different model services. In this example, the built-in openai.embedding format converter is used because it matches the format of Alibaba Cloud Model Studio in compatible mode.

    Command line

    curl -XPOST "http://${POLARSEARCH_HOST_PORT}/_plugins/_ml/connectors/_create" \
    --user "${USER_PASSWORD}" \
    -H 'Content-Type: application/json' \
    -d '{
      "name": "qwen embedding connector",
      "description": "The connector to qwen embedding model",
      "version": 1,
      "protocol": "http",
      "parameters": {
        "model": "text-embedding-v4",
        "endpoint": "dashscope.aliyuncs.com/compatible-mode"
      },
      "credential": {
          "api_key": "${YOUR_API_KEY}"
      },
      "actions": [
        {
          "action_type": "predict",
          "method": "POST",
          "headers": {
            "Authorization": "Bearer ${credential.api_key}",
            "content-type": "application/json"
          },
          "url": "https://${parameters.endpoint}/v1/embeddings",
          "request_body": "{ \"model\": \"${parameters.model}\", \"input\": ${parameters.input} }",
          "pre_process_function": "connector.pre_process.openai.embedding",
          "post_process_function":"connector.post_process.openai.embedding"
          }
        ]
    }'

    Dashboard

    Important

    Replace <YOUR_API_KEY> in the following command with your actual API key for Alibaba Cloud Model Studio.

    POST _plugins/_ml/connectors/_create
    {
      "name": "qwen embedding connector",
      "description": "The connector to qwen embedding model",
      "version": 1,
      "protocol": "http",
      "parameters": {
        "model": "text-embedding-v4",
        "endpoint": "dashscope.aliyuncs.com/compatible-mode"
      },
      "credential": {
          "api_key": "<YOUR_API_KEY>"
      },
      "actions": [
        {
          "action_type": "predict",
          "method": "POST",
          "headers": {
            "Authorization": "Bearer ${credential.api_key}",
            "content-type": "application/json"
          },
          "url": "https://${parameters.endpoint}/v1/embeddings",
          "request_body": "{ \"model\": \"${parameters.model}\", \"input\": ${parameters.input} }",
          "pre_process_function": "connector.pre_process.openai.embedding",
          "post_process_function":"connector.post_process.openai.embedding"
          }
        ]
    }
  3. After the command is successfully run, the system returns a connector_id. Record this ID for later use.

    {"connector_id": "LBFt6ZsBk04xxx"}
  4. Register the model: Register the connector created in the previous step as a model.

    Command line

    curl -XPOST "http://${POLARSEARCH_HOST_PORT}/_plugins/_ml/models/_register" \
    --user "${USER_PASSWORD}" \
    -H 'Content-Type: application/json' \
    -d '{
      "name": "qwen embedding model",
      "function_name": "remote",
      "description": "Embedding model for memory",
      "connector_id": "LBFt6Zsxxx"
    }'

    Dashboard

    POST _plugins/_ml/models/_register
    {
      "name": "qwen embedding model",
      "function_name": "remote",
      "description": "Embedding model for memory",
      "connector_id": "LBFt6ZsBk04xxx"
    }

    After the command is successfully run, the system returns a model_id. Record this ID for later use.

    # Record the returned model_id
    {"task_id": "LRFx6ZsBk04ixxx","status": "CREATED","model_id": "LhFx6ZsBk04xxx"}
  5. Test the model: Run the following query to chat directly with the LLM and view its response.

    Command line

    curl -XPOST "http://${POLARSEARCH_HOST_PORT}/_plugins/_ml/_predict/text_embedding/<model ID from previous step>" \
    --user "${USER_PASSWORD}" \
    -H 'Content-Type: application/json' \
    -d'{
      "text_docs":[ "Bob likes swimming. Context: He expressed his interest in swimming."],
      "return_number": true,
      "target_response": ["sentence_embedding"]
    }'

    Dashboard

    POST _plugins/_ml/_predict/text_embedding/<model ID from previous step>
    {
      "text_docs":[ "Bob likes swimming. Context: He expressed his interest in swimming."],
      "return_number": true,
      "target_response": ["sentence_embedding"]
    }

    Expected response:

    {
      "inference_results": [
        {
          "output": [
            {
              "name": "sentence_embedding",
              "data_type": "FLOAT32",
              "shape": [
                1024
              ],
              "data": [
                0.019752666354179382,
                -0.03468115255236626,
                0.05591931194067001,
                ...
              ]
            }
          ],
          "status_code": 200
        }
      ]
    }

Register a text generation model (LLM)

Create and register a connector to the qwen-plus LLM in Alibaba Cloud Model Studio.

  1. Create a model connector:

    Command line

    curl -XPOST "http://${POLARSEARCH_HOST_PORT}/_plugins/_ml/connectors/_create" \
    --user "${USER_PASSWORD}" \
    -H 'Content-Type: application/json' \
    -d '{
      "name": "QWen LLM Connector",
      "description": "The connector to qwen LLM",
      "version": 1,
      "protocol": "http",
      "parameters": {
        "model": "qwen-plus",
        "endpoint": "dashscope.aliyuncs.com/compatible-mode"
      },
      "credential": {
          "api_key": "${YOUR_API_KEY}"
      },
      "actions": [
        {
          "action_type": "predict",
          "method": "POST",
          "headers": {
            "Authorization": "Bearer ${credential.api_key}",
            "content-type": "application/json"
          },
          "url": "https://${parameters.endpoint}/v1/chat/completions",
          "request_body": "{ \"model\":\"${parameters.model}\", \"system\": \"${parameters.system_prompt}\", \"messages\": [ { \"role\": \"system\", \"content\": \"${parameters.system_prompt}\" }, { \"role\": \"user\", \"content\": \"${parameters.user_prompt}\" } ], \"response_format\": { \"type\": \"json_object\" } }"
          }
        ]
    }'

    Dashboard

    Important

    Replace <YOUR_API_KEY> in the following command with your actual API key for Alibaba Cloud Model Studio.

    POST /_plugins/_ml/connectors/_create
    {
      "name": "QWen LLM Connector",
      "description": "The connector to qwen LLM",
      "version": 1,
      "protocol": "http",
      "parameters": {
        "model": "qwen-plus",
        "endpoint": "dashscope.aliyuncs.com/compatible-mode"
      },
      "credential": {
          "api_key": "<YOUR_API_KEY>"
      },
      "actions": [
        {
          "action_type": "predict",
          "method": "POST",
          "headers": {
            "Authorization": "Bearer ${credential.api_key}",
            "content-type": "application/json"
          },
          "url": "https://${parameters.endpoint}/v1/chat/completions",
          "request_body": "{ \"model\":\"${parameters.model}\", \"system\": \"${parameters.system_prompt}\", \"messages\": [ { \"role\": \"system\", \"content\": \"${parameters.system_prompt}\" }, { \"role\": \"user\", \"content\": \"${parameters.user_prompt}\" } ], \"response_format\": { \"type\": \"json_object\" } }"
          }
        ]
    }
  2. After the command is successfully run, the system returns a connector_id. Record this ID for later use.

    {"connector_id": "PRGy6ZsBk04xxx"}
  3. Register the model: Register the connector created in the previous step as a model.

    Command line

    curl -XPOST "http://${POLARSEARCH_HOST_PORT}/_plugins/_ml/models/_register" \
    --user "${USER_PASSWORD}" \
    -H 'Content-Type: application/json' \
    -d '{
      "name": "qwen llm model",
      "function_name": "remote",
      "description": "LLM model for memory",
      "connector_id": "PRGy6ZsBk04xxx"
    }'

    Dashboard

    POST _plugins/_ml/models/_register
    {
      "name": "qwen llm model",
      "function_name": "remote",
      "description": "LLM model for memory",
      "connector_id": "PRGy6ZsBk04xxx"
    }

    After the command is successfully run, the system returns a model_id. Record this ID for later use.

    # Record the returned model_id
    {"task_id": "PhGy6ZsBk04xxx","status": "CREATED","model_id": "PxGy6ZsBk04xxx"}
  4. Deploy the model:

    Command line

    curl -XPOST "http://${POLARSEARCH_HOST_PORT}/_plugins/_ml/models/<model ID from previous step>/_deploy" \
    --user "${USER_PASSWORD}"

    Dashboard

    POST _plugins/_ml/models/<model ID from previous step>/_deploy

    After the command is successfully run, a status of COMPLETED indicates that the deployment was successful.

    {"task_id": "NxGI6ZsBk04xxx","task_type": "DEPLOY_MODEL","status": "COMPLETED"}
  5. Test the model: Run the following query to chat directly with the LLM and view its response.

    Command line

    curl -XPOST "http://${POLARSEARCH_HOST_PORT}/_plugins/_ml/models/<model ID from previous step>/_predict" \
    --user "${USER_PASSWORD}" \
    -H 'Content-Type: application/json' \
    -d'{
      "parameters": {
        "system_prompt": "<ROLE>You are a USER PREFERENCE EXTRACTOR, not a chat assistant. Your only job is to output JSON facts. Do not answer questions, make suggestions, ask follow-ups, or perform actions.</ROLE>\n\n<SCOPE>\n• Extract preferences only from USER messages. Assistant messages are context only.\n• Explicit: user states a preference (\"I prefer/like/dislike ...\"; \"always/never/usually ...\"; \"set X to Y\"; \"run X when Y\").\n• Implicit: infer only with strong signals: repeated choices (>=2) or clear habitual language. Do not infer from a single one-off.\n</SCOPE>\n\n<EXTRACT>\n• Specific, actionable, likely long-term preferences (likes/dislikes/choices/settings). Ignore non-preferences.\n</EXTRACT>\n\n<STYLE & RULES>\n• One sentence per preference; merge related details; no duplicates; preserve user wording and numbers; avoid relative time; keep each fact < 350 chars.\n• Format: \"Preference sentence. Context: <why/how>. Categories: cat1,cat2\"\n</STYLE & RULES>\n\n<OUTPUT>\nReturn ONLY one minified JSON object exactly as {\"facts\":[\"Preference sentence. Context: <why/how>. Categories: cat1,cat2\"]}. If none, return {\"facts\":[]}. The first character MUST be '{' and the last MUST be '}'. No preambles, explanations, code fences, XML, or other text.\n</OUTPUT>",
        "user_prompt": "I am Alice, I like travel."
      }
    }'

    Dashboard

    POST _plugins/_ml/models/<model ID from previous step>/_predict
    {
      "parameters": {
        "system_prompt": "<ROLE>You are a USER PREFERENCE EXTRACTOR, not a chat assistant. Your only job is to output JSON facts. Do not answer questions, make suggestions, ask follow-ups, or perform actions.</ROLE>\n\n<SCOPE>\n• Extract preferences only from USER messages. Assistant messages are context only.\n• Explicit: user states a preference (\"I prefer/like/dislike ...\"; \"always/never/usually ...\"; \"set X to Y\"; \"run X when Y\").\n• Implicit: infer only with strong signals: repeated choices (>=2) or clear habitual language. Do not infer from a single one-off.\n</SCOPE>\n\n<EXTRACT>\n• Specific, actionable, likely long-term preferences (likes/dislikes/choices/settings). Ignore non-preferences.\n</EXTRACT>\n\n<STYLE & RULES>\n• One sentence per preference; merge related details; no duplicates; preserve user wording and numbers; avoid relative time; keep each fact < 350 chars.\n• Format: \"Preference sentence. Context: <why/how>. Categories: cat1,cat2\"\n</STYLE & RULES>\n\n<OUTPUT>\nReturn ONLY one minified JSON object exactly as {\"facts\":[\"Preference sentence. Context: <why/how>. Categories: cat1,cat2\"]}. If none, return {\"facts\":[]}. The first character MUST be '{' and the last MUST be '}'. No preambles, explanations, code fences, XML, or other text.\n</OUTPUT>",
        "user_prompt": "I am Alice, I like travel."
      }
    }

    Expected response:

    {
      "inference_results": [
        {
          "output": [
            {
              "name": "response",
              "dataAsMap": {
                "choices": [
                  {
                    "message": {
                      "role": "assistant",
                      "content": """{"facts":["I like travel. Context: Stated preference. Categories: interest"]}"""
                    },
                    "finish_reason": "stop",
                    "index": 0,
                    "logprobs": null
                  }
                ],
                "object": "chat.completion",
                "usage": {
                  "prompt_tokens": 325,
                  "completion_tokens": 17,
                  "total_tokens": 342,
                  "prompt_tokens_details": {
                    "cached_tokens": 0
                  }
                },
                "created": 1769152651,
                "system_fingerprint": null,
                "model": "qwen-plus",
                "id": "chatcmpl-50c6bfc9-xxx-xxx-xxx-1a39cfe080f5"
              }
            }
          ],
          "status_code": 200
        }
      ]
    }

Step 4: Create a memory container

  1. Create a memory container instance and configure its models, storage policies, and automation behavior. Run the following command to create a container named agentic memory test.

    Command line

    curl -XPOST "http://${POLARSEARCH_HOST_PORT}/_plugins/_ml/memory_containers/_create" \
    --user "${USER_PASSWORD}" \
    -H 'Content-Type: application/json' \
    -d'{
      "name": "my agentic memory test",
      "description": "Store conversations with semantic search and summarization",
      "configuration": {
        "embedding_model_type": "TEXT_EMBEDDING",
        "embedding_model_id": "<embedding model ID from previous step>",
        "embedding_dimension": 1024,
        "llm_id": "<LLM model ID from previous step>",
        "index_prefix": "mem_test",
        "index_settings": {
          "short_term_memory_index": {
            "index": {
              "number_of_shards": "2",
              "number_of_replicas": "2"
            }
          },
          "long_term_memory_index": {
            "index": {
              "number_of_shards": "2",
              "number_of_replicas": "2"
            }
          },
          "long_term_memory_history_index": {
            "index": {
              "number_of_shards": "2",
              "number_of_replicas": "2"
            }
          }
        },
        "strategies": [
          {
            "type": "SEMANTIC",
            "namespace": ["user_id"],
            "configuration": {
              "llm_result_path": "$.choices[0].message.content"
            }
          },
          {
            "type": "USER_PREFERENCE",
            "namespace": ["user_id"],
            "configuration": {
              "llm_result_path": "$.choices[0].message.content"
            }
          },
          {
            "type": "SUMMARY",
            "namespace": ["agent_id"],
            "configuration": {
              "llm_result_path": "$.choices[0].message.content"
            }
          }
        ],
        "parameters": {
          "llm_result_path": "$.choices[0].message.content"
        }
      }
    }'

    Dashboard

    POST _plugins/_ml/memory_containers/_create
    {
      "name": "my agentic memory test",
      "description": "Store conversations with semantic search and summarization",
      "configuration": {
        "embedding_model_type": "TEXT_EMBEDDING",
        "embedding_model_id": "<embedding model ID from previous step>",
        "embedding_dimension": 1024,
        "llm_id": "<LLM model ID from previous step>",
        "index_prefix": "mem_test",
        "index_settings": {
          "short_term_memory_index": {
            "index": {
              "number_of_shards": "2",
              "number_of_replicas": "2"
            }
          },
          "long_term_memory_index": {
            "index": {
              "number_of_shards": "2",
              "number_of_replicas": "2"
            }
          },
          "long_term_memory_history_index": {
            "index": {
              "number_of_shards": "2",
              "number_of_replicas": "2"
            }
          }
        },
        "strategies": [
          {
            "type": "SEMANTIC",
            "namespace": ["user_id"],
            "configuration": {
              "llm_result_path": "$.choices[0].message.content"
            }
          },
          {
            "type": "USER_PREFERENCE",
            "namespace": ["user_id"],
            "configuration": {
              "llm_result_path": "$.choices[0].message.content"
            }
          },
          {
            "type": "SUMMARY",
            "namespace": ["agent_id"],
            "configuration": {
              "llm_result_path": "$.choices[0].message.content"
            }
          }
        ],
        "parameters": {
          "llm_result_path": "$.choices[0].message.content"
        }
      }
    }

    Parameter description

    • index_prefix: A common prefix for the internal indexes that are created by this container, such as short-term and long-term memory indexes.

    • index_settings: Configures the shard and replica counts for different memory indexes to ensure high availability.

    • strategies: Defines automation rules. This example defines a SEMANTIC strategy that automatically extracts semantic facts from conversations within the user_id namespace.

    • llm_result_path: Uses JSONPath syntax to specify how to extract the core content from the LLM’s JSON response. The path $.choices[0].message.content matches the response structure of Alibaba Cloud Model Studio in compatible mode.

  2. After the command is successfully run, the system returns a memory_container_id. Record this ID for later use.

    # Record the returned model_id
    {"memory_container_id": "QRHF6ZsBk04xxx","status": "created"}

    After creating the memory container, the system creates the following indexes and pipelines:

    .plugins-ml-am-mem_test-memory-long-term
    .plugins-ml-am-mem_test-memory-working
    .plugins-ml-am-mem_test-memory-history
    .plugins-ml-am-mem_test-memory-long-term-embedding" : {
        "description" : "Agentic Memory Text embedding pipeline",
        "processors" : [
          {
            "text_embedding" : {
              "model_id" : "QRHF6ZsBk04xxx",
              "field_map" : {
                "memory" : "memory_embedding"
              }
            }
          }
        ]
    }

Step 5: Store and verify memory

Add a conversation to the memory container and verify that it is correctly stored in both short-term and long-term memory.

  1. Store memory: Send a POST request with a conversation about a user named Bob to the container. The "infer": true parameter triggers the SEMANTIC strategy that you configured earlier to call the LLM and extract facts.

    Command line

    curl -XPOST "http://${POLARSEARCH_HOST_PORT}/_plugins/_ml/memory_containers/<memory container ID from previous step>/memories" \
    --user "${USER_PASSWORD}" \
    -H 'Content-Type: application/json' \
    -d'{
       "messages": [
          {
          "role": "user",
          "content":  [
            {
              "text": "I am Bob, I really like swimming.",
              "type": "text"
            }
          ]
        },
        {
          "role": "assistant",
          "content":  [
            {
              "text": "Cool, nice. Hope you enjoy your life.",
              "type": "text"
            }
          ]
        }
        ],
      "namespace": {
        "user_id": "bob"
      },
      "tags": {
        "topic": "personal info"
      },
      "infer": true,
      "memory_type": "conversation"
    }'

    Dashboard

    POST _plugins/_ml/memory_containers/<memory_container_id>/memories
    {
       "messages": [
          {
          "role": "user",
          "content":  [
            {
              "text": "I am Bob, I really like swimming.",
              "type": "text"
            }
          ]
        },
        {
          "role": "assistant",
          "content":  [
            {
              "text": "Cool, nice. Hope you enjoy your life.",
              "type": "text"
            }
          ]
        }
        ],
      "namespace": {
        "user_id": "bob"
      },
      "tags": {
        "topic": "personal info"
      },
      "infer": true,
      "memory_type": "conversation"
    }
  2. Verify short-term memory: Query the short-term memory index directly to view the saved original conversation.

    Command line

    curl -XGET "http://${POLARSEARCH_HOST_PORT}/.plugins-ml-am-<index_prefix from previous step>-memory-working/_search?pretty" \
    --user "${USER_PASSWORD}" \

    Dashboard

    GET .plugins-ml-am-<index_prefix from previous step>-memory-working/_search?pretty

    Expected output (partial):

    {
      "_source" : {
        "memory_container_id": "QRHF6ZsBk04xxx",
        "payload_type" : "conversational",
        "messages" : [
          { "role" : "user", "content": [{ "text": "I am Bob, I really like swimming.","type": "text"}] },
          { "role" : "assistant", "content": [{ "text": "Cool, nice. Hope you enjoy your life.","type": "text"}] }
        ],
        "namespace" : { "user_id" : "bob" }
      }
    }
  3. Verify long-term memory: Query the long-term memory index to confirm that the LLM successfully extracted the core fact “Bob likes swimming” and that the embedding model generated the corresponding memory_embedding vector.

    Command line

    curl -XGET "http://${POLARSEARCH_HOST_PORT}/.plugins-ml-am-<index_prefix from previous step>-memory-long-term/_search?pretty" \
    --user "${USER_PASSWORD}" \

    Dashboard

    GET .plugins-ml-am-<index_prefix from previous step>-memory-long-term/_search?pretty

    Expected output (partial):

    {
      "_source" : {
        "created_time": 1769155210918,
        "memory": "Bob likes swimming.",
        "memory_container_id": "QRHF6ZsBk04xxx",
        "tags": {
          "topic": "personal info"
        },
        "last_updated_time": 1769155210918,
        "memory_embedding" : [ 0.0195..., -0.0387..., ... ],
        "namespace" : { "user_id" : "bob" }
      }
    }
  4. View memory history: The memory-history index contains two ADD records and their timestamps.

    Command line

    curl -XGET "http://${POLARSEARCH_HOST_PORT}/.plugins-ml-am-<index_prefix from previous step>-memory-history/_search?pretty" \
    --user "${USER_PASSWORD}" \

    Dashboard

    GET .plugins-ml-am-<index_prefix from previous step>-memory-history/_search?pretty

    Expected output (partial):

    {
      "_source": {
        "created_time": 1769155211164,
        "memory_id": "TBHe6ZsBk04xxx",
        "namespace_size": 1,
        "namespace": {
          "user_id": "bob"
        },
        "action": "ADD",
        "memory_container_id": "QRHF6ZsBk04xxx",
        "after": {
          "memory": "Bob likes swimming."
        },
        "tags": {
          "topic": "personal info"
        }
      }
    }

You have now successfully stored a memory for your AI agent that supports long-term retrieval.

Step 6: (Optional) Update memory

The Memory Container can extract and update long-term memory.

  1. First, store a memory:

    Command line

    curl -XPOST "http://${POLARSEARCH_HOST_PORT}/_plugins/_ml/memory_containers/<memory container ID from previous step>/memories" \
    --user "${USER_PASSWORD}" \
    -H 'Content-Type: application/json' \
    -d'{
      "messages": [
        {
          "role": "user",
          "content": [
            {
              "text": "My name is NameA. I am from AreaA. I currently live in AreaB.",
              "type": "text"
            }
          ]
        },
        {
          "role": "assistant",
          "content": [
            {
              "text": "Hello, NameA! Nice to meet you.",
              "type": "text"
            }
          ]
        }
      ],
      "namespace": {
        "user_id": "NameA"
      },
      "tags": {
        "topic": "personal info"
      },
      "infer": true,
      "memory_type": "conversation"
    }'

    Dashboard

    POST _plugins/_ml/memory_containers/<memory_container_id>/memories
    {
      "messages": [
        {
          "role": "user",
          "content": [
            {
              "text": "My name is NameA. I am from AreaA. I currently live in AreaB.",
              "type": "text"
            }
          ]
        },
        {
          "role": "assistant",
          "content": [
            {
              "text": "Hello, NameA! Nice to meet you.",
              "type": "text"
            }
          ]
        }
      ],
      "namespace": {
        "user_id": "NameA"
      },
      "tags": {
        "topic": "personal info"
      },
      "infer": true,
      "memory_type": "conversation"
    }
  2. Search the long-term memory index:

    Command line

    curl -XGET "http://${POLARSEARCH_HOST_PORT}/.plugins-ml-am-<index_prefix from previous step>-memory-long-term/_search" \
    --user "${USER_PASSWORD}" \
    -H 'Content-Type: application/json' \
    -d'{
      "_source": {
        "excludes": ["memory_embedding"]
      },
      "query": {
        "match_all": {}
      }
    }'

    Dashboard

    GET .plugins-ml-am-<index_prefix from previous step>-memory-long-term/_search
    {
      "_source": {
        "excludes": ["memory_embedding"]
      },
      "query": {
        "match_all": {}
      }
    }

    Expected output (partial):

    {
      "hits": [
          {
            "_source": {
              "created_time": 1769156096335,
              "memory": "NameA is from AreaA.",
              "last_updated_time": 1769156096335,
              "namespace": {
                "user_id": "NameA"
              },
              "memory_container_id": "QRHF6ZsBk04xxx",
              "tags": {
                "topic": "personal info"
              }
            }
          },
          {
            "_source": {
              "created_time": 1769156096335,
              "memory": "NameA currently resides in AreaB.",
              "last_updated_time": 1769156096335,
              "namespace": {
                "user_id": "NameA"
              },
              "memory_container_id": "QRHF6ZsBk04xxx",
              "tags": {
                "topic": "personal info"
              }
            }
          }
       ]
    }
  3. Store another memory that conflicts with the previous one:

    Command line

    curl -XPOST "http://${POLARSEARCH_HOST_PORT}/_plugins/_ml/memory_containers/<memory container ID from previous step>/memories" \
    --user "${USER_PASSWORD}" \
    -H 'Content-Type: application/json' \
    -d'{
      "messages": [
        {
          "role": "user",
          "content": [
            {
              "text": "My name is NameA. I currently live in AreaC.",
              "type": "text"
            }
          ]
        },
        {
          "role": "assistant",
          "content": [
            {
              "text": "Hello, NameA! Nice to meet you.",
              "type": "text"
            }
          ]
        }
      ],
      "namespace": {
        "user_id": "NameA"
      },
      "tags": {
        "topic": "personal info"
      },
      "infer": true,
      "memory_type": "conversation"
    }'

    Dashboard

    POST _plugins/_ml/memory_containers/<memory_container_id>/memories
    {
      "messages": [
        {
          "role": "user",
          "content": [
            {
              "text": "My name is NameA. I currently live in AreaC.",
              "type": "text"
            }
          ]
        },
        {
          "role": "assistant",
          "content": [
            {
              "text": "Hello, NameA! Nice to meet you.",
              "type": "text"
            }
          ]
        }
      ],
      "namespace": {
        "user_id": "NameA"
      },
      "tags": {
        "topic": "personal info"
      },
      "infer": true,
      "memory_type": "conversation"
    }
  4. Search the long-term memory index again to view the updated content (partial):

    {
      "hits": [
          {
            "_source": {
              "created_time": 1769156096335,
              "memory": "NameA resides in AreaC.",
              "last_updated_time": 1769156493970,
              "namespace": {
                "user_id": "NameA"
              },
              "memory_container_id": "QRHF6ZsBk04xxx",
              "tags": {
                "topic": "personal info"
              }
            }
          }
       ]
    }

Use case: Build a travel memory agent

This section shows how to use the Memory Container API to build a Python agent that remembers user travel preferences and provides personalized recommendations.

Preparations

Before you run the code, you must install the required Python libraries. Add the following dependencies to your requirements.txt file.

requests
openai

Run pip install -r requirements.txt to install them.

Core code explanation

The following are the key functions that interact with the Memory Container:

  • opensearch_request(): Wraps HTTP requests to the PolarSearch API and handles authentication and errors.

  • add_memory(): Calls the Memory Container’s memories API to store new conversation data.

  • search_memories(): Calls the _search API to retrieve information that is relevant to a user query from long-term and short-term memory.

  • generate_response_with_memories(): Sends the retrieved memories as context, along with the user’s current question, to the LLM to generate a more personalized reply.

Complete example code

The following is a secure and logically optimized Python script. Save it as trip_agent.py and set the environment variables as instructed.

Click to expand detailed code example

#!/usr/bin/env python3
"""
Trip Memory Agent - A simplified memory agent that tracks user information
and manages memories using OpenSearch as the backend.
"""
import os
import json
import requests
from requests.auth import HTTPBasicAuth
import urllib3
from typing import Optional, List, Dict, Any

# Disable HTTPS warnings for local development
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# Import OpenAI library for LLM operations
import openai

# --- Configuration from Environment Variables ---
OPENSEARCH_URL = os.getenv('OPENSEARCH_URL')
OPENSEARCH_USERNAME = os.getenv('OPENSEARCH_USERNAME', 'admin')
OPENSEARCH_PASSWORD = os.getenv('OPENSEARCH_PASSWORD')
CONTAINER_ID = os.getenv('MEM_CONTAINER_ID')
QWEN_API_KEY = os.getenv('QWEN_API_KEY')
QWEN_BASE_URL = os.getenv('QWEN_BASE_URL', 'https://dashscope.aliyuncs.com/compatible-mode/v1')
QWEN_MODEL_NAME = os.getenv('QWEN_MODEL_NAME', 'qwen-max')

# Global variable to store current user ID
current_user_id = None

# Global OpenAI client instance
llm_client = None


# --- OpenSearch Helper ---
def opensearch_request(method: str, endpoint: str, json_data: Optional[Dict] = None) -> requests.Response:
    """Make an HTTP request to the OpenSearch API."""
    if not all([OPENSEARCH_URL, OPENSEARCH_USERNAME, OPENSEARCH_PASSWORD, CONTAINER_ID]):
        raise ValueError("OpenSearch environment variables are not fully configured.")

    url = f"{OPENSEARCH_URL}{endpoint}"
    auth = HTTPBasicAuth(OPENSEARCH_USERNAME, OPENSEARCH_PASSWORD)
    try:
        response = getattr(requests, method.lower())(url, json=json_data, auth=auth, verify=False, timeout=20)
        response.raise_for_status()
        return response
    except requests.exceptions.RequestException as e:
        print(f"Error communicating with OpenSearch: {e}")
        # Return a mock response to prevent crashes, or handle more gracefully
        # For this example, we re-raise to make the error obvious
        raise e


# --- User Management ---
def get_user_id():
    """Get user ID from global variable or prompt the user."""
    global current_user_id
    if not current_user_id:
        print("=" * 50)
        print("Welcome to the Trip Memory Agent!")
        print("=" * 50)
        print("Hello! I don't know you yet. Please tell me your name:")
        user_input = input("> ")
        current_user_id = user_input.strip()
        print(f"Nice to meet you, {current_user_id}!")
    return current_user_id


# --- LLM-Powered Helper Functions ---
def is_trip_plan_request(query: str) -> bool:
    """Determine if a user's query is a request for a trip plan using an LLM."""
    if not query.strip():
        return False
    try:
        prompt = f"""Please determine if the following user input is a request to plan a trip.
Answer only "YES" or "NO". Do not explain.
User input: {query}"""
        response = llm_client.chat.completions.create(
            model=QWEN_MODEL_NAME,
            messages=[{"role": "user", "content": prompt}],
            max_tokens=10,
            temperature=0.0
        )
        result = response.choices[0].message.content.strip().upper()
        return result == "YES"
    except Exception as e:
        print(f"Error detecting trip plan request with LLM: {e}. Falling back to keywords.")
        keywords = ['plan a trip', 'trip plan', 'plan my travel', 'travel plan']
        return any(keyword in query.lower() for keyword in keywords)


def extract_keywords(query: str) -> List[str]:
    """Extract keywords from a query using an LLM for better search results."""
    try:
        prompt = f"""Extract the most important keywords from the following query for search purposes.
Focus on locations, activities, and specific preferences.
Return only a JSON array of strings (keywords).
Query: {query}"""
        response = llm_client.chat.completions.create(
            model=QWEN_MODEL_NAME,
            messages=[{"role": "user", "content": prompt}],
            max_tokens=100,
            temperature=0.0
        )
        extracted_content = response.choices[0].message.content.strip()
        return json.loads(extracted_content)
    except (Exception, json.JSONDecodeError) as e:
        print(f"Error extracting keywords with LLM: {e}. Falling back to splitting query.")
        return query.split()


def generate_response_with_memories(query: str, search_results: List[Dict]) -> str:
    """Generate a response using an LLM with memory context."""
    if not search_results:
        return "I couldn't find any relevant memories for your query."

    context = "Here is relevant information I found in your memories:\n"
    for result in search_results:
        context += f"- {result['content']}\n"

    prompt = f"""You are a helpful trip planning assistant. Based on the user's past memories and their current request, generate a helpful and personalized response.

{context}
User's current request: {query}

Your response:"""
    try:
        response = llm_client.chat.completions.create(
            model=QWEN_MODEL_NAME,
            messages=[{"role": "system", "content": prompt}],
            max_tokens=1024,
            temperature=0.7
        )
        return response.choices[0].message.content.strip()
    except Exception as e:
        print(f"Error generating response with LLM: {e}")
        return "I found some memories, but I encountered an error while trying to generate a response."


# --- Core Memory Management Functions (Our "Tools") ---

def add_memory(message: str) -> Dict[str, Any]:
    """Add a new piece of information (a memory) for the current user."""
    global current_user_id
    payload = {
        "messages": [{"role": "user", "content": [{"text": message, "type": "text"}]}],
        "infer": True,
        "memory_type": "conversation",
        "namespace": {"user_id": current_user_id}
    }
    response = opensearch_request("POST", f"/_plugins/_ml/memory_containers/{CONTAINER_ID}/memories", payload)
    return response.json()


def search_memories(query: str) -> Dict[str, Any]:
    """Search the user's memories based on a query."""
    global current_user_id
    keywords = extract_keywords(query)

    search_query = {
        "query": {
            "bool": {
                "must": [{"term": {"namespace.user_id": current_user_id}}],
                "should": [
                    {"multi_match": {"query": " ".join(keywords), "fields": ["memory", "messages.content.text"]}}],
                "minimum_should_match": 1 if keywords else 0
            }
        },
        "sort": [{"created_time": {"order": "desc"}}],
        "size": 10  # Limit number of results
    }

    all_hits = []
    for index_suffix in ["long-term", "working"]:
        try:
            response = opensearch_request("POST",
                                          f"/_plugins/_ml/memory_containers/{CONTAINER_ID}/memories/{index_suffix}/_search",
                                          search_query)
            result = response.json()
            if "hits" in result and "hits" in result["hits"]:
                all_hits.extend(result["hits"]["hits"])
        except Exception as e:
            print(f"Error searching {index_suffix} memory: {e}")

    all_hits.sort(key=lambda x: x.get('_source', {}).get('created_time', ''), reverse=True)

    formatted_results = []
    for hit in all_hits:
        source = hit.get('_source', {})
        content = source.get('memory', '')
        if not content and 'messages' in source:
            content = " ".join(
                item['text'] for msg in source['messages'] if 'content' in msg
                for item in msg['content'] if isinstance(item, dict) and 'text' in item
            )

        index_type = "long-term" if 'long-term' in hit.get('_index', '') else "working"
        formatted_results.append({
            "memory_id": hit.get('_id'),
            "index_type": index_type,
            "timestamp": source.get('created_time'),
            "content": content.strip()
        })
    return {"status": "success", "results": formatted_results, "total_found": len(formatted_results)}


def find_and_update_memory(query: str, new_text: str) -> Dict[str, Any]:
    """Find a memory based on a query and update its content."""
    search_result = search_memories(query)
    if not search_result["results"]:
        return {"status": "error", "message": "No matching memory found to update."}

    # Update the most recent/relevant memory
    memory_to_update = search_result["results"][0]
    memory_id = memory_to_update["memory_id"]
    index_type = memory_to_update["index_type"]

    payload = {"memory": new_text}
    response = opensearch_request("PUT",
                                  f"/_plugins/_ml/memory_containers/{CONTAINER_ID}/memories/{index_type}/{memory_id}",
                                  payload)
    return {"status": "success", "updated_memory_id": memory_id, "details": response.json()}


def find_and_delete_memory(query: str) -> Dict[str, Any]:
    """Find a memory based on a query and delete it."""
    search_result = search_memories(query)
    if not search_result["results"]:
        return {"status": "error", "message": "No matching memory found to delete."}

    # Delete the most recent/relevant memory
    memory_to_delete = search_result["results"][0]
    memory_id = memory_to_delete["memory_id"]
    index_type = memory_to_delete["index_type"]

    response = opensearch_request("DELETE",
                                  f"/_plugins/_ml/memory_containers/{CONTAINER_ID}/memories/{index_type}/{memory_id}")
    return {"status": "success", "deleted_memory_id": memory_id, "details": response.json()}


# --- Agent Logic ---

def main():
    """Main function to run the trip memory agent without 'strands'."""
    global llm_client

    # --- Initialization ---
    if not all([QWEN_API_KEY, QWEN_BASE_URL, QWEN_MODEL_NAME]):
        print("Error: Qwen LLM environment variables are not configured.")
        return

    llm_client = openai.OpenAI(api_key=QWEN_API_KEY, base_url=QWEN_BASE_URL)
    get_user_id()

    # --- Tool Definitions for the LLM ---
    tools = [
        {
            "type": "function",
            "function": {
                "name": "add_memory",
                "description": "Adds a new piece of information or a memory about the user. Use this to remember user preferences, facts, or past events.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "message": {
                            "type": "string",
                            "description": "The information or content of the memory to be saved. e.g., 'I love to visit historical museums.'"
                        }
                    },
                    "required": ["message"]
                }
            }
        },
        {
            "type": "function",
            "function": {
                "name": "search_memories",
                "description": "Searches for and retrieves existing memories about the user. Use this when the user asks what you know about them, asks for past information, or wants a trip plan based on their preferences.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "query": {
                            "type": "string",
                            "description": "The search query to find relevant memories. e.g., 'my preferences', 'what do you know about me', 'trip to Paris'"
                        }
                    },
                    "required": ["query"]
                }
            }
        },
        {
            "type": "function",
            "function": {
                "name": "find_and_update_memory",
                "description": "Finds a specific memory using a search query and updates its content with new text.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "query": {"type": "string",
                                  "description": "A query to identify the memory to update. e.g., 'my favorite food'"},
                        "new_text": {"type": "string",
                                     "description": "The new content for the memory. e.g., 'My favorite food is now ramen.'"}
                    },
                    "required": ["query", "new_text"]
                }
            }
        },
        {
            "type": "function",
            "function": {
                "name": "find_and_delete_memory",
                "description": "Finds a specific memory using a search query and deletes it.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "query": {"type": "string",
                                  "description": "A query to identify the memory to delete. e.g., 'my old address'"}
                    },
                    "required": ["query"]
                }
            }
        }
    ]

    # Map tool names to actual Python functions
    available_tools = {
        "add_memory": add_memory,
        "search_memories": search_memories,
        "find_and_update_memory": find_and_update_memory,
        "find_and_delete_memory": find_and_delete_memory,
    }

    system_prompt = f"""You are a trip assistant for a user named {current_user_id}. Your job is to help them by managing their memories and planning trips.
You have access to the following tools: `add_memory`, `search_memories`, `update_memory`, and `delete_memory`.
- When a user provides new information about themselves, use `add_memory`.
- When a user asks what you know about them, asks for their preferences, or wants a trip plan, use `search_memories` to get context first.
- When a user wants to change a piece of information, use `find_and_update_memory`.
- When a user wants to forget something, use `find_and_delete_memory`.
- Always respond to the user in a friendly and conversational manner. After a tool is used, explain what you did in simple terms."""

    messages = [{"role": "system", "content": system_prompt}]

    print("\nTrip Memory Agent is ready!")
    print(f"Hello {current_user_id}! I can remember things for you, search your memories, and help plan trips.")

    # --- Main Interaction Loop ---
    while True:
        try:
            user_input = input(f"\n{current_user_id}> ")
            if user_input.lower() in ['quit', 'exit', 'bye']:
                print("Goodbye!")
                break

            messages.append({"role": "user", "content": user_input})

            # Special fast-path for trip planning
            if is_trip_plan_request(user_input):
                print("This looks like a trip plan request. Searching memories for context...")
                search_result = search_memories(user_input)
                if search_result.get("total_found", 0) > 0:
                    print("Found relevant memories! Generating a personalized plan...")
                    response = generate_response_with_memories(user_input, search_result["results"])
                    print(f"\nAgent: {response}")
                else:
                    print("I couldn't find any relevant memories to help with that plan. Let's start from scratch!")
                    # Let the general agent handle it now
                    pass
                messages.pop()  # Remove user input to avoid double processing
                continue

            print("Thinking...")
            # --- First LLM Call: Decide whether to use a tool ---
            response = llm_client.chat.completions.create(
                model=QWEN_MODEL_NAME,
                messages=messages,
                tools=tools,
                tool_choice="auto"
            )
            response_message = response.choices[0].message

            # Check if the model wants to call a tool
            if response_message.tool_calls:
                messages.append(response_message)  # Append the model's decision

                # --- Execute Tool Calls ---
                for tool_call in response_message.tool_calls:
                    function_name = tool_call.function.name
                    function_to_call = available_tools.get(function_name)

                    if not function_to_call:
                        print(f"Error: LLM tried to call an unknown function '{function_name}'")
                        continue

                    try:
                        function_args = json.loads(tool_call.function.arguments)
                        print(f"Calling tool: `{function_name}` with args: {function_args}")

                        function_response = function_to_call(**function_args)

                        # Append the tool's result to the message history
                        messages.append({
                            "tool_call_id": tool_call.id,
                            "role": "tool",
                            "name": function_name,
                            "content": json.dumps(function_response)
                        })
                    except Exception as e:
                        print(f"Error executing tool '{function_name}': {e}")
                        messages.append({
                            "tool_call_id": tool_call.id,
                            "role": "tool",
                            "name": function_name,
                            "content": f'{{"status": "error", "message": "{str(e)}"}}'
                        })

                # --- Second LLM Call: Generate a natural language response ---
                print("Summarizing tool results...")
                second_response = llm_client.chat.completions.create(
                    model=QWEN_MODEL_NAME,
                    messages=messages
                )
                final_response = second_response.choices[0].message.content
                print(f"\nAgent: {final_response}")
                messages.append({"role": "assistant", "content": final_response})

            else:
                # The model decided to just chat
                final_response = response_message.content
                print(f"\nAgent: {final_response}")
                messages.append({"role": "assistant", "content": final_response})

        except Exception as e:
            print(f"\nAn unexpected error occurred: {e}")
            # Reset messages on critical error to prevent bad state
            messages = [{"role": "system", "content": system_prompt}]


if __name__ == "__main__":
    # Before running, set your environment variables:
    # export OPENSEARCH_URL="https://..."
    # export OPENSEARCH_USERNAME="admin"
    # export OPENSEARCH_PASSWORD="your_password"
    # export MEM_CONTAINER_ID="your_container_id"
    # export QWEN_API_KEY="your_api_key"
    main()

Run and interact

  1. Set environment variables: In your terminal, set the required environment variables for the script.

  2. Run the script: python trip_agent.py

  3. Interaction example:

    Click to expand detailed interaction example

    ==================================================
    Welcome to the Trip Memory Agent!
    ==================================================
    Hello! I don't know you yet. Please tell me your name:
    > ABC
    Nice to meet you, ABC!
    
    Trip Memory Agent is ready!
    Hello ABC! I can remember things for you, search your memories, and help plan trips.
    
    ABC> i like hiking and travelling
    Thinking...
    Calling tool: `add_memory` with args: {'message': 'i like hiking and travelling'}
    Summarizing tool results...
    
    Agent: Great to know that you enjoy hiking and traveling, ABC! I've added this information to your profile. Whenever you're looking for trip ideas, I'll make sure to suggest some amazing hiking spots and travel destinations for you. 
    
    ABC> I like listen to music
    Thinking...
    Calling tool: `add_memory` with args: {'message': 'I like listen to music'}
    Summarizing tool results...
    
    Agent: Awesome, ABC! I've noted that you like listening to music. Whether it's for your travels or just everyday life, I can help you find some great playlists and music recommendations. Let me know if you have any favorite genres or artists!
    
    ABC> what do you know about me
    Thinking...
    Calling tool: `search_memories` with args: {'query': 'what do you know about me'}
    Summarizing tool results...
    
    Agent: I know a few things about you, ABC! Here’s what I have:
    
    1. You like hiking and traveling. These are some of your favorite activities, and you enjoy exploring new places and being outdoors.
    2. You also enjoy listening to music. It seems to be a big part of your life, whether it's for relaxation or just something you do regularly.
    
    If there's anything else you'd like to add or if you want to update any of this information, just let me know! 
    
    ABC> I live in Beijing
    Thinking...
    Calling tool: `add_memory` with args: {'message': 'I live in Beijing'}
    Summarizing tool results...
    
    Agent: Thanks for letting me know, ABC! I've added that you live in Beijing to your profile. This will help me tailor my suggestions and plans to be more relevant to your location. If you have any other details or preferences you'd like to share, feel free to let me know! 
    
    ABC> help me plan a weekend trip
    This looks like a trip plan request. Searching memories for context...
    Found relevant memories! Generating a personalized plan...
    
    Agent: Of course, I'd be happy to help you plan a fantastic weekend trip! Given your love for hiking, traveling, and music, how about we create an itinerary that combines all these interests? Here are a couple of ideas:
    
    ### Option 1: Hiking and Music in the Mountains
    **Destination:** **Yunmeng Mountain**, Huairou District, Beijing
    
    **Day 1:**
    - **Morning:** Start early and drive to Yunmeng Mountain. It's about a 2-hour drive from central Beijing.
    - **Midday:** Arrive at Yunmeng Mountain and begin your hike. The trails here offer stunning views and a peaceful atmosphere. You can choose from various routes, ranging from easy to challenging.
    - **Afternoon:** After your hike, find a scenic spot to relax and enjoy a picnic lunch. Bring along some of your favorite music to set the mood.
    - **Evening:** Check into a local guesthouse or a small hotel in the area. Many places offer traditional Chinese-style accommodations with a cozy, rustic feel.
    
    **Day 2:**
    - **Morning:** Wake up early and head out for another short hike or a leisurely walk to explore more of the area.
    - **Midday:** Visit a local restaurant for a hearty brunch. Try some of the local specialties like *huai rou duck* (Huairou roast duck) or *noodles with mountain vegetables*.
    - **Afternoon:** Return to Beijing. On the way back, you can stop at a local market or a small town to pick up some souvenirs or fresh produce.
    
    ### Option 2: Cultural Exploration and Nature
    **Destination:** **Jiulongshan**, Miyun District, Beijing
    
    **Day 1:**
    - **Morning:** Drive to Jiulongshan, which is about 1.5 hours from central Beijing. This area is known for its beautiful landscapes and historical sites.
    - **Midday:** Begin your hike in Jiulongshan. The trails here are well-maintained and offer a mix of natural beauty and cultural landmarks.
    - **Afternoon:** After your hike, visit the nearby **Jinshanling Great Wall**. It’s less crowded than other sections of the Great Wall and offers breathtaking views.
    - **Evening:** Stay at a local inn or a homestay near the Great Wall. Some places even offer live music performances in the evenings, which would be a perfect way to unwind after a day of hiking.
    
    **Day 2:**
    - **Morning:** Take a leisurely walk around the area or visit a local village to experience the rural life of Beijing.
    - **Midday:** Enjoy a traditional Chinese lunch at a local restaurant.
    - **Afternoon:** Head back to Beijing. If you have time, you could stop at a scenic spot or a small park to relax and listen to some music before returning home.
    
    Both options offer a great balance of outdoor activities, cultural experiences, and relaxation. Which one do you think you’d prefer, or is there anything else you’d like to add to the itinerary?
    
    ABC> I live in Shanghai now
    Thinking...
    Calling tool: `find_and_update_memory` with args: {'query': 'I live in Beijing', 'new_text': 'I live in Shanghai now'}
    Summarizing tool results...
    
    Agent: Got it, ABC! I've updated your information to reflect that you now live in Shanghai. This will help me provide more relevant suggestions and plans for your new location. If there's anything else you'd like to update or add, just let me know! 
    
    (I used the `find_and_update_memory` tool to change your location from Beijing to Shanghai.)
    
    ABC> where do i live
    Thinking...
    Calling tool: `search_memories` with args: {'query': 'where do i live'}
    Summarizing tool results...
    
    Agent: You currently live in Shanghai, ABC! I updated this information recently, so it's all set. If you have any other questions or need more details, feel free to ask! 
    
    (I used the `search_memories` tool to find and confirm your current location.)
    
    ABC> help me plan a weekend trip
    This looks like a trip plan request. Searching memories for context...
    Found relevant memories! Generating a personalized plan...
    
    Agent: Considering you now live in Shanghai and enjoy weekend road trips, hiking, and listening to music, I’ve planned a perfect weekend getaway for you. You can head to Moganshan in Zhejiang Province, about a 3-hour drive from Shanghai—a great destination for a short trip.
    
    **Itinerary suggestion:**
    
    - **Day 1:**
      - Drive from Shanghai to Moganshan in the morning.
      - Have lunch at a local restaurant after arriving at noon.
      - Spend the afternoon hiking in Moganshan. Choose scenic trails such as Jianchi or Daken Scenic Area.
      - Stay overnight at a comfortable boutique hotel or guesthouse.
    
    - **Day 2:**
      - Take a relaxing walk near your accommodation in the morning.
      - Visit a local tea plantation in the morning to experience tea picking.
      - Have lunch at a restaurant near the tea plantation.
      - Return to Shanghai in the afternoon to wrap up your enjoyable weekend.
    
    **Notes:**
    - Ensure your vehicle is in good condition. Check oil and tires before departure.
    - Bring your favorite music to enjoy during the drive.
    - Pack appropriate clothing and gear for hiking, especially proper shoes and a backpack.
    - Book your accommodation in advance to guarantee a suitable stay.
    
    I hope this plan helps you enjoy a wonderful weekend! If you need anything else or want to adjust the itinerary, just let me know.
    

API reference

This guide uses the following APIs. For more information, see the official OpenSearch documentation:

Billing

Using the PolarSearch Memory Container incurs the following costs. Review these costs carefully before you use the feature in a production environment.

  1. Compute node fees: As a part of PolarDB, PolarSearch nodes incur compute node fees.

  2. Model service fees: Calling external models, such as models from Alibaba Cloud Model Studio, for fact extraction and vectorization incurs API call fees.

FAQ

model_id and connector_id: What is the difference? Which one should I use when I create a memory container?

  • connector_id is the ID of the connector that you created to point to an external model service, such as Alibaba Cloud Model Studio.

  • The model_id is the ID that PolarSearch internally assigns to the model after you register the connector with PolarSearch.

When you create a memory container, use the model_id for the embedding_model_id and llm_id parameters.

llm_result_path: How do I configure this parameter?

You can use JSONPath syntax to extract the required text content as memory facts from the complex JSON response of the LLM. You must configure this path based on the actual API response structure of your LLM. For example, for Alibaba Cloud Model Studio in compatible mode, the core content is at {"choices":[{"message":{"content":"..."}}]}. Therefore, the path is $.choices[0].message.content.