拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

RAG实战:从企业FAQ到智能问答机器人的完整搭建指南

RAG实战:从企业FAQ到智能问答机器人的完整搭建指南

从年初开始写“生成式人工智能实战”这个系列,前四篇分别聊了提示词基础、API接入、向量化原理解读和模型选型策略,后台每天都有人留言催更。这篇第五篇,我想换个更综合的写法——不再单一讲某个组件,而是带着大家完整做一个能落地的应用:把一份企业内部的FAQ文档变成问答机器人。没错,就是目前生成式AI落地最频繁、性价比最高的“检索增强生成(RAG)”方向。

这个系列走到现在,读者里既有刚接触AI的实习生,也有已经在公司里尝试做AI产品化的技术负责人。这一篇的目标是让这两类人都能有所收获:新手可以跟着完整走一遍,把所有环节串成一条线;有基础的朋友可以直接跳到第3、4章,看我在实际运行中踩过的坑和调优方法。整个项目我尽量把原理讲透、把步骤拆得细一些,所有代码和配置都是可以直接拿走的,只要把文档换成你自己的,就能立马复制成一个小工具。

1. 项目全景与方案设计思路

1.1 单模型直答 vs. 知识库加持

很多人第一次做问答机器人,第一反应是“把文档往后端一丢,让大模型自己读”。我一开始也这么干过——直接把几十页技术手册塞进API请求里,结果基本没一次成功过:模型“读不懂”不存在的页面、回答内容开始凭空捏造、整个响应时间拉长到十几秒,而且API账单直线飙升。

对比下来,“先检索、后生成”的RAG架构,本质上是一种降维打击。它的核心思路不是让模型背下你的文档,而是让它拿着手电筒,到一个抽屉里找出几页相关材料,然后聚焦在材料范围内作答。详细拆解如下:

  • 精确命中:用户问的问题,先通过向量检索把最相关的几个片段翻出来。因为有召回这一步,答案的出处是能追溯的。
  • 成本可控:无需微调任何模型,也不需要把整份文档反复喂给模型,一次只传几个片段,Token开销小得多。
  • 更新自由:文档改了,向量索引重新同步即可,模型本身不用动,后台秒级生效。

说句实话,如果只是做一个三五条规则就能解答的极简问答,直接写死规则更省事。但一旦遇到“文档多、问答开放、要求随时更新”的场景,RAG几乎是目前最合适的路径。这个场景还有一个隐性要求:企业内部文档往往有保密诉求,不能直接送到第三方API,所以我在选型上特意把“本地可部署”作为一条硬性标准。

1.2 拆解整个项目的工作流

为了让后面的实操步骤不显得散乱,我们先把整个项目的链路画在脑子里(实际实现的时候按着这条线走就行):

  1. 准备原始文档(FAQ、操作手册、售后政策等)
  2. 文档清洗与格式化
  3. 切分成语义相对完整的文本块
  4. 调用嵌入模型把文本块向量化
  5. 把向量和原始文本存进向量数据库
  6. 用户提问时,把问题同样转为向量
  7. 在向量库中执行相似度检索,得到TopN片段
  8. 组装提示词,连同检索片段一起发给大模型
  9. 大模型组织答案并返回,前端渲染输出

你可能已经注意到,这9个环节里,第1到第5步做的是“建索引”,第6到第9步是“查索引并生成”。整个项目本质上就是索引的构建和查询两个部分。把复杂系统拆成这两半去理解,后面遇到问题定位也会快很多。

我在这个项目的选型上,形成了一条完全“本地友好”的工具链:嵌入模型用BGE-M3,向量数据库用轻量级Chroma,生成模型按需求灵活替换为大语言模型API。下文每一环我都会解释清楚“为什么是它”以及“它在整个链路里到底干了什么活”。

2. 关键技术与工具选型解析

2.1 为什么必须用嵌入模型,而不是关键词搜

这个问题几乎所有第一次接触RAG的读者都会问:文档检索用ES那种全文检索不香吗?为什么非要引入向量这层概念?

关键词检索确实在处理“精确名词”上很强大,比如搜“退款政策”,它能直接匹配带这个四个字的文档。但用户提问很少规规矩矩地照抄文档表述,他们可能会问“钱多久能退回来”“退货后什么时候打款”。这时候,纯关键词匹配就失灵了,因为字面上几乎没有交集。

