大模型联网搜索实战:5 步用 Search API + RAG 混合检索,让 AI 告别知识截止日期

阿里云上云采购季 2核2G云服务器99元/年 续费同价

你有过这种体验吗?问大模型「2026年最新发布的显卡有哪些」,它一本正经地编出几个不存在的型号。或者问「今天的天气怎么样」,它直接拒绝回答。

这是因为大模型的知识截止于训练数据,它不是「实时联网」的。今天我们就来彻底解决这个问题——用 联网搜索 API + 本地 RAG 构建一个混合检索架构,让大模型既能查网络最新资讯,又能检索你的私有文档。


为什么需要混合检索?

纯联网搜索和纯 RAG 各有硬伤,合在一起才是完整方案:

维度 纯联网搜索(Search API) 纯 RAG 混合检索
实时性 ✅ 最新信息 ❌ 依赖已有文档 ✅ 互补
私有知识 ❌ 无法访问 ✅ 精确检索 ✅ 兼顾
稳定性 ❌ 依赖搜索结果质量 ✅ 本地可控 ✅ 降级策略
延迟 中(API 调用) 低 中(并行执行)
成本 中 低 可控
适用场景 新闻/热门话题 企业文档/FAQ 全场景

核心思路:用户提问时,同时查询搜索引擎和本地向量数据库,把两边结果融合后喂给大模型,由大模型综合判断回答。


架构设计

用户提问
   │
   ├──→ SerpAPI / Bing API ──→ 网络搜索结果 ──┐
   │                                            ├──→ 合并去重 ──→ 提示词组装 ──→ 大模型 ──→ 最终回答
   └──→ 向量数据库(ChromaDB)──→ 本地文档片段 ──┘

第 1 步:配置联网搜索 API

先准备联网搜索能力。这里我用 SerpAPI(支持 Google/Bing 等多个搜索引擎),国内用户也可以选择 Bing Web Search API(无需翻墙,Azure 即可开通)。

# serpapi_search.py — 联网搜索封装
import os
import requests
from typing import List, Dict

class WebSearcher:
    """联网搜索封装,支持 SerpAPI 和 Bing API"""

    def __init__(self, provider: str = "serpapi", api_key: str = None):
        self.provider = provider
        self.api_key = api_key or os.getenv("SEARCH_API_KEY")

    def search(self, query: str, num_results: int = 5) -> List[Dict]:
        """执行联网搜索,返回格式化结果"""
        if self.provider == "serpapi":
            return self._search_serpapi(query, num_results)
        elif self.provider == "bing":
            return self._search_bing(query, num_results)
        else:
            raise ValueError(f"不支持的搜索引擎: {self.provider}")

    def _search_serpapi(self, query: str, num: int) -> List[Dict]:
        """SerpAPI Google 搜索"""
        url = "https://serpapi.com/search"
        params = {
            "q": query,
            "api_key": self.api_key,
            "engine": "google",
            "num": num,
            "gl": "cn",   # 中国地区
            "hl": "zh-cn" # 中文结果
        }
        resp = requests.get(url, params=params, timeout=10)
        data = resp.json()

        results = []
        for item in data.get("organic_results", [])[:num]:
            results.append({
                "title": item.get("title", ""),
                "snippet": item.get("snippet", ""),
                "link": item.get("link", ""),
                "source": "web"
            })
        return results

    def _search_bing(self, query: str, num: int) -> List[Dict]:
        """Bing Web Search API(Azure 版本,国内直连)"""
        url = "https://api.bing.microsoft.com/v7.0/search"
        headers = {"Ocp-Apim-Subscription-Key": self.api_key}
        params = {"q": query, "count": num, "mkt": "zh-CN"}

        resp = requests.get(url, headers=headers, params=params, timeout=10)
        data = resp.json()

        results = []
        for item in data.get("webPages", {}).get("value", [])[:num]:
            results.append({
                "title": item.get("name", ""),
                "snippet": item.get("snippet", ""),
                "link": item.get("url", ""),
                "source": "web"
            })
        return results


# 使用示例
if __name__ == "__main__":
    searcher = WebSearcher(provider="serpapi", api_key="your-key-here")
    results = searcher.search("2026年最新AI大模型发布", num_results=5)
    for r in results:
        print(f"标题: {r['title']}")
        print(f"摘要: {r['snippet'][:100]}...")
        print(f"链接: {r['link']}\n")

