Skip to main content

Amazon OpenSearch Service

Amazon OpenSearch is the memledger backend for agents that need hybrid search — vector recall ("things that look like this") plus BM25 lexical recall ("exact error code lookup") in a single query. memledger's OpenSearch backend signs every request with SigV4 using the IRSA-injected AWS credentials — no static API keys, no basic auth.

Validated end-to-end against a real OpenSearch domain — see Backend Validation.

When to choose OpenSearch over Aurora

OpenSearch wins for agents that need lexical recall on top of semantic recall — exact identifier / error-code / SKU lookup paired with vector similarity. Aurora + pgvector is the better default when your access pattern is purely semantic and you already operate Postgres.

Provisioning the domain

For production deployments:

SettingRecommendation
EngineOpenSearch 2.13 or newer
DeploymentMulti-AZ with standby
Instance typem6g.large.search or larger for sustained agent workloads
EncryptionAt-rest enabled, node-to-node TLS, HTTPS-only
Access policyIAM-based (no fine-grained access control needed for SigV4)

Note: memledger's smoke validation runs against a t3.small.search single-AZ domain — fine for verification, not a production shape. Multi-AZ + m6g.large.search is the floor for sustained workloads.

SigV4 auth

memledger's OpenSearch backend signs every HTTP request with the agent's IRSA credentials. There is no API key, no master user, no users.yml.

The agent's IRSA role needs es:ESHttp* on the domain ARN:

{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["es:ESHttp*"],
"Resource": [
"arn:aws:es:<region>:<account-id>:domain/<your-opensearch-domain>/*"
]
}]
}

The domain's own access policy must trust this role's ARN — typically:

{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::<account-id>:role/<your-irsa-role>"},
"Action": "es:ESHttp*",
"Resource": "arn:aws:es:<region>:<account-id>:domain/<your-opensearch-domain>/*"
}]
}

SDK configuration

Set the OpenSearch endpoint via environment or backend config:

import os
from memledger import Memledger
from memledger.models import EmbeddingConfig

ml = await Memledger.create(
backend_name="opensearch",
backend_config={
"endpoint": os.environ["OPENSEARCH_ENDPOINT"], # https://...
"region": "<your-region>",
"index": "agent_memory",
},
embedding_config=EmbeddingConfig(
provider="bedrock",
model="amazon.titan-embed-text-v2:0",
dimensions=1024,
),
)

Hybrid search combines BM25 lexical scoring with k-NN vector similarity in a single OpenSearch query. Tune the relative weight at the call site:

results = await ml.search_hybrid(
query="HikariCP connection pool exhausted",
namespace="/ops/incidents",
top_k=10,
vector_weight=0.7, # k-NN contribution (default 0.7)
text_weight=0.3, # BM25 contribution (default 0.3)
)

A higher vector_weight favors semantic similarity ("things that mean this"); a higher text_weight favors exact-token matches ("things that contain HikariCP"). For most agent workloads, the 0.7 / 0.3 default is a good starting point.

Index template

memledger creates the agent_memory index on first connection with an HNSW vector field sized for your embedding model:

FieldTypeNotes
embeddingknn_vector(1024)HNSW; space_type=l2, m=16, ef_construction=512
contenttextStandard analyzer; powers BM25 in hybrid search
namespacekeywordFilter dimension; never tokenized
Governance fieldskeyword / boolean / dateconfidence, hedged, created_by, workflow_id, etc.

To change HNSW parameters or analyzer, set them in backend_config["index_settings"] before the first connection — index settings on knn_vector are immutable post-create.

Embedding-dim alignment

Same constraint as Aurora: the knn_vector dimension is fixed at index-create time. Match your embedding model:

  • Bedrock Titan v2 → dimensions=1024
  • fastembed BAAI/bge-small → dimensions=384

Switching providers across a dim boundary requires reindexing into a new agent_memory index.

Next steps