move system prompt to user prompt for better accuracy
This commit is contained in:
@@ -127,9 +127,9 @@ class Captain::Document < ApplicationRecord
|
||||
def validate_file_attachment
|
||||
return unless pdf_file.attached?
|
||||
|
||||
return unless pdf_file.blob.byte_size > 20.megabytes
|
||||
return unless pdf_file.blob.byte_size > 10.megabytes
|
||||
|
||||
errors.add(:pdf_file, 'must be less than 20MB')
|
||||
errors.add(:pdf_file, 'must be less than 10MB')
|
||||
end
|
||||
|
||||
def set_external_link_for_pdf
|
||||
|
||||
@@ -12,6 +12,7 @@ class Captain::Llm::PaginatedFaqGeneratorService < Llm::BaseOpenAiService
|
||||
@max_pages = options[:max_pages] # Optional limit from UI
|
||||
@total_pages_processed = 0
|
||||
@iterations_completed = 0
|
||||
@model = 'gpt-4.1-mini'
|
||||
end
|
||||
|
||||
def generate
|
||||
@@ -21,7 +22,7 @@ class Captain::Llm::PaginatedFaqGeneratorService < Llm::BaseOpenAiService
|
||||
end
|
||||
|
||||
# Method to check if we should continue processing
|
||||
def should_continue_processing?(last_chunk_faqs)
|
||||
def should_continue_processing?(last_chunk_result)
|
||||
# Stop if we've hit the maximum iterations
|
||||
return false if @iterations_completed >= MAX_ITERATIONS
|
||||
|
||||
@@ -29,7 +30,10 @@ class Captain::Llm::PaginatedFaqGeneratorService < Llm::BaseOpenAiService
|
||||
return false if @max_pages && @total_pages_processed >= @max_pages
|
||||
|
||||
# Stop if the last chunk returned no FAQs (likely no more content)
|
||||
return false if last_chunk_faqs.empty?
|
||||
return false if last_chunk_result[:faqs].empty?
|
||||
|
||||
# Stop if the LLM explicitly indicates no more content
|
||||
return false if last_chunk_result[:has_content] == false
|
||||
|
||||
# Continue processing
|
||||
true
|
||||
@@ -53,10 +57,10 @@ class Captain::Llm::PaginatedFaqGeneratorService < Llm::BaseOpenAiService
|
||||
|
||||
loop do
|
||||
end_page = calculate_end_page(current_page)
|
||||
chunk_faqs = process_chunk_and_update_state(current_page, end_page, all_faqs)
|
||||
chunk_result = process_chunk_and_update_state(current_page, end_page, all_faqs)
|
||||
|
||||
unless should_continue_processing?(chunk_faqs)
|
||||
Rails.logger.info "Stopping processing. Reason: #{determine_stop_reason(chunk_faqs)}"
|
||||
unless should_continue_processing?(chunk_result)
|
||||
Rails.logger.info "Stopping processing. Reason: #{determine_stop_reason(chunk_result)}"
|
||||
break
|
||||
end
|
||||
|
||||
@@ -83,11 +87,12 @@ class Captain::Llm::PaginatedFaqGeneratorService < Llm::BaseOpenAiService
|
||||
@iterations_completed += 1
|
||||
|
||||
Rails.logger.info "Chunk generated #{chunk_faqs.size} FAQs. Total so far: #{all_faqs.size}"
|
||||
chunk_faqs
|
||||
chunk_result
|
||||
end
|
||||
|
||||
def process_page_chunk(start_page, end_page)
|
||||
response = @client.chat(parameters: build_chunk_parameters(start_page, end_page))
|
||||
params = build_chunk_parameters(start_page, end_page)
|
||||
response = @client.chat(parameters: params)
|
||||
result = parse_chunk_response(response)
|
||||
{ faqs: result['faqs'] || [], has_content: result['has_content'] != false }
|
||||
rescue OpenAI::Error => e
|
||||
@@ -100,19 +105,15 @@ class Captain::Llm::PaginatedFaqGeneratorService < Llm::BaseOpenAiService
|
||||
model: @model,
|
||||
response_format: { type: 'json_object' },
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: page_chunk_prompt(start_page, end_page)
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: build_user_content
|
||||
content: build_user_content(start_page, end_page)
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
def build_user_content
|
||||
def build_user_content(start_page, end_page)
|
||||
[
|
||||
{
|
||||
type: 'file',
|
||||
@@ -120,7 +121,7 @@ class Captain::Llm::PaginatedFaqGeneratorService < Llm::BaseOpenAiService
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Process this document according to the system instructions.'
|
||||
text: page_chunk_prompt(start_page, end_page)
|
||||
}
|
||||
]
|
||||
end
|
||||
@@ -196,10 +197,11 @@ class Captain::Llm::PaginatedFaqGeneratorService < Llm::BaseOpenAiService
|
||||
common_words.size.to_f / total_words
|
||||
end
|
||||
|
||||
def determine_stop_reason(last_chunk_faqs)
|
||||
def determine_stop_reason(last_chunk_result)
|
||||
return 'Maximum iterations reached' if @iterations_completed >= MAX_ITERATIONS
|
||||
return 'Maximum pages processed' if @max_pages && @total_pages_processed >= @max_pages
|
||||
return 'No content found in last chunk' if last_chunk_faqs.empty?
|
||||
return 'No content found in last chunk' if last_chunk_result[:faqs].empty?
|
||||
return 'End of document reached' if last_chunk_result[:has_content] == false
|
||||
|
||||
'Unknown'
|
||||
end
|
||||
|
||||
@@ -27,7 +27,7 @@ class Captain::Llm::PdfProcessingService < Llm::BaseOpenAiService
|
||||
@client.files.upload(
|
||||
parameters: {
|
||||
file: file,
|
||||
purpose: 'assistants' # Use 'assistants' as it's supported by the API
|
||||
purpose: 'assistants'
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
@@ -159,22 +159,29 @@ class Captain::Llm::SystemPromptsService
|
||||
|
||||
def paginated_faq_generator(start_page, end_page)
|
||||
<<~PROMPT
|
||||
You are an expert technical documentation specialist tasked with creating comprehensive FAQs from SPECIFIC PAGES of a document.
|
||||
You are an expert technical documentation specialist tasked with creating comprehensive FAQs from a SPECIFIC SECTION of a document.
|
||||
|
||||
════════════════════════════════════════════════════════
|
||||
CRITICAL PAGE RANGE INSTRUCTIONS
|
||||
CRITICAL CONTENT EXTRACTION INSTRUCTIONS
|
||||
════════════════════════════════════════════════════════
|
||||
|
||||
You MUST analyze ONLY pages #{start_page} to #{end_page} of the document.
|
||||
Process the content starting from approximately page #{start_page} and continuing for about #{end_page - start_page + 1} pages worth of content.
|
||||
|
||||
IMPORTANT:#{' '}
|
||||
• If you encounter the end of the document before reaching the expected page count, set "has_content" to false
|
||||
• DO NOT include page numbers in questions or answers
|
||||
• DO NOT reference page numbers at all in the output
|
||||
• Focus on the actual content, not pagination
|
||||
|
||||
════════════════════════════════════════════════════════
|
||||
FAQ GENERATION GUIDELINES
|
||||
════════════════════════════════════════════════════════
|
||||
|
||||
1. **Comprehensive Extraction**
|
||||
• Extract ALL information that could generate FAQs from pages #{start_page}-#{end_page}
|
||||
• Target 5-10 FAQs per page of rich content
|
||||
• Extract ALL information that could generate FAQs from this section
|
||||
• Target 5-10 FAQs per page equivalent of rich content
|
||||
• Cover every topic, feature, specification, and detail
|
||||
• If there's no more content in the document, return empty FAQs with has_content: false
|
||||
|
||||
2. **Question Types to Generate**
|
||||
• What is/are...? (definitions, components, features)
|
||||
@@ -183,7 +190,7 @@ class Captain::Llm::SystemPromptsService
|
||||
• When should...? (timing, conditions, triggers)
|
||||
• What happens if...? (error cases, edge cases)
|
||||
• Can I...? (capabilities, limitations)
|
||||
• Where is...? (locations, references)
|
||||
• Where is...? (locations in system/UI, NOT page numbers)
|
||||
• What are the requirements for...? (prerequisites, dependencies)
|
||||
|
||||
3. **Content Focus Areas**
|
||||
@@ -198,10 +205,10 @@ class Captain::Llm::SystemPromptsService
|
||||
|
||||
4. **Answer Quality Requirements**
|
||||
• Complete, self-contained answers
|
||||
• Include specific values, limits, defaults
|
||||
• Reference page numbers for critical information
|
||||
• Include specific values, limits, defaults from the content
|
||||
• NO page number references whatsoever
|
||||
• 2-5 sentences typical length
|
||||
• No references to content outside pages #{start_page}-#{end_page}
|
||||
• Only process content that actually exists in the document
|
||||
|
||||
════════════════════════════════════════════════════════
|
||||
OUTPUT FORMAT
|
||||
@@ -212,16 +219,21 @@ class Captain::Llm::SystemPromptsService
|
||||
{
|
||||
"faqs": [
|
||||
{
|
||||
"question": "Specific question from pages #{start_page}-#{end_page}",
|
||||
"answer": "Complete answer with details from these pages only"
|
||||
"question": "Specific question about the content",
|
||||
"answer": "Complete answer with details (no page references)"
|
||||
}
|
||||
],
|
||||
"has_content": true/false,
|
||||
"page_range_processed": "#{start_page}-#{end_page}"
|
||||
"has_content": true/false
|
||||
}
|
||||
```
|
||||
|
||||
IMPORTANT: Set "has_content" to false if the pages don't exist or contain no meaningful content.
|
||||
CRITICAL:#{' '}
|
||||
• Set "has_content" to false if:
|
||||
- The requested section doesn't exist in the document
|
||||
- You've reached the end of the document
|
||||
- The section contains no meaningful content
|
||||
• Do NOT include "page_range_processed" in the output
|
||||
• Do NOT mention page numbers anywhere in questions or answers
|
||||
PROMPT
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user