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

AI搜索从入门到落地:常见问题、架构思路与代码示例

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

AI搜索 常见问题汇总|附源码

本文围绕“AI搜索(AI Search)”的核心概念、常见问题、技术架构、落地实践与源码示例进行系统整理,适合正在做智能问答、企业知识库、RAG应用、AI客服、文档检索增强系统的开发者阅读。


一、什么是AI搜索?

AI搜索,通常指结合大语言模型(LLM)向量检索(Vector Search)语义理解重排序(Rerank)知识增强生成(RAG)等技术的新一代搜索系统。

传统搜索通常依赖关键词匹配,例如用户搜索“如何申请报销”,系统可能只查找包含“申请”“报销”这些关键词的文档。而AI搜索更关注用户真实意图,即使文档中写的是“费用 reimbursement 流程”或“差旅费用提交规范”,系统也能理解其与“报销申请”高度相关。

简单来说,AI搜索解决的是:

  • 用户问得不标准,也能搜到;
  • 文档写法不统一,也能理解;
  • 搜索结果不只是链接,还能直接生成答案;
  • 多篇文档内容可以被整合成一个自然语言回答;
  • 回答时可以附带来源,方便用户追溯依据。

二、AI搜索和传统搜索有什么区别?

对比项 传统搜索 AI搜索
核心方式 关键词匹配 语义理解 + 向量检索 + 生成式回答
用户输入 依赖准确关键词 支持自然语言提问
结果形式 文档列表、网页链接 直接答案 + 引用来源
召回能力 对同义词、改写较弱 对相似语义理解更强
适用场景 商品搜索、网页搜索、日志搜索 企业知识库、智能客服、文档问答、研究助手
主要技术 倒排索引、BM25 Embedding、RAG、LLM、Rerank

不过,AI搜索并不是要完全取代传统搜索。实际工程中,效果最好的方案往往是混合搜索(Hybrid Search):同时使用关键词检索和向量检索,再通过重排序模型融合结果。


三、AI搜索常见架构是什么?

一个典型AI搜索系统通常包含以下模块:

用户问题
  ↓
问题改写 / 意图识别
  ↓
关键词检索 + 向量检索
  ↓
结果合并与去重
  ↓
Rerank重排序
  ↓
上下文拼接
  ↓
大模型生成答案
  ↓
返回答案 + 引用来源

更完整的企业级AI搜索还可能包含:

  1. 数据接入层
    支持PDF、Word、Excel、网页、数据库、接口、Notion、飞书文档、Confluence等数据源。

  2. 文档解析层
    将不同格式的内容解析为统一文本,并保留标题、段落、页码、表格、图片OCR等信息。

  3. 文本切分层
    将长文档切分为适合向量化和大模型处理的文本块。

  4. 向量化层
    使用Embedding模型将文本转换为向量。

  5. 索引存储层
    存储向量、原文、元数据、权限信息。

  6. 检索层
    根据用户问题召回相关文本块。

  7. 生成层
    将检索结果作为上下文,让大模型基于资料生成答案。

  8. 权限与安全层
    确保用户只能搜索和查看自己有权限的数据。


四、AI搜索一定要用RAG吗?

大多数企业知识库类AI搜索都需要RAG。

RAG是Retrieval-Augmented Generation,即“检索增强生成”。它的核心思想是:
不要让大模型凭记忆回答,而是先检索相关资料,再基于资料回答。

为什么需要RAG?

  • 大模型训练数据不是实时的;
  • 大模型不知道企业内部私有知识;
  • 直接让模型回答容易产生幻觉;
  • 企业回答需要可追溯来源;
  • 文档经常更新,需要动态生效。

例如用户问:

公司差旅住宿标准是多少?

如果直接问通用大模型,它可能会编造一个标准。但如果通过RAG,系统会先从企业制度库中检索“差旅管理办法”“住宿费标准表”等内容,再让模型基于这些资料回答,并附带来源。


五、AI搜索为什么会回答错误?

AI搜索回答错误通常来自以下几个环节:

1. 文档本身质量差

如果原始文档内容过旧、表述混乱、格式复杂、数据冲突,那么AI搜索很难给出准确答案。

