Cloud & Databases

TencentDB Memory Retrieval Design: How AI Agents Find the Right Context

AI agents become significantly more useful when they can retain reliable knowledge across conversations and tasks. This guide explains TencentDB persistent memory design through practical architecture patterns covering memory capture, structured storage,…

46 min read
TencentDB Memory Retrieval Design: How AI Agents Find the Right Context
Advertisement
What You Will Learn
Why Storing Memory Is Not Enough
Exact Search vs Semantic Retrieval
Start With Scope Before Similarity
Why Scope Is More Important Than Similarity
⚡ Quick Answer
TencentDB memory retrieval designs enable AI agents to precisely locate relevant information from extensive stored memories, preventing information overload and ensuring useful context for specific user queries. This comprehensive retrieval approach combines metadata filtering, semantic search, and reranking, moving beyond simple keyword matching to deliver accurate and trustworthy data essential for informed agent responses.

TencentDB Persistent Memory Design is the part of an AI agent memory system that determines whether stored information can actually become useful at the moment an agent needs it.

Saving memories is only half the problem.

Imagine an agent has accumulated 10,000 records:

Code
Project preferences
User preferences
Testing strategies
API workflows
Past decisions
Temporary observations
Old project information
Procedures
Technical facts
Conversation-derived memories

The user asks:

“How should I create test users for the checkout flow?”

The agent does not need all 10,000 memories.

It needs the small number of memories that are relevant to this specific question.

That is the core problem solved by TencentDB memory retrieval.

A useful retrieval pipeline therefore looks like this:

Code
User Query
    ↓
Understand Query
    ↓
Identify Scope
    ↓
Filter Candidate Memories
    ↓
Search Relevant Memories
    ↓
Calculate Relevance
    ↓
Rank Results
    ↓
Remove Duplicates
    ↓
Apply Freshness / Confidence
    ↓
Build Agent Context
    ↓
LLM Response

The important idea is simple:

A memory system is only as useful as its ability to retrieve the right memory at the right moment.

Why Storing Memory Is Not Enough

Consider an application that stores this record:

JSON
{
  "memory_type": "preference",
  "content": "Create test users through the API.",
  "project": "checkout-platform"
}

The information is valuable.

But suppose the user asks:

Code
How should we create checkout users?

The database must somehow connect the question with the stored memory.

A basic implementation might search exact words:

SQL
SELECT *
FROM agent_memories
WHERE content ILIKE '%checkout%';

This can work for simple cases.

But now change the query:

Code
What's our preferred approach for generating accounts used by checkout tests?

The words are different.

The underlying meaning is similar.

This is where semantic retrieval becomes important.

Exact Search vs Semantic Retrieval

There are several ways an agent can find memory.

Retrieval approachStrengthWeakness
Exact keyword searchFast and predictableMisses different wording
SQL metadata filteringExcellent for scopeDoes not understand meaning
Full-text searchBetter textual matchingStill limited semantically
Vector searchFinds semantic similaritySimilarity does not guarantee correctness
Hybrid retrievalCombines multiple signalsMore architecture and tuning required

A robust TencentDB memory retrieval implementation does not necessarily choose one technique.

Instead, it combines them.

For example:

Code
User Query
    ↓
Metadata Filtering
    ↓
Keyword / Full-Text Search
    ↓
Vector Similarity
    ↓
Reranking
    ↓
Final Memories

This is fundamentally different from simply asking:

Code
"Which memory is closest to this sentence?"

The system should instead ask:

Code
"Which memories are relevant,
belong to the correct scope,
are sufficiently trustworthy,
are current,
and are useful for answering this query?"

That is a much stronger retrieval question.

Start With Scope Before Similarity

One of the most important retrieval principles is scope filtering.

Suppose the database contains:

Code
Project Alpha → Playwright
Project Beta → Cypress
Project Gamma → Selenium

The user asks:

Code
Which framework does Project Alpha use?

A vector search might find all three records because all are semantically related to browser automation.

That does not mean all three should reach the model.

Instead:

Code
Query
 ↓
Project = Alpha
 ↓
Search remaining memories
 ↓
Rank relevant records

A relational filter can therefore reduce the search space first.

For example:

SQL
SELECT
    id,
    content,
    memory_type,
    importance,
    confidence
FROM agent_memories
WHERE tenant_id = :tenant_id
  AND project_id = :project_id
  AND status = 'active';

Only after authorization and scope filtering should semantic ranking become responsible for finding the most relevant information.

This creates a powerful principle:

Use structured data to define where the agent is allowed to search, and semantic retrieval to determine what matters inside that boundary.

Why Scope Is More Important Than Similarity

Consider these memories:

Code
Memory A:
Project Alpha uses Playwright.

Memory B:
Project Beta uses Cypress.

Memory C:
The organization previously used Selenium.

Query:

Code
What browser framework does Alpha use?

All three contain related concepts.

But only Memory A directly answers the question.

A retrieval system that relies exclusively on semantic similarity can produce unexpected results.

A better approach is:

Python
def retrieve_memories(query, context):
    candidates = filter_by_scope(
        tenant_id=context.tenant_id,
        project_id=context.project_id
    )

    candidates = semantic_search(
        query=query,
        candidates=candidates
    )

    return rank(candidates)

The architecture becomes easier to reason about because every stage has a clear responsibility.

Build a Memory Retrieval Query

A practical PostgreSQL-based design might begin with a structured query:

SQL
SELECT
    id,
    content,
    memory_type,
    importance,
    confidence,
    created_at,
    updated_at
FROM agent_memories
WHERE tenant_id = :tenant_id
  AND agent_id = :agent_id
  AND status = 'active'
ORDER BY importance DESC
LIMIT 20;

This is not semantic retrieval yet.

It is candidate selection.

That distinction matters.

The database first establishes:

Code
Who?
Which agent?
Which scope?
Which memories are active?

Then another retrieval mechanism can determine:

Code
Which candidates are most relevant to this question?

Separating these responsibilities makes the system easier to optimize.

Add Memory Type to Retrieval

Not every query needs every type of memory.

Suppose your memory model includes:

Code
preference
project_fact
procedure
episodic
technical_fact
temporary

A question such as:

“How do we normally generate test data?”

may benefit heavily from:

Code
procedure
preference

while:

“Which framework does this project use?”

may benefit from:

Code
project_fact
technical_fact

You can therefore introduce type-aware retrieval:

Code
MEMORY_TYPES = {
    "procedure",
    "preference",
    "project_fact",
    "episodic",
    "technical_fact"
}

Then:

Python
def select_memory_types(query):
    if asks_about_process(query):
        return ["procedure", "preference"]

    if asks_about_project(query):
        return ["project_fact", "technical_fact"]

    return list(MEMORY_TYPES)

This is not mandatory for every application, but it demonstrates an important architectural principle:

retrieval should understand the structure of the memory it is searching.

Image

The Difference Between Candidate Retrieval and Final Retrieval

This distinction can dramatically improve an agent system.

Suppose the database contains 50,000 memories.

You do not want the LLM to process 50,000 records.

Instead:

Code
50,000 memories
      ↓
Scope filtering
      ↓
5,000 candidates
      ↓
Semantic retrieval
      ↓
100 candidates
      ↓
Reranking
      ↓
10 candidates
      ↓
Context selection
      ↓
5 memories
      ↓
LLM

Each stage reduces unnecessary information.

This is essentially a funnel.

Code
Broad
  ↓
Filtered
  ↓
Relevant
  ↓
Highly relevant
  ↓
Useful context

This approach can reduce both latency and token consumption.

Relevance Should Not Mean Similarity Alone

Suppose two memories have these scores:

Code
Memory A → similarity 0.94
Memory B → similarity 0.89

It would be tempting to select Memory A.

But what if:

Code
Memory A:
Updated 2 years ago
Confidence: 0.55
Project: Beta

Memory B:
Updated yesterday
Confidence: 0.98
Project: Alpha

Memory B may be dramatically more useful despite its lower similarity score.

This suggests a richer scoring model.

For example:

Python
def calculate_score(memory):
    return (
        0.45 * memory.semantic_score +
        0.20 * memory.scope_score +
        0.15 * memory.confidence +
        0.10 * memory.importance +
        0.10 * memory.freshness
    )

These weights are illustrative.

Your production system should evaluate and tune them using real retrieval data.

The important lesson is that semantic similarity should be one signal among several.

Add Freshness to the Retrieval Model

