上一篇 下一篇 分享链接 返回 返回顶部

AI搜索落地那些坑:从检索、RAG到源码实战避坑指南

发布人:慈云数据-客服中心 发布时间:21小时前 阅读量:4

AI搜索 使用避坑指南|附源码

在大模型应用快速普及的今天,“AI搜索”已经从一个新鲜概念变成了企业知识库、个人效率工具、客服系统、内容平台和开发者工具中的常见能力。
但很多人在真正落地 AI 搜索时会发现:看起来很简单,做起来坑很多
本文将围绕 AI 搜索的核心原理、常见误区、选型建议、工程实践和可运行源码示例,系统梳理一份实用避坑指南。


一、什么是 AI 搜索?

传统搜索通常依赖关键词匹配,例如用户输入“如何申请退款”,搜索系统会在文档中寻找包含“申请”“退款”等关键词的内容,然后按照相关性排序返回结果。

而 AI 搜索通常结合了以下能力:

  1. 语义理解

    • 不只看关键词是否一致,而是理解用户问题的含义。
    • 例如“钱能不能退回来”和“如何申请退款”在语义上是相近的。
  2. 向量检索

    • 将文本转换为向量,通过向量相似度找到语义接近的内容。
    • 常见技术包括 Embedding、向量数据库、近似最近邻检索等。
  3. 大模型生成

    • 检索到相关资料后,再由大语言模型组织答案。
    • 这类模式通常被称为 RAG,即 Retrieval-Augmented Generation,检索增强生成。
  4. 多轮上下文理解

    • AI 搜索不只是一次性搜索,还可以结合上下文继续追问。
    • 例如用户先问“这个产品怎么收费”,再问“那企业版呢”,系统需要知道“企业版”指的是前面提到的产品。

简单来说,AI 搜索并不是“把搜索框接上大模型”这么简单,而是一个由数据处理、向量化、检索、重排、生成、评估和监控组成的系统工程。


二、AI搜索的典型架构

一个常见的 AI 搜索系统大致包含以下模块:

用户问题
   ↓
问题改写 / 意图识别
   ↓
向量检索 / 关键词检索 / 混合检索
   ↓
结果重排
   ↓
上下文拼接
   ↓
大模型生成答案
   ↓
引用来源 / 置信度提示 / 结果返回

更完整一些,可以拆成:

模块 作用
文档采集 收集网页、PDF、Word、Markdown、数据库内容等
文档清洗 去除噪声、广告、重复内容、无效字符
文档切分 将长文档切成适合检索的小片段
Embedding 将文本转成向量
向量库 存储向量并支持相似度检索
检索策略 向量检索、关键词检索、混合检索
重排模型 对初步检索结果重新排序,提高准确率
Prompt模板 控制大模型回答方式
答案生成 根据检索内容生成自然语言答案
来源引用 标明答案依据,降低幻觉风险
评估监控 跟踪召回率、准确率、延迟、成本等

很多 AI 搜索项目失败,并不是大模型不够强,而是这些基础环节没有做好。


三、避坑一:不要把 AI 搜索等同于大模型问答

这是最常见的误区。

有些团队会直接把用户问题发送给大模型,然后让模型回答。短期看起来效果不错,但一旦问题涉及企业内部知识、最新资料、产品规则、合同条款,就容易出现以下问题:

  • 模型不知道你的私有数据;
  • 模型可能编造答案;
  • 回答没有来源依据;
  • 内容更新后模型无法同步;
  • 对专业领域知识理解不稳定。

例如你问模型:

“我们公司 2024 年企业版套餐支持几个子账号?”

如果这个信息只存在于公司内部文档中,通用大模型不可能天然知道。即使它回答了,也很可能是猜的。

正确方式是使用 RAG:

  1. 先从你的知识库中检索相关文档;
  2. 再把检索结果作为上下文交给大模型;
  3. 要求模型只能基于上下文回答;
  4. 如果上下文没有答案,就明确说不知道。

Prompt 示例:

你是一个严谨的企业知识库助手。
请只根据下面提供的资料回答用户问题。
如果资料中没有答案,请回答“根据已有资料无法确定”,不要编造。

资料:
{{context}}

用户问题:
{{question}}

