基于 RAG 的 AI GEO 内容优化引擎架构

基于 RAG 的 AI GEO 内容优化引擎架构

RAG(检索增强生成)不仅是问答系统的标配,更是 AI GEO 内容优化的核心引擎。本文讲解如何利用 RAG 架构分析内容在 AI 搜索中的表现,并自动生成优化建议。

一、为什么 AI GEO 需要 RAG?

AI GEO 的核心目标:让 AI 搜索引擎在回答用户问题时引用你的内容

这需要回答三个问题:

  1. 当前内容覆盖了什么? → 需要理解现有内容的语义
  2. AI 搜索在回答什么? → 需要理解 AI 回答的语义
  3. 差距在哪里? → 需要语义对比和差距分析

传统关键词匹配无法完成语义层面的对比,而 RAG 架构天然适合:

复制代码
┌──────────┐     ┌──────────┐     ┌──────────┐     ┌──────────┐
│ 内容库    │────▶│ 向量索引  │────▶│ 语义检索  │────▶│ 差距分析  │
│ (网页/文章)│    │ (Embedding)│   │ (Top-K)   │    │ (优化建议) │
└──────────┘     └──────────┘     └──────────┘     └──────────┘
                        ▲
                        │
                  ┌─────┴─────┐
                  │ AI 搜索回答 │
                  │ (Query)    │
                  └───────────┘

二、RAG 内容优化引擎架构

复制代码
┌─────────────────────────────────────────────────────────┐
│                 RAG 内容优化引擎                          │
│                                                         │
│  ┌───────────┐  ┌───────────┐  ┌───────────┐           │
│  │ 内容入库   │  │ 向量化    │  │ 索引管理   │           │
│  │ Pipeline  │─▶│ Pipeline │─▶│ Service   │           │
│  └───────────┘  └───────────┘  └─────┬─────┘           │
│                                       │                 │
│  ┌───────────┐  ┌───────────┐        │                 │
│  │ 差距检测   │◀─┤ 语义检索  │◀───────┘                 │
│  │ Engine    │  │ Service   │                          │
│  └─────┬─────┘  └───────────┘                          │
│        │                                                │
│  ┌─────┴─────┐  ┌───────────┐  ┌───────────┐          │
│  │ 优化建议   │─▶│ 建议生成   │─▶│ 效果追踪   │          │
│  │ 生成器     │  │ (LLM)     │  │ Service   │          │
│  └───────────┘  └───────────┘  └───────────┘          │
└─────────────────────────────────────────────────────────┘

2.1 内容入库 Pipeline

将企业已有内容(官网、博客、产品文档)入库:

python 复制代码
from dataclasses import dataclass, field
from typing import List, Optional
from enum import Enum

class ContentType(Enum):
    WEBPAGE = "webpage"
    BLOG = "blog"
    PRODUCT_DOC = "product_doc"
    FAQ = "faq"
    PRESS_RELEASE = "press_release"

@dataclass
class ContentItem:
    """内容项"""
    content_id: str
    url: str
    title: str
    body: str
    content_type: ContentType
    published_at: Optional[str] = None
    tags: List[str] = field(default_factory=list)
    word_count: int = 0

class ContentIngestionPipeline:
    """内容入库 Pipeline"""
    
    def __init__(self, chunker, embedder, vector_store, postgres):
        self.chunker = chunker      # 文本分块器
        self.embedder = embedder    # 向量化器
        self.vector_store = vector_store  # 向量存储
        self.postgres = postgres    # 元数据存储
    
    async def ingest(self, content: ContentItem) -> str:
        """入库流程:分块 → 向量化 → 存储"""
        
        # 1. 文本分块
        chunks = self.chunker.chunk(content.body, content.title)
        
        # 2. 批量向量化
        embeddings = await self.embedder.embed_batch(
            [chunk.text for chunk in chunks]
        )
        
        # 3. 存储向量
        vectors = []
        for chunk, embedding in zip(chunks, embeddings):
            vectors.append({
                "id": f"{content.content_id}_{chunk.index}",
                "embedding": embedding,
                "content_id": content.content_id,
                "url": content.url,
                "title": content.title,
                "chunk_text": chunk.text,
                "chunk_index": chunk.index,
                "content_type": content.content_type.value,
                "published_at": content.published_at,
            })
        
        await self.vector_store.insert_batch("content_vectors", vectors)
        
        # 4. 存储元数据
        await self.postgres.execute(
            """
            INSERT INTO content_items 
            (content_id, url, title, content_type, word_count, 
             chunk_count, ingested_at)
            VALUES ($1, $2, $3, $4, $5, $6, $7)
            ON CONFLICT (content_id) DO UPDATE SET
            title = EXCLUDED.title,
            word_count = EXCLUDED.word_count,
            chunk_count = EXCLUDED.chunk_count,
            updated_at = NOW()
            """,
            content.content_id, content.url, content.title,
            content.content_type.value, content.word_count,
            len(chunks), datetime.now()
        )
        
        return content.content_id