Some memories become less useful over time.

For example:

Code
January:
"The project uses API version 1."

Six months later:

Code
"The project migrated to API version 3."

A retrieval system that sees only semantic similarity may return both.

Freshness helps distinguish current knowledge from historical knowledge.

A simple freshness function could be:

Python
from datetime import datetime

def freshness(memory):
    age_days = (
        datetime.utcnow() - memory.updated_at
    ).days

    return max(0.0, 1 - age_days / 365)

Again, this is only a starting point.

Different memory types need different freshness strategies.

A user’s long-term preference may remain useful for years.

An API version may become obsolete within weeks.

Therefore:

Code
Memory Type
    ↓
Different Freshness Policy

is usually better than one global expiration rule.

Confidence and Importance Are Different

This distinction is often overlooked.

Consider:

Code
Memory A:
"The user prefers API-based test data."

Confidence = 0.98
Importance = 0.85

Now:

Code
Memory B:
"The user once mentioned trying Cypress."

Confidence = 0.99
Importance = 0.20

The second statement may be confidently observed but not particularly useful.

Therefore:

Code
Confidence ≠ Importance

Confidence answers:

How certain are we that this memory is correct?

Importance answers:

How valuable is this memory when constructing context?

A retrieval score can use both independently.

Do Not Return Every Relevant Memory

Suppose semantic search returns:

Code
1. Create test users through API.
2. Checkout tests use API-generated accounts.
3. API test data should be reusable.
4. Test users should not be created through UI.
5. Playwright is used for checkout testing.
6. API tests run before UI tests.

All six might be relevant.

But including all six could create unnecessary repetition.

A deduplication stage can collapse overlapping memories.

Python
def deduplicate(memories):
    unique = []

    for memory in memories:
        if not overlaps_existing(memory, unique):
            unique.append(memory)

    return unique

The objective is not:

Return as many memories as possible.

The objective is:

Return enough high-quality memories to answer the question accurately.

Context Quality Beats Context Quantity

A common AI-agent mistake is:

Code
More memory
=
Better answer

Usually:

Code
More relevant memory
=
Better answer

Consider:

Code
Context A:
5 highly relevant memories

Context B:
50 loosely related memories

Context A can be better because it gives the model a clearer signal.

This becomes increasingly important as the application’s memory database grows.

A Simple Retrieval Service

You can encapsulate retrieval behind a service:

Python
class MemoryRetriever:

    def retrieve(self, query, context, limit=5):
        candidates = self.get_candidates(
            query,
            context
        )

        ranked = self.rank(
            query,
            candidates
        )

        filtered = self.remove_duplicates(
            ranked
        )

        return filtered[:limit]

Then your agent code remains simple:

Code
memories = retriever.retrieve(
    query=user_query,
    context=agent_context,
    limit=5
)

prompt = build_prompt(
    user_query,
    memories
)

This separation is strategically useful.

The agent should not need to know whether retrieval internally uses:

Code
SQL
Full-text search
Vector search
Hybrid search
Reranking
Caching

Those implementation details belong behind the retrieval layer.

TencentDB Memory Retrieval vs Redis Lookup

It is useful to distinguish persistent memory retrieval from temporary state lookup.

Advertisement
CharacteristicTencentDB Memory RetrievalRedis Lookup
Primary rolePersistent memory/dataFast temporary state/cache
Structured SQL filteringStrongNot primary
Durable relational recordsStrongDifferent use case
Semantic retrievalPostgreSQL/vector architecture can support itUsually requires additional design
Complex metadata relationshipsStrongLess natural
Session statePossibleExcellent
CachingPossibleExcellent
Long-term agent knowledgeStrong fitUsually not primary

This does not mean Redis is inferior.

It means the systems solve different problems.

A useful architecture can be:

Code
Redis
 ↓
Short-Term Agent State

TencentDB
 ↓
Long-Term Agent Memory

Then the agent can combine both:

Code
Current Session
      +
Persistent Memories
      ↓
Final Context

Make Retrieval Explainable

When an agent uses a memory, you should ideally be able to determine:

Code
Why was this memory selected?
What scope matched?
What was its similarity?
How fresh was it?
What confidence did it have?
What caused it to outrank another memory?

For example:

JSON
{
  "memory_id": "mem_381",
  "semantic_score": 0.92,
  "confidence": 0.98,
  "importance": 0.85,
  "freshness": 0.96,
  "scope_match": true,
  "final_score": 0.93
}

This information can remain in observability logs rather than being exposed to the end user.

But it becomes extremely valuable when debugging incorrect agent behavior.

Interactive Challenge: Design Your Retrieval Rule

Imagine your database contains:

Code
A. User prefers Playwright.
B. Project Alpha uses Cypress.
C. Project Beta uses Playwright.
D. Project Alpha migrated to Playwright yesterday.
E. User experimented with Selenium two years ago.

The user asks:

“Which browser framework should I use for Project Alpha?”

Before reading further, decide:

  1. Which memories should be candidates?
  2. Which memory should win?
  3. Should the user’s general preference influence the answer?
  4. Should the old Selenium memory appear?
  5. Should the old Cypress memory remain active?

A sensible retrieval pipeline would identify:

Code
Project scope → Alpha
Current project fact → Playwright
Freshness → Recent
Old conflicting fact → Superseded
General preference → Supporting context
Selenium experiment → Irrelevant

That is the difference between retrieving related information and retrieving useful information.

The Core Retrieval Principle

A high-quality memory system should progressively narrow information:

Code
All Memories
    ↓
Authorized Memories
    ↓
Correct Tenant
    ↓
Correct Agent
    ↓
Correct Project / Scope
    ↓
Relevant Memory Type
    ↓
Semantic Candidates
    ↓
Fresh + Trusted Memories
    ↓
Reranked Results
    ↓
Deduplicated Context
    ↓
LLM

The database is therefore not simply answering:

“What memories do we have?”

The retrieval layer is answering a much more valuable question:

“Which memories should this agent consider right now?”

That is the foundation of effective TencentDB memory retrieval for persistent AI agents.

Designing the Retrieval Pipeline for Persistent AI Memory

TencentDB memory retrieval becomes much more powerful when retrieval is treated as a pipeline rather than a single database query.

A beginner implementation often looks like this:

Code
memories = db.search(query)

That approach hides several important questions:

Code
Who is asking?
Which tenant owns the memory?
Which project is active?
Which memories are still valid?
Which memory types matter?
Which results are semantically related?
Which result is actually more trustworthy?

A production-oriented retrieval layer should answer those questions in sequence.

Code
User Query
    ↓
Identity
    ↓
Authorization
    ↓
Scope Filtering
    ↓
Candidate Retrieval
    ↓
Semantic Matching
    ↓
Scoring
    ↓
Reranking
    ↓
Deduplication
    ↓
Context Selection

This architecture prevents one of the biggest mistakes in AI memory systems: assuming that the most semantically similar record is automatically the correct record.

Retrieval Should Start With the User’s Context

Before searching memory, the application should understand the current context.

For example:

Code
context = {
    "tenant_id": "company_001",
    "user_id": "user_123",
    "agent_id": "qa_agent",
    "project_id": "checkout_platform"
}

Now the retrieval service has boundaries.

Instead of asking:

Code
Find memories related to "test data"

it can ask:

Code
Find memories related to "test data"
belonging to this tenant,
this agent,
and this project.

That distinction becomes increasingly important as the memory store grows.

Without scope filtering, Project A’s knowledge can accidentally influence Project B.

Tenant Isolation Comes Before Semantic Search

Imagine a multi-tenant AI platform:

Diagram
Company A
 ├── Project Alpha
 └── Project Beta

Company B
 ├── Project Gamma
 └── Project Delta

The database might contain thousands of memories.

A dangerous retrieval implementation would perform semantic search across everything:

Code
results = vector_search(
    query="How do we create test users?"
)

The search engine may find highly similar memories from another company.

Even if the final model does not intentionally expose them, the architecture has already created an unnecessary security boundary violation.

Instead:

Code
results = vector_search(
    query="How do we create test users?",
    filters={
        "tenant_id": current_tenant,
        "project_id": current_project
    }
)

The exact implementation depends on the database and vector-search design, but the architectural rule remains:

Authorization and ownership should constrain retrieval before memories become candidate context.

Structured Filtering and Semantic Search Work Together

There is no need to choose between SQL filtering and semantic retrieval.

They solve different problems.

Consider:

Code
SQL / metadata:
"Which memories am I allowed to search?"

Semantic retrieval:
"Which allowed memories are relevant?"

This creates a two-stage model:

Code
              All Memories
                    ↓
          Metadata / Scope Filter
                    ↓
            Authorized Candidates
                    ↓
             Semantic Search
                    ↓
             Ranked Memories

For example:

SQL
SELECT
    id,
    content,
    memory_type,
    confidence,
    importance,
    updated_at
FROM agent_memories
WHERE tenant_id = :tenant_id
  AND agent_id = :agent_id
  AND project_id = :project_id
  AND status = 'active';

The resulting records can then participate in semantic ranking.

This is generally easier to reason about than treating vector similarity as the complete retrieval system.

Why Metadata Is So Valuable

Consider two memories:

JSON
{
  "content": "Use Playwright for browser automation.",
  "project_id": "alpha",
  "memory_type": "project_fact"
}

and:

JSON
{
  "content": "Use Playwright for browser automation.",
  "project_id": "beta",
  "memory_type": "project_fact"
}

Their semantic representations may be almost identical.

But their meaning is different because their scope is different.

This demonstrates why an embedding cannot replace metadata.

A useful memory record might therefore contain:

Code
id
tenant_id
user_id
agent_id
project_id
memory_type
content
embedding
confidence
importance
status
created_at
updated_at
expires_at
metadata

The embedding answers one question:

What is this memory semantically similar to?

Metadata answers other critical questions:

Who owns it?

Where does it apply?

What kind of memory is it?

Is it still active?

How trustworthy is it?

Memory Type Can Improve Retrieval Precision

Imagine the agent receives:

“What is our preferred way to create test data?”

The database might contain:

Code
preference
procedure
project_fact
episodic
technical_fact

Searching everything can introduce noise.

Instead, the retrieval system can classify the query.

Python
def classify_query(query):
    if "preferred" in query.lower():
        return ["preference", "procedure"]

    if "how do we" in query.lower():
        return ["procedure", "preference"]

    if "which framework" in query.lower():
        return ["project_fact", "technical_fact"]

    return None

Then:

Code
memory_types = classify_query(query)

results = retrieve(
    query=query,
    memory_types=memory_types
)

This is a simple rule-based example, but the same concept can later be implemented using an LLM classifier or another machine-learning model.

The strategy is more important than the specific implementation:

Use the question to determine what kind of memory deserves priority.

Query Expansion Can Help With Different Wording

Users rarely ask questions using the same language that was stored in memory.

Stored memory:

Code
"Create test accounts through the API."

User query:

Code
"What's our preferred approach for generating checkout users?"

The words differ significantly.

A query expansion layer can transform:

Code
What's our preferred approach for generating checkout users?

into concepts such as:

Code
test users
test accounts
checkout users
API-generated accounts
test data creation

A simplified implementation could look like:

Python
def expand_query(query):
    return [
        query,
        "test users",
        "test accounts",
        "checkout test data",
        "API generated users"
    ]

The resulting candidates can be merged before ranking.

For larger systems, an LLM can generate expansions dynamically.

However, query expansion should be evaluated carefully because poor expansions can introduce irrelevant candidates.

Hybrid Retrieval Is Often Stronger

Suppose the user searches:

Code
"Playwright API authentication strategy"

A vector search might find conceptually related memories.

A lexical search might find records containing the exact terms:

Code
Playwright
API
authentication

Combining them gives two useful signals.

Code
Keyword Search
      +
Vector Search
      ↓
Candidate Pool
      ↓
Reranking

A conceptual implementation:

Code
keyword_results = keyword_search(query)

vector_results = vector_search(query)

candidates = merge_results(
    keyword_results,
    vector_results
)

ranked = rerank(
    query,
    candidates
)

This is often called hybrid retrieval.

ApproachBest AtMain Limitation
Keyword searchExact terminologyWeak semantic understanding
Vector searchMeaning and related conceptsCan return semantically similar but incorrect results
Metadata filteringOwnership and scopeDoes not understand meaning
Hybrid retrievalCombining signalsMore complex to implement

For AI agent memory, the hybrid approach is particularly attractive because memories contain both structured metadata and natural-language content.

Image

Build a Candidate Pool Before Reranking

Reranking every memory in a large database is inefficient.

Instead, create a manageable candidate pool.

For example:

Code
1,000,000 memories
        ↓
Tenant filter
        ↓
100,000
        ↓
Project filter
        ↓
20,000
        ↓
Vector / keyword retrieval
        ↓
100
        ↓
Reranking
        ↓
10
        ↓
Context selection
        ↓
5

The exact numbers will depend on your workload.

The principle is:

Cheap filters should reduce the search space before expensive ranking operations.

This can improve:

  • Latency
  • Cost
  • Retrieval precision
  • Context quality
  • Database efficiency

Reranking Gives the System Another Decision Layer

Initial retrieval is usually optimized for recall.

That means:

“Give me potentially relevant memories.”

Reranking asks:

“Which of these candidates are actually the best memories for this query?”

Suppose the initial search returns:

Code
A → 0.93 similarity
B → 0.91 similarity
C → 0.89 similarity
D → 0.87 similarity

Now introduce additional signals:

Code
A → old, low confidence
B → current, high confidence
C → wrong project
D → current, high importance

The final ranking can change dramatically.

A conceptual scoring function:

Python
def score(memory):
    return (
        0.50 * memory["semantic_score"] +
        0.20 * memory["scope_score"] +
        0.10 * memory["confidence"] +
        0.10 * memory["importance"] +
        0.10 * memory["freshness"]
    )

This is not a universal formula.

Your evaluation data should determine the actual weights.

Freshness Should Be Memory-Type Aware

A common mistake is applying one expiration strategy to every memory.

Consider:

Code
User preference:
"I prefer API-first testing."

This may remain relevant for a long time.

Compare that with:

Code
Project fact:
"The project uses API version 2."

That could become obsolete quickly.

A better model is:

Code
FRESHNESS_POLICY = {
    "preference": "long",
    "project_fact": "medium",
    "api_version": "short",
    "temporary": "very_short"
}

Then retrieval can apply different freshness calculations.

This makes the memory system more realistic.

Confidence Should Influence Retrieval

Suppose the system has two memories:

Code
Memory A:
"The user prefers Playwright."
confidence = 0.99

Memory B:
"The user might prefer Cypress."
confidence = 0.41

If both are semantically relevant, Memory A should generally have more influence.

A simple scoring model:

Python
def confidence_adjusted_score(
    semantic_score,
    confidence
):
    return semantic_score * confidence

For example:

Code
Memory A:
0.91 × 0.99 = 0.9009

Memory B:
0.94 × 0.41 = 0.3854

Memory B had higher semantic similarity.

But its uncertainty makes it much less useful.

This illustrates why retrieval relevance and memory reliability should be treated as separate dimensions.

Importance Is Another Signal

A memory can be highly reliable but not particularly useful.

For example:

Advertisement
Code
"The user once tested Selenium in 2019."

The system might know this with high confidence.

But if the user asks:

“How should we automate checkout today?”

that memory has little value.

Compare:

Code
"Checkout test users must be created through the API."

This might have much greater importance for the query.

A retrieval score can therefore consider:

Code
semantic relevance
+
confidence
+
importance
+
freshness
+
scope

The goal is not mathematical perfection.

The goal is a ranking system that reflects how humans decide which knowledge matters.

Deduplicate Before Building the Prompt

Consider these records:

Code
1. Create test users through the API.
2. Test users should be generated through the API.
3. Checkout users are created through API endpoints.
4. API-generated test accounts are preferred.

A naive retrieval system might send all four to the LLM.

That wastes context.

A deduplication layer can identify semantic overlap:

Python
def remove_duplicates(memories):
    selected = []

    for memory in memories:
        if not semantically_duplicate(
            memory,
            selected
        ):
            selected.append(memory)

    return selected

The final context might contain only:

Code
Create checkout test users through the API.

One strong memory can be better than four repetitive memories.

Context Construction Is the Final Retrieval Step

Retrieval should not end when the database returns records.

The application still needs to construct useful context.

For example:

Python
def build_memory_context(memories):
    sections = []

    for memory in memories:
        sections.append(
            f"[{memory['memory_type']}] "
            f"{memory['content']}"
        )

    return "\n".join(sections)

The resulting context might look like:

Code
[project_fact]
Project Alpha uses Playwright.

