Class: Assistant::TechnicalSupport::ResponseGroundingAnswerSegments
- Inherits:
-
Object
- Object
- Assistant::TechnicalSupport::ResponseGroundingAnswerSegments
- Includes:
- Service
- Defined in:
- app/services/assistant/technical_support/response_grounding_answer_segments.rb
Overview
Splits one Sunny answer into stable, bounded segments so the grounding
grader cannot silently omit a material statement and still pass the turn.
Defined Under Namespace
Classes: Result
Constant Summary collapse
- MAX_SEGMENTS =
Maximum number of segments returned for a single answer.
20- MAX_ANSWER_CHARS =
Maximum answer length, in characters, that will be segmented.
30_000- ATOMIC_SEGMENT_CHARS =
Maximum length of an atomic segment chunk before further splitting.
700- SEGMENT_CHUNK_PATTERN =
Pattern used to split long segments into fixed-size chunks.
/.{1,#{ATOMIC_SEGMENT_CHARS}}/m- PATTERNS =
Regex patterns used to recognize source references, headings, and other
non-material answer lines. { source_label: /\A(?:sources?|verification sources?|references?|technical article|publication|manual|guide):?\z/i, source_link_sentence: %r{\A(?:(?:please\s+)?(?:use|review|see|read|open|follow)|here (?:is|are))\s+\[([^\]]+)\]\(https?://[^)]+\)\.?\z}i, markdown_link: %r{\[([^\]]+)\]\(https?://[^)]+\)}i, source_identifier: / \A(?:(?:technical|procedure)\s+article\s*\#\d+| (?:crm\s+)?publication\s+(?:\#\d+|[A-Z0-9][A-Z0-9-]{2,}))(?:\s+link)?\z /ix, source_reference_line: %r{ \A (?:(?:technical|procedure)\s+article\s*\#\d+| (?:crm\s+)?publication\s+(?:\#\d+|[A-Z0-9][A-Z0-9-]{2,})) \s*[·—–-]\s*(?<source_links>https?://\S+|\[[^\]\r\n]+\]\(https?://[^)\s]+\))\.? \z }ix, generic_source_link_label: / \A(?:source|(?:this|the)\s+(?:article|document|guide|manual|publication)| (?:public|crm)\s+link|public\s+(?:document|web\s+instruction\s+guide)| crm\s+publication(?:\s+link)?)\z /ix, case_reference: /\Atechnical support case reference:\s+case(?:\s+activity)?\s+notes?(?:\s+\([0-9-]+\))?\z/i, conversational: /\A(?:happy to help|thank you|thanks)[.!]?\z/i, presentation_heading: /\A(?:diagnosis|recommendations?|next steps?|sources?|references?)\z/i, material_source_title: / (?:[.!?]\z|\b(?:is|are|was|were|means?|indicates?|requires?|must|should|cannot|can't|will| does?|do|never|always|disconnect|press|hold|measure|test)\b) /ix, source_reference_with_title: / \A \*{0,2} (?:(?:technical|procedure)\s+article\s*\#\d+| (?:crm\s+)?publication\s+`?(?:\#\d+|[A-Z0-9][A-Z0-9-]{2,})`?) \*{0,2}\s*[·—–-]\s* (?<source_title>\*{1,2}[^*\r\n]+\*{1,2}|["“][^"”\r\n]+["”]) \s*[·—–-]\s*(?<source_links>.+)\z /ix, named_publication_reference: / \Apublication\s*[·—–-]\s*(?<source_title>["“][^"”\r\n]+["”])\s*[·—–-]\s* (?<source_links>.+)\z /ix, bare_url: %r{https?://[^\s<>"'\])]+}i, opening_fence: /\A(?:`{3,}|~{3,})/, list_prefix: /\A(?:(?:[-*+]|\d+[.)])(?:\s+|\z))+/ }.freeze
Class Method Summary collapse
-
.clearly_non_material?(text) ⇒ Boolean
Reports whether a grader may conservatively exclude a segment from technical-claim assessment.
-
.presentation_heading?(value) ⇒ Boolean
Reports whether a line is a standalone presentation label.
Instance Method Summary collapse
-
#call ⇒ Result
Returns stable answer segments in display order.
Methods included from Service
#attributes_used, #build_result, call, #logger
Methods included from Service::Initializer
Class Method Details
.clearly_non_material?(text) ⇒ Boolean
Reports whether a grader may conservatively exclude a segment from
technical-claim assessment.
103 104 105 106 |
# File 'app/services/assistant/technical_support/response_grounding_answer_segments.rb', line 103 def self.clearly_non_material?(text) values = text.to_s.lines.map(&:strip).compact_blank values.any? && values.all? { |value| atomic_non_material?(value) } end |
.presentation_heading?(value) ⇒ Boolean
Reports whether a line is a standalone presentation label.
179 180 181 182 183 |
# File 'app/services/assistant/technical_support/response_grounding_answer_segments.rb', line 179 def self.presentation_heading?(value) plain = value.to_s.delete('*_`').delete_suffix(':').strip plain.match?(PATTERNS.fetch(:presentation_heading)) end |
Instance Method Details
#call ⇒ Result
Returns stable answer segments in display order.
88 89 90 91 92 93 94 95 96 |
# File 'app/services/assistant/technical_support/response_grounding_answer_segments.rb', line 88 def call source = answer.to_s values = coalesce(segments(source.first(MAX_ANSWER_CHARS))) entries = values.map.with_index(1) do |text, index| { id: "answer-#{index}", text: } end Result.new(entries:, truncated: source.length > MAX_ANSWER_CHARS) end |