ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

RAG实战:用Chroma+Ollama构建本地知识库问答系统

RAG实战:用Chroma+Ollama构建本地知识库问答系统 如果你试过把最新产品文档、公司内部制度、行业研报直接丢给大模型问它“根据这份文档我们新版定价规则是什么”而它一本正经地胡编了一个答案——不用怀疑你不是一个人。我踩过这个坑而且踩得很惨。去年给团队做内部知识问答机器人选型时纠结了很久直接微调大模型成本高、周期长而且文档每周都在更新难道每周都重训一次用传统关键词搜索召回查“苹果公司的毛利率”系统把包含“苹果”“公司”“毛利率”的段落全捞出来排序乱七八糟。后来真正把问题解决掉的是一套组合拳向量数据库 嵌入模型 大模型也就是现在很多人说的 RAG检索增强生成方案。这篇文章就围绕这个思路展开手把手带你把“关键词搜索”换成“语义检索”给本地或云端的大模型外挂一个可以随时更新的知识库。整个方案我用的都是免费、可本地部署的组件Chroma 向量数据库、Ollama 本地模型、开源的 Embedding 模型以及现成的 Python 代码。你可以直接照抄也能根据自己的文档类型改造。1. 为什么大模型永远“学不完”最新知识以及关键词搜索为什么救不了场1.1 大模型的“知识截止日期”和微调的性价比困局先明确一个前提大模型的知识来自于训练时看到的语料。训练完成那一刻它的知识就冻结了。哪怕你现在打开 ChatGPT 或任意开源大模型问“今天某某公司发布了什么公告”只要这件事发生在训练数据截止之后模型就只能靠猜。这不是模型笨而是它的记忆机制决定的——它本质上是把海量文本压缩成参数而不是像数据库一样逐字记录。那能不能通过微调解决“知识不够新”的问题技术上可以但实操中很不划算。一次全量微调动辄需要几张显卡、几小时到几天不等的训练时间跑完之后如果知识又变了又得再来一轮。我见过不少团队把微调当成“知识更新”工具在用最后发现 80% 的算力都花在了反复训练那些本来几天后就过时的内容上。这里并不是说微调没用而是它更适合用来改变模型的“行为方式”或“表达能力”比如让它学会某种输出格式、某种领域的口吻而不是当作一个高频更新的知识存储介质。1.2 关键词搜索的两个致命弱点字面匹配和“没有上下文”既然大模型靠训练记忆不行那常见的做法就是“检索 生成”先从外部知识库中找到相关内容再喂给大模型让它作答。这个思路本身不新新的是“怎么检索”。传统的关键词搜索比如数据库里的 LIKE 查询、Elasticsearch 里的分词匹配本质都是在做字面匹配。你搜“怎么退款”它就去找同时包含“怎么”和“退款”的文档。问题来了如果文档里写的是“用户申请退货后款项将在 3 个工作日内原路返还”这句话里既没有“退款”也没有“怎么”关键词系统就漏掉了这条明明最该被召回的内容。更麻烦的是同义词、指代和口语化表达比如“钱什么时候回来”“退款要等几天”关键词搜索基本无能为力。另一个问题是“没有上下文”。关键词搜索返回的是一条条孤立的词频命中结果它不理解“苹果”在“苹果公司财报”和“水果批发价格”里是完全不同的概念。你问“苹果最近股价怎么样”它能给你召回一堆水果价格。向量数据库要解决的恰恰就是这两个问题语义匹配 上下文理解。1.3 向量检索的本质把文字变成坐标在语义空间里找“邻居”向量数据库做的事情可以粗略理解为把每一段文字映射成一组数字也就是“向量”并且让语义相近的文字在向量空间里彼此靠近。用一个生活化的类比你有一屋子的人每个人身上贴着一张写满特征的标签比如“会编程”“喜欢摄影”“去过西藏”。你想找“懂技术又爱旅行的人”不用挨个翻标签找“技术”“旅行”这样的字眼而是把每个人按照特征摆在一个多维空间里——会编程的站左边爱旅行的站前面去过西藏的站高处。然后你在心里构建一个“既懂技术又爱旅行”的理想位置看看谁离这个位置最近谁就是最匹配的人。向量数据库干的就是这件事离线阶段把知识库里的每段文本用嵌入模型转成向量并存储在线阶段把你的提问也转成一个向量然后用高效的相似度算法比如余弦相似度找出离它最近的 K 个文本向量。整个过程跟你是不是用了完全一样的词没有关系语义相近就能召回到。明白了这一点后面所有操作你都能看懂。不然你只会复制代码遇到换模型、换场景就容易抓瞎。2. 动手前的方案选型Chroma、Milvus、Ollama、Embedding 模型到底怎么挑2.1 为什么我选了 Chroma 向量数据库而不是 Milvus 或 FAISS做向量检索的组件现在不少常见的有 Chroma、Milvus、Qdrant、Weaviate、FAISS。我第一次选型时差点被它们之间的差异劝退后来根据“项目规模”做了一个简单的判断。如果只是个人学习、小团队内部工具、文档量在几十万条以内Chroma 是最合适的选择。它是一个嵌入式向量数据库可以像 SQLite 一样直接在本地跑不需要单独部署服务器pip install 之后就能用数据默认存在本地目录里重启不会丢。我这次教程就用的它。如果到了百万级以上的向量规模、需要分布式部署、多副本高可用、或者要和 Kubernetes 深度集成那就要上 Milvus 或 Qdrant 这类真正的“数据库”了它们在索引算法、水平扩展、运维监控上都做得更重更完善。至于 FAISS它是 Meta 开源的向量检索库不是完整数据库。它的定位是高性能的检索内核适合嵌在自己的 Pipeline 里但持久化、元数据过滤、增删改这些功能得自己补开发成本更高。我给你的建议是第一版先用 Chroma 跑通全流程让业务验证“向量检索 大模型”这条路是否走得通。等数据量真的涨上来了再平滑迁移到 Milvus 也不迟。Chroma 和 Milvus 在接口风格上都是向量入库 相似度查询迁移成本没有想象中那么大。2.2 本地大模型 与 Embedding 模型的选择有了向量数据库还需要两样东西一个是把文本变向量的嵌入模型另一个是用来生成最终答案的大模型。嵌入模型方面我强烈建议别一上来就用 OpenAI 的 text-embedding-ada-002 之类的大厂 API。原因有两个第一文档内容往往涉及内部信息出于隐私和合规考虑最好别把数据送到外部接口第二本地有完全够用的开源模型。这次我用的方案是 Ollama 拉取nomic-embed-text它是一个对英文和中文都有不错效果的轻量嵌入模型显存占用很低我的 MacBook Air M3 16G 都能跑得动。如果你是纯中文场景也可以考虑BAAI/bge-m3之类的国产模型通过 Ollama 或 HuggingFace 加载都行。大模型方面同样以 Ollama 为运行环境我本地拉取的是qwen2.5:7b也就是通义千问的 7B 版本。它在中文理解、指令遵循、还有“根据给定资料作答”这类任务上表现不错。如果你的机器配置更高可以换 14B 甚至 32B 的模型如果只有 CPU那就选 3B 或 4B 的小模型速度会慢一些但流程不受影响。整套技术栈总结如下组件选型说明向量数据库Chroma嵌入式本地运行Python API 友好嵌入模型Ollama nomic-embed-text免费、本地推理支持中英文大模型Ollama qwen2.5:7b本地部署负责根据检索内容生成回答文档处理框架LangChain 各文档加载器统一处理 PDF、Markdown、CSV 等格式文本切分器RecursiveCharacterTextSplitter把长文档切成适合向量检索的片段2.3 先梳理清楚 RAG 的完整数据流再写代码很多人写代码一上来就复制粘贴结果跑通了也不知道哪里出了问题。我习惯先把数据流画在脑子里再动手。RAG 方案分两条链路。离线链路把原始文档加载进来做清洗、切分成 chunk文本块每个 chunk 通过嵌入模型转成向量存入 Chroma。在线链路用户提问被转成向量在 Chroma 里做相似度检索取回 Top K 个 chunk把这些 chunk 连同问题一起拼成 Prompt交给大模型生成回答。大模型的任务不是“回忆事实”而是“根据我给你的这些片段组织语言回答问题”。如果检索到的片段里没有答案它应该明确说不知道而不是编。记住这个结构之后你后面遇到任何问题都知道去排查哪一段是文档没切好还是 embedding 效果差还是检索没召回还是 Prompt 写得太模糊这比瞎试参数高效多了。3. 手把手实操本地跑通一套“最新知识问答”系统3.1 环境准备安装 Ollama、拉取模型、创建项目目录这次实操用的环境是 macOS但 Windows 和 Linux 的操作几乎一样只是安装包不同。第一步去 Ollama 官网下载对应系统的安装包或者直接用命令行安装。装完后在终端里验证一下ollama --version然后拉取两个模型。第一个是嵌入模型第二个是问答大模型ollama pull nomic-embed-text ollama pull qwen2.5:7b如果你的磁盘空间比较紧张只想先跑通流程qwen2.5:7b 大概占 4.7GB 左右nomic-embed-text 很小不到 300MB。也可以先用更小的qwen2.5:3b代替 7B流程完全一样。模型拉取完成后用ollama list确认一下。接着创建项目目录和虚拟环境mkdir rag-demo cd rag-demo python3 -m venv .venv source .venv/bin/activate然后安装 Python 依赖pip install chromadb langchain langchain-community langchain-ollama pypdf不同 LangChain 版本里Ollama 和 Chroma 的导入路径可能会有变化。我这里用的是较新的写法OllamaEmbeddings 和 ChatOllama 从langchain_ollama导入Chroma 向量库从langchain_community.vectorstores导入。如果你在旧教程里看到from langchain.embeddings import OllamaEmbeddings那是最老版本的写法新版本已经挪到子模块里了。遇到 ModuleNotFoundError优先查一下是不是版本路径问题。3.2 准备一份测试文档并用代码完成切分接下来需要一份测试文档。我没有用网上现成的“通用知识库”而是自己写了一份模拟的公司售后政策 markdown 文件policy.md内容刻意包含了一些和“退款”“退货”不完全同义、但语义相关的句子方便测试向量检索和关键词搜索的区别。下面这份测试文档内容大概长这样# XX 数码产品售后政策2025 年修订版 ## 一、退换货规则 消费者自签收次日起 7 日内因产品质量问题申请退货的运费由我方承担。 因个人原因不喜欢、不想要了只要商品未经人为损坏且包装配件齐全可申请 7 天无理由退货但退货运费需自理。 15 日内出现性能故障消费者可选择换货或修理。 ## 二、维修周期 自收到返修设备之日起一般维修周期为 5 至 7 个工作日。 若涉及配件调货维修时间可能延长至 15 个工作日期间会通过短信同步进度。 ## 三、钱款返还说明 退货申请审核通过后财务部门会在 48 小时内发起原路退款。 信用卡支付一般 3 到 5 个工作日到账支付宝/微信支付通常秒到。你看这份文档里并没有直接出现“多久能拿到钱”这样的句子。如果用关键词搜索搜“钱什么时候退”大概率只能靠运气。但向量检索应该能找到“退款”“到账”那一段。下面写加载和切分的代码保存为ingest.pyfrom langchain_community.document_loaders import TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter loader TextLoader(policy.md, encodingutf-8) documents loader.load() text_splitter RecursiveCharacterTextSplitter( chunk_size300, chunk_overlap50, separators[\n\n, \n, 。, , , , , ] ) chunks text_splitter.split_documents(documents) print(f切分完成共 {len(chunks)} 个文本块) for idx, chunk in enumerate(chunks[:3]): print(f--- chunk {idx} ---) print(chunk.page_content)这里有两个关键参数chunk_size和chunk_overlap。chunk_size是每个块的最大字符数chunk_overlap是相邻块之间重叠的字符数。为什么要重叠因为很多关键信息可能刚好被一刀切成两半比如一句话前半句在 chunk A 末尾后半句在 chunk B 开头检索的时候两个块都不完整答案质量自然差。重叠可以在一定程度上缓解这个问题。实际项目里chunk_size 一般设在 300 到 800 之间太短则上下文不足太长则向量语义被稀释。如果你不确定可以先把两份 PDF 文档的内容手工切成不同的块大小跑一轮对比看哪一组检索出的片段更符合直觉。执行python ingest.py如果一切正常你会看到文本按标题和句号被切成十几个块。注意里面的separators列表顺序是有讲究的它决定了切分器优先按什么层级去切。我这里是先按空行段落切再按句末标点切最后才按空格和字符切这样能最大程度保留语义完整的句子。3.3 把文本块向量化并写入 Chroma文本切好之后下一步是把每个块转成向量连同原始文本和元数据一起写入 Chroma。写一个build_vector_store.pyfrom langchain_community.document_loaders import TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_ollama import OllamaEmbeddings from langchain_community.vectorstores import Chroma # 1. 加载并切分文档 loader TextLoader(policy.md, encodingutf-8) documents loader.load() text_splitter RecursiveCharacterTextSplitter( chunk_size300, chunk_overlap50, separators[\n\n, \n, 。, , , , , ] ) chunks text_splitter.split_documents(documents) # 2. 初始化嵌入模型这里用 Ollama 本地服务 embeddings OllamaEmbeddings(modelnomic-embed-text) # 3. 创建向量库并写入数据persist_directory 是本地持久化目录 vector_store Chroma.from_documents( documentschunks, embeddingembeddings, persist_directory./chroma_db ) print(f成功写入 {vector_store._collection.count()} 个向量)注意persist_directory这个参数它决定了向量数据持久化在哪个文件夹。Chroma 会把向量索引和元数据都写到这个目录里下次启动应用时直接加载这个目录不需要重新嵌入一遍。这一点比 FAISS 自己维护索引文件要省心很多。有个细节容易踩坑OllamaEmbeddings默认会连接http://localhost:11434这正是 Ollama 本地服务默认监听的端口。如果你改了 Ollama 的服务地址记得通过base_url参数显式指定。确认 Ollama 服务在运行可以用ollama serve启动也可以直接再开一个终端执行ollama ps查看当前加载了哪些模型。3.4 写一个最简单的问答脚本验证检索 生成效果向量库建好之后我们来写问答脚本ask.py。这一步要跑通“问题向量化 - 检索 TopK - 拼 Prompt - 大模型生成”的完整链路。from langchain_ollama import OllamaEmbeddings, ChatOllama from langchain_community.vectorstores import Chroma from langchain_core.prompts import ChatPromptTemplate # 加载已有向量库 embeddings OllamaEmbeddings(modelnomic-embed-text) vector_store Chroma( persist_directory./chroma_db, embedding_functionembeddings ) # 定义 Promot 模板让大模型只依据给定上下文回答 prompt_template ChatPromptTemplate.from_messages([ (system, 你是一个严谨的客服知识库助手。请仅根据以下资料片段回答问题。 如果资料中没有相关信息请直接回答根据现有资料无法回答该问题不要编造。), (human, 相关资料\n{context}\n\n问题{question}) ]) # 初始化本地大模型 llm ChatOllama(modelqwen2.5:7b, temperature0) # 用户问题 question 我在官网用信用卡买了台相机退货审核通过后大概多久能收到钱 # 检索 Top 4 个相关片段 retriever vector_store.as_retriever(search_kwargs{k: 4}) docs retriever.invoke(question) print( 召回的片段 ) for idx, doc in enumerate(docs): print(f--- 第 {idx 1} 个片段 ---) print(doc.page_content) # 生成回答 context \n.join([doc.page_content for doc in docs]) prompt prompt_template.invoke({context: context, question: question}) response llm.invoke(prompt) print( 最终回答 ) print(response.content)执行python ask.py我在本地的实测结果大概是这样的召回结果中第一条匹配到的正是“财务部门会在 48 小时内发起原路退款”和“信用卡支付一般 3 到 5 个工作日到账”那一段。最终模型给出的回答是“根据退货政策退货申请审核通过后财务部门会在 48 小时内发起原路退款。由于你是使用信用卡支付款项一般需要 3 到 5 个工作日到账。”这个答案不是模型背出来的而是它看到检索片段后组织出来的。对比一下如果我用传统关键词搜索手写一套词表去匹配“退款”“到账”除非我把“退货审核通过”“钱到账”全部做同义词扩展否则很难稳定召回这个片段。而向量检索一次就命中了。整个 Demo 到这里就是完整可运行的了后面我们再谈怎么调优。4. 检索质量调优让回答更准的几个关键细节4.1 调节 chunk_size 与 overlap 的节奏感很多人在调参时会把 chunk_size 调到很大觉得“上下文越长模型越能理解”。实际上在向量检索这个环节过大的 chunk 反而会稀释语义。我做过一个对比测试同一份合同文档用 200 字的块去检索“违约金比例”召回的前几个块基本都精准把块改成 1500 字之后召回的块里往往前半部分在讲付款方式、后半部分才提到违约金模型读起来反而抓不住重点。更好用的做法是先按 Markdown 或 Word 的标题层级做结构化切分再对每个结构块按长度二次切分。LangChain 的MarkdownHeaderTextSplitter就是干这个的它能把“一级标题下面的内容”作为一个整体处理避免把不同章节的内容混在一个向量里。另外你需要知道一个隐藏风险过小的chunk_overlap或者不合理的separators顺序会导致信息被拦腰截断。我的建议是 overlap 设置为 chunk_size 的 10% 到 20%至少要把一句完整的话保留在其中一段里。如果预算允许可以通过标注数据评估不同切分参数对最终问答准确率的影响但对大多数学者开始直觉加测试已经能解决 80% 的问题。4.2 Top K 参数与相似度阈值的配合使用as_retriever(search_kwargs{k: 4})里的k值直接影响最终喂给大模型的资料量。K 太小比如 1可能漏掉关键信息K 太大比如 10则会塞入不少无关片段干扰大模型判断。我常用 K 4 到 6。但只调 K 还不够。Chroma 允许你通过vector_store.similarity_search_with_relevance_scores()查看每个返回结果的相关性分数。这个分数能帮你判断检索结果是否靠谱比如当你的问题问得太偏、知识库里根本没有相关内容时Top 1 的分数可能也只有 0.3 左右。这时候就应该设置一个阈值比如低于 0.5 就回退到“资料不足”提示而不是硬着头皮让模型编。阈值高低取决于你选用的嵌入模型不同模型的分数分布范围不一样所以更稳妥的方法不是死记某一个阈值而是先用几个已知有答案和无答案的问题跑一遍画出分数分布再选出一个分界点。4.3 Prompt 设计是最后一道闸门检索质量再高如果 Prompt 写得模糊大模型照样给你胡诌。我自己的实践体会是RAG 场景下的系统 Prompt 至少要写清楚三件事第一模型的身份和任务边界比如“你是客服知识库助手”第二回答时只能依据哪些内容比如“只根据用户提供的资料段落作答”第三资料不足时的行为比如“如果不能确定答案请回答‘根据现有资料无法回答该问题’”。还有一个容易被忽略的点告诉模型“不要复述资料里没有的结论”比告诉它“请准确回答”有效得多。因为生成式模型的默认倾向是“填满对话”它会倾向于给用户一个完整句子哪怕内容是编的。给一个明确的、可执行的“不能说不知道”的逃生通道能明显减少幻觉。4.4 元数据过滤让检索范围更精确如果你的知识库比较杂有售后政策、产品规格、价格表等多个维度的文档一股脑全塞进同一个向量集合里检索时容易串味。解决办法是给不同来源的文档打标签。Chroma 支持在写入时通过metadata字段存元数据比如{source: policy, category: 售后}。查询时可以用where参数做过滤比如强制要求只搜索category 售后的向量。这样做能显著减少“问价格却答成产品参数”的问题。注意Chroma 的元数据过滤是精确匹配或范围匹配不是全文模糊搜索所以你需要提前设计好元数据字段而不是指望它像搜索引擎一样自动推理。5. 常见问题与排查技巧实录5.1 向量库能写入但查询永远返回空结果这个现象很迷惑人通常不是因为代码错误而是“查询向量”和“入库向量”不是同一个模型生产的。比如你入库时用了nomic-embed-text查询时换成了别家的嵌入模型向量维度可能都不一样就算维度恰好一样语义空间也不对齐检索结果必然失真。所以最关键的一条规则Embedding 模型一旦确定就不要轻易更换除非你重建整个向量库。5.2 中文文档加载后乱码或丢失内容如果你用TextLoader不加encoding参数加载一个 GBK 编码的 txt 文件很容易出现乱码。解决方法是加载的时候显式指定encodingutf-8或encodinggb18030。PDF 文档则要注意有些扫描件本质上是图片pypdf提取不出任何文字需要先用 OCR 工具转成文本。我曾经被一份“扫描版公司制度 PDF”坑了一下午后来一查才发现那个 PDF 根本没有任何文本层。5.3 每次运行 ingest.py 都会重复写入向量如果你在同一个持久化目录里多次运行写入脚本Chroma 不会自动帮你把旧数据清掉除非你设置了相同的ids。这会导致同一份文档在向量库里出现多份副本检索时相似内容扎堆效果下降。我的习惯是每次重新入库前先删除旧的向量库目录import shutil shutil.rmtree(./chroma_db, ignore_errorsTrue)再重新执行from_documents保证数据一致性。5.4 无 GPU 环境下推理速度太慢在纯 CPU 环境跑 7B 模型每个问题可能要等 20 秒以上体验很差。可以考虑两个优化方向一是换更小的模型比如qwen2.5:3b速度明显提升二是利用支持 CPU 加速的推理框架比如 Ollama 本身已经做了不少优化如果你的机器是 Apple Silicon注意安装 Arm 版本并且确保 Ollama 能以 Metal 后端运行能明显快于默认 CPU 模式。如果你的环境支持 GPU也可以考虑通过 vLLM 部署大模型推理速度会快很多但配置复杂度会上升。5.5 常见问题速查表现象可能原因处理方式查询返回结果明显不相关Embedding 模型不一致或切分过大统一模型、重建向量库、缩小 chunk相同数据反复写入未清理旧的持久化目录入库前删除目录或指定唯一 idsPDF 提取不到文字扫描件没有文本层先 OCR再走后续流程回答总是编造Prompt 没有加“资料不足时拒答”改进 Prompt增加相关性阈值判断加载 Chroma 时报版本冲突langchain 相关包版本过旧过新检查导入路径升级到 langchain-ollama 新写法中文效果差nomic-embed-text 对纯中文场景一般换用 bge-m3 等中文友好的嵌入模型5.6 检索出来了但答案还是错通常是 Prompt 的“约束力”不够我遇到过一种情况向量检索明明把正确段落都召回了但大模型给出的答案依然引用了自己训练时的记忆写了一段“通用但错误”的解释。出现这种情况极大概率是 Prompt 里的“只根据资料回答”写得太温柔了。我试过加强约束之后准确率明显提升——在 System 里强制加一句“你不得使用资料片段之外的任何先验知识如果片段不足以回答问题就老实说不知道。”对于 7B 级别的小模型这句话比“请确保准确性”管用得多。6. 这套方案能做什么不能做什么厘清了技术细节之后说说边界。这套“向量数据库 大模型外挂知识库”的方案的强项是处理频繁更新的私有知识、企业制度、产品文档、个人笔记等场景。它不像微调那样需要消耗算力反复训练每次知识更新只需要增量写入新的文档片段几分钟内就能生效。这也是我目前最推荐团队快速落地的知识问答形态。但它也不是万能药。如果业务场景要求模型具备某种全新的推理模式或输出风格比如你要让模型学会一套完全不同的对话规则那微调仍然有不可替代的价值。另外向量检索本身不保证“绝对正确”它返回的是语义近邻不是精确匹配。对精度要求极高的场景比如法律条款的逐字引用、数字报表的精确查询我会建议你用混合检索用关键词/正则先把候选集缩窄再用向量做语义排序最后交给大模型。不要把鸡蛋都放在一个篮子里。关于“大模型投毒测试”这类术语我也简单说一句跟方案选型相关的话因为 RAG 方案不修改模型权重外挂知识库的内容是可审查、可回滚的。这样可以避免把不可信的数据直接训练进模型参数里从流程上降低了数据投毒的风险范围。当然这不代表你可以不看检索来源任何 AI 应用上线之前日志记录和内容审核都是必要的。对我来说搭建这套系统最有成就感的一刻不是模型跑通的那一瞬间而是当团队成员开始把日常排查问题、翻文档的习惯改成直接向“知识库助手”提问的时候。这说明技术真的融进了工作流而不只是又一个演示 Demo。最后分享一个我个人的实践习惯给知识库里的每一份文档都加上来源字段比如文件名、更新时间、责任部门。这样当大模型给出一个答案之后我还能接着问一句“这个结论来自哪份文档”通过元数据去验证答案的可信度。别小看这一步在真实业务中它比提升 1% 的检索准确率更能赢得用户的信任。如果你想继续扩展可以试着把网页、邮件、在线表格都接进来做一个定时同步的“第二大脑”。向量数据库只是工具真正值钱的是你怎么设计知识入库的规则以及怎么让模型学会在不懂的时候闭嘴。
返回列表