File size: 15,228 Bytes
b990fc0
 
4a25546
 
 
 
364049b
2e6803f
4a25546
 
 
2e6803f
1169bfe
2e6803f
4a25546
2e6803f
4a25546
2e6803f
1169bfe
2e6803f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ac748c8
2e6803f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ac748c8
2e6803f
8662a42
507c6f8
2e6803f
 
b990fc0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2e6803f
b990fc0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2e6803f
b990fc0
 
2e6803f
 
b990fc0
 
 
 
 
 
 
 
 
 
2e6803f
b990fc0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2e6803f
b990fc0
 
faea0d1
b990fc0
 
364049b
b990fc0
 
 
2e6803f
 
 
 
b990fc0
 
 
 
364049b
b990fc0
faea0d1
2e6803f
364049b
b990fc0
2e6803f
 
364049b
b990fc0
 
 
2e6803f
 
364049b
2e6803f
 
364049b
b990fc0
364049b
b990fc0
2e6803f
 
364049b
b990fc0
2e6803f
 
364049b
b990fc0
 
 
2e6803f
 
364049b
 
b990fc0
364049b
b990fc0
4a25546
2e6803f
 
 
 
b990fc0
2e6803f
 
 
 
 
 
 
 
 
 
364049b
 
b990fc0
364049b
b990fc0
 
 
 
 
 
 
 
 
 
 
2e6803f
b990fc0
 
 
 
 
2e6803f
b990fc0
364049b
b990fc0
2e6803f
b990fc0
364049b
 
2e6803f
4a25546
b990fc0
2e6803f
b990fc0
 
 
 
 
 
2e6803f
b990fc0
2e6803f
 
ac748c8
b990fc0
 
 
 
 
 
 
 
 
 
 
 
 
2e6803f
69bc060
 
 
 
 
 
364049b
 
b990fc0
 
 
69bc060
 
 
 
 
 
 
 
 
364049b
 
69bc060
 
 
364049b
 
 
 
69bc060
 
 
364049b
2e6803f
b990fc0
 
 
2e6803f
 
b990fc0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
# Enhanced Content Optimization Module with RAG for GEO
# Integrates RAG functionality for better Generative Engine Optimization

import json
import re
from typing import Dict, Any, List, Optional
from langchain.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
from langchain.schema import Document