嵌入模型要解决的就是这个“语义鸿沟”。它会把句子转换成一串固定维度的数字向量(例如1024维),这个向量在数学空间中的相对位置蕴含了语义信息。“钱多久能退回来”和“退款到账时间”虽然在字面上不同,但在向量空间里会距离很近。我用一个生活化的类比来帮助理解:关键词检索像是拿着地图只查地名,必须一字不差;向量检索像是按经纬度找一个地点,只要描述出大概方位,它就能把你带到附近。

在这条链路上,嵌入模型的质量决定了检索的天花板。如果嵌出来的向量本身语义就不准,后面数据库和生成模型再努力都白搭。所以我选了BGE-M3作为嵌入主力,看重的是它三个特点:同时支持中文和英文,企业内部常见的中英文混杂文档不需要分开处理;输出最多8192个Token,长段落也能完整编码而不用过度截断;支持稀疏检索和稠密检索的混合模式,后续调优时多了一层工具手段。

2.2 向量数据库选型:Chroma为什么是现阶段的最优解

市面上能用的向量数据库不少,Milvus、Weaviate、Qdrant、FAISS各有拥趸。我在这类项目的早期阶段,几乎只用Chroma。原因不是它最强,而是它最贴合“快速落地”这个阶段目标。

  • 部署零负担:Chroma是嵌入式数据库,pip安装后直接以文件方式运行,不需要单独起服务。这就和SQLite与MySQL的对比一个道理:你写个日常小工具,用SQLite足够,没必要先架一台数据库服务器。
  • 接口友好:几行Python就能完成建库、写入、查询,完全面向Python生态设计。
  • 功能完整:自带集合管理、元数据过滤、持久化,足够支撑这个阶段的应用需求,不用折腾额外的中间件。

当然,如果未来文档量到了百万级、查询并发很高,那时候我会毫不犹豫换Milvus或Qdrant。但技术选型有一条基本原则:不要为了用某个工具而用某个工具,设计阶段最大的敌人是无谓的复杂度。等这台“原型车”跑通了,性能瓶颈在哪个环节完全测出来之后,再进行针对性升级,才是最经济的方式。

2.3 大模型生成:本地模型与云端API的组合策略

有了检索结果之后,还需要一个大模型来“组织语言”。这里要说明一个重要的分工:嵌入模型是负责“找得准”,大模型负责“答得好”,两者不是同一个东西。很多初学者会把嵌入模型和大模型混淆,其实它们的训练目标、输出形态完全不同,一定要区分清楚。

大模型这一层,我建议不要从一开始就锁死某一家。设计时把模型调用封装成一个通用接口,这样切换模型只需要改几行配置。如果文档涉及敏感信息、环境不允许外联,可以部署一个开源的量化模型(比如Qwen系列的中小尺寸版本)走纯内网;如果对生成质量和速度有更高要求,也可以接云端的商业API,几毫秒就能返回。

在实践时,我推荐“本地检索 + 云端生成”作为落地最快、性价比最高的组合:向量库和检索全部在本地做,把敏感数据留在内网;只把检索出来的几条文本片段发送给生成模型。这样一来,隐私风险和调用成本都控制在了合理区间。后续如果有合规要求,直接把生成模型也换成内网部署,整个架构不需要做任何其他调整。

3. 从零搭建完整链路:环境准备与核心实现

3.1 准备一套顺手的环境

动手之前,先把实验环境整利索。下面是我这几次实践下来验证过可以直接复现的版本组合,基本上都是2024年之后的稳定版本:

# 建议使用Python 3.10+,我用的3.11 python -m venv venv source venv/bin/activate # Windows下是 venv\Scripts\activate pip install chromadb==0.4.24 pip install sentence-transformers==3.0.1 pip install openai==1.35.0 pip install python-docx pypdf

需要提个醒:不要为了跟版本号而盲目升级到最新版。工具链里每一个库都有自身的依赖矩阵,比如sentence-transformers会约束torch的版本,装最新版可能引发依赖冲突。我在初期就踩过torch和CUDA版本不匹配的坑,白白浪费了半天去排查环境问题。照上面这组经过测试的版本走,可以省下不少时间。

3.2 文档清洗与格式化:决定成败的第一步

文档清洗,是整条链路里最不起眼却最能拉开差距的环节。原始文档从哪里来?一套企业内部FAQ,很可能是一个Word文件、一堆PDF、甚至还有从在线帮助中心导出的HTML。内容质量参差不齐,常见的情况包括:

  • 段首有各种手工缩进和多余空格
  • 表格内容被复制成了乱码文本
  • 图片说明文字和正文粘连在一起
  • 页眉页脚混入正文,“第1页/共10页”这种噪音出现

