通过抓取网站构建聊天机器人知识库:爬取、分块、引用
一个支持机器人只有在输入优质内容时才能表现出色。通过爬取您自己的网站——文档、常见问题解答、帮助中心——来清理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"])]

步骤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构建的网络数据负责收集方面。

常见问题解答
如何从网站构建聊天机器人知识库?
爬取网站以获得干净的markdown,将每个页面拆分为基于标题的块并附上来源URL,将这些块嵌入到向量数据库中,并在查询时检索最相关的块以支持模型的答案。安排重新爬取以保持其最新。这种RAG方法意味着编辑文档可以更新机器人的知识而无需重新训练。
我需要在我的内容上训练一个模型吗?
不需要——对于支持机器人来说,您不应该这样做。微调速度慢、成本高,并且每当您的内容发生变化时就会过时。检索增强生成将知识保存在模型外部的向量存储中,因此机器人始终从当前爬取中回答。只有在内容更改时才重新爬取和重新嵌入,这比重新训练便宜得多。
为什么要爬取到markdown而不是HTML?
原始HTML携带导航、广告、cookie横幅和脚本,这些都会增加噪音并浪费您的嵌入预算。Markdown保留了有意义的结构——标题、列表、表格——同时去掉了展示层,这正是LLM最干净地摄取的内容。更干净的输入意味着更相关的检索和更少的混淆答案。机器人也无法读取图像或视频,因此请将关键信息保留在文本中。
如何保持知识库的更新?
安排定期重新爬取,检测哪些页面发生了变化,并仅重新嵌入这些页面——完整的重新爬取会浪费带宽和计算资源。在每个块上存储一个获取日期,以便您可以判断答案来源的新鲜程度,并对索引进行版本管理,以便在糟糕的爬取降低检索效果时可以回滚。增量刷新使成本与实际变化成比例。
这里的流程就是产品:爬取到markdown,按标题分块,嵌入带来源的内容,检索并引用,然后按计划刷新。这样做,您的支持机器人就能从您的真实内容中回答,链接其来源,并随着您的网站发展保持最新——这是人们信任的机器人与他们学会绕开的机器人之间的区别。要通过标准接口为AI代理提供对相同数据的实时访问,请参阅我们关于QuantumProxies MCP服务器的指南。