模拟面试:私有语料库RAG设计
这篇文章将帮助你理清如何应对目前最常见的数据科学/ML系统设计案例研究之一——"如何设计一个 RAG 系统"。
AI模型价格对比 | AI工具导航 | ONNX模型库 | Vibe Coding教程 | PLC在线仿真器 | Tripo 3D | Meshy AI | ElevenLabs | KlingAI | ArtSpace | Phot.AI | InVideo
本文模拟了一场真实面试,因此采用多轮对话的形式。我会添加侧边栏注释,让你理解每一步背后的结构和策略。现在,让我们进入正题!
Interviewer: 设计一个检索增强生成(RAG)系统,使用私有语料库——即内部文档、PDF和知识库文章来回答用户问题。
第一部分:澄清问题
SideBar:在开始任何设计之前,目标是锚定目标、约束和规模。这展示了结构化思维,并防止你解决错误的问题。
Candidate: 好的。在我开始画任何东西之前,我想先问几个澄清问题,这样我就不会在毫无头绪的情况下做设计。
我首先想了解的是谁在使用这个系统以及他们想做什么。这是一个内部工具(比如员工询问公司政策和工程运维手册相关的问题),还是面向客户的,比如客服机器人?因为两者的故障模式完全不同。如果客户得到了错误答案,那是品牌问题。如果员工得到了关于HR政策的错误答案,那是运营问题。风险级别和可接受的延迟也完全不同。
Interviewer: 把它当作内部工具来考虑。员工使用内部文档提问:HR政策、工程运维手册、产品规格。
Candidate: 明白了。第二个问题:我想大致了解一下语料库的规模和更新频率。我们讨论的是几百个几乎不变的文档,还是一个大型、活跃更新的知识库?
Interviewer: 中型企业。大约50万份文档。半静态,每周更新或新增,不是实时的。
Candidate: 在继续之前我想确认的最后一件事:延迟预期是什么,业务对准确性与覆盖率的重视程度如何?具体来说,给出错误答案和说"我不知道",哪个更糟糕?
Interviewer: 同步聊天,端到端5秒以内。错误答案比没有答案更糟糕。这是企业级应用,错误信息会造成实际的运营损失。
Sidebar:三个澄清问题是最理想的数量。你已经锁定了(1)用例和故障模式,(2)语料库规模和动态性,以及(3)延迟和质量的权衡。不要过度澄清。面试官想看你做设计。在继续之前做一个总结。
Candidate: 完美。那么在我开始之前确认一下我们达成了一致:我们要构建一个内部企业知识助手,语料库大约50万份文档,每周更新,需要在5秒内响应,而且我们应该始终选择说"我不知道"而不是猜测。明白了。让我想一下,然后给你讲我的方案。
Sidebar:暂停30到60秒是完全可以的。大声说出你的思维过程。比如"我把这看作两个独立的系统,一个离线运行,一个在线处理请求……"这展示了你的思考过程,而这正是面试官在评估的。
第二部分:系统设计
Candidate: 好的,我从高层来构建这个问题的框架:这里实际上有两个不同的系统,我认为从一开始就把它们分开很重要。
第一个是离线索引管道。它负责接收原始文档,进行处理,将它们切成块,对这些块进行嵌入,并构建可搜索的索引。它按照自己的调度运行(在我们的场景中是每周一次),与任何实时计算有着完全不同的计算预算。
第二个是在线服务管道。这是每次用户提交问题时运行的部分。它处理查询理解、检索、重排序、上下文组装、生成和护栏。
我想把它们分开的原因是,将它们混在一起会导致系统要么太慢——因为你试图在实时路径中做繁重的离线工作——要么太陈旧——因为你为了达到延迟目标而在索引上偷工减料。保持它们的清晰使每个系统更容易独立优化和调试。
我应该先讲离线部分吗?
Interviewer: 好的,请开始。
2.1 文本预处理
Candidate: 好的,让我从离线管道的最开始讲起,也就是文档摄入。
文档会以各种格式到达:PDF、Word文档、HTML页面、Markdown文件、纯文本。我会使用一个文档解析层来从所有这些格式中提取干净的文本和结构。比如 Apache Tika,对于特别不寻常的格式再加自定义解析器。
现在,PDF是我会压力测试最重的格式。简单的PDF文本提取——就是你只拉取原始文本层——在文档布局复杂时会产生垃圾输出。表格被错误地线性化,脚注出现在段落中间,章节标题混入正文。对于企业语料库来说,这是一个严重的问题,因为表格通常包含最精确、可操作的信息,例如薪酬区间、错误代码定义、SLA阈值等。如果在解析阶段就损坏了这些内容,下游是无法修复的。我会使用一个理解文档结构的布局感知解析器,表格特别会被转换为结构化的 Markdown,以便可检索。
在这个阶段,我还会提取并保留文档级元数据:标题、作者、部门、创建日期、最后修改时间戳、文档类型和访问权限。这些元数据后来会成为过滤层。
Interviewer: 那些没有嵌入文本的扫描图像文档怎么办?
Candidate: 是的,这对较老的企业语料库来说是一个真实存在的问题。扫描的HR文件、法律文件等等。对于这些,我会在解析之前添加一个 OCR 阶段。我会使用像 AWS Textract 或类似的工具,因为它们对布局有更好的理解,特别是对于扫描文档中的表单和表格。OCR 输出进入相同的下游管道。我还会用源类型标记来标注这些文档,这样下游的质量检查可以区别对待它们。OCR 提取的文本噪声更高,你在调试检索失败时需要知道这一点。
Interviewer: 好的,听起来合理。请继续。
2.2 分块
Candidate: 好的,文本提取和清洗完成后,下一步是分块,我认为这实际上是大多数 RAG 系统出错而不自知的地方。你能获得的检索质量从根本上受限于你的分块质量。
基本的矛盾是:小块精确但丢失上下文,大块有上下文但稀释了检索信号。所以与其选择一个大小然后调优,我会采用一个三层级的层次化策略。
最细粒度层面,大约100个 token 的句子级块。这些适用于狭窄的事实查找查询,你想要精确匹配。中间层面,300到500个 token 的段落级块,这是大多数问题的主要检索单元。最顶层,大约800到1000个 token 的章节级块,我不用于检索,而是在找到匹配后用于上下文扩展。我稍后会回到这一点。
几个重要细节。相邻块之间重叠约10%到15%,这样相关上下文不会掉进两个边界的缝隙中。块边界应尊重文档结构,这意味着我不会在句子中间分割,理想情况下也不在段落中间分割,每个标题都包含在它引入的块的顶部,这样检索器就有主题上下文。每个块还存储指向其父文档的指针、它在文档中的位置以及相邻块的 ID。
Interviewer: 为什么要三个层级?不能只选一个好的大小然后调优吗?
Candidate: 问题在于不同的查询确实需要不同的粒度。"国内航班的最大报销额度是多少?"这是一个狭窄的事实查询,你想要紧凑、精确的块。"解释公司对绩效评估的方法"需要更多周围上下文才能产生连贯的答案,小块会让人感觉不完整。单一固定的块大小最终是一个折衷方案,在两者上都表现平庸。层次化方法让检索层在正确的粒度上命中,我提到的上下文扩展让生成无论匹配在哪里找到都能处理足够的上下文。
Interviewer: 你如何实际评估你的分块是否好?这似乎很难衡量。
Candidate: 确实很难衡量,大多数团队跳过了这一步,这正是他们无法在检索失败发生时正确诊断的原因。我会运行三项检查。第一项是边界敏感性测试:取一组有已知答案跨度的查询,验证答案文本完全包含在单个块中而不是被分割到两个块之间。如果答案跨度频繁跨过边界,说明分块配置有误。第二项是连贯性审计:抽样几百个块,让人类或 LLM 评委评估每个块是否在语义上自洽。一个从论证中间开始且没有上下文的块是危险信号。第三项是表格保真度检查:对于有已知表格的文档,验证提取的块是否正确表示了表格。这些是离线审计,不是生产指标,但在任何上线前和任何解析器或分块逻辑变更后都是必不可少的。
Interviewer: 好的,听起来不错。让我们继续你设计的下一部分。
2.3 表示与索引
Candidate: 好的,现在块已经存在了。下一个问题是我们如何在数值上表示它们并构建可搜索的东西。
每个块使用文本嵌入模型编码为密集向量。对于企业用途,我默认使用像 OpenAI 的 text-embedding-3-large 这样的模型。如果语料库高度特定于某个领域(即大量法律术语、医学术语或深度工程行话),在本例中可能是这样,我会考虑使用对比学习目标在领域内查询-段落对上微调嵌入模型,因为现成的模型在专业词汇上可能表现不佳。
有了向量之后,我们需要构建索引——这是下一个关键决策。我不会只建一个密集向量索引。我会构建两个并行索引:一个使用 HNSW 的密集索引(放在向量数据库中),以及一个使用 BM25 的稀疏索引(放在 Elasticsearch 等快速搜索引擎中)。你需要两者的原因是,密集检索擅长语义相似性,但在精确匹配方面确实有困难:产品代号、错误代码、精确法律引用、内部系统标识符。搜索 "error ER-4412" 的用户可能会得到语义上关于错误处理的文档,而不是实际定义该特定代码的那一份文档。BM25 不受这个问题影响,因为它是纯词法的。维护稀疏索引的成本很低,但对精确匹配查询的覆盖改善是显著的。
Interviewer: 当你想升级到更新的、更好的嵌入模型时会怎样?你已经有50万份文档已索引了。
Candidate: 这是一个真正的运营难题。当嵌入模型改变时,嵌入空间也变了,这意味着旧索引与新查询嵌入不兼容。你必须对整个语料库重新嵌入和重新索引。对于50万份文档,每份有多个块,这可能意味着数百万次嵌入调用,如果是自托管的话则需要大量 GPU 时间。
如果我需要真正把它投入生产——我会使用影子索引策略。你在旧索引继续服务流量的同时并行启动新索引。在过渡窗口期间同时运行两个检索器,对照一个保留的评估集比较检索质量,在验证新索引确实更好之后再切换。
Interviewer: 好的,听起来不错。我们继续。
2.4 查询理解
Candidate: 好的,离线部分就到这里。现在让我转向当用户提交问题时实时发生的事情。
第一件事不是检索。而是查询理解。用户输入的原始查询通常不是检索的最佳形式。用户的表达是对话式的。他们会问"如果我错过了开放注册截止日期会怎样?"这种冗长且口语化的问题。直接将其嵌入到与技术文档块相同的密集空间中会造成领域不匹配。
所以我会通过一个轻量级 LLM(一个快速的小模型,不是昂贵的生成模型)来重写查询,将其转换为一个或多个适合搜索的形式。那个问题可能变成"开放注册截止日期政策"和"错过开放注册的后果"。对于较长的多部分问题,这可能产生三到四个子查询,并行检索后合并。
还有一个更激进的变体叫做 HyDE(假设文档嵌入),你不是嵌入查询,而是生成一个简短的假设文档来回答问题,然后嵌入那个文档。直觉是,假设答案在嵌入空间中比问题本身更接近真实的文档段落。这对于复杂或抽象问题效果出奇地好。
除了查询重写,我还会从问题中提取任何元数据过滤器。"法务部门上个月发布了什么?"应该被转换为部门等于 Legal 以及最近30天的日期过滤器。在多轮对话中,我会在前面加上最近两三轮的简短摘要来提供上下文。否则,像"兼职员工呢?"这样的后续查询就会变得模糊。
Interviewer: 如果查询的内容是语料库根本没有涵盖的怎么办?查询重写和 HyDE 仍然会运行,你只是默默得到了糟糕的检索结果。
Candidate: 对,这正是置信度门控发挥作用的地方,我稍后会讲到。但在查询理解阶段,实际上有一件主动的事情可以做:查询意图分类。一个轻量级分类器可以在检索运行之前就标记出明显超出范围的查询:偏离主题的问题、关于外部公司的查询、匹配已知不支持类别的查询。这些立即得到一个干净的"不在范围内"的响应。这对用户更好,也节省了计算资源。对于范围内但检索恰好失败的查询——因为语料库有缺口——那由下游的置信度门控来捕获。
Interviewer: 好的,那我们继续。
2.5 检索:候选生成与重排序
Candidate: 好的,现在我们真正进入检索了。我分两个阶段运行:候选生成和重排序。
对于候选生成,密集检索和稀疏检索并行运行。我嵌入重写后的查询,命中 ANN 索引,获取前50个密集候选。同时运行 BM25 获取前50个稀疏候选。这两个列表使用 Reciprocal Rank Fusion,即 RRF 进行合并,它仅使用排名位置而非原始分数来组合排名。这很重要,因为余弦相似度和 BM25 相关性在完全不同的尺度上,所以你不能直接比较它们。RRF 完全绕过了这个问题。输出是一个大约50到80个唯一候选的合并列表。
这些候选然后通过一个交叉编码器重排序器。这里是关键区别。嵌入阶段使用的双编码器独立编码查询和每个文档,并在结果向量空间中计算相似度。交叉编码器将查询和候选文档一起作为联合输入,直接预测相关性分数。这种联合编码让模型在查询和文档之间进行细粒度的 token 级注意力计算,这是一个更准确的相关性信号。代价是速度要慢得多,这就是为什么你只在短名单而不是整个语料库上运行它。像 MiniLM 这样的模型可以在大约300毫秒内对80个候选打分。我们从中取前5到10个,传给上下文组装。
Interviewer: 为什么用 RRF 而不是一个真正权衡密集和稀疏信号的学习式融合模型?
Candidate: 学习式融合模型需要标注的训练数据,而且每当查询分布变化时都需要重新训练。RRF 完全不需要训练,不需要校准,而且在大多数基准测试中经验性地匹配或优于学习式融合。原因在于在管道的这个阶段,你仍然处于召回导向模式。你只需要一个干净的、去重的候选池。紧跟其后的重排序器在做所有精确度方面的工作。在这里添加学习式融合模型是增加了运营复杂性却只有边际收益。
Interviewer: 你说重排序器需要300毫秒。如果延迟预算缩减怎么办?
Candidate: 有几个调节手段。第一,减少候选池。对30个候选打分的延迟大约是80个的一半。第二,使用更小的重排序模型,有比 MiniLM 更小的蒸馏交叉编码器。第三,这对于企业场景最实用的——为热门查询缓存重排序结果。在企业环境中很多员工问相同的问题,比如"育儿假政策是什么?"或"如何提交报销?",你可以在查询嵌入聚类级别而非精确字符串级别缓存完整的检索结果,相当一部分流量命中缓存,零延迟成本。
Interviewer: 如果排名最高的块是两周前重新索引的,但源文档昨天更新了怎么办?
Candidate: 这是一个新鲜度问题,需要显式处理而不是悄悄地提供过时内容。在索引时,每个块存储其索引时间戳以及源文档的最后修改时间戳。在服务时,当组装顶部结果时,服务层会检查这两个时间戳是否匹配。如果不匹配——即源在块被索引后发生了变化——我会向用户显示一条提示:"此文档最近已更新。请以源文档为准进行核实。"对于每周批处理系统中的大多数文档来说,七天的过期窗口可能是可接受的。但对于时间关键文档——比如法律批准后立即变更的政策——我会添加一个优先重新索引队列,在更新后几分钟内处理那些特定文档,不受每周批次的限制。
Interviewer: 好的,听起来合理。我们继续。
2.6 上下文组装
Candidate: 好的,我们从重排序中获得了前5到10个块。现在我需要将它们组装成一个提示,给 LLM 最大的机会产生准确、有依据的答案。
在将块发送给模型之前我做两件事。第一,上下文扩展。对于每个块,我还拉取同一文档中的相邻块——紧接在其前面和后面的。一个在论证中间结束的块给 LLM 的信息是不完整的,而我在索引时建立的层次结构使这成为按块位置的廉价查找。
第二,上下文压缩。如果组装的上下文变得很长,我运行一个轻量级抽取式模型,对每个块中的每个句子按与特定查询的相关性打分,只保留得分最高的句子。这去除了噪声,实际上还减少了幻觉,因为如果无关上下文不存在,LLM 就不太可能抓住它不放。
最终的提示有三个部分。一个系统提示,明确约束模型:"仅根据提供的上下文回答。如果答案不在其中,请说明。不要推测或依赖通用知识。"检索到的块,每个标注了源文档名称、章节和最后修改日期。然后是用户的原始问题。
我会流式传输响应而不是等待完整生成。这样在大约1.5到2秒时第一个 token 就到达用户,极大改善了系统的感知速度,即使总时间接近5秒。
Interviewer: 如果两个检索到的块相互矛盾怎么办?比如三月份的一份政策文档说员工有15天,而上周更新的那份说12天。LLM 会试图默默地将它们综合起来。
Candidate: 是的,这是一个真实的场景,可能发生的最糟糕的事情是 LLM 把它们平均一下然后自信地说13天。正确的行为是暴露冲突,而不是默默解决它。我会在两个层面处理。在上下文组装时,如果两个块来自具有相同标题和部门但不同最后修改时间戳的文档,组装层将其标记为版本冲突并插入一条结构化通知:"注意:此文档存在两个版本。以下内容来自最新版本,日期为X。"然后在生成提示本身中,我会明确指示模型:"如果提供的上下文包含矛盾信息,请说明矛盾并引用两个来源,而不是选择其中一个。"用户可以明确看到冲突,并知道要去权威来源核实。
Interviewer: 听起来合理,我们继续讲你还能做什么?
2.7 护栏
Candidate: 好的,现在是护栏。这是很多生产 RAG 系统默默失败的地方,所以我想具体说明一下深度防御在这里到底意味着什么。
第一层也是最重要的是检索置信度门控。在甚至调用 LLM 之前,我检查最高重排序分数是否达到最低阈值。如果最高相关性的块没有达到该阈值,系统返回一条干净的"我在可用文档中找不到这个问题的可靠答案"然后停止。这至关重要。大多数 RAG 幻觉不是因为 LLM 在无人提示的情况下编造内容。它们发生是因为检索失败,LLM 被交付了一个空的或松散相关的上下文并被要求从参数化记忆中填补空白。从源头阻止这比任何数量的提示工程都更有效。
第二层是生成提示中的引用强制。模型被要求为它做出的每个事实性声明引用一个特定的源块。这迫使它锚定在检索到的内容上。如果它不能指向一个来源,它就不应该说。
第三层是生成后的忠实度评分。在模型产生响应之后,我运行一个轻量级 NLI 模型,检查响应中的每个句子是否确实被检索到的上下文所蕴含。不被蕴含的句子被标记出来,如果整体忠实度分数低于阈值,系统要么用更严格的提示重新生成,要么返回一个带免责声明的部分答案。
最后,UI 中的答案置信度分层。高置信度答案带完整引用发出。中等置信度的带"请以源文档核实"的注释和直接链接发出。低置信度的只说"我没有这个问题的可靠答案",并提供升级到人工专家的途径。
Interviewer: 忠实度评分步骤在生成之后运行,所以它增加了延迟。你如何保持端到端在5秒以内?
Candidate: 窍门是将忠实度评分与响应流式传输并行运行,而不是在它之后顺序运行。生成开始的那一刻,响应就被流式传输给用户。忠实度评分在流式传输过程中对每个完成的句子运行。它在 LLM 生成第二个句子的同时检查第一个句子。到用户读完前几个句子时,那些句子的忠实度检查已经完成。如果在响应过程中检测到违规,UI 会插入警告横幅而不是中断流。NLI 模型本身很小。一个蒸馏的 DeBERTa 变体可以在不到50毫秒内对一个句子打分,整个完整检查在典型响应上完全在流式传输窗口内完成。用户感知不到任何额外延迟。
Interviewer: 访问控制怎么办?你的检索层不知道给定的用户被允许查看哪些文档。
Candidate: 访问控制需要作为硬性的检索前过滤器来执行,而不是检索后的清理。在索引时,每个块都标注了其源文档的访问层级——无论是公开的、团队限制的还是高管专属的。在服务时,在运行任何检索查询之前,查找用户已认证的身份并解析其权限集。ANN 和 BM25 查询都限制在用户允许的访问层级内的块。对于未授权用户,机密内容永远不会进入候选池。这是服务端执行的,不是客户端,所以不能被绕过。一个值得指出的细微之处:一些企业文档有公开的前言但限制性的附录。文档级访问标记对这些不够细粒度。你需要块级访问标签来反映同一文件内的章节级权限。
第三部分:评估
Interviewer: 好的,评估方面呢?
Candidate: 我会在五个层面构建评估,每个层面针对管道的不同阶段。
第一是摄入和分块质量。这是最被忽视的层面,也是下游失败最上游的来源。如果块是坏的,其他什么都修复不了。我会运行三项检查:边界敏感性测试来验证答案跨度没有被分割到块边界之间,连贯性审计抽样块来检查它们是否在语义上自洽,以及有已知表格的文档的表格保真度检查。这些是离线审计,不是实时指标,但在任何上线前都是不可协商的。
第二层是检索质量。Recall@K 衡量有多少相关文档出现在前 K 个结果中。MRR 衡量第一个相关结果排名有多高,以及 NDCG@10 作为分级排名质量指标。我会分别测量纯密集、纯稀疏以及混合加 RRF 的表现,这样你可以精确量化每个组件的贡献。
第三是重排序质量,具体是重排序前后的 NDCG@5,确认交叉编码器确实在双编码器输出之上有所改善。我还会绘制不同重排序器大小的延迟-质量权衡曲线,找到正确的运行点。
第四是生成质量和基础性。主要指标是忠实度,即生成的声明中有多少被检索到的上下文所支持。次要指标是由 LLM 评委评分的答案完整度、幻觉率(定义为任何包含至少一个无支持声明的响应)以及引用准确度。
第五是端到端用户成功率。通过点赞/点踩反馈的任务完成率、无答案率、后续查询率(作为前一个答案是否完全解决了问题的代理指标),以及升级到人工专家的比率。
所有这些都建立在一个500到1000个问答对的黄金评估集之上,由主题专家手工验证,每季度刷新。
Interviewer: 构建1000个标注对听起来很贵。你实际上如何引导这个过程?
Candidate: 三种在实践中有效的方法。第一,从试点上线或现有搜索日志中挖掘真实用户查询。真实问题比合成问题价值高得多,因为它们反映了员工真正需要什么。第二,LLM 辅助标注:给定一个文档块,让 LLM 生成答案在该块中的合理问题,然后让人类审核一个样本。这让你以大约10倍于从零编写的速度达到1000个对。第三,从生产中积累隐式反馈作为弱标签:点赞/点踩信号、后续查询、升级。有噪声,但可扩展,而且它让评估集随时间被动增长。
Interviewer: 你把无答案率作为一个指标来追踪。你如何区分是语料库缺口还是检索出了问题?它们产生相同的数字。
Candidate: 完全正确,这就是为什么你不能仅从无答案率来诊断。你需要检索质量指标并行运行。如果无答案率飙升但评估集上的 Recall@K 保持稳定,那是语料库缺口。用户在问确实还没有被记录的东西。如果两者同时下降,那是检索回归,可能是由语料库更新或破坏索引的分块变更引起的。我还会添加一个信号:对未回答查询进行查询聚类。如果未回答的聚类映射到语料库中不存在的一个连贯主题,那是文档缺口,需要升级给内容团队。如果它分散在之前覆盖良好的各个主题中,那就是需要在索引管道中调查的技术回归。
Interviewer: 好的,评估方面还有什么?
3.1 在线监控
Candidate: 好的,最后一部分。一旦这个系统上线了,你如何确保它不会在无人注意的情况下慢慢退化?
我会持续监控三件事。第一,查询漂移。用户问的问题分布会随着公司的变化而随时间偏移。我会使用滚动相似度指标(与前30天窗口对比)来追踪传入查询的嵌入分布。漂移峰值通常意味着用户在问语料库尚未很好覆盖的主题,这是内容缺口信号,不是ML故障。
第二,逐查询质量信号。忠实度评分器在生产中对每个响应运行,所以我会聚合滚动忠实度率、滚动无答案率,以及按管道阶段分解的P95端到端延迟。最后一点很重要。如果P95恶化,你想立即知道瓶颈是检索、重排序还是生成。
第三,语料库健康探针。对于被查询最多的前100个文档,我会维护一组与每个文档关联的已知问答对。在每周批处理更新后,运行这些探针,如果 Recall@K 下降就发出警报。这能捕获文档更新默默破坏高流量主题检索的情况。
所有这些都汇入一个持续改进循环。来自用户信号的检索反馈被用于定期在领域内数据上微调嵌入模型和重排序器。满意度低的查询类别通过提示实验来针对性改进。无答案率高的主题聚类被呈现给内容团队作为文档缺口。
Interviewer: 基于用户反馈微调听起来理论上是好的,但用户只对他们实际收到的答案给出反馈。你的训练信号偏向于检索已经成功的情况。
Candidate: 是的,这就是暴露偏差问题。你只收集了你展示的内容的标签,所以你的微调数据系统地低估了检索失败的情况。两种方法来解决这个问题。第一,难负样本挖掘:对于每个正标注的查询-文档对,采样排名靠高但未被选为相关的文档。这些在微调目标中作为对比负样本,教会模型什么不该检索。第二,随机化探索:定期注入一小部分故意随机化的检索结果,展示模型通常不会排名靠高的文档,并收集对这些的反馈。这从当前检索分布之外产生标注示例。随机化结果在内部被标记,永远不作为权威答案展示,只用于构建训练信号。这与多臂老虎机系统中的 epsilon-greedy 探索原理相同。
Interviewer: 很全面。我认为这涵盖了我所有的内容。你有什么问题要问我吗?
Candidate: 实际上有的。在实践中,RAG 团队和实际编写文档的人之间的反馈循环是什么样的?根据我的经验,发布这类系统最困难的部分不是 ML。而是让文档作者以使检索可靠的方式组织他们的内容。你看到团队是通过工具、培训还是治理来解决这个问题?
Sidebar:提出一个识别技术系统背后组织摩擦的问题,表明了生产经验。最好的 RAG 系统的瓶颈不是检索算法。瓶颈是文档质量。问这个问题表明你真正上线过这样的系统。
4、核心要点
这个问题看起来像一个 ML 系统设计问题,但它实际上同时在测试五件事:你分离离线和在线关注点的能力、你在检索栈上的深度、你对基于 LLM 的系统在生产中在哪里失败的认知、你在每个阶段而非仅端到端评估的能力,以及你对系统如何随时间退化的直觉。
请特别注意我们强调的几点——
从一开始就保持离线和在线分离。 将它们混在一起会导致要么陈旧要么太慢的设计。当你说"我会把这看作两个独立的系统"的那一刻,你已经在展示架构成熟度了。
在分块上深入。 大多数候选人把它当作一个脚注。它是整个管道中最高杠杆的决策之一。层次化分块、结构感知、边界重叠和块到文档的链接是让检索可靠而非断断续续地工作的关键。
明确指出双索引。 仅密集检索在精确匹配查询上有系统性盲点。仅稀疏检索遗漏语义含义。混合加 RRF 是生产标准,具体指出它表明你在真正的检索系统上工作过。
分层设置护栏。 置信度门控、引用强制、忠实度评分和 UI 中的置信度分层各自捕获不同的故障模式。把系统提示当作解决幻觉的全部答案是一个危险信号。
分别评估每个阶段。 强候选人对分块、检索、重排序、生成和端到端成功都有明确的指标。把所有东西折叠成一个用户满意度指标使得几乎不可能诊断哪里出了问题。
关闭监控循环。 上线时的 RAG 系统不是一个完成的产品。查询漂移、语料库更新和不断积累的反馈都创造了持续改进的机会。明确描述这个循环是将生产思维与原型思维区分开来的关键。
以上就是全部内容!
原文链接:Data Science Case Study: Design a RAG System for a Private Corpus
汇智网翻译整理,转载请标明出处