Conflict Resolution
Handle contradictory memories correctly when users change preferences, correct past information, or update facts. Choose between recency-wins, confidence-wins, and explicit user confirmation strategies — or combine all three based on the memory category.
Start Free →- Running Dakera server (Quickstart)
- An agent ID with existing memories
- Understanding of
update_memoryandupdate_importanceSDK methods
The Problem: Contradictory Memories Erode User Trust
Real users change. "I prefer dark mode" becomes "actually I switched back to light mode." "I work at Acme Corp" becomes "I moved to BuildCo in January." A diet preference changes. A home city is updated. A phone number is corrected.
Without conflict resolution, your agent stores both the old and new facts. A recall query returns both — and the agent confidently uses the wrong one. Users who see their AI reference an outdated preference lose trust immediately. Worse, the agent does not know it is wrong. The solution is to detect contradictions at write time and resolve them deterministically, not probabilistically.
Recency wins: The most recently stored memory on a topic wins automatically. Best for preferences and mutable facts. Confidence wins: The memory with the higher importance score wins. Best when explicit corrections should override incidental mentions. Ask user: Surface the conflict to the user and request confirmation. Best for high-stakes facts like home address, medical information, and financial details.
Architecture
Conflict detection happens at write time. When storing a new preference or factual memory, your application checks for semantically similar existing memories using recall. If a candidate with high similarity (above 0.80) and an opposing semantic direction is found, a conflict is flagged and the resolution strategy is applied before the write completes.
- Detect: Before storing, recall similar memories and check for semantic contradiction
- Classify: Determine if the new memory updates, supersedes, or merely extends the existing one
- Resolve: Apply your chosen strategy: recency-wins, confidence-wins, or ask-user
- Update: Use
update_memoryto revise the winner; useupdate_importanceto demote the loser - Audit: Store a conflict log entry at low importance for traceability without polluting recall
Diagram: Conflict Detection — Two Contradictory Memories
Diagram: Resolution Strategy Decision Tree
Real-World Scenario: Personal Assistant Tracking Updated Preferences
Scenario: Clara AI builds a personal assistant that tracks user preferences across hundreds of interactions. User Sofia says "I'm vegetarian" in January. In April, Sofia mentions "I actually eat fish now." Without conflict resolution, Clara has two contradictory diet memories and the next restaurant recommendation may be wrong.
Clara implements write-time conflict detection: before storing any preference memory, it recalls the top 3 similar memories. If a match above 0.82 with a contradictory semantic direction is found, it applies recency-wins: the new preference becomes active at importance 0.80, and the old one drops to 0.15 so it decays within days. A conflict audit entry is stored at importance 0.4 for traceability.
Result: 94% of user preference updates are resolved automatically in under 200ms total latency. Only 6% — high-stakes facts like home address or medical conditions — escalate to explicit user confirmation. User trust scores improved by 28% after deployment.
Step-by-Step Implementation
-
Define your conflict detection scopeNot all memories can conflict. Only mutable facts and preferences can be contradicted. Immutable facts (birth year, historical events) and raw episodic memories (specific conversation turns) should bypass conflict detection. Define a list of memory tags eligible for conflict checking:
["preference", "fact", "profile"]. -
Recall similar memories before storingBefore every
store_memoryon a conflict-eligible type, callrecallwith a semantic query derived from the new content. Usemin_importance=0.3to catch even partially-decayed contradictions, andtop_k=3. Check if any result has similarity above your threshold (0.80–0.85). -
Classify the relationshipA similar memory may be an update ("I moved to Berlin" — same topic, new value), a contradiction ("I like coffee" vs. "I don't drink coffee"), or additive ("I like coffee" + "I also like tea" — no conflict). Use a lightweight LLM call or negation-pattern matching to distinguish these cases before triggering resolution.
-
Apply the resolution strategyPreferences: recency-wins automatically. High-stakes facts (address, medical): ask-user. Importance-discrepant memories: confidence-wins. Document which strategy applies to which memory category and enforce it in a strategy router function — never decide strategy ad-hoc at the call site.
-
Update winning memory and demote loserCall
update_memoryon the winning memory if its content needs updating. Callupdate_importanceon the losing memory to drop it to 0.1–0.15, enabling natural decay within days. Never hard-delete the losing memory immediately — it may be needed for audit or recovery within the next 30 days. -
Store a conflict audit entryAfter resolution, store a brief audit memory tagged with
"conflict-log"at importance 0.4 with a 90-day TTL. This allows you to trace the history of any fact change without surfacing it in normal recall (filtered out bymin_importance=0.5).
Before & After: Resolving a Preference Conflict
[
{
"id": "m-a1",
"content": "User prefers light mode",
"importance": 0.75,
"memory_type": "semantic",
"tags": ["preference", "ui"],
"created_at": "2026-01-15T10:00:00Z"
},
{
"id": "m-b1",
"content": "User switched back to dark mode",
"importance": 0.70,
"memory_type": "semantic",
"tags": ["preference", "ui"],
"created_at": "2026-05-20T14:22:00Z"
}
]
// recall("UI theme preference") returns BOTH
// Agent uses wrong memory ~50% of the time
// Users report "it keeps forgetting" — trust erodes
[
{
"id": "m-b1",
"content": "User prefers dark mode",
"importance": 0.80,
"tags": ["preference", "ui"],
"created_at": "2026-05-20T14:22:00Z"
},
{
"id": "m-a1",
"content": "[SUPERSEDED] User preferred light mode",
"importance": 0.15,
"tags": ["preference", "ui", "superseded"],
"created_at": "2026-01-15T10:00:00Z"
},
{
"id": "m-log-1",
"content": "CONFLICT LOG: light->dark mode, recency, 2026-05-20",
"importance": 0.4,
"tags": ["conflict-log"],
"ttl_seconds": 7776000
}
]
// recall("UI theme", min_importance=0.5) returns m-b1 only
// Correct preference on every recommendation
Implementation
# 1. Recall similar memories before storing new preference
curl "http://localhost:3300/v1/memory/recall?agent_id=assistant&query=UI+theme+display+mode&min_importance=0.3&top_k=3" -H "Authorization: Bearer dk-..."
# 2. If conflict: demote old memory
curl -X PUT http://localhost:3300/v1/memory/m-a1/importance -H "Authorization: Bearer dk-..." -H "Content-Type: application/json" -d '{"agent_id": "assistant", "importance": 0.15}'
# 3. Mark old as superseded
curl -X PUT http://localhost:3300/v1/memory/m-a1 -H "Authorization: Bearer dk-..." -H "Content-Type: application/json" -d '{
"agent_id": "assistant",
"content": "[SUPERSEDED] User preferred light mode",
"tags": ["preference", "ui", "superseded"]
}'
# 4. Store new winning memory
curl -X POST http://localhost:3300/v1/memory/store -H "Authorization: Bearer dk-..." -H "Content-Type: application/json" -d '{
"agent_id": "assistant",
"content": "User prefers dark mode (updated May 2026)",
"importance": 0.80,
"memory_type": "semantic",
"tags": ["preference", "ui"]
}'
# 5. Store conflict audit log (90-day TTL)
curl -X POST http://localhost:3300/v1/memory/store -H "Authorization: Bearer dk-..." -H "Content-Type: application/json" -d '{
"agent_id": "assistant",
"content": "CONFLICT LOG: light-mode -> dark-mode, recency strategy, 2026-05-20",
"importance": 0.4,
"memory_type": "episodic",
"tags": ["conflict-log"],
"ttl_seconds": 7776000
}'from dakera import DakeraClient
from datetime import datetime
import openai
client = DakeraClient(base_url="http://localhost:3300", api_key="dk-...")
oai = openai.OpenAI()
AGENT = "personal-assistant"
CONFLICT_ELIGIBLE_TAGS = {"preference", "fact", "profile"}
CONFLICT_THRESHOLD = 0.82
HIGH_STAKES_TAGS = {"medical", "address", "financial"}
def detect_contradiction(existing: str, new: str) -> bool:
"""LLM-based contradiction detection."""
resp = oai.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user",
"content": f"Do these two statements contradict? YES or NO.
A: {existing}
B: {new}"}]
)
return resp.choices[0].message.content.strip().upper() == "YES"
def resolve_conflict(existing_mem, new_content: str, new_importance: float, new_tags: list):
"""Apply recency-wins or escalate to ask-user for high-stakes facts."""
if any(t in HIGH_STAKES_TAGS for t in (existing_mem.tags or [])):
return {"action": "ask-user", "existing": existing_mem.content, "proposed": new_content}
# Recency wins: demote loser
client.update_importance(
agent_id=AGENT,
memory_id=existing_mem.id,
importance=0.15
)
client.update_memory(
agent_id=AGENT,
memory_id=existing_mem.id,
content=f"[SUPERSEDED] {existing_mem.content}",
tags=(existing_mem.tags or []) + ["superseded"]
)
# Store new winner
client.store_memory(
agent_id=AGENT,
content=new_content,
importance=new_importance,
memory_type="semantic",
tags=new_tags
)
# Audit log (90 day TTL)
client.store_memory(
agent_id=AGENT,
content=f"CONFLICT LOG: '{existing_mem.content[:50]}' -> '{new_content[:50]}', recency, {datetime.now().date()}",
importance=0.4,
memory_type="episodic",
tags=["conflict-log"],
ttl_seconds=7776000
)
return {"action": "resolved-recency"}
def store_with_conflict_check(content: str, importance: float, tags: list, memory_type: str = "semantic"):
"""Write-time conflict detection and resolution."""
if not CONFLICT_ELIGIBLE_TAGS.intersection(set(tags)):
return client.store_memory(agent_id=AGENT, content=content,
importance=importance, memory_type=memory_type, tags=tags)
candidates = client.recall(agent_id=AGENT, query=content, min_importance=0.3, top_k=3)
for mem in candidates.memories:
sim = getattr(mem, 'similarity_score', 0.0) or 0.0
if sim >= CONFLICT_THRESHOLD and detect_contradiction(mem.content, content):
return resolve_conflict(mem, content, importance, tags)
return client.store_memory(agent_id=AGENT, content=content,
importance=importance, memory_type=memory_type, tags=tags)
# Usage: preference update triggers conflict detection
store_with_conflict_check(
content="User prefers dark mode (confirmed May 2026)",
importance=0.80,
tags=["preference", "ui"]
)import { DakeraClient } from '@dakera-ai/dakera';
import OpenAI from 'openai';
const client = new DakeraClient({ baseUrl: 'http://localhost:3300', apiKey: 'dk-...' });
const oai = new OpenAI();
const AGENT = 'personal-assistant';
const CONFLICT_THRESHOLD = 0.82;
const CONFLICT_ELIGIBLE = new Set(['preference', 'fact', 'profile']);
const HIGH_STAKES = new Set(['medical', 'address', 'financial']);
async function detectContradiction(a: string, b: string): Promise<boolean> {
const r = await oai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: `Contradict? YES or NO.
A: ${a}
B: ${b}` }]
});
return r.choices[0].message.content?.trim().toUpperCase() === 'YES';
}
async function storeWithConflictCheck(
content: string, importance: number, tags: string[], memoryType = 'semantic'
) {
if (!tags.some(t => CONFLICT_ELIGIBLE.has(t))) {
return client.storeMemory(AGENT, { content, importance, memoryType, tags });
}
const candidates = await client.recall(AGENT, content, { min_importance: 0.3, top_k: 3 });
for (const mem of candidates.memories) {
if ((mem.similarityScore ?? 0) >= CONFLICT_THRESHOLD && await detectContradiction(mem.content, content)) {
if ((mem.tags ?? []).some((t: string) => HIGH_STAKES.has(t))) {
return { action: 'ask-user', existing: mem.content, proposed: content };
}
// Recency wins
await client.updateImportance(AGENT, { memory_id: mem.id, importance: 0.15 });
await client.updateMemory(AGENT, mem.id, {
content: `[SUPERSEDED] ${mem.content}`,
tags: [...(mem.tags ?? []), 'superseded']
});
await client.storeMemory(AGENT, { content, importance, memoryType, tags });
await client.storeMemory(AGENT, {
content: `CONFLICT LOG: '${mem.content.slice(0,50)}' -> '${content.slice(0,50)}', recency, ${new Date().toISOString().split('T')[0]}`,
importance: 0.4, memoryType: 'episodic', tags: ['conflict-log'], ttl_seconds: 7776000
});
return { action: 'resolved-recency' };
}
}
return client.storeMemory(AGENT, { content, importance, memoryType, tags });
}
// Usage
await storeWithConflictCheck('User prefers dark mode (May 2026)', 0.80, ['preference', 'ui']);use dakera_rs::{Client, StoreMemoryRequest, RecallRequest};
let client = Client::new("http://localhost:3300", "dk-...");
let agent = "personal-assistant";
// 1. Recall similar memories to detect conflicts
let candidates = client.recall(agent, RecallRequest {
query: "UI theme display mode preference".into(),
min_importance: Some(0.3),
top_k: Some(3),
..Default::default()
}).await?;
// 2. For each high-similarity candidate, check contradiction (your logic)
for mem in &candidates.memories {
if mem.similarity_score.unwrap_or(0.0) >= 0.82 {
if is_contradiction(&mem.content, "User prefers dark mode") {
// Demote old via REST: PUT /v1/memory/{id}/importance { importance: 0.15 }
// Update content via REST: PUT /v1/memory/{id} { content: "[SUPERSEDED]..." }
// Store new winner
client.store_memory(agent, StoreMemoryRequest {
content: "User prefers dark mode (May 2026)".into(),
importance: Some(0.80),
memory_type: "semantic".into(),
tags: vec!["preference".into(), "ui".into()],
..Default::default()
}).await?;
// Store audit log with TTL
client.store_memory(agent, StoreMemoryRequest {
content: format!("CONFLICT LOG: recency resolved 2026-05-20"),
importance: Some(0.4),
memory_type: "episodic".into(),
tags: vec!["conflict-log".into()],
ttl_seconds: Some(7776000),
..Default::default()
}).await?;
break;
}
}
}package main
import (
"context"
"fmt"
"time"
dakera "github.com/dakera-ai/dakera-go"
)
func storeWithConflictCheck(ctx context.Context, client *dakera.Client, agent, content string, importance float64, tags []string) error {
candidates, _ := client.Recall(ctx, agent, dakera.RecallRequest{
Query: content, MinImportance: 0.3, TopK: 3,
})
for _, mem := range candidates.Memories {
if mem.SimilarityScore >= 0.82 && isContradiction(mem.Content, content) {
if isHighStakes(mem.Tags) {
fmt.Printf("ASK USER: '%s' vs '%s'
", mem.Content, content)
return nil
}
// Demote loser via REST (PUT /v1/memory/{id}/importance)
// Store winner
client.StoreMemory(ctx, agent, dakera.StoreMemoryRequest{
Content: content, Importance: importance,
MemoryType: "semantic", Tags: tags,
})
// Audit log
client.StoreMemory(ctx, agent, dakera.StoreMemoryRequest{
Content: fmt.Sprintf("CONFLICT LOG: recency resolved %s", time.Now().Format("2006-01-02")),
Importance: 0.4, MemoryType: "episodic",
Tags: []string{"conflict-log"}, TtlSeconds: 7776000,
})
return nil
}
}
client.StoreMemory(ctx, agent, dakera.StoreMemoryRequest{
Content: content, Importance: importance, MemoryType: "semantic", Tags: tags,
})
return nil
}94% of preference conflicts auto-resolved in <200ms
Three lines of detect-and-resolve logic eliminate the most damaging class of memory bugs.
SDK Reference
| Method | SDK | Purpose |
|---|---|---|
recall(agent_id, query, min_importance, top_k) | Python | Retrieve similar memories to detect conflicts before storing |
recall(agentId, query, {min_importance, top_k}) | TypeScript | Retrieve similar memories to detect conflicts before storing |
update_memory(agent_id, memory_id, ...) | Python | Revise content and tags of loser or winner |
updateMemory(agentId, memoryId, request) | TypeScript | Revise content and tags of loser or winner |
update_importance(agent_id, memory_id, importance) | Python | Demote losing memory to trigger decay |
updateImportance(agentId, {memory_id, importance}) | TypeScript | Demote losing memory to trigger decay |
store_memory(agent_id, content, importance, tags, ttl_seconds) | Python | Store winning memory and TTL-limited audit log |
storeMemory(agentId, {content, importance, tags, ttl_seconds}) | TypeScript | Store winning memory and TTL-limited audit log |
Performance Considerations
- LLM contradiction detection is the bottleneck. Using gpt-4o-mini costs ~80ms per check. For high-volume systems (>100 stores/sec), replace with a local negation-detection model or a rules-based approach (keyword negation: "not", "no longer", "switched away from") to cut this to <5ms.
- Cache recent recall results. If storing multiple memories in rapid succession (e.g., at session start), the recall step returns the same candidates repeatedly. Cache results keyed by semantic query hash for 10 seconds at the application layer to avoid redundant Dakera calls.
- Conflict log TTL prevents unbounded accumulation. With a 90-day TTL (
ttl_seconds=7776000) on audit entries, the conflict log stays bounded. Without TTL, a busy personal assistant agent accumulates thousands of log entries in a year, adding noise at low-importance recall thresholds.
Edge Cases
"I used to hate dark mode" and "I prefer dark mode" share high similarity but are not contradictions — the first is historical, the second is current. Always include temporal marker detection: statements containing "used to", "previously", "back when I", or "before" should be classified as historical context, not conflicting facts, and bypass the conflict resolver.
A user may have a chain: light mode (Jan) → dark mode (Jun) → light mode again (Feb next year). If the resolver only checks the single highest-similarity match, it may resolve against the June memory and miss the January one. Process all candidates in descending importance order, demoting each loser before storing the winner.
Two agents simultaneously writing conflicting facts both pass the recall check before either write completes. Both write "wins." Use an optimistic locking strategy: pass the expected existing memory ID in your update and retry if the server rejects due to concurrent modification. Alternatively, serialize conflict-resolution writes per agent using a distributed lock (Redis, database advisory lock).
When ask-user triggers in an async channel (email, Slack webhook), the user's response arrives minutes to hours later. Store a pending conflict memory tagged ["conflict-pending"] at importance 0.6. When the response arrives, retrieve it by tag, apply the confirmed resolution, and remove the pending marker. Add a 72-hour timeout that defaults to recency-wins if the user does not respond.
"I'm not sure if I prefer Python or Go for this project" is not a conflict — it is genuine ambiguity. Train your contradiction classifier to distinguish contradiction from uncertainty. Ambiguous states should be stored as a single "user is evaluating X vs Y" memory. Tag it ["ambiguous"] to suppress future conflict detection on that topic until the user resolves it explicitly.
Advanced Configuration: Strategy Matrix & Confidence Scoring
Strategy Selection by Memory Category
| Category | Tags | Default Strategy | Override Condition |
|---|---|---|---|
| UI preferences | preference, ui | Recency wins | Never override |
| Diet / lifestyle | preference, lifestyle | Recency wins | Never override |
| Employment | fact, career | Recency wins | Confidence if new imp < old by 0.2 |
| Home address | fact, address | Ask user always | Never skip |
| Medical history | fact, medical | Ask user always | Never skip |
| Technical skills | fact, skill | Confidence wins | Additive if >30% semantically different |
Confidence-Wins Threshold
Only apply confidence-wins when importance scores diverge by at least 0.15 (existing=0.9 vs new=0.6 means existing wins; existing=0.6 vs new=0.9 means new wins). When the difference is less than 0.15, fall back to recency-wins to avoid over-riding genuinely uncertain cases.
Memory That Stays Accurate as the World Changes
Dakera's update and importance primitives give you full conflict resolution control — pick your strategy and ship in 30 minutes.
Get Started Free →