class ContentOptimizer:
    """Enhanced Content Optimizer with RAG capabilities for GEO"""

    def __init__(self, llm, vector_chunker=None):
        self.llm = llm
        self.vector_chunker = vector_chunker
        self.setup_prompts()
        self.setup_geo_knowledge_base()

    def setup_geo_knowledge_base(self):
        """Initialize GEO best practices knowledge base"""
        self.geo_knowledge = [
            """
            Generative Engine Optimization (GEO) Best Practices:
            
            1. Structure for AI Consumption:
            - Use clear headings and subheadings
            - Include bullet points and numbered lists
            - Provide direct, concise answers to common questions
            - Use schema markup when possible
            
            2. Content Format for LLMs:
            - Answer questions directly in the first sentence
            - Use "what, why, how" question patterns
            - Include relevant entities and proper nouns
            - Maintain factual accuracy with citations
            
            3. Semantic Optimization:
            - Include related terms and synonyms
            - Use entity-rich content (people, places, organizations)
            - Connect concepts with clear relationships
            - Optimize for topic clusters, not just keywords
            """,
            
            """
            AI Search Visibility Optimization:
            
            1. Query Intent Matching:
            - Address user intent explicitly
            - Use natural language patterns
            - Include question-answer pairs
            - Optimize for conversational queries
            
            2. Citation Worthiness:
            - Include authoritative sources and data
            - Use specific facts and statistics
            - Provide expert opinions and insights
            - Maintain consistent tone and expertise
            
            3. Multi-Query Coverage:
            - Address related questions in the same content
            - Use comprehensive topic coverage
            - Include long-tail and specific queries
            - Provide context for complex topics
            """,
            
            """
            Content Structure for AI Systems:
            
            1. Information Architecture:
            - Lead with key information
            - Use inverted pyramid structure
            - Include table of contents for long content
            - Break complex topics into digestible sections
            
            2. Conversational Readiness:
            - Write in active voice
            - Use clear, direct language
            - Include transitional phrases
            - Optimize sentence length (12-20 words)
            
            3. Context Completeness:
            - Define technical terms
            - Provide background information
            - Include relevant examples
            - Connect to broader topic context
            """
        ]

    def setup_prompts(self):
        """Initialize optimization prompts with RAG integration"""
        self.rag_enhancement_prompt = """
            You are a Generative Engine Optimization (GEO) specialist with access to best practices knowledge.
            
            Based on the provided GEO knowledge and the user's content, optimize the content for:
            1. AI search engines (ChatGPT, Claude, Gemini)
            2. LLM-based question answering systems
            3. Conversational AI interfaces
            4. Citation and reference systems
            
            Use the knowledge base to inform your optimization decisions.
            
            Knowledge Base Context:
            {context}
            
            Original Content:
            {content}
            
            Provide comprehensive GEO optimization in JSON format:
            ```json
            {{
              "geo_analysis": {{
                "current_geo_score": 7.5,
                "ai_search_visibility": 8.0,
                "query_intent_matching": 7.0,
                "conversational_readiness": 8.5,
                "citation_worthiness": 6.5,
                "context_completeness": 7.5
              }},
              "optimization_opportunities": [
                {{
                  "type": "Structure Enhancement",
                  "description": "Add clear headings and Q&A format",
                  "priority": "high",
                  "expected_impact": "Improve AI parsing by 25%"
                }}
              ],
              "optimized_content": {{
                "enhanced_text": "Your optimized content here...",
                "structural_improvements": ["Added FAQ section", "Improved headings"],
                "semantic_enhancements": ["Added related terms", "Improved entity density"]
              }},
              "geo_keywords": {{
                "primary_entities": ["entity1", "entity2"],
                "semantic_terms": ["term1", "term2"],
                "question_patterns": ["What is...", "How does..."],
                "related_concepts": ["concept1", "concept2"]
              }},
              "recommendations": [
                "Add more specific examples",
                "Include authoritative citations",
                "Improve conversational flow"
              ]
            }}
            ```
        """.strip()
        
        self.competitive_geo_prompt = """
            Analyze the content against GEO best practices and identify competitive optimization opportunities.

            GEO Knowledge Base:
            {context}

            Content to Analyze:
            {content}

            Provide competitive GEO analysis:
            ```json
            {{
              "competitive_gaps": {{
                "missing_question_patterns": ["What questions aren't covered"],
                "entity_gaps": ["Important entities not mentioned"],
                "semantic_opportunities": ["Related terms to include"],
                "structural_weaknesses": ["Formatting issues for AI"]
              }},
              "benchmark_comparison": {{
                "current_performance": {{
                  "ai_answerability": 6.5,
                  "semantic_richness": 7.0,
                  "structural_clarity": 8.0
                }},
                "optimization_potential": {{
                  "ai_answerability": 9.0,
                  "semantic_richness": 8.5,
                  "structural_clarity": 9.5
                }}
              }},
              "action_plan": [
                {{
                  "priority": "high",
                  "action": "Add FAQ section",
                  "rationale": "Improves direct question answering"
                }}
              ]
            }}
            ```
        """.strip()


    def optimize_content_with_rag(self, content: str, optimization_type: str = "geo_standard", analyze_only: bool = False) -> Dict[str, Any]:
        try:
            knowledge_docs = [Document(page_content=k, metadata={"source": "geo_best_practices"}) for k in self.geo_knowledge]
            context = "\n\n".join(self.geo_knowledge)

            if self.vector_chunker:
                qa_chain = self.vector_chunker.create_qa_chain(knowledge_docs, self.llm)
                geo_query = f"How to optimize this type of content for AI search engines: {content[:500]}"
                context_result = qa_chain({"query": geo_query})
                context = context_result.get("result", context)

            return self._competitive_geo_optimization(content, context) if optimization_type == "competitive_geo" else self._standard_geo_optimization(content, context, analyze_only)

        except Exception as e:
            return {"error": f"RAG-enhanced optimization failed: {str(e)}"}

    def _standard_geo_optimization(self, content: str, context: str, analyze_only: bool) -> Dict[str, Any]:
        try:
            prompt = ChatPromptTemplate.from_messages([
                SystemMessagePromptTemplate.from_template(self.rag_enhancement_prompt),
                HumanMessagePromptTemplate.from_template("Optimize this content using GEO best practices.")
            ])
            result = (prompt | self.llm).invoke({"context": context, "content": content[:5000]})
            parsed = self._parse_optimization_result(getattr(result, 'content', str(result)))
            parsed.update({
                'optimization_type': 'geo_standard',
                'rag_enhanced': True,
                'analyze_only': analyze_only,
                'original_length': len(content),
                'knowledge_sources': len(self.geo_knowledge)
            })
            return parsed
        except Exception as e:
            return {"error": f"Standard GEO optimization failed: {str(e)}"}

    def _competitive_geo_optimization(self, content: str, context: str) -> Dict[str, Any]:
        try:
            prompt = ChatPromptTemplate.from_messages([
                SystemMessagePromptTemplate.from_template(self.competitive_geo_prompt),
                HumanMessagePromptTemplate.from_template("Perform competitive GEO analysis.")
            ])
            result = (prompt | self.llm).invoke({"context": context, "content": content[:5000]})
            parsed = self._parse_optimization_result(getattr(result, 'content', str(result)))
            parsed.update({
                'optimization_type': 'competitive_geo',
                'rag_enhanced': True,
                'competitive_analysis': True
            })
            return parsed
        except Exception as e:
            return {"error": f"Competitive GEO optimization failed: {str(e)}"}

    def batch_optimize_with_rag(self, content_list: List[str], optimization_type: str = "geo_standard") -> List[Dict[str, Any]]:
        results = []
        for i, content in enumerate(content_list):
            try:
                result = self.optimize_content_with_rag(content, optimization_type)
                result['batch_index'] = i
                results.append(result)
            except Exception as e:
                results.append({
                    'batch_index': i,
                    'error': f"Batch GEO optimization failed: {str(e)}"
                })
        return results

    def analyze_geo_readability(self, content: str) -> Dict[str, Any]:
        try:
            words = content.split()
            sentences = [s.strip() for s in re.split(r'[.!?]+', content) if s.strip()]
            paragraphs = [p.strip() for p in content.split('\n\n') if p.strip()]

            metrics = {
                'questions': len(re.findall(r'\?', content)),
                'headings': len(re.findall(r'^#+\s', content, re.MULTILINE)),
                'lists': len(re.findall(r'^\s*[-*+]\s', content, re.MULTILINE)),
                'entities': len(re.findall(r'\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)*\b', content)),
                'numbers': len(re.findall(r'\b\d+\.?\d*\b', content)),
                'sentence_count': len(sentences),
                'word_count': len(words)
            }

            geo_score = self._calculate_geo_readability_score({
                'avg_words_per_sentence': metrics['word_count'] / metrics['sentence_count'] if metrics['sentence_count'] else 0,
                'questions_ratio': metrics['questions'] / metrics['sentence_count'] if metrics['sentence_count'] else 0,
                'structure_elements': metrics['headings'] + metrics['lists'],
                'entity_density': metrics['entities'] / metrics['word_count'] if metrics['word_count'] else 0,
                'numeric_data': metrics['numbers'] / metrics['word_count'] if metrics['word_count'] else 0
            })

            return {
                'geo_readability_metrics': metrics,
                'geo_readability_score': geo_score,
                'geo_recommendations': self._generate_geo_recommendations(metrics)
            }
        except Exception as e:
            return {'error': f"GEO readability analysis failed: {str(e)}"}

    def _calculate_geo_readability_score(self, m: Dict[str, float]) -> float:
        try:
            score = (
                max(0, 10 - abs(m['avg_words_per_sentence'] - 15) * 0.3) * 0.2 +
                min(10, m['questions_ratio'] * 50) * 0.25 +
                min(10, m['structure_elements'] * 1.5) * 0.25 +
                min(10, m['entity_density'] * 100) * 0.15 +
                min(10, m['numeric_data'] * 200) * 0.15
            )
            return round(score, 1)
        except Exception:
            return 5.0

    def _generate_geo_recommendations(self, m: Dict[str, int]) -> List[str]:
        r = []
        if m['questions'] == 0:
            r.append("Add FAQ section or question-based headings.")
        if m['headings'] < 2:
            r.append("Use more structured headings.")
        if m['lists'] == 0:
            r.append("Include bullet points or numbered lists.")
        if m['entities'] < 5:
            r.append("Add named or topical entities.")
        if m['questions'] / m['sentence_count'] < 0.1:
            r.append("Transform statements into Q&A pairs.")
        return r

    def _clean_json_string(self, json_str: str) -> str:
        json_str = json_str.replace("...", "")
        json_str = re.sub(r",\s*([}\]])", r"\\1", json_str)
        json_str = json_str.strip('`')
        return json_str

    def _parse_optimization_result(self, response_text: str) -> Dict[str, Any]:
        try:
            start = response_text.find('{')
            end = response_text.rfind('}') + 1
            if start != -1 and end != -1:
                json_str = self._clean_json_string(response_text[start:end])
                return json.loads(json_str)
            return {
                'raw_response': response_text,
                'parsing_error': 'No JSON structure found in response',
                'geo_analysis': {},
                'recommendations': []
            }
        except json.JSONDecodeError as e:
            return {
                'raw_response': response_text,
                'parsing_error': f'JSON decode error: {str(e)}',
                'geo_analysis': {},
                'recommendations': []
            }
        except Exception as e:
            return {
                'raw_response': response_text,
                'parsing_error': f'Unexpected error: {str(e)}',
                'geo_analysis': {},
                'recommendations': []
            }

    # Legacy support methods
    def optimize_content(self, content: str, analyze_only: bool = False, include_keywords: bool = True, optimization_type: str = "standard") -> Dict[str, Any]:
        return self.optimize_content_with_rag(content, optimization_type, analyze_only)

    def analyze_content_readability(self, content: str) -> Dict[str, Any]:
        return self.analyze_geo_readability(content)