[procedure]

Create checkout users through the API.

[preference]

Prefer API-generated test data over UI-generated test data.

This is much more useful to the model than dumping raw database rows.

Context Ordering Can Influence the Result

Suppose you have five memories.

Not every memory should necessarily appear in arbitrary order.

A useful strategy is:

Code
1. Most directly relevant fact
2. Supporting procedure
3. Current preference
4. Related project information
5. Lower-confidence supporting memory

For example:

Code
memories = sorted(
    memories,
    key=lambda x: x["final_score"],
    reverse=True
)

Then:

Code
context = memories[:5]

The model receives the strongest signals first.

The exact prompt strategy can be adjusted later through evaluation.

Retrieval Should Respect a Context Budget

Suppose your model has a large context window.

That does not mean you should fill it with memory.

Define a memory budget:

Code
MAX_MEMORY_TOKENS = 2000

Then:

Python
def select_by_budget(memories, budget):
    selected = []
    used = 0

    for memory in memories:
        tokens = estimate_tokens(
            memory["content"]
        )

        if used + tokens > budget:
            break

        selected.append(memory)
        used += tokens

    return selected

This prevents the memory subsystem from consuming the entire context window before the user’s actual question is processed.

Compare Three Retrieval Architectures

Architecture A: Simple SQL Retrieval

Code
Query
 ↓
SQL WHERE
 ↓
Rows
 ↓
LLM

Advantages

  • Simple
  • Easy to debug
  • Predictable
  • Excellent for structured filtering

Disadvantages

  • Weak semantic matching
  • Sensitive to wording
  • Limited for natural-language memory retrieval

Architecture B: Vector-Only Retrieval

Code
Query
 ↓
Embedding
 ↓
Vector Search
 ↓
LLM

Advantages

  • Semantic matching
  • Handles different wording
  • Easy conceptual model

Disadvantages

  • Can ignore scope
  • Can retrieve stale information
  • Similarity does not equal truth
  • Harder to enforce business rules through similarity alone

Architecture C: Hybrid Retrieval

Code
Query
 ↓
Authorization
 ↓
Metadata Filtering
 ↓
Keyword Search + Vector Search
 ↓
Merge
 ↓
Rerank
 ↓
Deduplicate
 ↓
Context
 ↓
LLM

Advantages

  • Stronger precision
  • Strong semantic coverage
  • Scope-aware
  • Supports multiple relevance signals
  • More suitable for production systems

Disadvantages

  • More components
  • Requires evaluation
  • More tuning
  • More operational complexity

For a production AI agent, the hybrid architecture generally provides the richest design space.

Build Retrieval as an Independent Component

Do not scatter memory queries throughout your application.

Avoid:

Shell
# agent.py
db.query(...)

# chatbot.py
db.query(...)

# tools.py
db.query(...)

# workflow.py
db.query(...)

Instead:

Code
Application
    ↓
Memory Service
    ↓
Retrieval Engine
    ↓
TencentDB

Then:

Code
memory = memory_service.retrieve(
    query=user_query,
    context=context
)

This gives you one place to improve:

Code
Ranking
Filtering
Caching
Logging
Evaluation
Deduplication
Security

without rewriting every agent workflow.

Add Retrieval Observability

A retrieval request should ideally produce structured telemetry.

For example:

JSON
{
  "request_id": "req_9821",
  "query": "How should checkout users be created?",
  "candidate_count": 87,
  "vector_results": 20,
  "keyword_results": 15,
  "merged_results": 27,
  "reranked_results": 10,
  "final_memories": 4,
  "latency_ms": 83
}

Now you can investigate questions such as:

Code
Why did retrieval return 87 candidates?
Why were only 4 finally selected?
Was vector search slow?
Did metadata filtering work?
Were duplicate memories removed?

This becomes essential when an AI agent behaves unexpectedly.

Measure Retrieval With Real Questions

You should eventually create an evaluation dataset.

For example:

JSON
[
  {
    "query": "How should checkout users be created?",
    "expected_memory_id": "mem_101"
  },
  {
    "query": "Which framework does Project Alpha use?",
    "expected_memory_id": "mem_204"
  },
  {
    "query": "What is the preferred test data strategy?",
    "expected_memory_id": "mem_310"
  }
]

Then evaluate:

Code
for case in evaluation_dataset:
    results = memory_service.retrieve(
        query=case["query"],
        context=case["context"]
    )

    assert case["expected_memory_id"] in [
        item["id"]
        for item in results
    ]

Now retrieval quality becomes measurable rather than subjective.

Interactive Exercise: Find the Retrieval Failure

Consider these memories:

Code
Memory 1
Project: Alpha
Content: "Alpha uses Playwright."
Updated: yesterday
Confidence: 0.98

Memory 2
Project: Beta
Content: "Beta uses Playwright."
Updated: today
Confidence: 0.99

Memory 3
Project: Alpha
Content: "Alpha previously used Cypress."
Updated: 2 years ago
Confidence: 0.96

Memory 4
Project: Alpha
Content: "Alpha may migrate to Selenium."
Updated: 3 months ago
Confidence: 0.45

Query:

Code
What browser framework does Alpha currently use?

A poor vector-only implementation might return Memory 2 because it is extremely recent and semantically similar.

A better retrieval pipeline understands:

Code
Tenant → correct
Project → Alpha
Status → active
Freshness → Memory 1 stronger
Confidence → Memory 1 stronger
Historical status → Memory 3 weaker
Low confidence → Memory 4 weaker

The retrieval result should therefore strongly favor Memory 1.

This small example demonstrates why TencentDB memory retrieval should be designed as a decision pipeline rather than a single similarity lookup.

The Retrieval Contract

A clean retrieval service should have an explicit contract.

Input:

JSON
{
    "query": str,
    "tenant_id": str,
    "agent_id": str,
    "project_id": str | None,
    "memory_types": list[str] | None,
    "limit": int
}

Output:

JSON
{
    "memories": [
        {
            "id": "...",
            "content": "...",
            "memory_type": "...",
            "confidence": 0.98,
            "importance": 0.90,
            "score": 0.94
        }
    ],
    "metadata": {
        "candidate_count": 40,
        "retrieval_latency_ms": 42
    }
}

This contract makes the retrieval layer predictable for the rest of the application.

It also makes testing much easier.

A Practical End-to-End Retrieval Function

Putting the concepts together:

Python
def retrieve_agent_memory(query, context):

    authorize(context)

    candidates = filter_memories(
        tenant_id=context.tenant_id,
        agent_id=context.agent_id,
        project_id=context.project_id
    )

    semantic_results = semantic_search(
        query,
        candidates
    )

    keyword_results = keyword_search(
        query,
        candidates
    )

    merged = merge_results(
        semantic_results,
        keyword_results
    )

    ranked = rerank(
        query=query,
        memories=merged
    )

    ranked = remove_duplicates(ranked)

    ranked = apply_freshness(ranked)

    ranked = apply_confidence(ranked)

    return select_context(
        ranked,
        max_items=5
    )

Notice what this function does not do.

It does not simply:

Code
return vector_search(query)

Instead, it treats retrieval as a sequence of decisions.

That is the architecture required when an AI agent moves from a small prototype toward a system that must reliably use persistent knowledge.

A Useful Mental Model

Think of retrieval like a hiring process.

Suppose 10,000 people apply for one position.

You do not interview everyone.

You progressively filter:

Code
10,000 applicants
      ↓
Basic requirements
      ↓
1,000
      ↓
Relevant experience
      ↓
100
      ↓
Strong candidates
      ↓
20
      ↓
Final interviews
      ↓
1

AI memory retrieval works similarly:

Code
All memories
      ↓
Authorization
      ↓
Scope
      ↓
Semantic relevance
      ↓
Confidence
      ↓
Freshness
      ↓
Importance
      ↓
Reranking
      ↓
Context budget
      ↓
Best memories

The database provides the candidates.

The retrieval architecture decides which candidates deserve the model’s attention.

Designing TencentDB Persistent Memory for Reliable AI Agents

TencentDB persistent memory design determines whether an AI agent can maintain useful knowledge over time without turning its database into an uncontrolled collection of old, duplicated, or conflicting information.

An agent can retrieve information perfectly and still produce poor results if the underlying memory structure is weak.

Consider an agent that stores:

Code
User preferences
Project facts
Past decisions
Procedures
Temporary observations
Conversation summaries
Tool results
Historical information

