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:
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:
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:
{
"memory_type": "preference",
"content": "Create test users through the API.",
"project": "checkout-platform"
}
The information is valuable.
But suppose the user asks:
How should we create checkout users?
The database must somehow connect the question with the stored memory.
A basic implementation might search exact words:
SELECT *
FROM agent_memories
WHERE content ILIKE '%checkout%';
This can work for simple cases.
But now change the query:
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 approach | Strength | Weakness |
|---|---|---|
| Exact keyword search | Fast and predictable | Misses different wording |
| SQL metadata filtering | Excellent for scope | Does not understand meaning |
| Full-text search | Better textual matching | Still limited semantically |
| Vector search | Finds semantic similarity | Similarity does not guarantee correctness |
| Hybrid retrieval | Combines multiple signals | More architecture and tuning required |
A robust TencentDB memory retrieval implementation does not necessarily choose one technique.
Instead, it combines them.
For example:
User Query
↓
Metadata Filtering
↓
Keyword / Full-Text Search
↓
Vector Similarity
↓
Reranking
↓
Final Memories
This is fundamentally different from simply asking:
"Which memory is closest to this sentence?"
The system should instead ask:
"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:
Project Alpha → Playwright
Project Beta → Cypress
Project Gamma → Selenium
The user asks:
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:
Query
↓
Project = Alpha
↓
Search remaining memories
↓
Rank relevant records
A relational filter can therefore reduce the search space first.
For example:
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:
Memory A:
Project Alpha uses Playwright.
Memory B:
Project Beta uses Cypress.
Memory C:
The organization previously used Selenium.
Query:
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:
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:
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:
Who?
Which agent?
Which scope?
Which memories are active?
Then another retrieval mechanism can determine:
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:
preference
project_fact
procedure
episodic
technical_fact
temporary
A question such as:
“How do we normally generate test data?”
may benefit heavily from:
procedure
preference
while:
“Which framework does this project use?”
may benefit from:
project_fact
technical_fact
You can therefore introduce type-aware retrieval:
MEMORY_TYPES = {
"procedure",
"preference",
"project_fact",
"episodic",
"technical_fact"
}
Then:
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.
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:
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.
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:
Memory A → similarity 0.94
Memory B → similarity 0.89
It would be tempting to select Memory A.
But what if:
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:
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:
January:
"The project uses API version 1."
Six months later:
"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:
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:
Memory Type
↓
Different Freshness Policy
is usually better than one global expiration rule.
Confidence and Importance Are Different
This distinction is often overlooked.
Consider:
Memory A:
"The user prefers API-based test data."
Confidence = 0.98
Importance = 0.85
Now:
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:
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:
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.
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:
More memory
=
Better answer
Usually:
More relevant memory
=
Better answer
Consider:
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:
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:
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:
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.
| Characteristic | TencentDB Memory Retrieval | Redis Lookup |
|---|---|---|
| Primary role | Persistent memory/data | Fast temporary state/cache |
| Structured SQL filtering | Strong | Not primary |
| Durable relational records | Strong | Different use case |
| Semantic retrieval | PostgreSQL/vector architecture can support it | Usually requires additional design |
| Complex metadata relationships | Strong | Less natural |
| Session state | Possible | Excellent |
| Caching | Possible | Excellent |
| Long-term agent knowledge | Strong fit | Usually not primary |
This does not mean Redis is inferior.
It means the systems solve different problems.
A useful architecture can be:
Redis
↓
Short-Term Agent State
TencentDB
↓
Long-Term Agent Memory
Then the agent can combine both:
Current Session
+
Persistent Memories
↓
Final Context
Make Retrieval Explainable
When an agent uses a memory, you should ideally be able to determine:
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:
{
"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:
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:
- Which memories should be candidates?
- Which memory should win?
- Should the user’s general preference influence the answer?
- Should the old Selenium memory appear?
- Should the old Cypress memory remain active?
A sensible retrieval pipeline would identify:
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:
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:
memories = db.search(query)
That approach hides several important questions:
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.
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:
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:
Find memories related to "test data"
it can ask:
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:
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:
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:
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:
SQL / metadata:
"Which memories am I allowed to search?"
Semantic retrieval:
"Which allowed memories are relevant?"
This creates a two-stage model:
All Memories
↓
Metadata / Scope Filter
↓
Authorized Candidates
↓
Semantic Search
↓
Ranked Memories
For example:
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:
{
"content": "Use Playwright for browser automation.",
"project_id": "alpha",
"memory_type": "project_fact"
}
and:
{
"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:
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:
preference
procedure
project_fact
episodic
technical_fact
Searching everything can introduce noise.
Instead, the retrieval system can classify the query.
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:
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:
"Create test accounts through the API."
User query:
"What's our preferred approach for generating checkout users?"
The words differ significantly.
A query expansion layer can transform:
What's our preferred approach for generating checkout users?
into concepts such as:
test users
test accounts
checkout users
API-generated accounts
test data creation
A simplified implementation could look like:
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:
"Playwright API authentication strategy"
A vector search might find conceptually related memories.
A lexical search might find records containing the exact terms:
Playwright
API
authentication
Combining them gives two useful signals.
Keyword Search
+
Vector Search
↓
Candidate Pool
↓
Reranking
A conceptual implementation:
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.
| Approach | Best At | Main Limitation |
|---|---|---|
| Keyword search | Exact terminology | Weak semantic understanding |
| Vector search | Meaning and related concepts | Can return semantically similar but incorrect results |
| Metadata filtering | Ownership and scope | Does not understand meaning |
| Hybrid retrieval | Combining signals | More complex to implement |
For AI agent memory, the hybrid approach is particularly attractive because memories contain both structured metadata and natural-language content.
Build a Candidate Pool Before Reranking
Reranking every memory in a large database is inefficient.
Instead, create a manageable candidate pool.
For example:
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:
A → 0.93 similarity
B → 0.91 similarity
C → 0.89 similarity
D → 0.87 similarity
Now introduce additional signals:
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:
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:
User preference:
"I prefer API-first testing."
This may remain relevant for a long time.
Compare that with:
Project fact:
"The project uses API version 2."
That could become obsolete quickly.
A better model is:
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:
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:
def confidence_adjusted_score(
semantic_score,
confidence
):
return semantic_score * confidence
For example:
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:
"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:
"Checkout test users must be created through the API."
This might have much greater importance for the query.
A retrieval score can therefore consider:
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:
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:
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:
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:
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:
[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:
1. Most directly relevant fact
2. Supporting procedure
3. Current preference
4. Related project information
5. Lower-confidence supporting memory
For example:
memories = sorted(
memories,
key=lambda x: x["final_score"],
reverse=True
)
Then:
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:
MAX_MEMORY_TOKENS = 2000
Then:
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
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
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
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:
# agent.py
db.query(...)
# chatbot.py
db.query(...)
# tools.py
db.query(...)
# workflow.py
db.query(...)
Instead:
Application
↓
Memory Service
↓
Retrieval Engine
↓
TencentDB
Then:
memory = memory_service.retrieve(
query=user_query,
context=context
)
This gives you one place to improve:
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:
{
"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:
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:
[
{
"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:
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:
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:
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:
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:
{
"query": str,
"tenant_id": str,
"agent_id": str,
"project_id": str | None,
"memory_types": list[str] | None,
"limit": int
}
Output:
{
"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:
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:
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:
10,000 applicants
↓
Basic requirements
↓
1,000
↓
Relevant experience
↓
100
↓
Strong candidates
↓
20
↓
Final interviews
↓
1
AI memory retrieval works similarly:
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:
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.
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:
{
"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:
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:
"Use API-generated users for checkout tests."
That sentence is the content.
But the actual memory should contain much more:
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.
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:
Tenant
↓
User
↓
Agent
↓
Project
↓
Memory
Not every memory needs every level.
For example:
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:
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 type | Example | Typical lifetime |
|---|---|---|
| Preference | Prefer API-first testing | Long |
| Project fact | Project uses Playwright | Medium/long |
| Procedure | Create users through API | Medium |
| Episodic | Deployment failed yesterday | Short/medium |
| Temporary | Current debugging hypothesis | Short |
| Technical fact | API endpoint requires OAuth | Depends |
This distinction makes TencentDB persistent memory design much more practical because different memories can receive different retrieval and lifecycle policies.
For example:
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.
Avoid the Single Giant Memory Table
A simple design might start with:
agent_memories
and put everything inside it.
That can work initially.
But as the application grows, different concerns begin competing:
Memory content
Embedding
Relationships
History
Versions
Access control
Expiration
Retrieval statistics
A more scalable architecture separates concerns logically.
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
| Characteristic | Flat Memory | Structured Memory |
|---|---|---|
| Easy prototype | Excellent | Good |
| Metadata filtering | Limited | Strong |
| Memory lifecycle | Difficult | Strong |
| Version tracking | Weak | Strong |
| Relationships | Weak | Strong |
| Retrieval control | Limited | Strong |
| Long-term maintainability | Low | High |
A flat model might look like:
{
"content": "User prefers Playwright."
}
A structured model:
{
"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.
Consider:
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:
Project
↓
Checkout
↓
API Authentication
↓
Test User Creation
A simple relationship table:
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:
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:
Memory A:
Project Alpha uses Cypress.
Later:
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:
Memory B
↓
supersedes
↓
Memory A
Then:
{
"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:
Project uses Cypress.
with:
Project uses Playwright.
you can maintain versions:
Memory 101
Version 1
"Cypress"
Version 2
"Playwright"
A conceptual version table:
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:
Candidate
↓
Validated
↓
Active
↓
Superseded
↓
Archived
For temporary memories:
Candidate
↓
Active
↓
Expired
For example:
VALID_STATUSES = {
"candidate",
"active",
"superseded",
"archived",
"expired"
}
Then retrieval can simply exclude inappropriate states:
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:
"The authentication service may be causing the failure."
That is a hypothesis.
It should not automatically become permanent knowledge.
A better design distinguishes:
Observation
Hypothesis
Validated fact
For example:
{
"content": "Authentication service may be causing the failure.",
"memory_type": "temporary",
"confidence": 0.45,
"status": "candidate"
}
After validation:
{
"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:
Conversation
↓
Observation
↓
Candidate memory
↓
Validation
↓
Persistent memory
For example:
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.
Content
↓
Embedding Model
↓
Vector
Therefore, if the content changes:
Old content
↓
Old embedding
should not remain attached to:
New content
A simple update workflow:
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:
retrieval_count
last_retrieved_at
successful_retrievals
user_feedback
A memory might have:
{
"retrieval_count": 42,
"successful_retrievals": 39,
"last_retrieved_at": "2026-08-16T10:20:00Z"
}
This data can eventually help identify:
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:
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:
retrieval_count ≠ importance
Keep those signals separate.
A Strong Memory Record
Putting the concepts together:
{
"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:
"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:
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:
| Statement | Type | Confidence | Lifecycle |
|---|---|---|---|
| Team prefers Playwright | Preference | High | Long |
| Alpha uses Playwright | Project fact | High | Active |
| Users created through API | Procedure | High | Active |
| Authentication may be failing | Hypothesis | Low | Temporary |
| Alpha used Cypress | Historical fact | High | Superseded |
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.
| Capability | Chat History | Persistent Agent Memory |
|---|---|---|
| Stores conversation | Strong | Can |
| Long-term knowledge | Weak | Strong |
| Structured metadata | Limited | Strong |
| Confidence | Usually absent | Supported |
| Importance | Usually absent | Supported |
| Lifecycle | Conversation-based | Memory-based |
| Retrieval | Chronological/contextual | Relevance-based |
| Contradiction handling | Weak | Can be explicit |
| Cross-session knowledge | Limited | Strong |
Chat history answers:
“What was said?”
Persistent memory answers:
“What should the agent remember and use later?”
A mature AI application often needs both.
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:
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:
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.
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:
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:
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:
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:
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.
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:
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:
The checkout API previously returned 401 because
its OAuth configuration was incorrect.
The temporary hypothesis:
It may be an authentication issue.
does not deserve the same status.
A memory extraction layer can therefore distinguish:
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.
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:
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
500,000 memories
but many are:
duplicates
temporary observations
outdated facts
contradictions
low-confidence guesses
System B
50,000 memories
with:
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:
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:
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:
Canonical Memory:
Checkout test users should be generated through API endpoints.
Conceptually:
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.
Conflict Resolution Needs a Strategy
Consolidation becomes more difficult when memories disagree.
Suppose the database contains:
Memory A:
Project Alpha uses Cypress.
Created: January
Confidence: 0.92
and:
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:
recency
confidence
source
scope
validation status
memory type
explicit replacement relationship
A conceptual implementation:
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:
User explicitly stated it
↓
High reliability
Agent inferred it
↓
Medium reliability
Agent guessed it
↓
Low reliability
You can encode source information:
{
"content": "Project Alpha uses Playwright.",
"source": {
"type": "user_statement",
"confidence": 0.98
}
}
Compare that with:
{
"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:
{
"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:
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:
[
"Checkout authentication uses OAuth."
]
return:
[
{
"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:
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:
User prefers Playwright.
The user now asks:
Show me how to implement this in Cypress.
The agent should not respond:
You prefer Playwright, so I will not show Cypress.
The current request has higher immediate authority.
A useful priority model is:
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:
"Always use Playwright."
prefer:
"The user prefers Playwright for browser automation."
The second statement preserves context.
This matters because preferences can have exceptions.
Similarly:
Bad:
"Project always uses API authentication."
Better:
"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:
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:
Retrieval Recall
Precision
Ranking Quality
Latency
Context Size
Answer Accuracy
For example:
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:
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:
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:
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:
project_id = request["project_id"]
return retrieve(project_id)
Better:
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:
memory.delete(memory_id)
but deletion may also need to address:
Primary memory
Embedding
Relationships
Versions
Cached retrieval results
Derived summaries
Indexes
Audit records
A deletion workflow could be:
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:
"What framework does Project Alpha use?"
may occur hundreds of times.
A cache can store the retrieval result temporarily:
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.
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
| Strategy | Strength | Weakness | Suitable For |
|---|---|---|---|
| Chat history | Simple context | Poor long-term organization | Conversations |
| Key-value memory | Fast and simple | Limited semantic retrieval | Small preferences |
| Vector memory | Strong semantic search | Needs metadata and ranking | Knowledge retrieval |
| Hybrid memory | Semantic + structured | More complex | Production agents |
| Graph-enhanced memory | Relationships | Higher complexity | Complex knowledge domains |
The best architecture is not automatically the most complicated one.
A small agent may only need:
Structured metadata
+
Semantic retrieval
A complex enterprise agent may benefit from:
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:
1. Memory Capture
↓
2. Memory Validation
↓
3. Persistent Storage
↓
4. Indexing
↓
5. Retrieval & Ranking
↓
6. Memory Governance
Memory Capture
Extract potentially useful information:
candidate = extract_memory(message)
Memory Validation
Determine whether it deserves persistence:
validated = validate(candidate)
Persistent Storage
Store structured memory:
memory_store.save(validated)
Indexing
Generate or update search representations:
indexer.index(validated)
Retrieval and Ranking
Find relevant memories:
results = memory_store.retrieve(
query,
context
)
Governance
Handle:
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:
"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:
[
{
"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.
for candidate in candidates:
candidate.confidence = assess_confidence(candidate)
if should_store(candidate):
memory_store.save(candidate)
Later, the user asks:
How should I design checkout automation?
Retrieval identifies:
Playwright
API-generated users
Existing checkout procedure
The context builder produces:
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:
Database + embeddings = memory
It does not.
A useful persistent memory system requires:
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
- 50 Playwright Commands Every QA Engineer Should Know
- How to Build a More Reliable Test Automation Architecture
- Test Automation Framework vs Test Suite: The Critical Difference Every Engineer Should Understand
- Test Automation Framework Health: 9 Signs Your Tests Are Lying to You
- RAG Powered Performance Testing: Make k6 Tests Smarter With Real API Behavior
Internal Series Links
- Learn MCP – Zero to Hero
- Learn AI Agents for QA – Zero to Hero
- Playwright Automation – Zero to Hero
- TencentDB Agent Memory: Complete Zero to Hero
- LangGraph: Complete Zero to Hero
- Learn Python – Zero to Hero
- OpenAI Codex: Complete Zero to Hero
- Cursor AI: Complete Zero to Hero
- Claude Code Tutorial: Complete Zero to Hero
- AutoGen: Complete Zero to Hero Guide
- Free QA Resources Built From Real Experience
- QA Glossary: Test Automation Terms Every Engineer Should Know
External Links
- Tencent Cloud Agent Memory product page
- Tencent Cloud Agent Memory introduction
- Tencent Cloud self-developed Agent integration guide
- TencentDB Agent Memory GitHub repository
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.