如果直接拿这些内容去切分,会把噪音一并向量化,检索时会优先命中这些垃圾信息。我习惯的做法是先把所有原始文档统一转成Markdown或纯文本,再做一轮清理。下面这段代码是我常用的一套清洗流程:

import re def clean_text(raw: str) -> str: # 去除页眉页脚的页码痕迹 raw = re.sub(r"第\s*\d+\s*页[,,]\s*共\s*\d+\s*页", "", raw) # 折叠多余空行 raw = re.sub(r"\n{3,}", "\n\n", raw) # 去首尾空白 raw = raw.strip() return raw

这个函数本身不复杂,关键价值在于它建立了一个“清洗意识”:在进入向量化之前,你的文本越干净,后面所有环节的信噪比就越高。实际项目里,文档类型越杂,这个环节值得投入的时间就越多。建议在处理完一批文档后,抽样抽取几个片段向量化后再查一遍,看检索返回的内容是否干净可用。

3.3 文本切分的“讲究”:大小不是拍脑袋定的

文档清洗完毕后,下一个环节是切分。切分策略直接决定检索效果,这也是整个链路里最需要经验的地方。

文本切分要考虑两个极端:块太大,混入的无关信息多,检索到的片段在语义上不够聚焦;块太小,语义被割裂,比如把“退款政策”的条款从“适用条件”中硬拆开,导致搜索不到完整的上下文。

我先后做过好几组对比实验,在一个500条内部产品FAQ的语料上,分别用128、256、512、1024个字符切分,如下是效果差异:

切块大小(字符)检索准确率(主观评测)回答完整度单次调用Token数观察结论
128偏低偏低节约信息太碎,回答缺乏上下文
256最好好适中语义块完整,问答匹配度高
512较好较好偏高仍有较好表现,但可能掺杂质
1024不稳定一般很高块内噪音太多,检索准确率下降

最终我固定为按256~512个字符切分,并在切分时增加重叠区。设置重叠区是很多教程容易忽略的细节:如果两个相关句恰好被切分边界隔开,没有重叠区的话,后半段会丢失前半段的语义;加上一个50字符左右的重叠,就能让边界两侧的内容“握手”,避免信息断裂。我实际用的切分逻辑如下:

def chunk_text(text: str, chunk_size: int = 300, overlap: int = 50) -> list[str]: chunks = [] start = 0 while start < len(text): end = start + chunk_size chunks.append(text[start:end]) if end >= len(text): break start = end - overlap return chunks

这里还有个分块级别的经验:同一文档的大标题最好能和正文放在同一个块里。例如FAQ中“三、退换货政策”这个标题和下面具体条款如果被切开,检索时只找到条款本身,模型不知道它属于“退换货政策”这个大类,回答时缺少归属感。遇到这种情况,我会把标题作为元数据附在块上一起入库,调用时直接向上追溯标题。

3.4 向量化与写入Chroma:完整可跑的索引构建代码

清洗和切分都完成之后,就可以正式构建向量索引了。这里我用到sentence-transformers加载BGE-M3模型,因为模型较大(大约2GB左右),第一次运行会有下载和加载过程,后续再跑就直接走本机缓存。

from sentence_transformers import SentenceTransformer # 加载中文友好的嵌入模型 # 如果首次下载慢,可以设置镜像源 embedding_model = SentenceTransformer("BAAI/bge-m3")

加载模型这一步,BGE模型本身有中文README的指导,推荐在查询时加上一条指令前缀“为这个句子生成表示以用于检索相关文章:”,这个前缀能小幅提升检索效果。不过我在实验中发现,对FAQ问答场景来说提升幅度不大,所以后续没有使用,你可以自行测试。

接下来把切好的文本块逐条做向量化并写入Chroma:

import chromadb from chromadb.config import Settings client = chromadb.PersistentClient(path="./faq_db", settings=Settings(anonymized_telemetry=False)) collection = client.get_or_create_collection( name="product_faq", metadata={"hnsw:space": "cosine"} # 用余弦距离计算相似度 ) # 将每个文本块转换为向量 texts = [...] # 来自chunk_text的切分结果 ids = [f"doc_{i}" for i in range(len(texts))] embeddings = [embedding_model.encode(t).tolist() for t in texts] collection.add( ids=ids, embeddings=embeddings, # 向量直接传给Chroma documents=texts, # 原始文本一并用元数据保存 metadatas=[{"source": "faq_manual_2024", "chunk_index": i} for i in range(len(texts))] )

上面代码里有一个细节值得注意:我把原始文本传进了documents字段,而不是只存向量。这样做的价值在于,向量库本身兼有存储功能,在查询时可以直接拿到原文,不需要额外维护一份“id到原文”的映射表,极大简化了代码结构。

关于向量库空间参数,我特意设置了hnsw:space: cosine,也就是用余弦相似度来衡量语义距离。为什么用余弦而不是欧式距离?因为BGE生成的向量通常需要做归一化,余弦相似度对向量的“方向”敏感,对绝对大小不敏感,这正好契合语义相似度的需求。实际测试中,我先后对比过cosine和l2,在同一个知识库上,cosine的Top5命中率要明显稳定。

还有一点要提醒:Chroma是本地持久化存储,代码中指定的./faq_db会生成一个目录,里面包含了索引和元数据。如果后续文档更新,不能直接add重复内容,而是要对文档内容hash一下生成唯一ID,再对旧ID做delete和update,保证集合里不会累积过期数据。

3.5 实现检索与生成的完整问答函数

索引建好之后,核心的问答部分反而比较简单了,一共就3步。先看下面的代码,我会逐步解释每一行的意图:

from openai import OpenAI import os client = OpenAI( api_key=os.getenv("OPENAI_API_KEY"), # 换成你的模型服务密钥 base_url=os.getenv("OPENAI_BASE_URL"), # 云端或本地的服务地址 ) def ask(question: str, top_k: int = 4) -> str: # 第一步:把问题转为向量,再从Chroma里检索 q_emb = embedding_model.encode(question).tolist() results = collection.query( query_embeddings=[q_emb], n_results=top_k, include=["documents", "metadatas", "distances"] ) # 第二步:把检索到的文本拼成上下文 contexts = results["documents"][0] context_text = "\n\n---\n\n".join(contexts) # 第三步:组装提示词并调用大模型 prompt = f"""你是公司内部AI客服助手。 请仅根据以下知识库内容回答用户问题。 如果知识库中没有相关信息,请直接回答“知识库中暂未收录该问题”。 不要编造任何不在知识库中的事实。 【知识库内容】 {context_text} 【用户问题】 {question} """ resp = client.chat.completions.create( model="gpt-4o-mini", # 按你的服务配置调整 messages=[ {"role": "system", "content": "你是一名严谨的技术文档问答助手。"}, {"role": "user", "content": prompt} ], temperature=0.3, max_tokens=500, ) return resp.choices[0].message.content

这个ask函数是整个项目的核心入口,它看起来只有30行左右,但里面的设计决策不少。第一,top_k=4不是随便定的,我曾经分别测试过2、4、6、8这几个值:2的时候回答常常漏掉关键对比信息,6以上时模型容易同时面对多段不相关的上下文,输出开始出现自相矛盾。4在大多数文档规模下是“答案信息量”和“干扰项”的平衡点。第二,提示词里“没有相关信息就直接说不知道”这句话很重要,它能在很大程度上抑制大模型的“幻觉”——模型宁可承认不知道,也不该硬编一个答案。第三,温度参数设成0.3,保留了少量灵活性,但又不至于让每次回答完全随机。对于客服问答这种对一致性有要求的场景,温度偏高绝对是个坑,我亲眼见过同一个问题拿到一到两个截然不同的答案。

大模型调用这里我用OpenAI的Python包来演示,因为它的接口已经成了事实上的行业标准。实际项目中,你完全可以把base_url换成任意兼容OpenAI协议的本地服务,代码一行都不用改。这种“套壳式”设计,让生成模型这一层始终保持了可替换的灵活性。

4. 常见问题与排查技巧实录

4.1 检索效果不佳:命中不准确怎么办

问“怎么申请退款”,结果返回了“退款政策适用范围”却没找到申请流程。这是RAG项目里最常见的故障。问题几乎都出在检索这一环,而不是生成模型。

我从多个项目中总结了一套“三步兜底排查法”,分享出来直接可复用:

  1. 打印检索结果,看看返回的Top5到底是不是相关文档。如果不相关,可以先用一个简单问题直接跑一次collection.query,检查是不是向量化或索引写入环节出了问题,包括清洗是否干净、切分是否破坏了原句。
  2. 单独测试嵌入模型的效果。先把问题转成向量,再去Chroma里人工查看距离最近的几条,确认模型是否理解问题。
  3. 如果检索引擎状态正常,就调整切分参数。把块长度从300分别改成150、500做对照实验,观察返回上下文的“纯度”变化。通常FAQ或手册类文档,答案多集中在“条件+流程+结果”的结构中,所以让每个块覆盖一个完整主题反而比缩小切块更有效。

还有一个容易被忽视的检索缺陷来源:文档存在大量同义词或近义表述。比如文档里写“退货”,用户问“退换”,如果你的嵌入模型词典里“退货”和“退换”距离不够近,召回就可能失败。这时候与其频繁调整嵌入模型,不如在文档清洗阶段建立“同义词归一化”规则,把“退款”“退货政策”“退款说明”统一改写为标准说法,效果来得更直接。

4.2 大模型幻觉:回答里出现了文档里没有的“事实”

我曾经收到一个测试反馈:用户问“这款设备是否支持5G”,系统返回了一大段关于“5G频段支持”的说明,可这份文档从头到尾都没提过5G。这就是典型的大模型幻觉。排查思路如下:

最常见的原因是提示词里的约束不够硬。有些开发者仅仅是简单地把上下文拼在问题前面,模型很容易把过去预训练的记忆带出来。我在实践中把提示词升级成了“知识库内容”和“用户问题”分离的结构,同时增加强约束语句“禁止使用知识库之外的信息。若无法回答,直接回复无法回答”,实测幻觉出现的概率能下降超过4成。

其次,检查增强检索环节是否真的“拿到了”关键上下文。有一次我的知识库里明明存了“产品重量为1.2kg”,但检索结果里就是没召回。后来发现是切分时把“产品重量为1.2kg”这个信息块切到了两个文本块里,模型只看到了半句。找到原因后,我把重叠区从50个字符扩大到了80个,这个问题就消失了。

如果做了上述调整仍频繁产生幻觉,建议在最终答案上追加一个“置信度判断”:让模型在回答的同时,给出“该回答在知识库中的依据片段”。把这个依据一起显示给用户,既能方便用户核实,也能反向倒逼模型更谨慎地参考上下文。

4.3 性能与成本:首响应太慢、调用费用太高

本地检索跑得很快,但一到大模型生成环节,小到几秒多到十几秒。常见场景下,多数原因是:

  • 模型输入过长。如果每回检索都塞4段以上长文本,请求耗时会显著增加。对策是把检索上限调回3~4段,并给每段加上“最大长度截断”,整个上下文控制在1500个Token内。
  • 生成模型本身慢。云端API有时因为节假日或高峰流量增大响应延迟,这就需要在产品和架构层面做兜底:要么切换更快的模型,要么增加缓存层,把高频问题永久缓存,命中后直接返回。
  • 向量化同步任务拖慢主链路。如果每次调用ask都现场执行一次embedding_model.encode(question),当模型从磁盘加载到显存这个过程本身就有不可避免的等待。解决方法是把嵌入模型启动时预加载成全局单例,不用每次查询都重新加载。

关于成本控制,有一条很实用的经验:在Chroma查询时,可以先按n_results=6召回,然后在代码里按相似度分数过滤掉超过某个阈值的片段,保留最相关的3段再拼接上下文。看起来只是多了一步过滤,实际调用Token数能明显下降,因为省掉了很多相关性不足的无效内容。

4.4 文档更新后索引不同步的处理

企业文档从不是一成不变的。问答机器人上线之后,最常遇到的就是“我改了文档,机器人还在答旧内容”。这背后就是索引同步问题。Chroma的collection.add不会自动去重,我需要手动管理ID:

import hashlib def doc_id(text: str) -> str: return hashlib.md5(text.encode()).hexdigest() # 每次新增/更新文档时 for chunk in new_chunks: cid = doc_id(chunk) existing_ids = set(collection.get(ids=[cid])["ids"]) if cid in existing_ids: collection.update(ids=[cid], documents=[chunk], embeddings=[embedding_model.encode(chunk).tolist()]) else: collection.add(ids=[cid], documents=[chunk], embeddings=[embedding_model.encode(chunk).tolist()])

使用内容哈希作为文档ID,好处是幂等:同一个内容无论重复提交多少次,最终都只保留一条,不会产生重复索引。除此之外,建议在元数据里记录“最后更新时间”,每次全量同步时根据时间戳清理失效文档。这个过程单独封装成sync_documents()函数,配合每天的定时任务跑一次,就能让线上机器人对文档变化保持敏感。

5. 调优方向与实际项目复盘

5.1 基于评测反馈做迭代:不要凭感觉调参

项目上线后,最忌讳的是“凭感觉调参数”。正确的做法是先把评估集建起来,这样才能在迭代时辨别改动是好是坏。我做的评估集包含了30个“问答对”以及对应的“期望命中文档标题”,每次调参后我都会重新跑一遍,计算“命中期望文档的比例”。

如果命中率低于80%,优先调整切分和检索层;如果命中率高于80%但答案依然不理想,则把精力放在提示词和生成模型上。这个简单的对比机制,能帮我把精力聚集在最值得优化的环节,而不是东改一下西改一下变成无头苍蝇。

在这个评估流程里,一个容易被漏掉的是“答案延迟和Token消耗”。同样的功能,A方案用的上下文短、响应快,B方案堆了大量冗余信息,效果看似相同但成本天差地别。我建议把“回答字数、调用Token数、检索耗时”三个指标一并纳入评估记录,用数据指导优化,不用情绪指导优化。

5.2 用混合检索挽救长尾问题

只靠向量检索,有些边缘情况会持续拉低系统得分。比如用户问“你们有线上客服吗”,文档里的说法是“支持在线咨询,工作时间9:00-18:00待命”。向量可能能匹配上“在线咨询”,却不一定匹配“线上客服”这种同义换说法。我引入了一层简单的关键词权重的混合检索,把向量检索结果和BM25关键词检索结果按比例融合,效果立竿见影。

实现混合检索时,不需要复杂的数学融合逻辑。Chroma支持where元数据过滤,同时可以用Python结合jieba分词做简易BM25,再按”向量距离排名与关键词命中排名加权”取交集。虽然这部分代码需要多写几十行,但对长尾问题的覆盖率提升非常明显。如果你用的向量库不支持混合检索,也可以用现成的Ranker工具包在内存里完成重排,不用非要升级整个系统。

5.3 从原型走向可维护:模块化与日志体系

项目到了交付阶段,最容易翻车的地方反而是工程化和可维护性。如果整个应用只有一个巨型脚本,后面每次改动都可能拖垮别的地方。我建议至少拆出四个模块:

  • loader.py:负责文档读取、清洗、切分
  • embedder.py:封装嵌入模型加载和向量化接口
  • vector_store.py:封装Chroma的初始化、更新、查询
  • generator.py:封装大模型调用与提示词模板

同时,在ask函数里加上结构化日志,记录检索命中的文档ID、相似度分数、用户问题、模型返回的答案。这套日志体系的价值在排查问题时体现得淋漓尽致:没有日志时,用户反馈“答案不对”你只能靠猜;有日志后,可以直接回溯到“这次回答到底用了哪些上下文”。在把项目转交给同事的时候,这份日志就是最有效的交接文档。

我还遇到过一个细节问题:线上环境里并发请求多个用户,如果全局向量库连接和嵌入模型实例是被多个线程共享的,容易出现资源竞争和结果串扰。我的解决办法是把Chroma和嵌入模型实例做成进程级的单例,同时在调用ask时加一层轻量级锁。这一步在生产环境是必须处理的硬伤,否则并发稍高各种奇怪问题全部暴露出来。

5.4 下一步还能做什么:从问答到更多形态的应用

整个项目跑通之后,这个技术栈的延伸空间其实很大。除了问答机器人,用同一条“本地知识库 + 向量检索 + 大模型生成”的链路,还能做很多事:

  • 文档摘要助手:把长文档切成块后并行调用模型生成摘要,再汇总输出,效果比单次硬喂全文档稳定得多。
  • 智能写作辅助:检索历史方案作为参考,辅助生成新方案草稿,既保证专业性,也避免完全凭空发挥。
  • 客服工单分类:把历史工单向量化,新工单进来时自动找到最相似的历史案例,辅助处理人员判断优先级。

对我个人来说,这套项目最大的收获不是某个库用得多熟练,而是建立起了一种“系统工程”的思维方式:从数据源头开始,每个环节都有它在整体中的职责,每个模型都有它擅长与不擅长的边界。你能在哪个环节上把问题限制住,整个系统的输出质量就在哪个环节上得到提升。

最后再分享一个非常实用的小技巧:做完向量化和检索之后,不要急着接大模型。先单独用向量数据库的“只检索不生成”模式跑一遍所有测试问题,看着返回结果自己心里对知识库的覆盖能力有个底,再跑去接生成模型。很多人一上来就把全部组件一次性拼接,出了问题根本不知道该切在哪里。把对接顺序拆分,一步一步验证,所有问题都变得可定位、可解决。这也是我写这个系列以来最想传递给读者的一个核心经验。

返回列表