2.2 文本分块策略

python 复制代码
from dataclasses import dataclass

@dataclass
class TextChunk:
    index: int
    text: str
    start_pos: int
    end_pos: int

class SemanticChunker:
    """语义分块器 - 按语义边界分块"""
    
    def __init__(
        self,
        chunk_size: int = 512,
        overlap: int = 50,
        min_chunk_size: int = 100
    ):
        self.chunk_size = chunk_size
        self.overlap = overlap
        self.min_chunk_size = min_chunk_size
    
    def chunk(self, text: str, title: str = "") -> List[TextChunk]:
        """分块主逻辑"""
        # 在标题前加上上下文
        full_text = f"{title}\n\n{text}" if title else text
        
        # 按段落分割
        paragraphs = self._split_paragraphs(full_text)
        
        # 合并段落到目标大小
        chunks = []
        current = []
        current_len = 0
        
        for para in paragraphs:
            if current_len + len(para) > self.chunk_size and current_len >= self.min_chunk_size:
                # 当前块已满,保存并开始新块
                chunk_text = "\n\n".join(current)
                chunks.append(chunk_text)
                
                # 保留 overlap
                overlap_text = current[-1][-self.overlap:] if self.overlap > 0 else ""
                current = [overlap_text] if overlap_text else []
                current_len = len(overlap_text)
            
            current.append(para)
            current_len += len(para)
        
        # 最后一块
        if current:
            chunks.append("\n\n".join(current))
        
        return [
            TextChunk(index=i, text=text, start_pos=0, end_pos=len(text))
            for i, text in enumerate(chunks)
        ]
    
    def _split_paragraphs(self, text: str) -> List[str]:
        """按段落分割"""
        # 中文文本按换行分割
        paragraphs = [p.strip() for p in text.split("\n") if p.strip()]
        
        # 如果段落过长,进一步按句子分割
        result = []
        for para in paragraphs:
            if len(para) > self.chunk_size:
                sentences = self._split_sentences(para)
                result.extend(sentences)
            else:
                result.append(para)
        
        return result
    
    def _split_sentences(self, text: str) -> List[str]:
        """按句子分割(中英文)"""
        import re
        # 匹配中英文句子结束符
        sentences = re.split(r'(?<=[。!?.!?])\s*', text)
        return [s.strip() for s in sentences if s.strip()]

2.3 向量化服务

python 复制代码
from typing import List
import httpx

class EmbeddingService:
    """向量化服务"""
    
    def __init__(self, api_key: str, model: str = "text-embedding-3-large"):
        self.api_key = api_key
        self.model = model
        self.client = httpx.AsyncClient(timeout=30.0)
        self._cache = {}  # 生产环境用 Redis
    
    async def embed(self, text: str) -> List[float]:
        """单文本向量化"""
        # 缓存检查
        cache_key = hash(text)
        if cache_key in self._cache:
            return self._cache[cache_key]
        
        response = await self.client.post(
            "https://api.openai.com/v1/embeddings",
            json={
                "model": self.model,
                "input": text,
                "dimensions": 1536
            },
            headers={"Authorization": f"Bearer {self.api_key}"}
        )
        response.raise_for_status()
        
        embedding = response.json()["data"][0]["embedding"]
        self._cache[cache_key] = embedding
        return embedding
    
    async def embed_batch(self, texts: List[str], batch_size: int = 64) -> List[List[float]]:
        """批量向量化"""
        results = []
        for i in range(0, len(texts), batch_size):
            batch = texts[i:i + batch_size]
            response = await self.client.post(
                "https://api.openai.com/v1/embeddings",
                json={
                    "model": self.model,
                    "input": batch,
                    "dimensions": 1536
                },
                headers={"Authorization": f"Bearer {self.api_key}"}
            )
            response.raise_for_status()
            
            data = response.json()["data"]
            for item in sorted(data, key=lambda x: x["index"]):
                results.append(item["embedding"])
        
        return results