这样可以显著降低幻觉风险。


四、避坑二:文档切分不是越小越好

很多人在做向量检索时,会把文档随意切成固定长度,比如每 500 个字符一段。这样虽然简单,但经常导致语义被切断。

例如原文:

企业版套餐支持最多 100 个子账号。
如需更多子账号,可以联系客户经理开通增购服务。

如果切分不合理,可能变成:

片段1:企业版套餐支持最多
片段2:100 个子账号。如需更多子账号,可以联系客户经理开通增购服务。

用户问“企业版支持几个子账号”,检索可能只命中片段1,导致无法回答。

更合理的做法是:

  • 按标题、段落、章节切分;
  • 保留一定重叠内容;
  • 每个 chunk 保持完整语义;
  • 记录文档标题、章节路径、来源链接等元数据;
  • 对 FAQ、表格、规则类内容单独处理。

推荐切分策略:

文档类型 推荐策略
FAQ 一个问答对作为一个 chunk
Markdown 按标题层级切分
PDF 先做版面解析,再按段落切分
表格 转换成结构化文本
产品文档 按功能模块和小节切分
合同条款 按条款编号切分

不要只关注 chunk size,更要关注语义完整性


五、避坑三:只用向量检索,容易漏掉精确信息

向量检索擅长语义相似,但对一些精确匹配场景并不总是可靠。例如:

  • 订单号:OD20240518001
  • 错误码:E10037
  • 产品型号:X-Pro-Max-2024
  • API 字段名:user_id
  • 法律条款编号:第十二条
  • 特定人名、地名、编号

如果用户搜索“E10037 是什么错误”,向量检索可能不如关键词检索稳定。

因此,实际项目中更推荐使用混合检索

最终结果 = 向量检索结果 + 关键词检索结果 + 规则召回结果

常见做法:

  1. 使用向量检索召回语义相关内容;
  2. 使用 BM25 或全文索引召回关键词相关内容;
  3. 合并去重;
  4. 使用重排模型重新排序;
  5. 取 Top N 作为大模型上下文。

这样既能处理自然语言问题,也能处理编号、术语、字段名等精确搜索。


六、避坑四:Embedding模型不要盲目选择

Embedding 模型决定了文本向量的质量。很多人随便选一个模型就上线,结果搜索质量很差。

选择 Embedding 模型时,要关注:

指标 说明
中文能力 中文语义理解是否优秀
领域适配 是否适合金融、医疗、法律、代码等场景
向量维度 维度越高不一定越好,但影响存储和计算成本
最大输入长度 长文本是否会被截断
成本 调用费用或部署成本
延迟 是否满足实时搜索要求
私有化能力 企业数据是否允许外发

如果是中文知识库,建议优先选择中文表现较好的 Embedding 模型,而不是默认使用英文语料效果更强的模型。

另外,要注意一个常见问题:

Embedding 模型一旦更换,历史向量通常需要重新生成。

因为不同模型生成的向量空间不一致,不能混用。否则检索结果会非常混乱。


七、避坑五:Top K 不是越大越好

很多开发者为了“尽量多召回”,会把检索数量设置得很大,比如 Top 50、Top 100,然后全部塞给大模型。

这样会带来几个问题:

  1. 上下文太长,成本增加
  2. 无关内容干扰模型判断
  3. 答案变得啰嗦甚至错误
  4. 延迟明显上升
  5. 超过上下文窗口限制

更合理的做法是:

  • 初召回可以稍大,例如 Top 20;
  • 重排后取 Top 3~8;
  • 根据问题复杂度动态调整;
  • 对不同文档片段设置权重;
  • 优先保留权威来源和更新时间较新的内容。

示例策略:

向量检索 Top 20
关键词检索 Top 20
合并去重
重排 Top 8
拼接上下文
生成答案

不要让大模型在大量噪声中“自己找重点”。检索系统应该先尽量把高质量上下文准备好。


八、避坑六:Prompt必须限制回答边界

AI 搜索最怕“看似合理但实际错误”的答案。为了降低风险,Prompt 必须明确约束模型行为。

一个较好的 Prompt 应包含:

  • 角色说明;
  • 回答依据;
  • 不知道时的处理方式;
  • 引用来源要求;
  • 输出格式;
  • 禁止编造;
  • 语言风格。