If everything is stored in one undifferentiated table, retrieval eventually becomes difficult.

A stronger design gives every memory a clear identity, scope, lifecycle, and reliability profile.

Diagram
Memory
 ├── Identity
 ├── Ownership
 ├── Scope
 ├── Type
 ├── Content
 ├── Semantic Representation
 ├── Confidence
 ├── Importance
 ├── Freshness
 ├── Lifecycle
 └── Relationships

This is the foundation of TencentDB persistent memory design for production-oriented AI agents.

Start With the Memory Object

Before thinking about vector search or ranking, define what a memory actually is.

A practical representation might look like:

JSON
{
  "id": "mem_10021",
  "tenant_id": "tenant_01",
  "user_id": "user_123",
  "agent_id": "qa_agent",
  "project_id": "checkout",
  "memory_type": "procedure",
  "content": "Create checkout test users through the API.",
  "confidence": 0.96,
  "importance": 0.90,
  "status": "active",
  "created_at": "2026-08-16T10:30:00Z",
  "updated_at": "2026-08-16T10:30:00Z"
}

Notice that the content itself is only one field.

The rest of the structure answers critical questions:

Code
Who owns this?
Where does it apply?
What kind of knowledge is it?
How reliable is it?
How important is it?
Is it still active?
When was it last updated?

This metadata becomes extremely valuable during retrieval.

Separate Memory Identity From Memory Content

A common beginner mistake is treating the text itself as the memory.

For example:

Code
"Use API-generated users for checkout tests."

That sentence is the content.

But the actual memory should contain much more:

Code
Memory ID
Project
Agent
Type
Content
Confidence
Importance
Status
Timestamps
Embedding

This allows the retrieval layer to distinguish between two identical sentences belonging to different projects.

Code
Project Alpha
→ Use API-generated users.

Project Beta
→ Use API-generated users.

The content is identical.

The meaning is not.

That is why scope belongs to the memory object rather than being inferred from text.

Design Memory Around Scope

A useful hierarchy is:

Code
Tenant
  ↓
User
  ↓
Agent
  ↓
Project
  ↓
Memory

Not every memory needs every level.

For example:

Code
Global technical fact
→ Agent scope

User preference
→ User scope

Project convention
→ Project scope

Temporary workflow state
→ Session scope

This makes retrieval more precise.

A simplified schema could be:

SQL
CREATE TABLE agent_memories (
    id UUID PRIMARY KEY,
    tenant_id UUID NOT NULL,
    user_id UUID,
    agent_id UUID,
    project_id UUID,
    memory_type VARCHAR(50) NOT NULL,
    content TEXT NOT NULL,
    confidence NUMERIC(4,3),
    importance NUMERIC(4,3),
    status VARCHAR(20) DEFAULT 'active',
    created_at TIMESTAMP NOT NULL,
    updated_at TIMESTAMP NOT NULL
);

The exact schema can evolve, but the architectural principle should remain:

Store enough structure around the memory so retrieval does not have to guess its meaning or ownership.

Memory Types Should Be Explicit

Not all memories behave the same way.

Consider these categories:

Memory typeExampleTypical lifetime
PreferencePrefer API-first testingLong
Project factProject uses PlaywrightMedium/long
ProcedureCreate users through APIMedium
EpisodicDeployment failed yesterdayShort/medium
TemporaryCurrent debugging hypothesisShort
Technical factAPI endpoint requires OAuthDepends

This distinction makes TencentDB persistent memory design much more practical because different memories can receive different retrieval and lifecycle policies.

For example:

Code
MEMORY_POLICY = {
    "preference": {
        "decay": "slow",
        "importance": "high"
    },
    "temporary": {
        "decay": "fast",
        "importance": "low"
    },
    "project_fact": {
        "decay": "medium",
        "importance": "high"
    }
}

The point is not that these exact policies are universal.

The point is that memory should have behavior, not merely storage.

Image
Image

Avoid the Single Giant Memory Table

A simple design might start with:

Code
agent_memories

and put everything inside it.

That can work initially.

But as the application grows, different concerns begin competing:

Code
Memory content
Embedding
Relationships
History
Versions
Access control
Expiration
Retrieval statistics

A more scalable architecture separates concerns logically.

Diagram
agent_memories
       │
       ├── memory_embeddings
       │
       ├── memory_relationships
       │
       ├── memory_versions
       │
       └── memory_events

This does not necessarily mean every project needs five physical tables.

The important concept is separation of responsibilities.

Compare Flat Memory With Structured Memory

CharacteristicFlat MemoryStructured Memory
Easy prototypeExcellentGood
Metadata filteringLimitedStrong
Memory lifecycleDifficultStrong
Version trackingWeakStrong
RelationshipsWeakStrong
Retrieval controlLimitedStrong
Long-term maintainabilityLowHigh

A flat model might look like:

JSON
{
  "content": "User prefers Playwright."
}

A structured model:

JSON
{
  "id": "mem_22",
  "memory_type": "preference",
  "scope": {
    "user_id": "u1"
  },
  "content": "User prefers Playwright.",
  "confidence": 0.98,
  "importance": 0.82,
  "status": "active"
}

The second model requires more work.

But it gives the retrieval system far more information to make decisions.

Add Memory Relationships

Some memories should not exist in isolation.

Advertisement

Consider:

Code
Memory A:
Project uses Playwright.

Memory B:
Checkout tests use API authentication.

Memory C:
Checkout test users are created through the API.

These memories are related.

A relationship layer could represent:

Code
Project
  ↓
Checkout
  ↓
API Authentication
  ↓
Test User Creation

A simple relationship table:

SQL
CREATE TABLE memory_relationships (
    source_memory_id UUID NOT NULL,
    target_memory_id UUID NOT NULL,
    relationship_type VARCHAR(50) NOT NULL,
    PRIMARY KEY (
        source_memory_id,
        target_memory_id,
        relationship_type
    )
);

Possible relationship types include:

Code
supports
contradicts
supersedes
derived_from
related_to
depends_on

This can become extremely useful when an agent needs more than isolated facts.

Contradictions Need First-Class Treatment

Persistent memory eventually encounters contradictions.

Imagine:

Code
Memory A:
Project Alpha uses Cypress.

Later:

Code
Memory B:
Project Alpha migrated to Playwright.

Deleting Memory A immediately may destroy useful historical information.

Keeping both as equally active is worse.

A better approach is to represent the relationship:

Code
Memory B
   ↓
supersedes
   ↓
Memory A

Then:

JSON
{
  "memory_id": "mem_205",
  "status": "active",
  "supersedes": "mem_101"
}

The retrieval system can prioritize the newer memory while preserving historical context.

This is one of the most important differences between ordinary application data and agent memory.

Agent memory has to deal with changing knowledge.

Versioning Prevents Silent Data Loss

Instead of overwriting:

Code
Project uses Cypress.

with:

Code
Project uses Playwright.

you can maintain versions:

Code
Memory 101
Version 1
"Cypress"

Version 2
"Playwright"

A conceptual version table:

SQL
CREATE TABLE memory_versions (
    id UUID PRIMARY KEY,
    memory_id UUID NOT NULL,
    version_number INTEGER NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP NOT NULL,
    created_by VARCHAR(50)
);

Now the agent can answer two different questions:

What is true now?

and:

What was true previously?

That distinction is valuable in debugging, auditing, and long-running workflows.

Memory Lifecycle Should Be Explicit

A memory should not necessarily live forever.

A useful lifecycle might be:

Code
Candidate
   ↓
Validated
   ↓
Active
   ↓
Superseded
   ↓
Archived

For temporary memories:

Code
Candidate
   ↓
Active
   ↓
Expired

For example:

Code
VALID_STATUSES = {
    "candidate",
    "active",
    "superseded",
    "archived",
    "expired"
}

Then retrieval can simply exclude inappropriate states:

Code
WHERE status = 'active'

rather than trying to understand whether every record is still useful.

Temporary Memory Should Not Pollute Long-Term Memory

Imagine an agent is debugging an API.

During the investigation it records:

Code
"The authentication service may be causing the failure."

That is a hypothesis.

It should not automatically become permanent knowledge.

A better design distinguishes:

Code
Observation
Hypothesis
Validated fact

For example:

JSON
{
  "content": "Authentication service may be causing the failure.",
  "memory_type": "temporary",
  "confidence": 0.45,
  "status": "candidate"
}

After validation:

JSON
{
  "content": "Authentication service caused the failure.",
  "memory_type": "project_fact",
  "confidence": 0.96,
  "status": "active"
}

This prevents speculative information from contaminating future agent decisions.

Memory Promotion Is Better Than Blind Storage

A useful strategy is:

Code
Conversation
     ↓
Observation
     ↓
Candidate memory
     ↓
Validation
     ↓
Persistent memory

For example:

Python
def promote_memory(candidate, evidence):
    if evidence.is_strong:
        candidate.status = "active"
        candidate.confidence = 0.95
        candidate.memory_type = "project_fact"

    return candidate

This creates a quality-control mechanism.

Not everything an agent hears deserves permanent storage.

That principle should be central to TencentDB persistent memory design.

Embeddings Should Be Treated as Derived Data

The textual memory is the source of truth.

The embedding is a representation derived from it.

Code
Content
   ↓
Embedding Model
   ↓
Vector

Therefore, if the content changes:

Code
Old content
   ↓
Old embedding

should not remain attached to:

Code
New content

A simple update workflow:

Python
def update_memory(memory, new_content):
    memory.content = new_content
    memory.embedding = embed(new_content)
    memory.updated_at = now()

    return memory

This keeps semantic retrieval aligned with the actual stored content.

Store Retrieval Metadata

You can also capture how memories are used.

For example:

Code
retrieval_count
last_retrieved_at
successful_retrievals
user_feedback

A memory might have:

JSON
{
  "retrieval_count": 42,
  "successful_retrievals": 39,
  "last_retrieved_at": "2026-08-16T10:20:00Z"
}

This data can eventually help identify:

Code
Frequently useful memories
Rarely useful memories
Frequently retrieved but incorrect memories
Stale memories

That opens the door to retrieval optimization based on actual usage.

Do Not Confuse Retrieval Frequency With Importance

A frequently retrieved memory is not automatically important.

For example:

Code
Memory A
"Current authentication token format"
Retrieved 5,000 times.

Memory B
"Critical regulatory requirement"
Retrieved 20 times.

Memory A has higher retrieval frequency.

Memory B may have much higher business importance.

Therefore:

Code
retrieval_count ≠ importance

Keep those signals separate.

A Strong Memory Record

Putting the concepts together:

JSON
{
  "id": "mem_9821",
  "tenant_id": "tenant_01",
  "user_id": "user_123",
  "agent_id": "qa_agent",
  "project_id": "checkout",
  "memory_type": "procedure",
  "content": "Create checkout test users through the API.",
  "confidence": 0.97,
  "importance": 0.91,
  "status": "active",
  "version": 3,
  "embedding": "[vector]",
  "created_at": "2026-08-01T09:00:00Z",
  "updated_at": "2026-08-16T09:00:00Z"
}

This record is far more useful than simply storing:

Code
"Create checkout test users through the API."

because the retrieval system now has context for deciding whether that memory should be trusted and used.

Interactive Design Challenge

Imagine you are designing memory for a QA agent.

The agent learns these statements:

Code
1. The team prefers Playwright.
2. Project Alpha uses Playwright.
3. Checkout test users are created through an API.
4. Yesterday's checkout failure might be caused by authentication.
5. Project Alpha used Cypress last year.

Decide how you would classify them.

A sensible model might be:

StatementTypeConfidenceLifecycle
Team prefers PlaywrightPreferenceHighLong
Alpha uses PlaywrightProject factHighActive
Users created through APIProcedureHighActive
Authentication may be failingHypothesisLowTemporary
Alpha used CypressHistorical factHighSuperseded

This exercise demonstrates why memory architecture is not simply a database-design problem.

It is a knowledge-management problem.

TencentDB Persistent Memory Design vs Simple Chat History

Chat history and persistent agent memory are related, but they are not the same.

CapabilityChat HistoryPersistent Agent Memory
Stores conversationStrongCan
Long-term knowledgeWeakStrong
Structured metadataLimitedStrong
ConfidenceUsually absentSupported
ImportanceUsually absentSupported
LifecycleConversation-basedMemory-based
RetrievalChronological/contextualRelevance-based
Contradiction handlingWeakCan be explicit
Cross-session knowledgeLimitedStrong

Chat history answers:

“What was said?”

Persistent memory answers:

“What should the agent remember and use later?”

A mature AI application often needs both.

Code
Current Conversation
        +
Persistent Memory
        ↓
Agent Context

Keep the Agent Memory Layer Independent

Your application should not become tightly coupled to a particular storage implementation.

Use an abstraction:

Python
class MemoryStore:

    def save(self, memory):
        raise NotImplementedError

    def retrieve(self, query, context):
        raise NotImplementedError

    def update(self, memory_id, changes):
        raise NotImplementedError

    def archive(self, memory_id):
        raise NotImplementedError

Then:

Python
class TencentDBMemoryStore(MemoryStore):

    def save(self, memory):
        ...

    def retrieve(self, query, context):
        ...

    def update(self, memory_id, changes):
        ...

    def archive(self, memory_id):
        ...

This architecture keeps the agent independent from the storage implementation.

If your retrieval strategy changes later, the agent interface does not have to change.

Test the Memory Layer Like a Production Component

Do not test only whether a row was inserted.

Test the actual behavior.

Python
def test_project_scope():
    results = memory.retrieve(
        query="Which browser framework is used?",
        project_id="alpha"
    )

    assert all(
        item.project_id == "alpha"
        for item in results
    )

Test lifecycle:

Python
def test_archived_memory_is_not_retrieved():
    results = memory.retrieve(
        query="old project configuration"
    )

    assert all(
        item.status == "active"
        for item in results
    )

Test contradiction handling:

Python
def test_new_memory_supersedes_old_memory():
    results = memory.retrieve(
        query="current browser framework"
    )

    assert results[0].content == (
        "Project Alpha uses Playwright."
    )

Testing retrieval behavior is essential because a technically correct database can still produce an unreliable agent.

The Strategic Architecture

A strong persistent-memory system can ultimately be viewed as:

Diagram
                    AI AGENT
                       │
             ┌─────────┴─────────┐
             │                   │
       Current Context      Memory Service
                                 │
                    ┌────────────┼────────────┐
                    │            │            │
                 Storage      Retrieval    Lifecycle
                    │            │            │
                 TencentDB   Ranking      Versioning
                    │            │            │
                    └────────────┼────────────┘
                                 │
                         Trusted Context
                                 │
                              LLM

The database stores the knowledge.

The memory service gives that knowledge structure.

The retrieval layer decides what matters.

The lifecycle layer decides what remains trustworthy.

The agent receives only the context it needs.

That is the real purpose of TencentDB persistent memory design: not simply keeping information for a long time, but turning persistent information into controlled, scoped, trustworthy knowledge that an AI agent can repeatedly use.

Building a Production-Ready TencentDB Memory Workflow

TencentDB persistent memory design becomes valuable when the memory lifecycle, retrieval process, validation rules, and agent behavior work together as one system.

A database can store millions of records, but an AI agent does not need millions of records.

It needs the right knowledge, at the right time, with the right confidence and scope.

That distinction is what separates a simple memory database from an agent memory architecture.

From Memory Storage to Memory Intelligence

A complete memory workflow can be represented as:

Code
User Interaction
      ↓
Memory Candidate
      ↓
Classification
      ↓
Validation
      ↓
Persistence
      ↓
Indexing
      ↓
Retrieval
      ↓
Ranking
      ↓
Context Construction
      ↓
Agent Response
      ↓
Feedback
      ↓
Memory Update

Notice the feedback loop.

The system does not stop when the AI generates an answer.

The answer itself can provide evidence about whether a memory was useful.

Python
def process_interaction(message, context):
    candidates = extract_memory_candidates(message)

    for candidate in candidates:
        if should_store(candidate):
            memory = validate_memory(candidate, context)
            memory_store.save(memory)

    memories = memory_store.retrieve(
        query=message,
        context=context
    )

    context_data = build_context(memories)

    return agent.generate(
        message=message,
        memory=context_data
    )

This approach creates a memory subsystem that continuously participates in the agent’s reasoning process.

Decide What Deserves Permanent Memory

The most important optimization is often not faster retrieval.

It is better memory creation.

If an agent stores everything, retrieval becomes progressively noisier.

Consider this conversation:

Code
User: The checkout API is returning 401.

Agent: It may be an authentication issue.

User: Yes, the OAuth configuration was incorrect.

Should the system store all three statements?

Probably not.