三、差距分析引擎

3.1 核心逻辑

差距分析是 RAG 内容优化引擎的核心:

python 复制代码
from dataclasses import dataclass
from typing import List, Optional

@dataclass
class GapAnalysisResult:
    """差距分析结果"""
    keyword: str
    platform: str
    ai_answer: str                    # AI 搜索的回答
    covered_topics: List[str]        # 你的内容已覆盖的主题
    missing_topics: List[str]        # AI 回答中提到但你未覆盖的主题
    coverage_score: float            # 覆盖率 0-1
    relevant_content: List[dict]     # 相关内容块
    suggestions: List[str]           # 优化建议

class GapAnalysisEngine:
    """差距分析引擎"""
    
    def __init__(self, vector_store, embedder, llm_client):
        self.vector_store = vector_store
        self.embedder = embedder
        self.llm = llm_client
    
    async def analyze(
        self,
        keyword: str,
        platform: str,
        ai_answer: str,
        top_k: int = 5
    ) -> GapAnalysisResult:
        """分析内容差距"""
        
        # 1. 将 AI 回答向量化
        answer_embedding = await self.embedder.embed(ai_answer)
        
        # 2. 在内容库中检索相关内容
        search_results = await self.vector_store.search(
            collection="content_vectors",
            query_vector=answer_embedding,
            top_k=top_k,
            filter_expr=f'tenant_id == "{tenant_id}"'
        )
        
        # 3. 提取 AI 回答中的主题
        ai_topics = await self._extract_topics(ai_answer)
        
        # 4. 提取已有内容覆盖的主题
        covered_topics = await self._extract_topics_from_results(search_results)
        
        # 5. 计算差距
        missing_topics = [t for t in ai_topics if t not in covered_topics]
        coverage_score = 1 - (len(missing_topics) / max(len(ai_topics), 1))
        
        # 6. 生成优化建议
        suggestions = await self._generate_suggestions(
            keyword, ai_answer, covered_topics, missing_topics
        )
        
        return GapAnalysisResult(
            keyword=keyword,
            platform=platform,
            ai_answer=ai_answer,
            covered_topics=covered_topics,
            missing_topics=missing_topics,
            coverage_score=coverage_score,
            relevant_content=[
                {"url": r["url"], "title": r["title"], "score": r["score"]}
                for r in search_results
            ],
            suggestions=suggestions
        )
    
    async def _extract_topics(self, text: str) -> List[str]:
        """从文本中提取主题(使用 LLM)"""
        prompt = f"""分析以下文本,提取其中的核心主题(每个主题用短语表示)。
只返回主题列表,每行一个,不要额外解释。

文本:
{text}

主题:"""
        
        response = await self.llm.chat(
            messages=[{"role": "user", "content": prompt}],
            temperature=0.3
        )
        
        topics = [t.strip() for t in response.split("\n") if t.strip()]
        return topics
    
    async def _extract_topics_from_results(self, results: List[dict]) -> List[str]:
        """从检索结果中提取主题"""
        all_topics = []
        for r in results:
            text = r.get("chunk_text", "")
            topics = await self._extract_topics(text)
            all_topics.extend(topics)
        return list(set(all_topics))
    
    async def _generate_suggestions(
        self, keyword, ai_answer, covered, missing
    ) -> List[str]:
        """生成优化建议"""
        prompt = f"""你是 AI GEO 内容优化专家。分析以下差距,给出具体的优化建议。

关键词: {keyword}
AI 搜索回答中涉及的主题: {', '.join(missing)}
已有内容覆盖的主题: {', '.join(covered)}

请给出 3-5 条具体可执行的优化建议,包括:
1. 需要补充的内容主题
2. 内容结构调整建议
3. 提升被 AI 引用概率的具体方法

建议:"""
        
        response = await self.llm.chat(
            messages=[{"role": "user", "content": prompt}],
            temperature=0.7
        )
        
        return [s.strip() for s in response.split("\n") if s.strip()]

