Advanced Lifecycle

Conflict Resolution

⏰ ~30 min to implement 📦 Requires: Dakera v0.11+

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 →
Prerequisites
  • Running Dakera server (Quickstart)
  • An agent ID with existing memories
  • Understanding of update_memory and update_importance SDK 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.

Three Resolution Strategies

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_memory to revise the winner; use update_importance to demote the loser
  • Audit: Store a conflict log entry at low importance for traceability without polluting recall

Diagram: Conflict Detection — Two Contradictory Memories

EXISTING MEMORY "User prefers light mode" importance: 0.75 | stored: Jan 15 tags: [preference, ui] NEW INCOMING MEMORY "User switched back to dark mode" Conflict Detected similarity: 0.88 opposing direction confirmed Resolution: Recency new memory wins old: importance 0.75 -> 0.15 new: stored at 0.80 Done clean Conflict Detection at Write Time recall similar + detect contradiction + resolve + update = clean memory store

Diagram: Resolution Strategy Decision Tree

Conflict Detected Select strategy by memory category preference / mutable fact importance diff >= 0.15 high-stakes fact Recency Wins newest memory becomes active old importance drops to 0.15 Confidence Wins higher importance score wins loser demoted; winner updated Ask User surface conflict in conversation store confirmed answer + log Automatic (<200ms) Automatic (<200ms) Interactive (user response)

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

  1. Define your conflict detection scope
    Not 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"].
  2. Recall similar memories before storing
    Before every store_memory on a conflict-eligible type, call recall with a semantic query derived from the new content. Use min_importance=0.3 to catch even partially-decayed contradictions, and top_k=3. Check if any result has similarity above your threshold (0.80–0.85).
  3. Classify the relationship
    A 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.
  4. Apply the resolution strategy
    Preferences: 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.
  5. Update winning memory and demote loser
    Call update_memory on the winning memory if its content needs updating. Call update_importance on 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.
  6. Store a conflict audit entry
    After 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 by min_importance=0.5).

Before & After: Resolving a Preference Conflict

Before — two contradictory memories
[
  {
    "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
After — resolved (recency wins)
[
  {
    "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.

Start Building →

SDK Reference

MethodSDKPurpose
recall(agent_id, query, min_importance, top_k)PythonRetrieve similar memories to detect conflicts before storing
recall(agentId, query, {min_importance, top_k})TypeScriptRetrieve similar memories to detect conflicts before storing
update_memory(agent_id, memory_id, ...)PythonRevise content and tags of loser or winner
updateMemory(agentId, memoryId, request)TypeScriptRevise content and tags of loser or winner
update_importance(agent_id, memory_id, importance)PythonDemote losing memory to trigger decay
updateImportance(agentId, {memory_id, importance})TypeScriptDemote losing memory to trigger decay
store_memory(agent_id, content, importance, tags, ttl_seconds)PythonStore winning memory and TTL-limited audit log
storeMemory(agentId, {content, importance, tags, ttl_seconds})TypeScriptStore winning memory and TTL-limited audit log

Performance Considerations

<200ms
End-to-end conflict resolution (recall + classify + update)
94%
Preference conflicts auto-resolved without user prompt
3
Dakera API calls per conflict resolution workflow
  • 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

Edge Case 1: False Positive — Historical vs. Current Statements

"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.

Edge Case 2: Conflict Chains — Three or More Overlapping Memories

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.

Edge Case 3: Multi-Agent Race Condition on Shared Namespace

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).

Edge Case 4: Ask-User in Async Channels

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.

Edge Case 5: Genuine Ambiguity (User Has Not Decided)

"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

CategoryTagsDefault StrategyOverride Condition
UI preferencespreference, uiRecency winsNever override
Diet / lifestylepreference, lifestyleRecency winsNever override
Employmentfact, careerRecency winsConfidence if new imp < old by 0.2
Home addressfact, addressAsk user alwaysNever skip
Medical historyfact, medicalAsk user alwaysNever skip
Technical skillsfact, skillConfidence winsAdditive 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 →