The useful long-term memory is:

Code
The checkout API previously returned 401 because
its OAuth configuration was incorrect.

The temporary hypothesis:

Code
It may be an authentication issue.

does not deserve the same status.

A memory extraction layer can therefore distinguish:

Code
Observation
Hypothesis
Decision
Preference
Fact
Procedure
Historical event
Temporary state

Use a Memory Gate

A simple memory gate can prevent low-value information from entering persistent storage.

Python
def should_store(candidate):
    if candidate.is_temporary:
        return False

    if candidate.confidence < 0.70:
        return False

    if candidate.importance < 0.50:
        return False

    return True

A more sophisticated implementation could use an LLM:

Code
decision = memory_classifier.evaluate({
    "content": candidate.content,
    "context": context
})

if decision.store:
    memory_store.save(candidate)

The important principle is:

Persistence should be a deliberate decision, not an automatic side effect of every conversation.

Memory Quality Is More Important Than Memory Quantity

Imagine two systems.

System A

Code
500,000 memories

but many are:

Code
duplicates
temporary observations
outdated facts
contradictions
low-confidence guesses

System B

Code
50,000 memories

with:

Code
high-quality facts
clear ownership
confidence scores
lifecycle states
relationships
freshness metadata

System B may produce significantly better agent behavior.

This is why optimizing only for storage capacity is the wrong goal.

The real objective is:

Advertisement
Code
Useful Knowledge / Retrieved Noise

The higher this ratio becomes, the more useful the memory system becomes.

Introduce Memory Consolidation

Long-running agents can accumulate multiple memories expressing essentially the same information.

For example:

Code
Memory 1:
Use API-generated users for checkout tests.

Memory 2:
Checkout users should be created through the API.

Memory 3:
The preferred checkout test-data strategy is API-based.

Memory 4:
Generate checkout accounts using API endpoints.

Instead of keeping four independent records forever, a consolidation process can create one canonical memory:

Code
Canonical Memory:
Checkout test users should be generated through API endpoints.

Conceptually:

Python
def consolidate(memories):
    groups = cluster_similar_memories(memories)

    consolidated = []

    for group in groups:
        canonical = create_canonical_memory(group)
        consolidated.append(canonical)

    return consolidated

This improves both storage quality and retrieval precision.

Image
Image

Conflict Resolution Needs a Strategy

Consolidation becomes more difficult when memories disagree.

Suppose the database contains:

Code
Memory A:
Project Alpha uses Cypress.
Created: January
Confidence: 0.92

and:

Code
Memory B:
Project Alpha uses Playwright.
Created: August
Confidence: 0.95

The system should not simply select the record with the highest embedding similarity.

A conflict resolver can consider:

Code
recency
confidence
source
scope
validation status
memory type
explicit replacement relationship

A conceptual implementation:

Python
def resolve_conflict(a, b):
    if b.supersedes == a.id:
        return b

    if a.supersedes == b.id:
        return a

    score_a = (
        a.confidence * 0.5 +
        freshness(a) * 0.3 +
        source_quality(a) * 0.2
    )

    score_b = (
        b.confidence * 0.5 +
        freshness(b) * 0.3 +
        source_quality(b) * 0.2
    )

    return a if score_a >= score_b else b

The exact weighting should come from evaluation.

The architectural lesson is more important:

Conflicting knowledge should be resolved explicitly rather than accidentally.

Source Quality Matters

Not every memory comes from the same source.

Consider:

Code
User explicitly stated it
        ↓
High reliability

Agent inferred it
        ↓
Medium reliability

Agent guessed it
        ↓
Low reliability

You can encode source information:

JSON
{
  "content": "Project Alpha uses Playwright.",
  "source": {
    "type": "user_statement",
    "confidence": 0.98
  }
}

Compare that with:

JSON
{
  "content": "Project Alpha may use Playwright.",
  "source": {
    "type": "agent_inference",
    "confidence": 0.52
  }
}

Both may be semantically similar.

They should not have equal authority.

Give Memories Provenance

For enterprise AI systems, provenance can become extremely useful.

A memory might record:

JSON
{
  "memory_id": "mem_201",
  "content": "Checkout authentication uses OAuth.",
  "source_type": "conversation",
  "source_id": "conversation_891",
  "created_by": "user",
  "created_at": "2026-08-15T14:30:00Z"
}

Now the system can answer:

Code
Where did this memory come from?
When was it created?
Who provided it?
Can it be verified?

This is especially useful when an agent provides an answer that someone later challenges.

Retrieval Should Return Evidence, Not Just Text

Instead of returning:

Code
[
    "Checkout authentication uses OAuth."
]

return:

JSON
[
    {
        "content": "Checkout authentication uses OAuth.",
        "confidence": 0.96,
        "importance": 0.88,
        "source_type": "user_statement",
        "updated_at": "2026-08-15"
    }
]

Now the agent can reason about the evidence.

For example:

Python
def build_context(memories):
    return [
        {
            "fact": memory.content,
            "confidence": memory.confidence,
            "source": memory.source_type
        }
        for memory in memories
    ]

This creates a more transparent relationship between memory retrieval and generation.

Do Not Let Memory Override the User’s Current Request

Persistent knowledge should support the current conversation.

It should not blindly override it.

Suppose memory says:

Code
User prefers Playwright.

The user now asks:

Code
Show me how to implement this in Cypress.

The agent should not respond:

Code
You prefer Playwright, so I will not show Cypress.

The current request has higher immediate authority.

A useful priority model is:

Code
Current explicit instruction
        ↓
Current conversation context
        ↓
Validated persistent memory
        ↓
Historical memory
        ↓
Low-confidence inference

This distinction prevents memory from becoming a source of unwanted behavior.

Memory Should Be Contextual, Not Absolute

Instead of storing:

Code
"Always use Playwright."

prefer:

Code
"The user prefers Playwright for browser automation."

The second statement preserves context.

This matters because preferences can have exceptions.

Similarly:

Bad:

Code
"Project always uses API authentication."

Better:

Code
"Checkout API tests currently use OAuth authentication."

Precise language produces better retrieval and safer reasoning.

Build a Memory Evaluation Set

A memory architecture should be evaluated with realistic questions.

Create test cases such as:

JSON
evaluation_cases = [
    {
        "query": "Which browser automation framework does Alpha use?",
        "expected": "Playwright"
    },
    {
        "query": "How should checkout users be created?",
        "expected": "API"
    },
    {
        "query": "What authentication mechanism does checkout use?",
        "expected": "OAuth"
    }
]

Then measure:

Code
Retrieval Recall
Precision
Ranking Quality
Latency
Context Size
Answer Accuracy

For example:

Python
def evaluate_case(case):
    memories = memory.retrieve(
        query=case["query"],
        context=case["context"]
    )

    answer = agent.answer(
        query=case["query"],
        memories=memories
    )

    return evaluate_answer(
        answer,
        case["expected"]
    )

This converts memory development from guesswork into an engineering process.

Test Failure Scenarios, Not Just Happy Paths

A serious evaluation suite should include:

Code
Correct memory exists
No relevant memory exists
Two memories conflict
Memory is outdated
Memory belongs to another project
Memory has low confidence
Duplicate memories exist
Memory has expired
User contradicts stored preference

For example:

Python
def test_cross_project_isolation():
    results = memory.retrieve(
        query="Which framework is used?",
        context={
            "tenant_id": "tenant_a",
            "project_id": "alpha"
        }
    )

    assert not any(
        m.project_id == "beta"
        for m in results
    )

This is where database correctness and AI correctness meet.

Security Must Be Part of Memory Architecture

Persistent memory can contain sensitive application knowledge.

Therefore:

Code
Authentication
Authorization
Tenant isolation
Encryption
Audit logging
Retention
Deletion

should be considered part of the architecture rather than optional additions.

The retrieval service should never trust a caller-provided project identifier without validating ownership.

Bad:

Code
project_id = request["project_id"]

return retrieve(project_id)

Better:

Code
project = authorize_project(
    user=current_user,
    project_id=request["project_id"]
)

if not project:
    raise PermissionError()

return retrieve(project.id)

The memory layer should assume that authorization matters.

Deletion Must Actually Work

Users and organizations may eventually require memory deletion.

A system should support:

Code
memory.delete(memory_id)

but deletion may also need to address:

Code
Primary memory
Embedding
Relationships
Versions
Cached retrieval results
Derived summaries
Indexes
Audit records