3.2 语义相似度计算

python 复制代码
import numpy as np
from typing import List

class SemanticSimilarity:
    """语义相似度计算"""
    
    @staticmethod
    def cosine_similarity(vec_a: List[float], vec_b: List[float]) -> float:
        """余弦相似度"""
        a = np.array(vec_a)
        b = np.array(vec_b)
        dot = np.dot(a, b)
        norm = np.linalg.norm(a) * np.linalg.norm(b)
        return float(dot / norm) if norm > 0 else 0.0
    
    @staticmethod
    def max_similarity(query_vec: List[float], doc_vecs: List[List[float]]) -> float:
        """查询向量与文档向量集的最大相似度"""
        return max(
            SemanticSimilarity.cosine_similarity(query_vec, dv)
            for dv in doc_vecs
        )
    
    @staticmethod
    def average_similarity(query_vec: List[float], doc_vecs: List[List[float]], top_k: int = 5) -> float:
        """Top-K 平均相似度"""
        scores = [
            SemanticSimilarity.cosine_similarity(query_vec, dv)
            for dv in doc_vecs
        ]
        scores.sort(reverse=True)
        return np.mean(scores[:top_k]) if scores else 0.0

四、优化建议生成器

4.1 建议模型

python 复制代码
from enum import Enum

class SuggestionType(Enum):
    ADD_TOPIC = "add_topic"              # 新增主题内容
    ENHANCE_STRUCTURE = "enhance_structure"  # 增强内容结构
    ADD_FAQ = "add_faq"                  # 添加 FAQ
    UPDATE_STATS = "update_stats"        # 更新数据
    ADD_CITATION = "add_citation"        # 增加权威引用
    RESTRUCTURE = "restructure"          # 重新组织内容

@dataclass
class OptimizationSuggestion:
    suggestion_id: str
    keyword: str
    platform: str
    type: SuggestionType
    title: str
    description: str
    priority: int  # 1-5, 5最高
    expected_impact: str
    action_items: List[str]
    created_at: str

4.2 建议生成器

python 复制代码
class SuggestionGenerator:
    """优化建议生成器"""
    
    def __init__(self, llm_client):
        self.llm = llm_client
    
    async def generate(
        self, 
        gap_result: GapAnalysisResult,
        brand_name: str
    ) -> List[OptimizationSuggestion]:
        """基于差距分析生成优化建议"""
        
        suggestions = []
        
        # 1. 缺失主题建议
        for topic in gap_result.missing_topics:
            suggestion = await self._generate_topic_suggestion(
                topic, gap_result, brand_name
            )
            suggestions.append(suggestion)
        
        # 2. 结构优化建议
        if gap_result.coverage_score < 0.5:
            suggestions.append(await self._generate_structure_suggestion(gap_result))
        
        # 3. FAQ 建议
        suggestions.append(await self._generate_faq_suggestion(gap_result, brand_name))
        
        # 按优先级排序
        suggestions.sort(key=lambda x: x.priority, reverse=True)
        
        return suggestions
    
    async def _generate_topic_suggestion(
        self, topic: str, gap: GapAnalysisResult, brand: str
    ) -> OptimizationSuggestion:
        prompt = f"""为品牌「{brand}」生成一条 AI GEO 内容优化建议。

关键词: {gap.keyword}
缺失主题: {topic}
当前覆盖率: {gap.coverage_score:.0%}

请生成:
1. 标题(简短描述优化方向)
2. 详细描述(为什么需要这个内容,如何影响 AI 搜索引用)
3. 3-5 个具体行动项
4. 预期影响

格式:JSON"""
        
        response = await self.llm.chat(
            messages=[{"role": "user", "content": prompt}],
            temperature=0.7,
            response_format={"type": "json_object"}
        )
        
        import json
        data = json.loads(response)
        
        return OptimizationSuggestion(
            suggestion_id=f"sug_{hash(topic) % 100000}",
            keyword=gap.keyword,
            platform=gap.platform,
            type=SuggestionType.ADD_TOPIC,
            title=data["title"],
            description=data["description"],
            priority=4,
            expected_impact=data.get("expected_impact", ""),
            action_items=data.get("action_items", []),
            created_at=datetime.now().isoformat()
        )