示例:

你是一个专业、严谨的知识库问答助手。

请遵守以下规则:
1. 只能根据【参考资料】回答问题。
2. 如果参考资料中没有相关信息,请回答:“根据已有资料无法确定。”
3. 不要编造政策、价格、数字、日期、链接。
4. 如果答案来自多个资料,请综合后回答。
5. 回答后列出引用来源。

【参考资料】
{{context}}

【用户问题】
{{question}}

请用中文回答,格式如下:
答案:
引用来源:

这个 Prompt 不能彻底消灭幻觉,但可以显著减少不受控回答。


九、避坑七:不要忽视来源引用

AI 搜索不是普通聊天机器人。用户更关心答案是否可信。

如果系统只返回一句话:

企业版支持 100 个子账号。

用户可能会继续问:

依据在哪里?是哪份文档写的?什么时候更新的?

所以,AI 搜索最好提供引用信息:

答案:
企业版套餐默认支持最多 100 个子账号。如需更多子账号,可联系客户经理开通增购服务。

引用来源:
1. 《企业版套餐说明》/ 账号权限 / 子账号数量
2. 更新时间:2024-05-18

引用来源有几个好处:

  • 增强用户信任;
  • 方便用户核对;
  • 便于排查错误;
  • 降低法律和合规风险;
  • 有助于内部知识治理。

工程实现时,每个 chunk 都应保留 metadata,例如:

{
  "doc_id": "pricing_enterprise_2024",
  "title": "企业版套餐说明",
  "section": "账号权限",
  "url": "https://example.com/docs/pricing-enterprise",
  "updated_at": "2024-05-18"
}

十、避坑八:知识库更新机制必须提前设计

AI 搜索不是一次性导入文档就结束。企业文档会不断变化:

  • 产品价格更新;
  • 政策规则变动;
  • 接口字段调整;
  • 旧文档废弃;
  • 新文档发布;
  • 权限范围变化。

如果没有更新机制,AI 搜索很快就会变成“过期答案生成器”。

建议设计以下机制:

  1. 增量更新

    • 只处理发生变化的文档,而不是每次全量重建。
  2. 版本管理

    • 保留文档版本号,避免新旧内容混乱。
  3. 删除同步

    • 原文档删除后,向量库中的对应 chunk 也要删除。
  4. 定时重建

    • 对重要知识库定期重新清洗和向量化。
  5. 状态标识

    • 标记文档是有效、废弃、草稿还是归档。
  6. 更新时间权重

    • 检索排序时优先较新的有效文档。

特别提醒:
删除和废弃文档的同步经常被忽略。
如果旧政策仍留在向量库中,AI 搜索就可能给出过期甚至违规答案。


十一、避坑九:权限控制不能只放在前端

企业 AI 搜索经常涉及权限问题:

  • 普通员工不能查看财务报表;
  • 销售只能查看自己区域客户资料;
  • 外部用户不能访问内部文档;
  • 不同部门知识库权限不同。

很多系统只在前端隐藏入口,但后端检索仍然可能召回无权限文档。这是非常危险的。

正确做法:

  1. 每个文档和 chunk 都带权限 metadata;
  2. 检索时根据用户身份过滤;
  3. 生成答案时只传入用户有权限的上下文;
  4. 日志中避免记录敏感原文;
  5. 对高敏内容进行脱敏;
  6. 对权限变更实时生效。

示例 metadata:

{
  "doc_id": "finance_report_2024_q1",
  "title": "2024年Q1财务报告",
  "permission": ["finance", "executive"],
  "confidential_level": "high"
}

检索过滤逻辑:

def has_permission(user_roles, doc_permissions):
    return bool(set(user_roles) & set(doc_permissions))

不要让大模型“事后过滤”。
权限控制应该发生在检索阶段之前或检索阶段中


十二、避坑十:必须建立评估体系

很多 AI 搜索项目上线前只做人工体验:

“我问了几个问题,感觉还不错。”

这远远不够。AI 搜索需要长期评估。

推荐关注以下指标:

指标 含义
Recall@K 正确文档是否被召回
MRR 正确结果排名是否靠前
Answer Accuracy 答案是否正确
Faithfulness 答案是否忠于资料
Citation Accuracy 引用是否准确
Latency 响应延迟
Cost 单次查询成本
No Answer Rate 无法回答比例
User Feedback 用户点赞、点踩、追问情况

你可以准备一批标准测试集:

[
  {
    "question": "企业版支持几个子账号?",
    "expected_doc_id": "pricing_enterprise_2024",
    "expected_answer": "默认最多支持100个子账号"
  },
  {
    "question": "E10037代表什么错误?",
    "expected_doc_id": "error_code_manual",
    "expected_answer": "认证令牌已过期"
  }
]

每次调整切分策略、Embedding 模型、Top K、Prompt、重排模型,都应该用测试集回归验证,而不是凭感觉上线。


十三、AI搜索最小可用源码示例

下面给出一个简化版 AI 搜索示例,使用 Python 实现。
为了方便理解,这里不依赖复杂向量数据库,而是使用 sentence-transformers 生成向量,使用 numpy 计算余弦相似度。

说明:此示例适合学习和本地实验。生产环境建议使用 Milvus、Qdrant、Weaviate、Elasticsearch、OpenSearch、pgvector 等专业检索组件。


1. 安装依赖

pip install sentence-transformers numpy

2. 示例源码

import numpy as np
from sentence_transformers import SentenceTransformer


class SimpleAISearch:
    def __init__(self, model_name="shibing624/text2vec-base-chinese"):
        """
        初始化 AI 搜索引擎
        :param model_name: 中文 Embedding 模型
        """
        self.model = SentenceTransformer(model_name)
        self.documents = []
        self.embeddings = None

    def add_documents(self, docs):
        """
        添加文档
        docs 格式:
        [
            {
                "id": "doc_001",
                "title": "企业版套餐说明",
                "content": "企业版套餐默认支持最多100个子账号...",
                "metadata": {
                    "source": "产品文档",
                    "updated_at": "2024-05-18"
                }
            }
        ]
        """
        self.documents.extend(docs)
        texts = [doc["content"] for doc in self.documents]
        self.embeddings = self.model.encode(texts, normalize_embeddings=True)

    def search(self, query, top_k=3):
        """
        语义搜索
        :param query: 用户问题
        :param top_k: 返回数量
        """
        if self.embeddings is None or len(self.documents) == 0:
            return []

        query_embedding = self.model.encode([query], normalize_embeddings=True)[0]

        scores = np.dot(self.embeddings, query_embedding)

        top_indices = np.argsort(scores)[::-1][:top_k]

        results = []
        for index in top_indices:
            doc = self.documents[index]
            results.append({
                "id": doc["id"],
                "title": doc["title"],
                "content": doc["content"],
                "metadata": doc.get("metadata", {}),
                "score": float(scores[index])
            })

        return results


def build_context(search_results):
    """
    将检索结果拼接为大模型上下文
    """
    context_parts = []
    for i, item in enumerate(search_results, start=1):
        context_parts.append(
            f"资料{i}:\n"
            f"标题:{item['title']}\n"
            f"内容:{item['content']}\n"
            f"来源:{item['metadata'].get('source', '未知')}\n"
            f"更新时间:{item['metadata'].get('updated_at', '未知')}\n"
        )
    return "\n".join(context_parts)


def build_prompt(question, context):
    """
    构造 RAG Prompt
    """
    return f"""
你是一个专业、严谨的知识库问答助手。

请遵守以下规则:
1. 只能根据【参考资料】回答问题。
2. 如果参考资料中没有答案,请回答“根据已有资料无法确定”。
3. 不要编造数字、政策、价格、日期或链接。
4. 回答后请列出引用来源。

【参考资料】
{context}

【用户问题】
{question}

请按以下格式回答:
答案:
引用来源:
"""


