Class: Phronomy::Memory::Retrieval::Semantic

Inherits:
Base
  • Object
show all
Defined in:
lib/phronomy/memory/retrieval/semantic.rb

Overview

Retrieval strategy that returns the k semantically closest messages to the query.

Messages are indexed in a VectorStore on save. On retrieval, the query is embedded and the k nearest messages are returned. Falls back to the k most recent messages when no query is provided.

Examples:

retrieval = Phronomy::Memory::Retrieval::Semantic.new(
  embeddings: Phronomy::Embeddings::RubyLLMEmbeddings.new(model: "text-embedding-3-small"),
  k: 10
)

Instance Method Summary collapse

Constructor Details

#initialize(embeddings:, store: nil, k: 10) ⇒ Semantic

Returns a new instance of Semantic.

Parameters:



21
22
23
24
25
26
27
# File 'lib/phronomy/memory/retrieval/semantic.rb', line 21

def initialize(embeddings:, store: nil, k: 10)
  @store = store || Phronomy::VectorStore::InMemory.new
  @embeddings = embeddings
  @k = k
  @index = {}   # id => message
  @counter = 0
end

Instance Method Details

#clear_index(thread_id:) ⇒ Object

Clear indexed messages for a thread.

Parameters:

  • thread_id (String)


47
48
49
50
51
52
53
# File 'lib/phronomy/memory/retrieval/semantic.rb', line 47

def clear_index(thread_id:)
  ids = @index.select { |id, _| id.start_with?("#{thread_id}:") }.keys
  ids.each do |id|
    @index.delete(id)
    @store.remove(id: id)
  end
end

#index(thread_id:, messages:) ⇒ Object

Index a new batch of messages so they are searchable on future #select calls. Called by ConversationManager#save.

Parameters:

  • thread_id (String)
  • messages (Array)


34
35
36
37
38
39
40
41
42
# File 'lib/phronomy/memory/retrieval/semantic.rb', line 34

def index(thread_id:, messages:)
  messages.each do |msg|
    id = "#{thread_id}:#{@counter}"
    @counter += 1
    embedding = @embeddings.embed(msg.content.to_s)
    @store.add(id: id, embedding: embedding, metadata: {thread_id: thread_id, message: msg})
    @index[id] = msg
  end
end

#select(messages, query: nil) ⇒ Array

Return semantically relevant messages, or recent messages when query is nil.

Parameters:

  • messages (Array)

    full history (used as fallback when query is nil)

  • query (String, nil) (defaults to: nil)

    current user input for semantic search

Returns:

  • (Array)


60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/phronomy/memory/retrieval/semantic.rb', line 60

def select(messages, query: nil)
  if query && !query.strip.empty?
    query_embedding = @embeddings.embed(query)
    results = @store.search(query_embedding: query_embedding, k: @k * 3)
    results
      .select { |r| r[:metadata][:thread_id] == extract_thread_from_results(r, messages) }
      .first(@k)
      .map { |r| r[:metadata][:message] }
  else
    messages.last(@k)
  end
end