例如:

  • 同一制度有多个版本;
  • 文档没有标题层级;
  • 扫描PDF OCR识别错误;
  • 表格数据被解析成乱序文本;
  • 新旧政策同时存在但未标记有效期。

2. 切分策略不合理

如果文本块切得太小,关键信息可能被拆散;如果切得太大,检索不精准,还会浪费上下文窗口。

例如一条制度包含:

员工国内出差住宿标准如下:
一线城市:600元/晚
二线城市:450元/晚
三线城市:350元/晚

如果切分时只保留了“一线城市:600元/晚”,而丢失了上文“住宿标准”,模型可能难以理解上下文。

3. 向量模型不适合

Embedding模型直接影响语义检索效果。中文场景下,如果使用对中文支持较弱的模型,召回质量会明显下降。

4. 检索召回不足

如果相关文档没有被召回,后面的LLM再强也无法正确回答。

5. Prompt设计不严谨

如果提示词没有明确要求“只能基于上下文回答”,模型可能会使用自身知识补全,从而产生幻觉。

6. 缺少Rerank

向量检索召回的结果不一定排序最优。很多时候,最相关的文本块排在第5、第8甚至更后面。如果直接取Top 3,可能漏掉关键内容。


六、AI搜索如何降低幻觉?

可以从以下几个方面优化:

1. 强约束Prompt

提示词中明确要求:

  • 只能基于给定上下文回答;
  • 如果上下文不足,必须回答“不知道”或“资料中未提及”;
  • 不允许编造制度、金额、日期、联系人;
  • 回答必须引用来源。

示例:

你是企业知识库助手。请严格基于【参考资料】回答用户问题。
如果参考资料中没有答案,请回答“根据现有资料无法确定”。
不要编造不存在的政策、金额、日期或流程。
回答后请列出引用来源。

2. 使用引用来源

让模型在答案中标明:

  • 文档名称;
  • 章节标题;
  • 页码;
  • 原文片段;
  • 更新时间。

这既能增强可信度,也方便用户人工核验。

3. 增加拒答机制

对于资料不足、权限不足、问题超范围的情况,系统应明确拒答,而不是强行生成。

4. 检索结果打分过滤

如果最高相关度分数太低,可以不进入生成环节,直接提示“未找到相关资料”。

5. 引入Rerank模型

通过Rerank模型对候选文本重新排序,提升最终上下文质量。


七、AI搜索的核心技术有哪些?

1. Embedding向量化

Embedding是将文本转换为数字向量的过程。语义相近的文本在向量空间中距离更近。

例如:

  • “如何申请报销”
  • “费用报销流程是什么”
  • “差旅费用怎么提交”

这三句话关键词不同,但语义接近,对应向量也会更接近。

2. 向量数据库

向量数据库用于存储和检索Embedding向量。常见方案包括:

  • FAISS
  • Milvus
  • Qdrant
  • Weaviate
  • Chroma
  • Elasticsearch Vector Search
  • PostgreSQL + pgvector

小型项目可以从FAISS或Chroma开始,企业生产环境可以考虑Milvus、Qdrant或Elasticsearch。

3. BM25关键词检索

BM25是传统搜索中的经典算法。它对精确关键词、编号、专有名词、产品型号等特别有效。

例如:

  • “合同编号 HT-2024-001”
  • “iPhone 15 Pro”
  • “GB/T 35273”
  • “API 500错误”

这些场景中,关键词检索往往比纯向量检索更可靠。

4. Hybrid Search混合检索

混合检索结合BM25和向量检索,兼顾关键词准确性和语义召回能力。

常见融合方式:

  • 分数加权;
  • Reciprocal Rank Fusion;
  • 先分别召回,再合并去重;
  • 使用Rerank统一排序。

5. Rerank重排序

Rerank模型会对“问题-文档片段”进行更精细的相关性判断。相比Embedding检索,Rerank通常更准,但计算成本更高,因此一般用于对Top 20或Top 50候选结果进行二次排序。

6. LLM生成答案