if __name__ == "__main__":
    docs = [
        {
            "id": "pricing_enterprise_2024",
            "title": "企业版套餐说明",
            "content": "企业版套餐默认支持最多100个子账号。如需更多子账号,可以联系客户经理开通增购服务。",
            "metadata": {
                "source": "产品文档中心",
                "updated_at": "2024-05-18"
            }
        },
        {
            "id": "refund_policy",
            "title": "退款规则说明",
            "content": "用户购买标准版后,如未使用核心服务,可在7天内提交退款申请。企业定制服务不支持无理由退款。",
            "metadata": {
                "source": "售后政策文档",
                "updated_at": "2024-04-10"
            }
        },
        {
            "id": "error_code_manual",
            "title": "错误码说明",
            "content": "E10037 表示认证令牌已过期。用户需要重新登录或刷新 Token 后再次请求接口。",
            "metadata": {
                "source": "开发者文档",
                "updated_at": "2024-03-22"
            }
        }
    ]

    search_engine = SimpleAISearch()
    search_engine.add_documents(docs)

    question = "企业版最多可以创建多少个子账号?"

    results = search_engine.search(question, top_k=2)

    print("检索结果:")
    for r in results:
        print(r["title"], r["score"])

    context = build_context(results)
    prompt = build_prompt(question, context)

    print("\n生成给大模型的 Prompt:")
    print(prompt)

3. 运行结果示例

运行后可能得到类似结果:

检索结果:
企业版套餐说明 0.78
退款规则说明 0.42

生成给大模型的 Prompt:

你是一个专业、严谨的知识库问答助手。

请遵守以下规则:
1. 只能根据【参考资料】回答问题。
...

在真实项目中,你还需要把生成的 Prompt 发送给大模型 API,例如 OpenAI、通义千问、智谱、Claude、DeepSeek 或本地部署模型。


十四、增加关键词检索的混合搜索示例

上面的示例只有向量检索,对错误码、订单号、字段名等精确信息可能不够稳定。下面给出一个简单的混合检索版本。


混合检索源码

import re
import numpy as np
from sentence_transformers import SentenceTransformer


class HybridAISearch:
    def __init__(self, model_name="shibing624/text2vec-base-chinese"):
        self.model = SentenceTransformer(model_name)
        self.documents = []
        self.embeddings = None

    def add_documents(self, docs):
        self.documents.extend(docs)
        texts = [doc["content"] for doc in self.documents]
        self.embeddings = self.model.encode(texts, normalize_embeddings=True)

    def keyword_score(self, query, text):
        """
        简单关键词评分。
        生产环境建议使用 Elasticsearch / OpenSearch / BM25。
        """
        tokens = re.findall(r"[A-Za-z0-9_\-]+|[\u4e00-\u9fa5]", query)
        if not tokens:
            return 0.0

        score = 0
        for token in tokens:
            if token in text:
                score += 1

        return score / len(tokens)

    def search(self, query, top_k=3, vector_weight=0.7, keyword_weight=0.3):
        if self.embeddings is None or len(self.documents) == 0:
            return []

        query_embedding = self.model.encode([query], normalize_embeddings=True)[0]
        vector_scores = np.dot(self.embeddings, query_embedding)

        final_scores = []

        for i, doc in enumerate(self.documents):
            text = doc["title"] + "\n" + doc["content"]
            k_score = self.keyword_score(query, text)

            final_score = (
                vector_weight * float(vector_scores[i])
                + keyword_weight * float(k_score)
            )

            final_scores.append(final_score)

        top_indices = np.argsort(final_scores)[::-1][:top_k]

        results = []
        for index in top_indices:
            doc = self.documents[index]
            results.append({
                "id": doc["id"],
                "title": doc["title"],
                "content": doc["content"],
                "metadata": doc.get("metadata", {}),
                "score": float(final_scores[index]),
                "vector_score": float(vector_scores[index]),
                "keyword_score": float(
                    self.keyword_score(query, doc["title"] + "\n" + doc["content"])
                )
            })

        return results


if __name__ == "__main__":
    docs = [
        {
            "id": "error_code_manual",
            "title": "错误码说明",
            "content": "E10037 表示认证令牌已过期。用户需要重新登录或刷新 Token 后再次请求接口。",
            "metadata": {"source": "开发者文档"}
        },
        {
            "id": "account_login",
            "title": "登录问题排查",
            "content": "如果用户无法登录,可以检查账号密码是否正确、验证码是否过期、网络是否正常。",
            "metadata": {"source": "帮助中心"}
        },
        {
            "id": "api_token",
            "title": "Token 使用说明",
            "content": "Access Token 用于接口认证。请勿将 Token 暴露在客户端代码中。",
            "metadata": {"source": "API文档"}
        }
    ]

    engine = HybridAISearch()
    engine.add_documents(docs)

    question = "E10037 是什么错误?"
    results = engine.search(question, top_k=3)

    for item in results:
        print(item)

