chatwoot-develop/enterprise/app/services/captain/tools/search_documentation_service.rb

80 lines
2.4 KiB
Ruby
Executable File

class Captain::Tools::SearchDocumentationService < Captain::Tools::BaseTool
LOW_CONFIDENCE_DISTANCE = 0.55
def self.name
'search_documentation'
end
description 'Search and retrieve documentation from knowledge base'
param :query, desc: 'Search Query', required: true
def initialize(assistant, user: nil, conversation: nil)
@conversation = conversation
super(assistant, user: user)
end
def execute(*args, **params)
actual_params = resolve_params(args, params)
query = actual_params[:query]
Rails.logger.info { "#{self.class.name}: #{query}" }
responses = assistant.responses.approved.search(query)
log_results(query, responses)
return 'No FAQs found for the given query' if responses.empty? || low_confidence?(responses.first)
responses.map { |response| format_response(response) }.join
end
private
def log_results(query, responses)
request_id = extract_request_id
conversation_id = @conversation&.id
response_count = responses.length
scores = responses.map { |response| result_score_payload(response) }
top_response = responses.first
top_snippet = top_response&.answer.to_s.strip[0, 200]
low_confidence = top_response&.neighbor_distance.to_f > LOW_CONFIDENCE_DISTANCE if top_response
Rails.logger.info(
"[CAPTAIN][search_documentation] request_id=#{request_id} conversation_id=#{conversation_id} query=#{query.inspect} " \
"count=#{response_count} results=#{scores} low_confidence=#{low_confidence} top_snippet=#{top_snippet.inspect}"
)
end
def low_confidence?(response)
return false unless response.respond_to?(:neighbor_distance)
response.neighbor_distance.to_f > LOW_CONFIDENCE_DISTANCE
end
def result_score_payload(response)
{
id: response.id,
distance: response.neighbor_distance
}
end
def extract_request_id
return RequestStore.store[:request_id] if defined?(RequestStore) && RequestStore.store[:request_id].present?
Thread.current[:request_id] || 'unknown'
end
def format_response(response)
formatted_response = "
Question: #{response.question}
Answer: #{response.answer}
"
if response.documentable.present? && response.documentable.try(:external_link)
formatted_response += "
Source: #{response.documentable.external_link}
"
end
formatted_response
end
end