diff --git a/enterprise/app/services/captain/assistant/memory_prompt_injector.rb b/enterprise/app/services/captain/assistant/memory_prompt_injector.rb index 0fab1c499..4ff522ef8 100644 --- a/enterprise/app/services/captain/assistant/memory_prompt_injector.rb +++ b/enterprise/app/services/captain/assistant/memory_prompt_injector.rb @@ -1,6 +1,7 @@ class Captain::Assistant::MemoryPromptInjector def initialize(conversation:) @conversation = conversation + @memory_block_cache = {} end def recall_enabled? @@ -14,24 +15,39 @@ class Captain::Assistant::MemoryPromptInjector # Wraps the given base system prompt with a block # when recall is enabled and memories are found. Degrades gracefully: # returns the untouched base prompt on any failure or absent context. + # Caches the memory block per-message-text within the injector instance so + # Agents::Runner evaluating instructions multiple times per turn does not + # re-hit EmbeddingService or pgvector on every call. def append_memory_block(base_prompt, message_text) return base_prompt unless recall_enabled? return base_prompt if @conversation&.contact.blank? - memories = Captain::ContactMemories::RecallService.new( - contact: @conversation.contact, - query_text: message_text, - unit_id: resolve_unit_id - ).call + block = memory_block_for(message_text) + return base_prompt if block.blank? - memory_block = Captain::ContactMemories::PromptInjectionService.new(memories: memories).call - return base_prompt if memory_block.blank? - - [base_prompt, memory_block].join("\n\n") + [base_prompt, block].join("\n\n") + rescue StandardError => e + # Absolute guard: memory recall NEVER blocks or breaks the agent response. + Rails.logger.error("[Captain V2] MemoryPromptInjector unexpected failure: #{e.class}: #{e.message}") + base_prompt end private + def memory_block_for(message_text) + key = message_text.to_s + return @memory_block_cache[key] if @memory_block_cache.key?(key) + + memories = Captain::ContactMemories::RecallService.new( + contact: @conversation.contact, + query_text: key, + unit_id: resolve_unit_id + ).call + + @memory_block_cache[key] = + Captain::ContactMemories::PromptInjectionService.new(memories: memories).call + end + def resolve_unit_id return nil if @conversation.blank?