五、效果追踪闭环

python 复制代码
class OptimizationTracker:
    """优化效果追踪器"""
    
    def __init__(self, postgres):
        self.postgres = postgres
    
    async def track_optimization(
        self, 
        suggestion_id: str,
        keyword: str,
        platform: str,
        before_score: float
    ):
        """记录优化前的基线"""
        await self.postgres.execute(
            """
            INSERT INTO optimization_tracking 
            (suggestion_id, keyword, platform, before_score, status, created_at)
            VALUES ($1, $2, $3, $4, 'pending', NOW())
            """,
            suggestion_id, keyword, platform, before_score
        )
    
    async def update_after_score(
        self, suggestion_id: str, after_score: float
    ):
        """更新优化后的分数"""
        await self.postgres.execute(
            """
            UPDATE optimization_tracking 
            SET after_score = $1, status = 'completed', updated_at = NOW()
            WHERE suggestion_id = $2
            """,
            after_score, suggestion_id
        )
    
    async def get_optimization_report(self, tenant_id: str) -> dict:
        """获取优化效果报告"""
        rows = await self.postgres.fetch(
            """
            SELECT 
                COUNT(*) as total,
                COUNT(*) FILTER (WHERE after_score > before_score) as improved,
                AVG(after_score - before_score) as avg_improvement
            FROM optimization_tracking ot
            JOIN collection_tasks ct ON ot.keyword = ct.keyword
            WHERE ct.tenant_id = $1
            """,
            tenant_id
        )
        
        if rows:
            r = rows[0]
            return {
                "total_optimizations": r["total"],
                "improved_count": r["improved"],
                "success_rate": r["improved"] / r["total"] if r["total"] > 0 else 0,
                "avg_score_improvement": float(r["avg_improvement"] or 0)
            }
        return {}

六、架构决策记录

ADR-005: 选择语义分块而非固定长度分块

背景:内容入库需要分块,固定长度会割裂语义。

决策:采用语义分块(按段落 + 句子边界),设置 overlap。

后果

  • ✅ 语义完整性更好,检索质量更高
  • ✅ overlap 减少边界信息丢失
  • ❌ 分块大小不均匀,需要额外处理
  • ❌ 处理速度略慢于固定分块

ADR-006: 主题提取使用 LLM 而非规则

背景:差距分析需要从 AI 回答和内容中提取主题。

决策:使用 LLM 提取主题,而非关键词提取算法。

后果

  • ✅ 主题理解更准确,能捕捉隐含主题
  • ✅ 可处理多语言、多领域内容
  • ❌ 成本较高,延迟较高
  • ❌ LLM 输出不稳定,需要后处理

七、总结

RAG 内容优化引擎是 AI GEO 的核心差异化能力:

  1. 内容入库:语义分块 + 向量化 + 多维索引
  2. 差距分析:AI 回答 vs 自有内容的语义对比
  3. 建议生成:LLM 驱动的可执行优化建议
  4. 效果追踪:优化前后评分对比闭环
  5. 语义检索:向量相似度计算覆盖度

本文为 AI GEO 系统架构系列第 4 篇,共 15 篇。

相关推荐
生态学者1 小时前
Ecological Indicators | 机场鸟调的新方法:用 AWM-PC1 读懂干扰梯度下的鸟类群落
人工智能·算法·r语言·微信公众平台
GoCodingInMyWay1 小时前
DeepSeek Harness 开始
ai·agent·deepseek·harness
阿伟玩不懂1 小时前
AI帮你读PDF——自建文档解析与智能问答工具
人工智能·pdf
燐妤1 小时前
梦绘片场 ProjectDream故事总纲场景剧本篇:从项目到成片的 AI 漫剧工作台
人工智能·git·ai·项目开发
AINative软件工程1 小时前
AI Agent 证据仲裁工程实践:别让检索结果按自信程度撒谎
人工智能·后端·ai编程
头发够用的程序员1 小时前
Nsight Systems 零基础入门:Trace采集与基础操作
人工智能·深度学习·神经网络·计算机视觉·边缘计算·分析工具
neocheng_5221 小时前
怎么评判 AI 证书的社会认可度?证书价值要结合实际场景判断
人工智能
搜yyzk6685 小时前
智能电话机器人:1天千通电话,高效低成本
人工智能