1. 知识库到底在解决什么问题
1.1 从“资料囤积”到“AI 可检索”的鸿沟
我见过太多人做智能体项目时卡在同一个地方:模型接好了,提示词也调了,工具调用也配了,但一问稍微具体点的问题,智能体就开始胡说八道。原因很简单——它不知道你的业务细节。你手头有一堆产品文档、客服话术、内部规范、历史工单,但这些资料散落在飞书文档、Notion 页面、本地 Word、PDF 附件、甚至微信收藏里。智能体看不到它们,自然答不准。
知识库要解决的核心问题就一句话:把非结构化的、散落各处的资料,变成智能体在推理时能够实时检索到的结构化知识片段。注意这里的关键词是“实时检索”,不是“训练进模型”。很多人第一反应是“我把资料喂给模型微调不就行了”,这条路不是不能走,但成本高、更新慢、容易灾难性遗忘,而且你每次改一版产品手册都得重新训一遍,完全不现实。
RAG(Retrieval-Augmented Generation,检索增强生成)是目前最主流的解法。它的逻辑很朴素:用户提问 → 系统去知识库里找最相关的几段内容 → 把这几段内容和问题一起塞给模型 → 模型基于这些内容生成回答。整个过程模型不需要“记住”你的资料,它只需要“读懂”当前检索到的片段。这样做的好处是资料更新即时生效,你改完文档重新索引一下就行,模型侧完全不用动。
1.2 哪些场景必须上知识库
不是所有智能体都需要知识库。如果你做的是一个通用闲聊机器人,或者一个纯靠提示词就能搞定的简单任务(比如“把这段话翻译成英文”),那确实没必要。但以下场景,没有知识库基本做不出可用产品:
- 企业客服智能体:用户问“你们企业版和个人版的发票政策有什么区别”,模型不可能凭空知道,必须从产品文档和财务规范里检索。
- 内部运维助手:工程师问“上次那个数据库连接池爆满是怎么处理的”,需要从历史工单和运维手册里找答案。
- 法律/专利辅助工具:需要从法条库、案例库、专利文档中检索相关条款和先例。
- 个人知识管理:你读了三百篇文章,想让 AI 帮你回忆“那篇讲 MCP 协议和传统 API 区别的文章里提到了什么”,没有知识库就只能靠记忆。
- 销售支持智能体:销售问“客户说竞品价格比我们低 20%,我该怎么回应”,需要从竞品分析文档和成功案例库里检索话术。
这些场景的共同点是:答案存在于特定资料中,且资料会更新,模型本身不具备这些知识。知识库就是连接“死资料”和“活智能体”的那座桥。
1.3 知识库不是“向量数据库”的同义词
很多人一提到知识库就想到向量数据库,觉得搭个 Milvus 或者 Chroma 就完事了。这是一个典型的认知偏差。向量数据库只是知识库的一个存储组件,完整的知识库流水线至少包含五个环节:
- 数据接入:从各种来源(本地文件、在线文档、数据库、API)把原始资料拉进来。
- 数据清洗与预处理:去掉页眉页脚、乱码、重复内容,把 PDF 里的表格还原成可读文本。
- 分块与向量化:把长文档切成合适大小的片段,用嵌入模型转成向量。
- 索引与存储:把向量和原始文本一起存进数据库,建立快速检索结构。
- 检索与重排:用户提问时,先粗筛出候选片段,再用重排模型精排,把最相关的几条送给大模型。
这五个环节里,分块策略和检索重排是最容易翻车的地方,后面会详细展开。向量数据库只是第 4 步的一个工具,把它当成知识库的全部,就像把冰箱当成整个厨房。
2. 核心组件选型与架构设计
2.1 嵌入模型:知识库的“翻译官”
嵌入模型的作用是把文本转成向量,让语义相近的内容在向量空间里距离更近。选嵌入模型时,很多人只看排行榜,但实际落地要考虑三个维度:
第一,语言支持。如果你的资料以中文为主,必须选中文语义理解好的模型。有些英文榜单上排名很高的模型,中文表现可能一塌糊涂。我实测下来,BGE 系列(如 bge-large-zh-v1.5)和 M3E 系列在中文场景下表现稳定,而且可以本地部署,不依赖外部 API。
第二,维度与性能的平衡。向量维度越高,表达能力越强,但存储和检索成本也越高。768 维和 1024 维在实际检索效果上差距没有想象中那么大,但存储成本差 30% 以上。如果资料量在百万级以下,1024 维完全够用;如果上千万级,建议考虑 768 维甚至更低,配合量化技术。
第三,是否支持指令微调。有些嵌入模型支持在检索时加指令前缀,比如“为这个句子生成表示以用于检索相关文章”,这能显著提升检索准确率。BGE 系列就支持这种用法,实测在技术文档场景下召回率能提升 5 到 8 个百分点。
注意:嵌入模型一旦选定,整个知识库的向量空间就固定了。如果中途换模型,所有资料必须重新向量化。所以选型时宁可多花两天做对比测试,也不要上线后才发现效果不行。
2.2 分块策略:切得好比选得好更重要
分块是知识库流水线里最被低估的环节。我见过太多项目,嵌入模型用的是顶配,向量数据库也是企业级,但检索效果就是不行,最后发现是分块切得稀碎——一段完整的话被切成三截,每截都缺主语,向量化之后语义完全走样。
分块的核心矛盾是:块太小,语义不完整;块太大,噪声太多。一个 200 字的块可能只讲了半个概念,检索出来模型看不懂;一个 2000 字的块可能包含五六个不同主题,向量被平均之后哪个主题都不像。
我的经验值是:中文技术文档,块大小控制在 300 到 500 字之间,重叠 50 到 80 字。这个范围是经过多次实测的——小于 300 字,很多段落被拦腰截断;大于 500 字,检索精度明显下降。重叠是为了防止关键信息刚好落在切割边界上被丢掉。
但固定大小分块只是保底方案。更好的做法是按语义边界分块:优先在段落结束、标题切换、列表项之间切,实在找不到自然边界再按字数硬切。LangChain 的 RecursiveCharacterTextSplitter 就是干这个的,它会依次尝试用双换行、单换行、句号、逗号来切,尽量保持语义完整。
还有一种进阶策略是父子块:存的时候用小块(200 字)做向量检索,但返回给模型的是它所属的大块(1000 字)。这样检索精度高,模型拿到的上下文也完整。Dify 和 RAGFlow 都支持这种模式,实测在问答场景下效果提升明显。
2.3 向量数据库:别为了“先进”而过度设计
向量数据库选型是另一个容易过度设计的地方。我见过一个日活不到一千的内部工具,非要上分布式 Milvus 集群,结果运维成本比开发成本还高。选型要匹配实际数据量和并发量:
| 数据库 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| Chroma | 原型验证、小规模 | 零配置、Python 原生 | 不适合生产、并发差 |
| FAISS | 离线检索、嵌入式 | 性能极强、无依赖 | 不支持增删改、无持久化 |
| Milvus | 中大规模生产 | 功能全、生态好 | 运维复杂、资源占用高 |
| Qdrant | 中小规模生产 | 轻量、Rust 性能好 | 社区相对小 |
| pgvector | 已有 PostgreSQL | 和业务数据同库 | 大规模性能一般 |
| Elasticsearch | 已有 ES 集群 | 混合检索方便 | 向量功能较新 |
我的建议是:原型阶段用 Chroma 或 FAISS,生产环境如果数据量在百万级以下且已有 PostgreSQL,直接用 pgvector;如果数据量更大或需要独立扩展,选 Qdrant 或 Milvus。不要一上来就追求“企业级”,先把流程跑通再说。
2.4 检索策略:从“能查到”到“查得准”
检索环节决定了知识库的上限。最基础的向量相似度检索(余弦相似度或内积)只能解决“语义相近”的问题,但实际场景中往往需要更复杂的策略:
混合检索是把向量检索和关键词检索(BM25)的结果融合。向量检索擅长语义匹配,比如用户问“怎么退款”,能匹配到“退货流程”的文档;关键词检索擅长精确匹配,比如用户问“错误码 E5021”,能精确找到包含这个码的文档。两者结合,召回率通常能提升 10 到 15 个百分点。
重排是在粗筛之后加一个精排模型。向量检索返回的 Top 20 里,真正相关的可能只有 5 条,而且排序不一定准。重排模型(如 BGE-Reranker)会逐条计算问题和文档的相关性分数,重新排序。这一步计算量不大(只处理几十条),但效果提升非常明显,实测能把 Top 3 准确率从 60% 拉到 85% 以上。
查询改写是另一个实用技巧。用户的问题往往口语化、有歧义,直接拿去检索效果不好。可以先用一个小模型把问题改写成更适合检索的形式,或者生成多个相关查询分别检索再合并结果。比如用户问“那个新功能怎么用”,可以改写成“新功能 使用说明 操作步骤”,检索命中率会高很多。
3. 从零搭建知识库的完整实操
3.1 环境准备与依赖安装
我以 Python 技术栈为例,走一遍完整流程。这套方案在本地和服务器上都能跑,依赖都是开源的,不需要任何特殊网络环境。
# 创建虚拟环境 python -m venv kb-env source kb-env/bin/activate # Windows 用 kb-env\Scripts\activate # 安装核心依赖 pip install langchain langchain-community chromadb sentence-transformers pip install pypdf unstructured markdown beautifulsoup4 pip install rank-bm25 jieba这里解释一下每个包的作用:langchain提供文档加载和分块的抽象;chromadb是向量存储;sentence-transformers用来加载嵌入模型;pypdf和unstructured处理 PDF 和各类文档;rank-bm25和jieba用于关键词检索和中文分词。
嵌入模型我选BAAI/bge-large-zh-v1.5,这是目前中文开源嵌入模型里综合表现最稳的之一。首次运行会自动下载模型文件,大约 1.3GB,之后缓存在本地。
from sentence_transformers import SentenceTransformer # 加载嵌入模型,首次会自动下载 model = SentenceTransformer('BAAI/bge-large-zh-v1.5') # 测试一下 sentences = ["知识库怎么搭建", "如何构建智能体的检索系统"] embeddings = model.encode(sentences, normalize_embeddings=True) print(embeddings.shape) # 应该是 (2, 1024)注意:
normalize_embeddings=True很重要。归一化之后,余弦相似度等价于内积,检索时计算更快,而且不同长度的文本向量可比性更好。
3.2 文档加载与清洗
假设你有一批 PDF 和 Markdown 文档放在./docs目录下。加载和清洗的代码如下:
from langchain_community.document_loaders import PyPDFLoader, UnstructuredMarkdownLoader from langchain.text_splitter import RecursiveCharacterTextSplitter import os import re def load_documents(doc_dir): documents = [] for filename in os.listdir(doc_dir): filepath = os.path.join(doc_dir, filename) if filename.endswith('.pdf'): loader = PyPDFLoader(filepath) documents.extend(loader.load()) elif filename.endswith('.md'): loader = UnstructuredMarkdownLoader(filepath) documents.extend(loader.load()) return documents def clean_text(text): # 去掉多余空白 text = re.sub(r'\s+', ' ', text) # 去掉页眉页脚常见模式(如纯数字行) text = re.sub(r'\n\d+\n', '\n', text) # 去掉乱码字符 text = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f]', '', text) return text.strip() documents = load_documents('./docs') for doc in documents: doc.page_content = clean_text(doc.page_content)清洗这一步看起来简单,但实际效果差异很大。我处理过一批从某系统导出的 PDF,每页顶部都有“内部资料 请勿外传”的水印文字,如果不清理,每个块里都混着这句话,向量化之后所有块的相似度都被拉高了,检索精度直接崩掉。所以清洗不是可选项,是必选项。
3.3 分块与向量化入库
分块用递归字符分割器,优先按语义边界切:
text_splitter = RecursiveCharacterTextSplitter( chunk_size=400, chunk_overlap=60, separators=["\n\n", "\n", "。", "!", "?", ";", ",", " ", ""], length_function=len, ) chunks = text_splitter.split_documents(documents) print(f"原始文档 {len(documents)} 页,切分成 {len(chunks)} 个块")注意separators的顺序:先尝试双换行(段落边界),再单换行,再中文句号、感叹号、问号、分号、逗号,最后才是空格和空字符。这样能最大程度保证块内语义完整。
接下来向量化并存入 Chroma:
from langchain_community.vectorstores import Chroma from langchain_community.embeddings import HuggingFaceEmbeddings embedding_function = HuggingFaceEmbeddings( model_name="BAAI/bge-large-zh-v1.5", model_kwargs={'device': 'cpu'}, # 有 GPU 就改成 'cuda' encode_kwargs={'normalize_embeddings': True} ) vectorstore = Chroma.from_documents( documents=chunks, embedding=embedding_function, persist_directory="./chroma_db", collection_name="my_knowledge_base" ) vectorstore.persist() print("入库完成")如果资料量很大(比如超过 10 万块),建议分批入库,每批 5000 块,避免内存爆掉。另外device参数根据实际情况选,CPU 也能跑,只是慢一些,10 万块大概需要 20 到 30 分钟。
3.4 检索与重排的代码实现
基础检索很简单:
query = "智能体怎么接入知识库" results = vectorstore.similarity_search_with_score(query, k=10) for doc, score in results: print(f"分数: {score:.4f} | 内容: {doc.page_content[:100]}...")但基础检索的排序往往不够准。加上重排:
from sentence_transformers import CrossEncoder reranker = CrossEncoder('BAAI/bge-reranker-large') def retrieve_with_rerank(query, vectorstore, top_k=20, final_k=5): # 粗筛 candidates = vectorstore.similarity_search(query, k=top_k) # 精排 pairs = [[query, doc.page_content] for doc in candidates] scores = reranker.predict(pairs) # 按分数排序 ranked = sorted(zip(candidates, scores), key=lambda x: x[1], reverse=True) return [doc for doc, score in ranked[:final_k]]重排模型比嵌入模型大,推理慢一些,但只处理 20 条候选,耗时通常在 200 毫秒以内,完全可以接受。实测下来,加了重排之后,Top 3 里包含正确答案的比例从 62% 提升到了 87%,这个投入非常值得。
3.5 混合检索的融合策略
混合检索需要同时跑向量检索和 BM25,然后融合结果。融合算法常用 RRF(Reciprocal Rank Fusion,倒数排名融合):
from rank_bm25 import BM25Okapi import jieba import numpy as np # 构建 BM25 索引 tokenized_corpus = [list(jieba.cut(chunk.page_content)) for chunk in chunks] bm25 = BM25Okapi(tokenized_corpus) def hybrid_retrieve(query, vectorstore, bm25, chunks, top_k=10): # 向量检索 vector_results = vectorstore.similarity_search(query, k=top_k) vector_ids = [chunk.metadata.get('id') for chunk in vector_results] # BM25 检索 tokenized_query = list(jieba.cut(query)) bm25_scores = bm25.get_scores(tokenized_query) bm25_top_indices = np.argsort(bm25_scores)[::-1][:top_k] # RRF 融合 rrf_scores = {} for rank, doc in enumerate(vector_results): doc_id = doc.metadata.get('id', str(hash(doc.page_content))) rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + 1 / (60 + rank + 1) for rank, idx in enumerate(bm25_top_indices): doc_id = chunks[idx].metadata.get('id', str(hash(chunks[idx].page_content))) rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + 1 / (60 + rank + 1) # 按融合分数排序返回 sorted_ids = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True) # 这里需要根据 id 找回原始文档,实际实现时建议用字典映射 return sorted_ids[:top_k]RRF 里的常数 60 是经验值,来自原论文,实际用的时候不用改。这个融合策略的好处是不需要调权重,向量检索和关键词检索的分数尺度不同,直接加权平均很难调,RRF 只看排名不看绝对分数,鲁棒性更好。
4. 常见问题与排查技巧实录
4.1 检索结果不相关怎么办
这是最高频的问题。排查顺序如下:
第一步,检查分块质量。把检索到的块打印出来看,是不是被切得支离破碎。如果是,调整chunk_size和chunk_overlap,或者换用语义分块。
第二步,检查嵌入模型是否匹配语言。如果资料是中文但用了英文嵌入模型,效果肯定差。换成 BGE 中文系列试试。
第三步,检查查询是否需要改写。用户口语化的问题直接检索效果往往不好。加一个查询改写步骤,用一个小模型把问题改写成更正式的检索语句。
第四步,加混合检索和重排。如果前三步都做了还不行,基本就是检索策略太单一,上混合检索和重排。
我遇到过一个典型案例:用户问“报销流程是什么”,知识库里有完整的报销制度文档,但检索出来的全是“流程”相关的其他文档。原因是“报销”这个词在文档里出现频率不高,向量检索被“流程”带偏了。后来加了 BM25 混合检索,“报销”作为关键词被精确匹配到,问题就解决了。
4.2 知识库更新后检索不到新内容
这是增量更新的问题。向量数据库支持增删改,但很多人入库时用了from_documents一次性全量写入,后续更新不知道怎么处理。正确做法是用add_documents增量添加,用delete删除旧版本:
# 增量添加新文档 new_chunks = text_splitter.split_documents(new_documents) vectorstore.add_documents(new_chunks) # 删除旧版本文档(按 metadata 过滤) vectorstore.delete(where={"source": "old_doc.pdf"})但要注意,Chroma 的删除是按 ID 或 metadata 过滤的,如果你的块没有唯一 ID,删除会很麻烦。所以入库时一定要给每个块生成唯一 ID,并把源文件路径、版本号写进 metadata。这个习惯能省掉后面无数麻烦。
4.3 中文分块把句子切断了
中文没有空格,按字符数硬切很容易把句子拦腰截断。RecursiveCharacterTextSplitter 的中文分隔符配置很关键。我试过只用["\n\n", "\n", " ", ""],结果中文句子被切得乱七八糟。加上中文标点之后好很多:
separators=["\n\n", "\n", "。", "!", "?", ";", ",", " ", ""]但即便如此,还是会有切断的情况。更稳妥的做法是用专门的中文分块器,比如按句号、问号、感叹号、分号、逗号逐级切分,保证每个块至少包含一个完整句子。如果对精度要求极高,可以用 NLP 工具做句法分析,按从句边界切,但计算成本会高不少。
4.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 检索结果完全不相关 | 嵌入模型语言不匹配 | 检查模型名称是否含 zh | 换中文嵌入模型 |
| 检索结果相关性差 | 分块太大或太小 | 打印块内容检查 | 调整 chunk_size 到 300-500 |
| 新文档检索不到 | 未增量入库 | 检查数据库文档数 | 用 add_documents 增量添加 |
| 相同问题每次答案不同 | 检索结果不稳定 | 检查是否有重排 | 加固定重排模型 |
| 检索速度慢 | 向量维度太高或数据量大 | 测单次检索耗时 | 降维或加索引 |
| 关键词搜不到 | 纯向量检索不匹配关键词 | 测试 BM25 单独检索 | 上混合检索 |
| PDF 内容乱码 | 编码或字体问题 | 打印原始文本 | 换 PDF 解析库或手动清洗 |
| 表格内容丢失 | PDF 表格解析失败 | 检查解析后文本 | 用 unstructured 的表格模式 |
4.5 几个踩过的坑
坑一:metadata 里塞了太多东西。我一开始把整个文档的元信息都塞进每个块的 metadata,结果存储膨胀了三倍,检索也变慢。后来只保留source、page、chunk_id三个字段,清爽很多。
坑二:忘了归一化。嵌入向量不归一化的话,余弦相似度和内积不等价,Chroma 默认用 L2 距离,结果排序会乱。一定要在encode_kwargs里加normalize_embeddings=True。
坑三:重排模型和嵌入模型不匹配。重排模型是在特定嵌入模型的基础上训练的,如果混用不同系列的模型,效果可能反而变差。建议嵌入和重排用同一系列,比如都用 BGE。
坑四:忽略了查询长度。用户的问题如果特别长(比如贴了一大段报错日志),直接向量化效果很差。可以先做查询摘要,提取关键信息再检索。
坑五:没有做检索结果去重。如果知识库里有多个版本文档,检索可能返回同一内容的不同版本,浪费上下文窗口。可以在检索后按内容相似度去重,或者入库时就做好版本管理。
5. 知识库与智能体的对接方式
5.1 通过 MCP 协议标准化接入
MCP(Model Context Protocol)是当前智能体工具调用领域的热门协议,它定义了一套标准接口,让智能体能够以统一的方式访问外部资源。把知识库封装成 MCP Server 之后,任何支持 MCP 的智能体框架都能直接调用,不需要为每个框架单独写适配代码。
一个最简单的知识库 MCP Server 大概长这样:
from mcp.server import Server from mcp.types import Tool, TextContent server = Server("knowledge-base") @server.list_tools() async def list_tools(): return [ Tool( name="search_knowledge", description="搜索知识库,返回最相关的文档片段", inputSchema={ "type": "object", "properties": { "query": {"type": "string", "description": "搜索查询"} }, "required": ["query"] } ) ] @server.call_tool() async def call_tool(name: str, arguments: dict): if name == "search_knowledge": query = arguments["query"] results = retrieve_with_rerank(query, vectorstore) return [TextContent(type="text", text="\n\n".join([doc.page_content for doc in results]))]这样封装之后,智能体只需要知道“有一个叫 search_knowledge 的工具可以查知识库”,具体底层用的是 Chroma 还是 Milvus、嵌入模型是什么,智能体完全不用关心。这就是 MCP 的价值——把知识库的实现细节和智能体的调用逻辑解耦。
5.2 直接集成到智能体框架
如果不走 MCP,也可以直接在智能体框架里集成。以 LangChain 为例:
from langchain.agents import Tool, AgentExecutor, create_react_agent from langchain_openai import ChatOpenAI retriever_tool = Tool( name="knowledge_search", func=lambda q: "\n\n".join([doc.page_content for doc in retrieve_with_rerank(q, vectorstore)]), description="当需要查询内部资料时使用此工具,输入是搜索查询" ) llm = ChatOpenAI(model="gpt-4", temperature=0) agent = create_react_agent(llm, [retriever_tool], prompt) agent_executor = AgentExecutor(agent=agent, tools=[retriever_tool])这种方式更直接,但和框架绑定较深。如果以后换框架,需要重写适配层。MCP 的好处就是换框架不用改知识库侧代码。
5.3 检索时机与上下文注入策略
知识库检索不是每次对话都要触发。如果用户只是说“你好”,没必要去查知识库。常见的触发策略有三种:
第一种,工具调用模式。把知识库封装成一个工具,让模型自己决定什么时候调用。这适合问题类型多样的场景,但模型可能会漏调或误调。
第二种,前置检索模式。每次用户提问都先检索一遍,把结果作为上下文注入提示词。这适合问答类智能体,保证每次都有资料参考,但会增加延迟和 token 消耗。
第三种,混合模式。先用一个轻量分类器判断问题是否需要查知识库,需要则检索,不需要则直接走通用对话。这是实际落地中最常用的方案,兼顾效果和成本。
上下文注入时要注意 token 预算。如果检索返回 5 个块,每个块 400 字,加起来 2000 字,再加上系统提示词和对话历史,很容易超过模型上下文窗口。建议对检索结果做截断或摘要,只保留最核心的部分。
6. 效果评估与持续优化
6.1 怎么判断知识库好不好用
知识库上线之后不能凭感觉说“好像还行”,要有量化指标。最核心的两个指标是:
召回率:在所有相关问题中,知识库能检索到正确答案的比例。测试方法是准备一批问题,每个问题标注好正确答案所在的文档,然后看检索 Top K 里是否包含该文档。召回率低于 80% 就说明检索环节有问题。
准确率:检索返回的结果中,真正相关的比例。如果召回率高但准确率低,说明返回了太多无关内容,会干扰模型生成。准确率低于 60% 就需要加严重排或调整分块。
这两个指标需要人工标注测试集,前期投入大概半天到一天,但后续每次调整都能快速验证效果,非常值得。
6.2 持续优化的三个方向
方向一:补充资料。如果发现某些问题检索不到,先看知识库里有没有对应资料。没有就补,有但检索不到就调检索策略。
方向二:优化分块。如果检索到的块内容不完整,调整分块参数。如果块内主题太杂,尝试更小的块或语义分块。
方向三:迭代重排。如果 Top 10 里有正确答案但排不到前面,说明重排模型不够好,可以换更大的重排模型,或者用业务数据微调重排模型。
我个人的经验是,一个知识库从能用变成好用,通常需要两到三轮迭代。第一轮解决“能不能查到”,第二轮解决“查得准不准”,第三轮解决“快不快”。不要指望一次配置就完美,留出迭代时间。
6.3 一个容易被忽略的点:知识库的“保鲜”
知识库和代码一样,会腐烂。产品文档更新了、流程改了、新政策发布了,如果知识库不同步,智能体就会给出过时甚至错误的答案。我见过最严重的情况是,客服智能体还在引用两年前已经废止的退款政策,导致用户投诉。
解决办法是建立知识库更新流程:文档变更时自动触发重新索引,或者至少每周做一次全量同步。如果用的是在线文档(如飞书、Notion),可以配置 webhook,文档一改就自动推送更新。这个流程不建立,知识库的长期价值会大打折扣。
我个人在实际操作中的体会是,知识库项目最难的不是技术选型,而是持续维护。技术方案再先进,如果没人负责更新资料、没人看检索日志、没人迭代优化,三个月后就会变成一个没人用的摆设。所以在启动项目之前,先想清楚谁来维护、多久 review 一次、更新流程是什么。这些问题比选哪个向量数据库重要得多。