fix(captain-memory): strengthen RecallService logging context and document timeout tradeoff
This commit is contained in:
parent
502c3d1698
commit
0fee1b3c2f
@ -12,21 +12,41 @@ class Captain::ContactMemories::RecallService
|
|||||||
def call
|
def call
|
||||||
return [] if @contact.blank? || @query_text.blank?
|
return [] if @contact.blank? || @query_text.blank?
|
||||||
|
|
||||||
|
# NOTE: Timeout.timeout is used here as a hard cap on both embedding API + DB query.
|
||||||
|
# It has known hazards (async exception, can't cleanly interrupt C-level I/O, can
|
||||||
|
# corrupt connection state) — tradeoff accepted because this service is non-critical:
|
||||||
|
# any failure returns [] and the agent degrades gracefully without memory. Phase 6
|
||||||
|
# will consider refactoring the DB portion to Postgres statement_timeout for safer
|
||||||
|
# cancellation.
|
||||||
Timeout.timeout(TIMEOUT_SECONDS) do
|
Timeout.timeout(TIMEOUT_SECONDS) do
|
||||||
query_embedding = Captain::Llm::EmbeddingService.new(account_id: @contact.account_id).get_embedding(@query_text)
|
query_embedding = Captain::Llm::EmbeddingService.new(account_id: @contact.account_id).get_embedding(@query_text)
|
||||||
return [] if query_embedding.blank?
|
return [] if query_embedding.blank?
|
||||||
|
|
||||||
Captain::ContactMemory
|
nearest_memories(query_embedding)
|
||||||
.active
|
|
||||||
.for_contact(@contact.id)
|
|
||||||
.scope_compatible(@unit_id)
|
|
||||||
.where.not(embedding: nil)
|
|
||||||
.nearest_neighbors(:embedding, query_embedding, distance: 'cosine')
|
|
||||||
.limit(@top_k)
|
|
||||||
.to_a
|
|
||||||
end
|
end
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
Rails.logger.warn("[ContactMemory::RecallService] #{e.class}: #{e.message}")
|
log_failure(e)
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def nearest_memories(query_embedding)
|
||||||
|
Captain::ContactMemory
|
||||||
|
.active
|
||||||
|
.for_contact(@contact.id)
|
||||||
|
.scope_compatible(@unit_id)
|
||||||
|
.where.not(embedding: nil)
|
||||||
|
.nearest_neighbors(:embedding, query_embedding, distance: 'cosine')
|
||||||
|
.limit(@top_k)
|
||||||
|
.to_a
|
||||||
|
end
|
||||||
|
|
||||||
|
def log_failure(error)
|
||||||
|
Rails.logger.warn(
|
||||||
|
"[ContactMemory::RecallService] #{error.class}: #{error.message} " \
|
||||||
|
"(contact_id=#{@contact&.id} account_id=#{@contact&.account_id})"
|
||||||
|
)
|
||||||
|
Rails.logger.warn(error.backtrace.first(5).join("\n")) unless error.is_a?(Timeout::Error)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -62,8 +62,9 @@ RSpec.describe Captain::ContactMemories::RecallService do
|
|||||||
|
|
||||||
it 'skips memories with NULL embedding' do
|
it 'skips memories with NULL embedding' do
|
||||||
create(:captain_contact_memory, contact: contact, account: account, embedding: nil)
|
create(:captain_contact_memory, contact: contact, account: account, embedding: nil)
|
||||||
|
valid = create(:captain_contact_memory, contact: contact, account: account, embedding: Array.new(1536, 0.1))
|
||||||
result = described_class.new(contact: contact, query_text: 'x').call
|
result = described_class.new(contact: contact, query_text: 'x').call
|
||||||
expect(result).to eq([])
|
expect(result).to eq([valid])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user