最后,大语言模型根据检索到的上下文生成自然语言回答。优秀的生成阶段应做到:

  • 答案简洁准确;
  • 不超出资料范围;
  • 结构清晰;
  • 支持引用;
  • 必要时给出操作步骤;
  • 对不确定信息明确说明。

八、文档应该如何切分?

文档切分是AI搜索效果的关键环节之一。

常见切分方式

  1. 固定长度切分
    按字符数或Token数切分,例如每块500字,重叠100字。

  2. 按标题层级切分
    按一级标题、二级标题、三级标题切分,更适合制度文档、说明书。

  3. 按语义切分
    根据段落语义和主题变化自动切分。

  4. 表格特殊处理
    表格不能简单按行乱切,需要保留表头、单位、字段含义。

推荐策略

对于中文企业知识库,可以采用:

  • 每个Chunk控制在300~800中文字符;
  • 相邻Chunk保留50~150字符重叠;
  • 保留文档标题、章节标题作为元数据;
  • 表格转成结构化文本;
  • 对特别长的制度文档按章节切分;
  • 对FAQ类内容保持“问题+答案”完整。

九、AI搜索是否需要联网?

不一定。

AI搜索可以分为两类:

1. 私有知识库搜索

数据来自企业内部文档,不需要联网。系统只在内部知识库中检索并回答。

适合:

  • 企业制度问答;
  • 产品手册问答;
  • 内部运维知识库;
  • 法务合同条款检索;
  • 医疗、金融、政务私有资料问答。

2. 联网搜索增强

当需要实时信息时,可以接入搜索引擎或网页爬虫。

适合:

  • 新闻查询;
  • 股票行情;
  • 学术动态;
  • 竞品分析;
  • 招投标信息;
  • 市场报告。

需要注意的是,联网搜索必须考虑信息可信度、来源过滤、内容时效性与合规风险。


十、AI搜索如何做权限控制?

企业级AI搜索必须处理权限问题。否则,用户可能通过自然语言问答获取本不该看到的内容。

常见权限控制方式:

1. 索引时写入权限元数据

每个文档或文本块写入:

{
  "doc_id": "doc_001",
  "title": "薪酬管理制度",
  "department": "HR",
  "acl": ["user_001", "user_002", "role_hr"]
}

2. 检索时过滤权限

用户发起查询时,系统先获取用户身份和角色,只召回其有权限访问的文本块。

3. 生成前再次校验

即使检索层过滤过,生成前也应检查上下文是否都属于用户可访问范围。

4. 答案中避免泄露无权限来源

不要让模型看到无权限内容,否则即使最后不展示来源,也可能在答案中泄露信息。


十一、AI搜索性能如何优化?

1. 缓存常见问题

对于高频问题,可以缓存最终答案或检索结果。

2. 异步处理文档入库

文档解析、切分、向量化通常较耗时,应使用队列异步处理。

3. 控制Top K

召回过多会增加Rerank和LLM成本。一般可以:

  • 向量召回Top 20;
  • BM25召回Top 20;
  • 合并后Rerank Top 10;
  • 最终取Top 3~5进入LLM。

4. 使用流式输出

答案生成可能需要数秒,流式输出能显著改善用户体验。

5. 分层检索

先检索文档级别,再检索章节级别,最后定位段落,可提升大规模知识库下的性能和准确率。


十二、AI搜索源码示例

下面给出一个简化版AI搜索系统源码,使用:

  • FastAPI 提供接口;
  • sentence-transformers 生成Embedding;
  • FAISS 做向量检索;
  • 本地内存保存文本块;
  • 示例中LLM部分用函数模拟,实际可替换为OpenAI、通义千问、DeepSeek、Claude、GLM等模型API。

1. 安装依赖

pip install fastapi uvicorn sentence-transformers faiss-cpu numpy

2. 项目结构

ai-search-demo/
├── main.py
├── requirements.txt
└── README.md

3. requirements.txt

fastapi
uvicorn
sentence-transformers
faiss-cpu
numpy

4. main.py

from fastapi import FastAPI
from pydantic import BaseModel
from sentence_transformers import SentenceTransformer
import faiss
import numpy as np
from typing import List, Dict, Any