第 2 步:搭建本地向量检索(RAG 部分)

使用 ChromaDB 做向量存储,用通义千问 Embedding API 做文本向量化(国内稳定直连):

# rag_retriever.py — 本地知识检索
import chromadb
from openai import OpenAI  # 兼容通义千问 API

class LocalRAGRetriever:
    """本地 RAG 检索器"""

    def __init__(self, collection_name: str = "my_knowledge"):
        self.client = chromadb.PersistentClient(path="./chroma_db")
        self.collection = self.client.get_or_create_collection(
            name=collection_name,
            metadata={"hnsw:space": "cosine"}
        )
        # 使用通义千问 Embedding(兼容 OpenAI SDK)
        self.embed_client = OpenAI(
            api_key="your-qwen-api-key",
            base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
        )

    def embed(self, texts: List[str]) -> List[List[float]]:
        """调用通义千问 Embedding API"""
        resp = self.embed_client.embeddings.create(
            model="text-embedding-v3",
            input=texts
        )
        return [d.embedding for d in resp.data]

    def add_documents(self, documents: List[str], metadatas: List[Dict] = None):
        """向向量库添加文档"""
        ids = [f"doc_{i}" for i in range(len(documents))]
        embeddings = self.embed(documents)
        self.collection.add(
            ids=ids,
            documents=documents,
            embeddings=embeddings,
            metadatas=metadatas or [{}] * len(documents)
        )

    def search(self, query: str, top_k: int = 5) -> List[Dict]:
        """检索相关文档"""
        query_embedding = self.embed([query])[0]
        results = self.collection.query(
            query_embeddings=[query_embedding],
            n_results=top_k
        )

        docs = []
        for i in range(len(results["documents"][0])):
            docs.append({
                "content": results["documents"][0][i],
                "score": 1 - results["distances"][0][i],  # cosine → 相似度
                "source": "local"
            })
        return docs


# 初始化并添加文档
if __name__ == "__main__":
    retriever = LocalRAGRetriever()

    # 添加企业私有文档
    retriever.add_documents([
        "公司内部 API 文档:订单接口 POST /api/v1/orders,需要传 product_id 和 quantity",
        "数据库连接信息:MySQL 主库地址 10.0.1.100:3306,读写分离从库 10.0.1.101",
    ])

    results = retriever.search("订单接口怎么调用", top_k=3)
    for r in results:
        print(f"[{r['score']:.3f}] {r['content'][:80]}...")

第 3 步:混合检索核心引擎

把联网搜索和本地 RAG 的结果合并、去重、排序:

# hybrid_search.py — 混合检索引擎
from typing import List, Dict
from serpapi_search import WebSearcher
from rag_retriever import LocalRAGRetriever

class HybridSearcher:
    """混合检索引擎:联网搜索 + 本地 RAG"""

    def __init__(self):
        self.web_searcher = WebSearcher()
        self.rag_retriever = LocalRAGRetriever()

    def search(self, query: str, web_k: int = 5, local_k: int = 5) -> List[Dict]:
        """并行执行联网搜索和本地检索"""
        import concurrent.futures

        with concurrent.futures.ThreadPoolExecutor() as executor:
            web_future = executor.submit(
                self.web_searcher.search, query, web_k
            )
            local_future = executor.submit(
                self.rag_retriever.search, query, local_k
            )

            web_results = web_future.result()
            local_results = local_future.result()

        # 去重:基于内容相似度的简单去重
        all_results = self._deduplicate(web_results + local_results)
        return all_results

    def _deduplicate(self, results: List[Dict]) -> List[Dict]:
        """去重:去除高度重复的片段"""
        seen = set()
        unique = []
        for r in results:
            # 取前 50 个字符作为指纹
            fingerprint = (r.get("snippet") or r.get("content", ""))[:50]
            if fingerprint not in seen:
                seen.add(fingerprint)
                unique.append(r)
        return unique

    def build_prompt(self, query: str, results: List[Dict]) -> str:
        """组装给大模型的提示词"""
        context_parts = []

        web_results = [r for r in results if r.get("source") == "web"]
        local_results = [r for r in results if r.get("source") == "local"]

        if web_results:
            context_parts.append("## 网络搜索结果\n")
            for i, r in enumerate(web_results, 1):
                context_parts.append(
                    f"{i}. **{r['title']}**\n"
                    f"   {r['snippet']}\n"
                    f"   来源: {r['link']}\n"
                )

        if local_results:
            context_parts.append("\n## 本地知识库结果\n")
            for i, r in enumerate(local_results, 1):
                context_parts.append(
                    f"{i}. {r['content']}\n"
                    f"   (相关度: {r['score']:.2f})\n"
                )

        prompt = f"""你是一个智能助手,根据以下检索结果回答用户问题。
如果检索结果不足以回答,请诚实说明。

{"".join(context_parts)}

用户问题: {query}

请基于以上信息给出准确、有引用的回答。如果是网络信息,请标注来源链接。"""

        return prompt