这个混合检索示例虽然简单,但能说明一个关键思想:

AI 搜索不应该只依赖单一召回方式。
向量检索解决语义相似,关键词检索解决精确匹配,重排模型解决排序质量。


十五、生产环境落地建议

如果你要将 AI 搜索用于生产环境,可以参考以下实践清单。

1. 数据层

  • 建立统一文档入口;
  • 保留原文链接和版本;
  • 做好清洗、去重和格式转换;
  • 对敏感信息脱敏;
  • 对文档设置权限字段;
  • 建立增量更新机制。

2. 检索层

  • 使用混合检索;
  • 对 Query 做改写和扩展;
  • 针对编号、错误码、订单号做精确召回;
  • 引入 Reranker;
  • 控制 Top K;
  • 根据权限过滤结果;
  • 对过期文档降权或剔除。

3. 生成层

  • Prompt 明确限制回答边界;
  • 要求基于上下文回答;
  • 没有依据时明确说不知道;
  • 输出引用来源;
  • 对高风险领域增加审核;
  • 避免把大量噪声上下文交给模型。

4. 评估层

  • 建立标准问答集;
  • 评估召回率和答案准确率;
  • 记录用户反馈;
  • 定期人工抽检;
  • 对失败问题分类归因;
  • 调整后必须回归测试。

5. 运维层

  • 监控响应延迟;
  • 监控模型调用成本;
  • 记录检索命中文档;
  • 记录无答案问题;
  • 设置超时和降级策略;
  • 防止敏感信息进入日志。

十六、常见问题 FAQ

Q1:AI搜索一定要用向量数据库吗?

不一定。
如果数据量很小,例如只有几百篇文档,可以先用本地向量矩阵或关系型数据库加向量扩展。
如果数据量达到几十万、几百万 chunk,则建议使用专业向量数据库或支持向量检索的搜索引擎。

Q2:RAG 能完全解决大模型幻觉吗?

不能。
RAG 可以降低幻觉,但不能彻底消除。你还需要:

  • 高质量检索;
  • 严格 Prompt;
  • 来源引用;
  • 答案校验;
  • 人工审核;
  • 用户反馈机制。

Q3:为什么检索到了正确文档,模型还是答错?

可能原因包括:

  • 正确文档排名太靠后;
  • 上下文太长,模型忽略关键内容;
  • Prompt 约束不够;
  • 文档中存在冲突信息;
  • 模型能力不足;
  • 问题需要多步推理。

解决方式包括重排、缩短上下文、优化 Prompt、清理冲突文档、提升模型能力等。

Q4:AI搜索适合哪些场景?

适合:

  • 企业知识库;
  • 客服问答;
  • 产品文档助手;
  • API 文档搜索;
  • 法务合规查询;
  • 内部制度查询;
  • 售前售后支持;
  • 研发知识管理。

不太适合直接用于:

  • 无依据的开放式预测;
  • 高风险医疗诊断;
  • 无人工审核的法律结论;
  • 数据极度混乱且无人维护的知识库。

十七、总结

AI 搜索的核心不是“接入一个大模型”,而是让系统能够准确找到资料、可靠组织答案、清楚标明依据、持续更新知识

真正可用的 AI 搜索,需要同时做好:

  • 文档清洗;
  • 合理切分;
  • Embedding 选型;
  • 混合检索;
  • 重排优化;
  • Prompt 约束;
  • 来源引用;
  • 权限控制;
  • 增量更新;
  • 效果评估;
  • 成本和延迟监控。

如果只是简单地“把问题丢给大模型”,短期可能看起来很智能,但长期一定会遇到幻觉、过期、权限、成本和稳定性问题。

一句话总结:

AI 搜索不是模型能力的单点竞争,而是数据工程、检索工程和生成工程的系统协同。

把这些坑提前避开,你的 AI 搜索系统才更有可能从“能演示”走向“能上线、能复用、能持续迭代”。

目录结构
全文