app = FastAPI(title="AI Search Demo")

# 1. 加载Embedding模型
# 中文场景可以替换为更适合中文的模型,例如 bge-small-zh-v1.5
model = SentenceTransformer("sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")

# 2. 模拟知识库文档
documents = [
    {
        "id": "doc_001",
        "title": "差旅管理制度",
        "content": "员工国内出差住宿标准如下:一线城市每晚不超过600元,二线城市每晚不超过450元,三线城市每晚不超过350元。报销时需提交发票、行程单和审批单。"
    },
    {
        "id": "doc_002",
        "title": "费用报销流程",
        "content": "员工完成费用支出后,应在30天内通过财务系统提交报销申请。报销材料包括正规发票、付款凭证、业务说明及直属上级审批记录。"
    },
    {
        "id": "doc_003",
        "title": "请假管理办法",
        "content": "员工申请年假需提前3个工作日在OA系统提交申请。病假需提供医院证明。连续请假超过5天的,应由部门负责人审批。"
    },
    {
        "id": "doc_004",
        "title": "信息安全规范",
        "content": "员工不得将公司内部资料上传至未经批准的外部平台。涉及客户数据、合同、财务信息的文件,应按照公司数据分级制度进行保护。"
    }
]

# 3. 简单切分函数
def split_text(text: str, chunk_size: int = 120, overlap: int = 30) -> List[str]:
    chunks = []
    start = 0

    while start < len(text):
        end = start + chunk_size
        chunk = text[start:end]
        chunks.append(chunk)

        if end >= len(text):
            break

        start = end - overlap

    return chunks


# 4. 构建文本块
chunks: List[Dict[str, Any]] = []

for doc in documents:
    text_chunks = split_text(doc["content"])

    for index, chunk in enumerate(text_chunks):
        chunks.append({
            "chunk_id": f"{doc['id']}_chunk_{index}",
            "doc_id": doc["id"],
            "title": doc["title"],
            "content": chunk
        })


# 5. 构建向量索引
texts = [item["content"] for item in chunks]
embeddings = model.encode(texts, normalize_embeddings=True)
embeddings = np.array(embeddings).astype("float32")

dimension = embeddings.shape[1]
index = faiss.IndexFlatIP(dimension)
index.add(embeddings)


class SearchRequest(BaseModel):
    query: str
    top_k: int = 3


class SearchResult(BaseModel):
    title: str
    content: str
    score: float


def retrieve(query: str, top_k: int = 3) -> List[Dict[str, Any]]:
    """
    根据用户问题进行向量检索
    """
    query_embedding = model.encode([query], normalize_embeddings=True)
    query_embedding = np.array(query_embedding).astype("float32")

    scores, indices = index.search(query_embedding, top_k)

    results = []
    for score, idx in zip(scores[0], indices[0]):
        if idx == -1:
            continue

        item = chunks[idx]
        results.append({
            "title": item["title"],
            "content": item["content"],
            "score": float(score),
            "doc_id": item["doc_id"],
            "chunk_id": item["chunk_id"]
        })

    return results


def build_prompt(query: str, contexts: List[Dict[str, Any]]) -> str:
    """
    构造给大模型的Prompt
    """
    context_text = ""

    for i, item in enumerate(contexts, start=1):
        context_text += f"\n[资料{i}]\n"
        context_text += f"标题:{item['title']}\n"
        context_text += f"内容:{item['content']}\n"

    prompt = f"""
你是企业知识库问答助手。请严格基于【参考资料】回答用户问题。

要求:
1. 如果参考资料中没有答案,请回答“根据现有资料无法确定”;
2. 不要编造不存在的制度、金额、日期或流程;
3. 回答要简洁、准确、有条理;
4. 最后列出引用来源标题。

【用户问题】
{query}

【参考资料】
{context_text}

【回答】
"""
    return prompt