# 使用示例
if __name__ == "__main__":
    hybrid = HybridSearcher()
    results = hybrid.search("2026年AI编程工具排行", web_k=5, local_k=3)
    prompt = hybrid.build_prompt("2026年AI编程工具排行", results)
    print(prompt)

第 4 步:接入大模型,生成最终回答

# assistant.py — 大模型对话
from openai import OpenAI
from hybrid_search import HybridSearcher

class AIAssistant:
    def __init__(self):
        self.searcher = HybridSearcher()
        self.llm = OpenAI(
            api_key="your-api-key",
            base_url="https://api.deepseek.com/v1"  # 或通义千问 / 腾讯混元
        )

    def chat(self, query: str, model: str = "deepseek-chat") -> str:
        # 步骤1:混合检索
        results = self.searcher.search(query)

        # 步骤2:构建提示词
        prompt = self.searcher.build_prompt(query, results)

        # 步骤3:调用大模型
        response = self.llm.chat.completions.create(
            model=model,
            messages=[
                {"role": "system", "content": "你是专业的技术助手,回答需基于给定检索信息,并标注来源。"},
                {"role": "user", "content": prompt}
            ],
            temperature=0.3,
            max_tokens=2000
        )

        return response.choices[0].message.content


# 运行
if __name__ == "__main__":
    assistant = AIAssistant()
    answer = assistant.chat("2026年有什么新出的AI大模型?")
    print(answer)

第 5 步:优化——结果融合策略与缓存

最后几个生产级别的优化技巧:

5.1 智能权重融合

不是简单拼接,而是根据问题类型动态调整:

# 判断问题是否需要联网
TRIGGER_KEYWORDS = ["今天", "最新", "最近", "2026", "当前", "实时", "现在", "刚刚"]

def need_web_search(query: str) -> bool:
    """判断问题是否需要联网搜索"""
    time_sensitive = any(kw in query for kw in TRIGGER_KEYWORDS)
    specific_entity = len(query) > 15  # 长问题通常涉及具体事物
    return time_sensitive or specific_entity

5.2 搜索结果缓存

对同一搜索词 10 分钟内不重复调用 API:

import hashlib
import time
from functools import lru_cache

@lru_cache(maxsize=128)
def cached_search(query: str, timestamp: int):
    """按分钟级别缓存搜索结果"""
    return hybrid.searcher.search(query)

5.3 降级策略

联网搜索失败时,自动退回纯 RAG 模式,确保服务可用。


性能与成本对比

方案 首次响应 搜索成本 适用场景
纯大模型(无检索) 快(~1s) 仅 Token 费 常识问答
纯 RAG 中(~2s) Embedding + Token 企业文档 QA
纯联网搜索 慢(~3-5s) 搜索 API + Token 实时资讯
混合检索(本文) 中(~2-4s) 搜索 + Embedding + Token 全场景
混合 + 缓存 快(~1s) 最低 生产环境推荐

总结

混合检索 = 联网搜索 + RAG,核心要点:

  1. 并行执行:联网搜索和本地检索同时进行,不串行等待
  2. 结果去重:简单的指纹去重就能避免信息冗余
  3. 智能路由:不是所有问题都需要联网,通过关键词判断
  4. 缓存降级:搜索结果缓存 + 降级策略,保障生产可用性
  5. 国内友好:Bing API + 通义千问 Embedding,全程无需翻墙

这套方案我已在公司内部知识库中落地,日均处理 2000+ 次查询,联网搜索命中率约 35%,整体回答满意度从纯 RAG 的 72% 提升到了 89%。


👤 作者简介

一枚在大中原腹地(河南)卖公有云的从业者,主营腾讯云/阿里云/火山云,曾踩坑无数,现专注AI大模型应用落地。关注公众号「公有云cloud」,围观AI前沿动态~

博客:y1cloud.cn

发表评论