通过抓取网站构建聊天机器人知识库:爬取、分块、引用

一个支持机器人只有在输入优质内容时才能表现出色。通过爬取您自己的网站——文档、常见问题解答、帮助中心——来清理markdown并将其分块以便检索。这是保持答案有依据和引用的流程。

一个支持聊天机器人只有在输入优质内容时才能表现出色,而最好的输入通常是您自己的网站——您已经维护的文档、常见问题解答和帮助中心文章。问题在于机器人无法像人一样阅读网站;它需要干净、分块、可检索的文本,并在每个部分附上来源。这个指南是完整的流程:爬取网站到markdown,分块,将其嵌入向量存储,并在查询时检索引用的上下文——一个随着网站变化而保持准确的知识库。

为什么选择RAG而不是微调

您可以在您的内容上微调一个模型,但对于支持机器人来说,这不是合适的工具:重新训练速度慢、成本高,并且一旦您编辑文档就会过时。检索增强生成(RAG)将知识保留在模型之外——将您的内容分块、嵌入,并在提问时检索最相关的部分并将其作为上下文提供给模型。更新文档,重新爬取,机器人的知识也随之更新。需求是真实存在的:根据KPMG的研究,聊天机器人市场预计到2030年将以23.3%的年复合增长率增长,69%的人已经使用聊天机器人,斯坦福和麻省理工学院对5,179名支持代理的研究显示,生成式AI辅助平均提高了14%的生产力——对于最新的代理则提高了35%。

步骤1:爬取网站到干净的markdown

将原始HTML输入到RAG流程中会因导航、cookie横幅和脚本标签而污染它。相反,直接爬取到markdown——它保留了标题、列表和表格,同时去掉了展示噪音,这正是LLM最适合摄取的内容。具有爬取模式的Scraper API可以遍历网站并将每个页面返回为干净的markdown:

import requests

resp = requests.post(
    "https://api.quantumproxies.io/crawl",
    json={"url": "https://docs.example.com", "limit": 300, "format": "markdown"},
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    timeout=120,
)
pages = resp.json()["pages"]  # each: {"url": ..., "markdown": ...}

爬取您自己的网站很简单;爬取受地理或速率限制的文档时,选择正确的IP进行路由很重要,以便爬取完成而不是中途停滞。以markdown为先的原则与为LLM提供新鲜网络数据的原则相同。

步骤2:按标题分块,保留来源

不要嵌入整页——检索在聚焦的块上效果最好。将每个页面按其标题拆分,以便每个块都是一个连贯的主题,并在每个块上附上来源URL,以便您以后可以引用。这也是一个好的来源文档发挥作用的地方:具有清晰标题的文章,每个标题一个想法,并在文本中重述问题的块远比一大段文字(机器人无法推断出人类会理解的上下文)要好:

def chunk_markdown(md, source_url):
    chunks, cur = [], {"heading": "", "text": ""}
    for line in md.splitlines():
        if line.startswith("#"):
            if cur["text"].strip():
                chunks.append({**cur, "source": source_url})
            cur = {"heading": line.lstrip("# ").strip(), "text": ""}
        else:
            cur["text"] += line + "\n"
    if cur["text"].strip():
        chunks.append({**cur, "source": source_url})
    return chunks

all_chunks = [c for p in pages for c in chunk_markdown(p["markdown"], p["url"])]
RAG流程将网站转变为支持机器人:爬取到markdown,按标题分块,嵌入到向量数据库,检索并引用
在查询时检索引用的块,而不是将整个网站塞入提示中——有依据且可检查。

步骤3:嵌入、存储并通过引用检索

将每个块嵌入到一个向量中,并将其与来源URL作为元数据插入到向量数据库中。在查询时,嵌入用户的问题,提取最匹配的前几个,并将其文本作为上下文传递给模型——并显示来源链接,以便答案可检查,而不是一个黑箱:

# index each chunk with its source as metadata
for c in all_chunks:
    vec = embed(c["heading"] + "\n" + c["text"])
    index.upsert(id=uid(c), values=vec,
                 metadata={"source": c["source"], "text": c["text"]})

# at query time: retrieve, ground, and cite
hits = index.query(embed(user_question), top_k=4)
context = "\n\n".join(h.metadata["text"] for h in hits)
citations = list({h.metadata["source"] for h in hits})
answer = llm(f"Answer using only this context:\n{context}", question=user_question)

将模型固定在检索到的上下文中——并告诉它仅从该上下文中回答——这就是防止支持机器人自信地编造政策的原因。引用有双重作用:它们让用户可以验证,并让您发现机器人何时在寻找错误的文档。有关更深入的机制,我们关于RAG流程不陈旧的指南进一步探讨了分块和检索。

步骤4:刷新,否则它会腐烂

知识库是一个活的东西。文档被重写,价格变化,新文章出现——而一个从上个月的爬取中回答的机器人会自信地给出错误答案。安排重新爬取,比较每个页面与其上一个版本,并仅重新嵌入更改的部分,以便刷新保持低成本。在每个块上保留一个获取日期,以便您始终知道答案来源的最新程度。如果您宁愿消费一个托管的、始终新鲜的供稿,而不是自己运行爬取和刷新,为LLM构建的网络数据负责收集方面。

知识库聊天机器人的统计数据:23.3%市场年复合增长率,69%的人使用聊天机器人,支持生产力提升14-35%
支持机器人的转变正在进行中;一个有依据、有引用的知识库是使其值得信赖的原因。

爬取任何网站到干净的markdown供您的机器人使用

常见问题解答

如何从网站构建聊天机器人知识库?

爬取网站以获得干净的markdown,将每个页面拆分为基于标题的块并附上来源URL,将这些块嵌入到向量数据库中,并在查询时检索最相关的块以支持模型的答案。安排重新爬取以保持其最新。这种RAG方法意味着编辑文档可以更新机器人的知识而无需重新训练。

我需要在我的内容上训练一个模型吗?

不需要——对于支持机器人来说,您不应该这样做。微调速度慢、成本高,并且每当您的内容发生变化时就会过时。检索增强生成将知识保存在模型外部的向量存储中,因此机器人始终从当前爬取中回答。只有在内容更改时才重新爬取和重新嵌入,这比重新训练便宜得多。

为什么要爬取到markdown而不是HTML?

原始HTML携带导航、广告、cookie横幅和脚本,这些都会增加噪音并浪费您的嵌入预算。Markdown保留了有意义的结构——标题、列表、表格——同时去掉了展示层,这正是LLM最干净地摄取的内容。更干净的输入意味着更相关的检索和更少的混淆答案。机器人也无法读取图像或视频,因此请将关键信息保留在文本中。

如何保持知识库的更新?

安排定期重新爬取,检测哪些页面发生了变化,并仅重新嵌入这些页面——完整的重新爬取会浪费带宽和计算资源。在每个块上存储一个获取日期,以便您可以判断答案来源的新鲜程度,并对索引进行版本管理,以便在糟糕的爬取降低检索效果时可以回滚。增量刷新使成本与实际变化成比例。

这里的流程就是产品:爬取到markdown,按标题分块,嵌入带来源的内容,检索并引用,然后按计划刷新。这样做,您的支持机器人就能从您的真实内容中回答,链接其来源,并随着您的网站发展保持最新——这是人们信任的机器人与他们学会绕开的机器人之间的区别。要通过标准接口为AI代理提供对相同数据的实时访问,请参阅我们关于QuantumProxies MCP服务器的指南。

为您的机器人提供始终新鲜的网络数据