def fake_llm_generate(prompt: str, query: str, contexts: List[Dict[str, Any]]) -> str:
    """
    示例函数:模拟大模型生成。
    生产环境中应替换为真实LLM API调用。
    """
    if not contexts:
        return "根据现有资料无法确定。"

    combined_context = "\n".join([item["content"] for item in contexts])
    source_titles = list(dict.fromkeys([item["title"] for item in contexts]))

    # 非常简化的规则模拟,仅用于演示
    if "住宿" in query or "差旅" in query:
        if "一线城市" in combined_context:
            return (
                "根据资料,员工国内出差住宿标准为:"
                "一线城市每晚不超过600元,二线城市每晚不超过450元,"
                "三线城市每晚不超过350元。报销时需提交发票、行程单和审批单。\n\n"
                f"引用来源:{', '.join(source_titles)}"
            )

    if "报销" in query:
        return (
            "员工完成费用支出后,应在30天内通过财务系统提交报销申请。"
            "报销材料包括正规发票、付款凭证、业务说明及直属上级审批记录。\n\n"
            f"引用来源:{', '.join(source_titles)}"
        )

    if "请假" in query or "年假" in query:
        return (
            "员工申请年假需提前3个工作日在OA系统提交申请。"
            "病假需提供医院证明。连续请假超过5天的,应由部门负责人审批。\n\n"
            f"引用来源:{', '.join(source_titles)}"
        )

    return (
        "根据检索到的资料,暂未找到足够明确的信息回答该问题。"
        f"\n\n引用来源:{', '.join(source_titles)}"
    )


@app.post("/search")
def search(req: SearchRequest):
    """
    只返回检索结果
    """
    results = retrieve(req.query, req.top_k)
    return {
        "query": req.query,
        "results": results
    }


@app.post("/ask")
def ask(req: SearchRequest):
    """
    检索 + 生成答案
    """
    contexts = retrieve(req.query, req.top_k)

    # 简单相关度阈值过滤
    contexts = [item for item in contexts if item["score"] >= 0.3]

    prompt = build_prompt(req.query, contexts)

    # 实际项目中,将prompt发送给大模型
    answer = fake_llm_generate(prompt, req.query, contexts)

    return {
        "query": req.query,
        "answer": answer,
        "contexts": contexts
    }


@app.get("/")
def root():
    return {
        "message": "AI Search Demo is running.",
        "endpoints": ["/search", "/ask"]
    }

5. 启动服务

uvicorn main:app --reload --port 8000

6. 测试搜索接口

curl -X POST "http://127.0.0.1:8000/search" \
  -H "Content-Type: application/json" \
  -d '{"query":"出差住酒店可以报销多少钱?","top_k":3}'

7. 测试问答接口

curl -X POST "http://127.0.0.1:8000/ask" \
  -H "Content-Type: application/json" \
  -d '{"query":"一线城市出差住宿标准是多少?","top_k":3}'

可能返回:

{
  "query": "一线城市出差住宿标准是多少?",
  "answer": "根据资料,员工国内出差住宿标准为:一线城市每晚不超过600元,二线城市每晚不超过450元,三线城市每晚不超过350元。报销时需提交发票、行程单和审批单。\n\n引用来源:差旅管理制度",
  "contexts": [
    {
      "title": "差旅管理制度",
      "content": "员工国内出差住宿标准如下:一线城市每晚不超过600元,二线城市每晚不超过450元,三线城市每晚不超过350元。报销时需提交发票、行程单和审批单。",
      "score": 0.82,
      "doc_id": "doc_001",
      "chunk_id": "doc_001_chunk_0"
    }
  ]
}

十三、如何接入真实大模型?

上面的示例中,fake_llm_generate只是模拟函数。在真实项目中,可以替换为大模型API。

伪代码如下:

def call_llm(prompt: str) -> str:
    response = client.chat.completions.create(
        model="your-model-name",
        messages=[
            {"role": "system", "content": "你是严谨的企业知识库助手。"},
            {"role": "user", "content": prompt}
        ],
        temperature=0.2
    )

    return response.choices[0].message.content

然后在/ask接口中替换:

answer = call_llm(prompt)

建议参数:

  • temperature设置为0~0.3,降低随机性;
  • 开启流式输出,提升体验;
  • 对答案做敏感词和权限校验;
  • 对超长上下文做截断或摘要;
  • 记录问题、召回文档、模型输出,便于评估和调优。

