Portal Community

Recall Modes

ModeMethodWhen to Use
RecencyRecallRecentAsync(agentId, userId, limit)Default — always inject the N most recent sessions
SemanticSearchAsync(agentId, userId, query, topK)Find episodes topically relevant to the current message
SessionGetBySessionAsync(sessionId)Load a specific past session for replay or inspection

Semantic Recall Implementation

public async Task<IReadOnlyList<EpisodeSnippet>> SearchAsync(
    Guid agentId, Guid userId, string query, int topK = 3,
    CancellationToken ct = default)
{
    // Embed the query
    var queryEmbedding = await _embeddingProvider.EmbedAsync(query, ct);

    // Load episode summary embeddings for this user+agent
    var episodes = await _db.Episodes
        .Where(e => e.AgentId == agentId
                 && e.UserId == userId
                 && e.SummaryEmbedding != null
                 && !e.IsArchived)
        .Select(e => new { e.Id, e.Summary, e.SummaryEmbedding, e.StartedAt })
        .ToListAsync(ct);

    // Rank by cosine similarity
    var ranked = episodes
        .Select(e => new
        {
            e.Id,
            e.Summary,
            e.StartedAt,
            Score = CosineSimilarity(queryEmbedding, DeserializeEmbedding(e.SummaryEmbedding!))
        })
        .OrderByDescending(e => e.Score)
        .Take(topK)
        .Where(e => e.Score >= _config.MinRecallScore)  // threshold filter
        .ToList();

    return ranked.Select(e => new EpisodeSnippet
    {
        EpisodeId = e.Id,
        Summary = e.Summary!,
        Date = e.StartedAt,
        RelevanceScore = e.Score
    }).ToList();
}

Injecting Recalled Episodes into Context

The MemoryOrchestrator formats recalled episodes as a context block for the LLM:

private string FormatEpisodeContext(IReadOnlyList<EpisodeSnippet> episodes)
{
    if (!episodes.Any()) return string.Empty;

    var sb = new StringBuilder();
    sb.AppendLine("[Past Conversation Context]");
    sb.AppendLine("Relevant past conversations with this user:");

    foreach (var ep in episodes.OrderBy(e => e.Date))
    {
        sb.AppendLine($"- {ep.Date:yyyy-MM-dd}: {ep.Summary}");
    }

    return sb.ToString();
    // Injected as a system-role message before the user's message
}

Recall Configuration

// In MemoryConfig on AgentComposite
public class EpisodicRecallConfig
{
    public bool Enabled { get; set; } = true;
    public int RecentSessionsToInject { get; set; } = 2;   // always inject last N sessions
    public int SemanticTopK { get; set; } = 3;              // semantic search results
    public float MinRecallScore { get; set; } = 0.65f;      // below this, don't inject
    public bool UseSessionSummaries { get; set; } = true;   // summary vs. full messages
}
Summary vs. Full Messages

By default, recalled episodes are injected as summaries (much shorter), not full message threads. This preserves token budget. If an agent needs to recall exact wording from past sessions, set UseSessionSummaries = false — but be aware this significantly increases token usage.