A deletion workflow could be:

SQL
Delete Request
     ↓
Authorization
     ↓
Mark Memory Deleted
     ↓
Remove Retrieval Eligibility
     ↓
Delete Derived Representations
     ↓
Invalidate Cache
     ↓
Audit Operation

The exact retention requirements depend on the application and applicable policies.

The architectural lesson is simple:

If your system can create persistent memory, it also needs a deliberate strategy for changing and removing that memory.

Caching Can Improve Repeated Retrieval

Some agent workloads ask similar questions repeatedly.

For example:

Code
"What framework does Project Alpha use?"

may occur hundreds of times.

A cache can store the retrieval result temporarily:

Code
cache_key = hash(
    tenant_id,
    project_id,
    query
)

cached = cache.get(cache_key)

if cached:
    return cached

But memory updates must invalidate relevant cached results.

Python
def update_memory(memory):
    memory_store.update(memory)

    cache.invalidate(
        project_id=memory.project_id
    )

Otherwise, the agent may continue receiving stale information.

Compare Different Memory Strategies

StrategyStrengthWeaknessSuitable For
Chat historySimple contextPoor long-term organizationConversations
Key-value memoryFast and simpleLimited semantic retrievalSmall preferences
Vector memoryStrong semantic searchNeeds metadata and rankingKnowledge retrieval
Hybrid memorySemantic + structuredMore complexProduction agents
Graph-enhanced memoryRelationshipsHigher complexityComplex knowledge domains

The best architecture is not automatically the most complicated one.

A small agent may only need:

Code
Structured metadata
+
Semantic retrieval

A complex enterprise agent may benefit from:

Code
Structured memory
+
Vector retrieval
+
Relationships
+
Versioning
+
Provenance
+
Lifecycle management

Architecture should follow the actual requirements.

A Practical Production Blueprint

A mature implementation can be organized into six layers:

Code
1. Memory Capture
        ↓
2. Memory Validation
        ↓
3. Persistent Storage
        ↓
4. Indexing
        ↓
5. Retrieval & Ranking
        ↓
6. Memory Governance

Memory Capture

Extract potentially useful information:

Code
candidate = extract_memory(message)

Memory Validation

Determine whether it deserves persistence:

Code
validated = validate(candidate)

Persistent Storage

Store structured memory:

Code
memory_store.save(validated)

Indexing

Generate or update search representations:

Code
indexer.index(validated)

Retrieval and Ranking

Find relevant memories:

Code
results = memory_store.retrieve(
    query,
    context
)

Governance

Handle:

Code
expiration
deletion
versioning
conflicts
auditing
quality

This separation makes the system easier to maintain and test.

A Complete Conceptual Example

Imagine a QA agent receives:

Code
"Our checkout team has standardized on Playwright.
Create test users through the API because UI registration
makes the tests slow."

The memory extractor could identify:

JSON
[
  {
    "type": "project_fact",
    "content": "Checkout team uses Playwright."
  },
  {
    "type": "procedure",
    "content": "Create checkout test users through the API."
  },
  {
    "type": "reason",
    "content": "UI registration makes checkout tests slower."
  }
]

Each candidate is independently evaluated.

Code
for candidate in candidates:
    candidate.confidence = assess_confidence(candidate)

    if should_store(candidate):
        memory_store.save(candidate)

Later, the user asks:

Code
How should I design checkout automation?

Retrieval identifies:

Code
Playwright
API-generated users
Existing checkout procedure

The context builder produces:

Code
Relevant project knowledge:

1. The checkout team uses Playwright.
2. Checkout test users should be created through the API.
3. UI registration is avoided because it increases test execution time.

The LLM can now produce an answer grounded in persistent project knowledge rather than rediscovering the same information from scratch.

The Strategic Lesson

The biggest mistake in AI memory projects is thinking:

Code
Database + embeddings = memory

It does not.

A useful persistent memory system requires:

Code
Storage
+
Scope
+
Classification
+
Validation
+
Lifecycle
+
Retrieval
+
Ranking
+
Conflict resolution
+
Provenance
+
Evaluation

The database is only one component.

The real engineering challenge is deciding what should be remembered, how it should be represented, when it should be retrieved, and when it should no longer be trusted.

Internal Blog Links

Internal Series Links

External Links

People Asked Questions

What is TencentDB Agent Memory?

Answer should explain that TencentDB Agent Memory is Tencent Cloud’s memory service for AI Agents, supporting persistent memory across sessions/tasks and retrieval of relevant knowledge.

How does TencentDB Agent Memory work?

Explain the memory-write and memory-retrieval lifecycle.

What is persistent memory in an AI Agent?

Explain how persistent memory allows knowledge to survive beyond the immediate conversation context.

How should AI Agent memory be structured?

Explain scope, type, confidence, importance, lifecycle, provenance, and relationships.

What is the difference between chat history and persistent AI memory?

Explain that chat history records conversation, while persistent memory selectively stores reusable knowledge.

How does AI Agent memory retrieval work?

Explain filtering, semantic retrieval, hybrid search, ranking, deduplication, and context selection.

How do you prevent outdated AI memories?

Explain freshness, versioning, superseding, expiration, and consolidation.

How do you handle conflicting AI memories?

Explain provenance, confidence, recency, explicit superseding relationships, and validation.

Is TencentDB Agent Memory suitable for production AI Agents?

Answer based on architecture, integration requirements, security, evaluation, and workload—not an unconditional yes.

AI Overview Optimization

Question:
How is TencentDB persistent memory be designed?

TencentDB persistent memory design is the architecture used to store, organize, retrieve, validate, and govern knowledge that an AI Agent needs across conversations and tasks. A reliable design combines structured metadata, persistent storage, semantic or hybrid retrieval, ranking, lifecycle management, and memory-quality controls.

Question:
What is persistent AI memory?

Persistent AI memory should not store every conversation message. A production memory system should selectively retain useful facts, preferences, procedures, decisions, and validated knowledge while controlling scope, confidence, freshness, duplication, and conflicts.

These answer-first paragraphs improve AI extraction without turning the article into an FAQ-only page.

AEO Optimization

Question:
How should persistent AI memory be designed?

Persistent AI memory should use structured records with ownership, scope, memory type, confidence, importance, lifecycle state, timestamps, and semantic representations. Retrieval should then combine authorization, metadata filtering, semantic search, ranking, and context selection.

Conclusion

A reliable AI agent should not remember everything.

It should remember the right things.

That means separating preferences from temporary observations, distinguishing current facts from historical information, attaching memories to the correct tenant and project, tracking confidence and importance, preserving provenance, resolving contradictions, and continuously evaluating retrieval quality.

A well-designed TencentDB-based memory architecture therefore becomes more than persistent storage. It becomes a controlled knowledge layer between the application and the AI model.

The strongest design principle is simple:

Store selectively, structure aggressively, retrieve intelligently, and continuously validate what the agent remembers.

Final Key Takeaways

  • Persistent memory is more than storing conversation history.
  • Every memory should have ownership, scope, type, confidence, importance, and lifecycle information.
  • Temporary hypotheses should not automatically become permanent knowledge.
  • Memory consolidation reduces duplication and retrieval noise.
  • Contradictions should be represented and resolved explicitly.
  • Versioning allows current and historical knowledge to coexist safely.
  • Embeddings should be treated as derived representations of memory content.
  • Provenance helps determine where a memory came from and how trustworthy it is.
  • Current user instructions should not be blindly overridden by old persistent memories.
  • Retrieval quality should be measured with realistic evaluation datasets.
  • Security, deletion, retention, and tenant isolation belong inside the memory architecture.
  • The objective is not maximum memory volume; it is maximum useful knowledge with minimum retrieval noise.
  • The best persistent-memory architecture is the one that allows an AI agent to remember information selectively, accurately, contextually, and safely.

Continue Learning

Explore more expert articles on Mobile Testing, Backend & API, AI & Agentic, AI Tools, n8n, LangChain, CrewAI, MCP Servers, AI Agents, LlamaIndex, Docker, FastAPI, Playwright, Cypress, Test Automation, DevOps, and Software Engineering at www.skakarh.com.

QAPulse by SK delivers expert release analysis, AI engineering insights, enterprise automation strategies, migration guidance, DevOps best practices, and practical testing knowledge to help software professionals build scalable, intelligent, and production-ready software systems.

Advertisement
Found this helpful? Clap to let Shahnawaz know — you can clap up to 50 times.