十四、AI搜索上线前需要做哪些评估?

AI搜索不是“接上大模型就能上线”,上线前建议至少做以下评估。

1. 召回率评估

准备一批标准问题,人工标注每个问题应该命中的文档,检查系统是否能召回。

指标包括:

  • Recall@3;
  • Recall@5;
  • Recall@10;
  • MRR;
  • NDCG。

2. 答案准确率评估

人工判断模型答案是否正确、完整、是否有幻觉。

可以分为:

  • 完全正确;
  • 部分正确;
  • 答非所问;
  • 编造内容;
  • 无法回答但强行回答。

3. 引用准确性评估

检查答案引用的资料是否真的支持答案内容。

4. 权限测试

使用不同角色账号测试:

  • 普通员工能否看到薪酬信息;
  • 部门员工能否看到其他部门文档;
  • 离职员工账号是否失效;
  • 外部用户是否能搜索内部资料。

5. 压力测试

评估并发能力、响应时间和模型调用成本。


十五、AI搜索常见问题汇总

Q1:AI搜索适合哪些业务场景?

适合企业知识库问答、客服机器人、法律文档检索、合同审查、产品手册问答、售前方案助手、运维故障排查、研发文档搜索、财务制度问答、HR政策咨询等场景。

Q2:数据量很少,还需要向量数据库吗?

如果数据量很小,例如只有几十篇文档,可以先使用内存向量索引或FAISS。等数据增长、需要权限管理、高可用和持久化时,再迁移到专业向量数据库。

Q3:只用大模型,不做检索可以吗?

不建议。大模型不知道企业私有数据,也无法保证信息实时准确。对于企业知识库场景,RAG几乎是必需的。

Q4:为什么搜出来的内容看起来相关,但答案还是不对?

可能是检索到了相似但不包含答案的内容,也可能是多个文本块之间存在冲突。需要优化切分、增加Rerank、提高上下文质量,并让模型严格基于资料回答。

Q5:Embedding模型怎么选?

中文场景建议选择对中文语义支持较好的模型,例如BGE中文系列、多语言Embedding模型或企业自训练模型。选择时要结合自己的测试集评估,不要只看公开榜单。

Q6:Top K设置多少合适?

没有固定答案。一般可以先向量召回Top 10~20,再Rerank取Top 3~5进入大模型。Top K太小容易漏召回,太大则增加噪声和成本。

Q7:AI搜索如何处理表格?

表格要尽量保留结构,例如把表头和每一行拼成完整描述。不要只抽取单元格文本,否则模型可能不知道数字对应的字段含义。

Q8:AI搜索可以支持图片和扫描件吗?

可以,但需要OCR或多模态模型。扫描PDF需要先进行文字识别,再进入文档解析和向量化流程。

Q9:AI搜索如何保证答案可追溯?

需要在文档入库时保存元数据,例如文档ID、标题、章节、页码、URL、更新时间。生成答案时要求模型附带引用来源。

Q10:AI搜索成本高吗?

成本主要来自Embedding、Rerank和LLM调用。优化方式包括缓存、批量向量化、本地部署Embedding模型、控制上下文长度、使用更小模型处理简单问题等。


十六、总结

AI搜索的本质不是简单地“给搜索框接一个大模型”,而是构建一套从数据接入、文档解析、文本切分、向量化、检索召回、重排序、上下文构造到答案生成的完整系统。

要做好AI搜索,关键在于:

  • 数据质量要高;
  • 切分策略要合理;
  • 检索召回要充分;
  • Rerank要提升排序质量;
  • Prompt要严格约束;
  • 答案要有引用来源;
  • 权限控制必须前置;
  • 上线前必须评估。

如果只是做Demo,几十行代码就能实现一个基础AI搜索;但如果要做企业级可用系统,则需要在准确性、稳定性、安全性、成本和可观测性上持续优化。

AI搜索不是一次性项目,而是一个持续迭代的工程体系。只有把“搜索”和“生成”两个环节都做好,才能真正让用户获得可信、准确、可追溯的智能问答体验。

目录结构
全文