将 RAG 部署到生产环境

34 阅读48分钟

现在,你已经了解了一条 RAG 流水线的所有组件,包括基础组件和高级组件,因此可以很容易地搭建出一个相当不错的概念验证(POC)。对于第一个 POC 来说,通常会选择一个对组织有显著价值、但初始投入相对较低的使用场景。这样,你就可以学习它实际是如何工作的,并亲身理解 RAG 的运行方式。

让一个 RAG 概念验证跑起来是一件很有趣的事。你拿一个强大的大语言模型,让它指向你的文档或数据,在向量数据库中实现查询嵌入向量和 chunk 嵌入向量之间的向量相似度,然后 voilà——你就可以开始提问,并基于文档内容获得真实答案。

如果你把它当作副项目来做,只需要适度的时间和精力。然而,如果你的目标是构建一个生产级 RAG 应用,它需要可扩展、安全、快速,并且为公司提供任务关键型服务——那就是另一回事了。

从 POC 走向生产级 RAG 应用部署,会给企业带来许多挑战,横跨技术、运营和组织多个领域。随着 RAG 应用扩展,你通常会遇到延迟瓶颈、供应商集成复杂性、数据安全要求,以及跨学科专业能力缺口。

在本章中,我们会更深入地讨论其中一些挑战,并在可能的情况下,讨论解决这些挑战或最小化负面影响的策略。虽然本章会包含一些代码片段,但我们不会提供一套完整的端到端生产 RAG 流水线代码库。真正生产级的 RAG 架构很少能被包含在单个脚本或 notebook 中——它是一个分布式系统,严重依赖基础设施即代码、复杂编排,以及企业特定的集成模式;这些内容过于庞大,也过于依赖具体环境,不可能在本章完整覆盖。

将 RAG 推向生产所需的大量“重活”,涉及的是标准、严谨的软件工程和 DevOps 实践,而不是 RAG 特有逻辑。高可用、负载均衡、容器化(Docker/Kubernetes)、密钥管理和 CI/CD 流水线等概念,是任何任务关键型企业应用的通用要求。

因此,本章主要聚焦系统设计、架构和策略,以及如何从 POC 过渡到生产。

生产环境中 RAG 面临的挑战

一个可扩展的生产级 RAG 技术栈,比最初看起来更难构建。这里有许多障碍,包括响应质量、延迟、安全、支持和成本。

响应质量与减少幻觉

无论你是用 RAG 构建 AI 助手、问答应用、自动化 RFP(request for proposal,提案请求)响应应用,还是任何其他使用场景,RAG 流水线输出响应的质量,通常都是最需要关注的特性。

用户往往会远离他们无法信任的应用。因此,如果大量响应不准确,或者包含幻觉,用户对这个应用的信任会大幅下降,使其基本变得不可用。

理解导致低质量响应的各种因素非常重要,这样你才能识别原因并解决问题。

原因 1:没有相关数据

想象一条基于三星电视用户手册信息的 RAG 流水线。如果用户询问某个具体三星电视型号的问题,但该型号的用户手册没有包含在数据中,那么显然,系统没有任何信息可以用来支撑它的响应。

再举一个例子:一家投资银行构建了一条 RAG 流水线,它基于两个特定数据集:

所有公开的美国证券交易委员会(SEC)文件,例如 10-K;

该银行自己的内部研究报告。

一位分析师可能会提出一个复杂的比较问题,例如:“我们内部研究中提到的 Nvidia 主要营收驱动风险是什么?它和 SambaNova Systems 的风险画像相比如何?”

系统完全具备回答查询前半部分的能力,因为它可以访问关于 Nvidia 的专有报告。然而,SambaNova 是一家私营实体(至少在本书写作时是如此),因此没有公开申报文件;如果这家投资银行内部也从未覆盖过它,那么 RAG 系统知识库中就没有任何关于其风险画像的相关数据。

在这两个例子中,通常会发生的是:检索流水线仍然会拿出一些被检索到的事实,但这些事实并不相关,而 LLM 仍然会使用这些无关事实生成响应,这个响应可能是错误的。

通过追踪用户查询和响应质量,你可以识别这类问题,并更新你的 RAG 数据集,使其包含准确响应任何用户查询所需的全部信息。

当你发现缺失数据时,直觉上会想简单地重新运行摄取脚本,更新向量数据库。对于 POC 来说,这没问题,但在生产使用中,必须采取适当谨慎措施。摄取脚本中的一个 bug,例如损坏的字符编码或格式错误的元数据,可能会“污染”你的实时索引,降低所有人的结果质量。一个典型模式是“staging verification workflow”(预发布验证工作流),也就是维护一个单独的“staging collection”(可以是生产索引的一个较小子集,也可以是完整克隆)。新数据首先被摄取进 staging,然后一组自动化检索单元测试会查询 staging collection,以验证新文档是否可检索、格式是否正确。只有这些测试通过之后,数据才会被“提升”并摄取进实时生产索引。

除了发现缺失文件之外,这也是与领域专家(SME)合作至关重要的地方。许多情况下,只有领域专家才能判断某个文档是否真正是“source of truth”(事实来源),或者只是一个过时版本,应该被清除,以确保 RAG 系统不会基于过时信息作答。

原因 2:检索流水线薄弱

假设数据集中确实有正确的信息,那么下一个罪魁祸首通常就是检索流水线的质量。大多数 POC 都从一个简单的向量搜索方法开始,也就是语义搜索,并使用向量数据库。

当你扩展到生产环境时,可用文档数量会增加,使准确检索任务变得困难得多,因为对于任何给定查询,潜在匹配项都会多得多,这就需要更复杂的过滤和排序机制。此外,随着数据集规模增长,向量搜索和关键词搜索机制所使用的索引也会变得更大、更复杂,需要高效算法来快速更新和搜索。

通常,你需要额外能力,例如混合搜索或各种类型的重排序器(第 3 章已经讨论过),才能实现高质量检索流水线。这通常意味着分布式存储,并需要机制来确保一致性、容错能力和高效数据检索,而所有这些都会增加复杂性。

底线是:RAG 是“垃圾进,垃圾出”。如果你在扩展到生产时没有充分投资构建强检索流水线,那么提供给 LLM 的事实就不会像 POC 中那样准确,响应质量也会下降。

原因 3:LLM 幻觉

即使检索完美,LLM 也经常难以忠实地把源文档(或 chunks)中提供的证据或事实整合进最终响应,从而产生幻觉。

一个复杂因素是被检索事实的可变性和潜在不完整性。在许多情况下,检索组件返回的文档可能无法覆盖回答查询所需的完整信息范围,导致生成模型用推断信息填补空白。这种填补空白的行为可能会无意中产生幻觉,而检测这些不准确之处又更加复杂,因为生成文本可能部分被检索数据支持,从而形成一个“事实性光谱”,而不是清晰的真/假二元划分。

当你考虑生产级 RAG 应用时,需要选择幻觉率较低的 LLM,同时考虑在 RAG 流水线中实现高级技术来检测和纠正幻觉,正如我们在第 3 章中讨论过的那样。

这会增加大量额外研发工作,远远超出 POC 所需范围。

原因 4:提示词工程

RAG 的基础提示词看起来可能相当简单,正如你在第 1 章中看到的那样。但为你的 RAG 系统设计一个更好的提示词,可以显著正向影响响应质量。

例如,你的基础提示词可能是:

prompt = """
Use the following pieces of context to answer the question at the end.
{context}
Question: {question}
Helpful Answer:"""

一个常见的改进版提示词可能如下:

prompt = """
Use the following pieces of context to answer the question at the end. If you 
don't know the answer, just say that you don't know; don't try to make up an 
answer. 
{context}
Question: {question}
Helpful Answer:"""

向 LLM 提供具体指令,例如这里的 “If you don’t know the answer, just say that you don’t know; don’t try to make up an answer”,可以成为提升响应质量的强大方式,尤其是当你的生产 RAG 覆盖更多文档和边界情况,而较基础的提示词无法覆盖这些情况时。

因此,谨慎的提示词设计对于抑制低质量响应至关重要,同时也可以为抵御提示词注入攻击提供更好防御,并且需要在大量查询上进行充分测试。

高延迟

企业级 RAG 系统必须协调语义搜索、混合搜索、重排序、生成式 LLM,以及 RAG 流水线中其他任何组件的计算负载,同时满足用户对快速响应时间的期望。

对于检索组件——语义搜索、混合搜索和重排序——一个不错的目标是平均不超过 300 ms;而对于生成式 LLM,小模型的延迟通常在 2–3 秒范围内,顶级前沿 LLM 甚至可能达到 5–10 秒,“推理”模型则会更高。如果你加入幻觉检测或修正,也会增加额外延迟。

在原型阶段,通常会优先考虑功能而不是速度;而当你转向生产部署时,应用需要满足更严格的延迟阈值,接近公开可用 ChatGPT 的水平,通常端到端在几秒范围内。

POC 期间的数据量通常只是生产环境数据规模的一小部分,而这种规模增长很容易导致显著更高的延迟,因为所有组件都会承受更高负载:

向量数据库必须被正确索引,以维持低延迟响应。

混合搜索必须优化,以便在处理更多数据时不显著增加延迟。

重排序器可能需要处理更多候选项。

提供给生成式 LLM 的 chunks 数量可能需要增加,以维持准确率。

更进一步,你可能会发现,在生产中需要替换 POC 期间使用的一些组件,以避免更高延迟。例如,你在 POC 中使用的向量数据库可能把所有数据都保存在内存中;但在生产中,这显然需要扩展,而你选择的向量数据库在更大规模下可能表现不佳。

需要强调的是,你不仅要把平均延迟控制在可接受范围内,还要控制尾部延迟,例如第 95 百分位,这类延迟可能损害整体用户体验,尤其是在复杂或资源密集型查询下。

为了缓解高延迟,你需要考虑多种技术,包括并行化和自动扩缩容、使用替代 LLM、软件或硬件加速、高效数据索引,以及缓存。

并行化和自动扩缩容

通过自动扩缩容来并行化端到端查询流程,使 RAG 流水线能够高效处理低查询量或高查询量,同时不显著影响延迟,这需要谨慎的系统设计。

一个常见设计模式是一组解耦微服务,其中一个无状态编排服务作为中央“大脑”。这个编排器是一个轻量级、CPU-bound 的 API 服务器,接收用户查询并管理端到端流程。它首先调用嵌入服务,然后编排器会同时“扇出”多个请求——例如,它会在完全相同的时间查询向量数据库和一个基于关键词的词法搜索服务。

这种并行 fan-out/gather 模式确保检索延迟由最慢的数据源决定,而不是所有数据源延迟之和。收集完所有候选 chunks 后,编排器将它们发送给专用重排序服务,最后把优化后的上下文传给 LLM 生成服务。

这种解耦设计是自动扩缩容的关键:每个服务,也就是 CPU-bound 的编排器、I/O-bound 的向量数据库,以及昂贵的 GPU-bound LLM 服务,都会作为自己的副本组进行管理,并可根据其特定资源瓶颈独立扩容或缩容,例如 CPU 使用率、正在进行的 GPU 请求数量,或数据库连接数。

使用替代 LLM

生产环境中使用和 POC 完全相同的 LLM 并不少见,尤其是当你希望在响应质量方面保持相同性能特征时。然而,如果你在 POC 中使用了前沿大规模 LLM——也许只是因为它容易获取且可用——那么如果生产中延迟成为问题并需要降低延迟,你可能会发现尝试更小、更快的 LLM 是有益的。

如果你需要在生产中使用不同的 LLM,这当然可能对响应质量产生重大影响,因此需要非常谨慎地测试 RAG 流水线,并进行 RAG 评估(见第 6 章),以确保质量没有下降。

软件或硬件加速

在解耦微服务架构中,你可以为每个 pod 分配特定硬件和软件优化,以最大化性能,前提是你的流水线确实执行这些功能,而不是调用外部 API 服务。

例如,你的嵌入、重排序和 LLM pod 可以通过 Nvidia A100 或 H100 等强大 GPU 获得显著性能提升。更重要的是,你可以使用 vLLM、TensorRT-LLM 或 Text Generation Inference(TGI)这样的优化推理服务器来服务这些模型。这个软件层是不可妥协的,因为它实现了关键技术,例如连续批处理(用于高效处理来自许多并行编排器 pod 的并发生成请求)和 paged attention(用于大幅削减 RAG 中常见长上下文的内存开销)。

高效数据索引

如果你使用本地托管的向量数据库,实现 HNSW(第 2、3 章讨论过)或 Inverted File Product Quantization(IVFPQ)这样的近似最近邻(ANN)索引,是获得低延迟的前提。

类似地,对于词法搜索,需要为 Elasticsearch 或 OpenSearch 索引实现适当分片,并使用合适的文本分析器,以便关键词查找足够快速。设计良好的索引可以让检索 pod 在毫秒级响应,使编排器的 fan-out 策略可行。

缓存

缓存可以作为高速内存键值存储实现,例如 Redis 或 Dragonfly,并位于各种微服务“前面”,帮助降低整体延迟。缓存可以在以下组件中实现:

完整响应缓存

编排器首先对原始用户查询进行哈希。如果该 key 存在,它会立即返回已存储的最终 LLM 生成答案,绕过整个 RAG 流水线。这对常见且完全相同的问题非常有效。

检索缓存

编排器对查询嵌入进行哈希。然后检查检索步骤的结果,也就是 chunks 列表,是否已缓存。如果已缓存,它会跳过整个检索步骤,只返回这些结果。

Chunk 缓存

检索出 chunk ID 后,编排器可以检查缓存中是否有这些 chunks 的实际文本内容,从而省去对潜在慢速文档存储(如 S3 或 Postgres)的调用。

在输入通常是自然语言查询的 RAG 应用中,实现语义缓存可能很有益。语义缓存会对已存储的缓存 key 执行相似度搜索,而不是要求精确字符串匹配。由于终端用户很少两次用完全相同的措辞提出同一个问题,例如 “How do I reset my password?” 与 “I need to change my password”,传统基于哈希的缓存经常无法命中。在语义缓存中,传入查询会被嵌入成向量,系统会在缓存中搜索语义上接近的历史查询(超过某个相似度阈值)。如果找到匹配,系统会返回与该相似查询关联的缓存响应,从而显著提高自然语言输入的缓存命中率。

通过在这些层次上战略性地缓存,你可以绕过系统中最昂贵的部分。然而,核心挑战是缓存失效。如果处理不当,缓存失效可能引入错误响应,尤其是在数据快速变化的环境中。相比只依赖简单的 time-to-live(TTL)机制,更健壮的方案是让数据摄取流水线在文档新增或更新时发布事件,例如发布到 Redis Pub/Sub channel 或 Kafka topic。订阅服务随后可以主动清除任何与该文档相关的缓存条目,确保 RAG 系统永远不会提供过期数据。

一如既往,健壮且持续的监控有助于实时识别延迟瓶颈,也能识别更系统性的延迟问题,并进行纠正。

下面我们用一个基于 LangChain 的简单示例演示检索缓存的思想,完整示例代码可在 GitHub 上获得。这里我们使用 Redis(虽然 Dragonfly 或 KeyDB 也是不错替代)进行语义缓存。

import numpy as np
from langchain_core.documents import Document

class SemanticCachedRetriever(BaseRetriever):
    """
    A retriever that uses embedding similarity for semantic caching.
    Similar queries can hit the cache even if worded differently.
    """
    _base_retriever: any = PrivateAttr()
    _embeddings: any = PrivateAttr()
    _similarity_threshold: float = PrivateAttr(default=0.85)
    _cache_embeddings: List[np.ndarray] = PrivateAttr(default_factory=list)
    _cache_results: List[List[Document]] = PrivateAttr(default_factory=list)
    _cache_queries: List[str] = PrivateAttr(default_factory=list)
    
    def __init__(
        self, 
        base_retriever, 
        embeddings,
        similarity_threshold: float = 0.85,
        **kwargs
    ):
        super().__init__(**kwargs)
        self._base_retriever = base_retriever
        self._embeddings = embeddings
        self._similarity_threshold = similarity_threshold
        self._cache_embeddings = []
        self._cache_results = []
        self._cache_queries = []
    
    def _cosine_similarity(self, a: np.ndarray, b: np.ndarray) -> float:
        """Compute cosine similarity between two vectors."""
        return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
    
    def _find_similar_cached(
        self, query_embedding: np.ndarray
    ) -> Optional[Tuple[List[Document], str, float]]:
        """Find cached results for a semantically similar query."""
        best_similarity = 0.0
        best_match = None
        best_query = None
        
        for i, cached_emb in enumerate(self._cache_embeddings):
            similarity = self._cosine_similarity(query_embedding, cached_emb)
            if similarity > best_similarity:
                best_similarity = similarity
                best_match = self._cache_results[i]
                best_query = self._cache_queries[i]
        
        if best_similarity >= self._similarity_threshold:
            return best_match, best_query, best_similarity
        return None
    
    def _get_relevant_documents(self, query: str) -> List[Document]:
        # Compute embedding for the query
        query_embedding = np.array(self._embeddings.embed_query(query))
        
        # Check for semantically similar cached query
        cache_hit = self._find_similar_cached(query_embedding)
        if cache_hit:
            docs, original_query, similarity = cache_hit
            print(f"Semantic Cache Hit! (similarity: {similarity:.3f})")
            print(f"  Matched query: "{original_query}"")
            return docs
        
        # Cache miss - perform retrieval
        print("Semantic Cache Miss. Performing vector search...")
        results = self._base_retriever.invoke(query)
        
        # Store in cache
        self._cache_embeddings.append(query_embedding)
        self._cache_results.append(results)
        self._cache_queries.append(query)
        
        return results
    
    def clear_cache(self):
        """Clear the semantic cache."""
        self._cache_embeddings = []
        self._cache_results = []
        self._cache_queries = []

可以看到,它通过利用语义相似度工作:我们不是对查询进行哈希,而是将其转换成向量嵌入,并用余弦相似度与此前缓存的查询向量进行比较。如果发现某个历史查询足够相似(超过我们定义的阈值,这里选择 0.85),就立即返回已存储结果。如果没有命中,就执行标准检索,并把新查询、其嵌入和结果存入本地内存缓存,以备未来使用。

然而,虽然精确匹配缓存或语义缓存在 POC 中实现简单,但在生产规模下,缓存会变得显著更复杂。你需要处理三个主要挑战:

缓存失效

当新文档被摄取或旧文档被更新时,基于旧数据的缓存检索结果可能会变得过时。由于你控制摄取流程,可以使用基于触发器的方法,使任何基于被更新文档的响应失效。

淘汰策略

随着缓存增长,它最终会消耗机器上的所有可用 RAM。为了解决这个问题,你可以配置淘汰策略,通常是 least recently used(LRU,最近最少使用),这样缓存就知道应该删除哪些旧检索结果,为新结果腾出空间。

水平扩展

单个 Redis 实例可能不足以处理企业工作负载的吞吐量或内存需求。在这种情况下,可以利用集群(分片),根据 key 的哈希自动把数据拆分到多台机器上,使缓存能够水平扩展。

Redis LangCache 正是为此目的而设计的,交钥匙 RAG 平台通常也会把这些策略作为其技术栈的一部分实现。

数据安全与隐私

生产 RAG 部署必须在三个关键攻击面上实现纵深防御策略:摄取层、向量和词法数据库,以及生成步骤。下面我们更深入地讨论每一项。

摄取层安全

像任何 ETL(extract, transform, load,抽取、转换、加载)流水线一样,你的摄取流程需要使用标准加密协议,确保数据从数据源移动到 RAG 流水线期间的安全。此外,你的实现需要确保同样的安全协议贯穿整个 RAG 流水线和每个组件——文档抽取、专门的表格和图片处理、切分、嵌入,以及存储进向量数据库、词法数据存储,甚至图数据库(如果使用,见第 9 章)。

如果你的数据包含 personally identifiable information(PII,个人身份信息)或 protected health information(PHI,受保护健康信息),你需要考虑脱敏策略,同时确保脱敏不会因为信息丢失而降低响应质量。

常见方法包括 masking(掩码),即用通用占位符替换数据,例如 “Ofer Mendelevitch” 变成 “XXXX”;或 nulling,即完全移除数据。这些方法的主要问题是会造成严重信息损失;系统不仅丢失了值本身,例如名字 “Ofer Menedelevitch”,还丢失了该值与周围文本之间的上下文关系。例如,将医疗记录从 “Dr. Smith prescribed Tylenol to Forrest” 脱敏成 “XXXX prescribed YYYY to ZZZZ”,会让句子在回答处方相关问题时不那么可用。

为解决这个问题,一种更高级的策略是 entity-aware redaction(实体感知脱敏),或 typed masking(类型化掩码),它会用敏感数据所属类别替换敏感数据,例如 “[DOCTOR_NAME] prescribed [MEDICATION] to [PATIENT_NAME]”。这种方法保留了数据中的语义结构和关系,使 RAG 模型能够理解正在发生什么(医生给患者开药),同时不暴露具体私人细节,从而在安全需求和响应质量之间取得平衡。

下面是一个示例:我们使用 Microsoft 的 presidio_analyzerpresidio_anonymizer 库执行实体感知脱敏。AnalyzerEngine 类可以识别电话号码或人名等具体实体类型,而 AnonymizerEngine 随后可以将其替换为 <PERSON><PHONE_NUMBER> 这样的“token”。

from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine
from presidio_anonymizer.entities import OperatorConfig

def entity_aware_redaction(text):
    """
    Implements the 'Typed Masking' strategy.
    Replaces sensitive data with its category (e.g., [PERSON])
    """
    analyzer = AnalyzerEngine()
    anonymizer = AnonymizerEngine()

    # 1. Detect PII entities
    results = analyzer.analyze(
        text=text, entities=["PERSON", "PHONE_NUMBER"], language='en'
    )

    # 2. Replace with entity type (preserving semantic structure)
    anonymized_result = anonymizer.anonymize(
        text=text,
        analyzer_results=results,
        operators={
            "PERSON": OperatorConfig("replace", {"new_value": "<PERSON>"}),
            "PHONE_NUMBER": OperatorConfig(
                "replace", {"new_value": "<PHONE_NUMBER>"}
            ),
        }you e
    )
    
    return anonymized_result.text
input = "Dr. Bao called 123-555-1122."
output = entity_aware_redaction(input)
print(output)

输出为:

Dr. <PERSON> called <PHONE_NUMBER>.

如果你需要遵守 International Standards Organization/International Electrotechnical Commission(ISO/IEC)27001 关于信息来源的标准,那么你需要能够证明数据历史是可信的,并且没有被秘密篡改。这里的常见方法是基于哈希的追踪:它在 RAG 流水线的每个步骤中为数据创建一个唯一“数字指纹”(hash),提供一条可验证的证据链,审计员可以用它确认数据完整性。

数据存储保护

在 RAG 流水线中,向量数据库充当向量嵌入及其关联文本数据的存储库。使用混合搜索时,实现中还会包含一个针对词法搜索优化的独立文本数据库。如果你实现知识图谱(见第 9 章),还要再加上一个图数据库。

在所有情况下,这里的核心要求都是加密(静态加密和传输中加密)以及基于角色的访问控制(RBAC),以遵守公司安全政策,或执行欧盟 General Data Protection Regulation(GDPR)的“minimum necessary”原则,也就是只存储应用功能所需的必要数据,定期审查并清除不必要数据,并实施 privacy-by-design 原则,最大限度减少敏感信息暴露。

和任何安全企业系统一样,你需要为所有这些数据存储实现网络安全最佳实践,以及持续监控和事件响应协议。

防止数据泄漏

你的 RAG 数据存储包含所有可用于驱动 RAG 查询的数据。它包括各种各样的文档和数据,每种数据在组织中都受不同权限级别约束。例如,有些文档可能对所有员工可访问,而其他文档仍然是机密的,只对高管可见,例如 CEO,或只对 HR 部门可见。

避免与权限相关的潜在数据泄漏的一种常见方法,是将基于权限的过滤机制集成到查询流程中。该过滤器利用公司的 RBAC 政策,确保只有查询用户有权访问的数据才会被传递给生成式 LLM。通过这样做,你可以防止未授权访问,并缓解暴露机密信息的风险。这是一种有效方法,尽管它要求所有被摄取进 RAG 流水线的数据都具有一致的基于角色权限策略。

无论你如何实现这种基于权限的防御,定期审计查询流程、彻底测试过滤机制,以及持续监控外部交互,都是检测和防止数据泄漏的关键措施。

除了内部访问控制之外,一个常见隐私担忧是数据泄漏给 LLM 提供商。具体来说,当与外部供应商托管的 LLM 交互时,例如 OpenAI、Anthropic 或 Google,对 LLM 的调用会把内部数据通过网络发送给外部托管的 LLM,以生成响应。这会引入一个风险:敏感数据可能以你无意的方式被存储。外部 LLM 提供商可能会记录这些查询,或保留输入数据的临时缓存,从而潜在导致数据泄漏。即使数据经过匿名化,也可能发生这种暴露,因为模式或元数据仍可能揭示敏感洞察。

这通常会导致处理高度敏感数据的企业级 RAG 应用要求本地部署模型,使用开源 LLM,例如 OpenAI 的 gpt-oss、Meta 的 Llama 4、Qwen 或 DeepSeek,这些模型可以托管在你的数据中心或虚拟私有云(VPC)中,而不会给你的数据带来潜在风险。

LLM 生成护栏

生成式 LLM 负责结合被检索数据和用户查询来产生最终输出。为了与公司政策和监管要求对齐,你的 RAG 应用必须纳入健壮保护措施,防止生成不允许的内容。

这些保护措施通常称为 guardrails(护栏),我们在第 1 章中介绍过,并在第 3 章“实现护栏”中更详细讨论过。它们处理的问题包括仇恨言论、有偏语言,或任何其他形式的有害或不适当输出。

从 POC 走向生产时,这类护栏会成为严格要求;而要让它们在规模化场景下工作,通常不仅需要谨慎实现,还需要全面日志记录和监控,用于追踪 RAG 流水线性能、识别潜在违规,并在事件发生时促进快速响应。

在生产中,允许终端用户报告有问题的输出,是及早捕获这类问题的一种简单但有效策略,可以支持系统安全和合规措施的持续改进。通过将这些防御策略整合进 RAG 应用,你不仅能提升生成响应的质量和安全性,也能在企业环境中增强整体系统完整性和可信度。

供应商混乱与集成困境

构建生产级 RAG 技术栈,不只是组装一个向量数据库、一个嵌入模型和一个生成式 LLM。随着技术栈演进,你可能需要集成额外组件,以维持高质量响应和稳健性能。

这些组件可能包括:

内容抽取

用于从 PDF 文件、Word 文档或 PowerPoint 文件中抽取文本的外部 API。如果你实现高级切分方法,可能还需要额外内容转换。

数据解析

专门用于以高准确率和不同语言解析表格和图片的 API。

高级检索

增强检索算法,例如混合搜索和重排序。

响应质量保障

用于检测和修正幻觉的模型。

安全与合规

用于加密、数据治理、基于角色的访问控制和 PII 脱敏的组件。

知识图谱

如果你集成知识图谱或 GraphRAG(见第 9 章),还需要图数据库和专门的图准备流程。

这种“自己构建”的方法通常会导致架构碎片化且脆弱,你需要负责集成和维护每一处连接。

你不仅要采购并引入这些系统或服务,还需要把它们全部集成进 RAG 技术栈,并确保它们和谐协同工作,维持高正常运行时间和低延迟。此外,将它们集成进系统监控基础设施和安全流程,对于及早识别漏洞并维护整体系统完整性至关重要。

为了帮助评估集成一个新组件或子系统所带来的复杂性,表 4-1 提供了一份快速检查清单,你可以使用它。你的具体企业要求可能会要求额外问题。

表 4-1 复杂性评估检查清单

评估领域需要询问供应商的关键问题
API 与集成你的服务是否提供稳定 REST API、gRPC endpoint,或客户端 SDK(例如 Python、Java)?API 速率限制是多少?是否支持批处理?支持哪些认证方式(例如简单 API key、OAuth 2.0)?
数据与格式接受哪些具体数据格式(例如 JSON、multipart/form-data)?输出格式是什么?该输出能否直接进入下一个组件,还是需要自定义转换层?
安全与合规数据在传输中和静态时如何加密?如何处理 PII?供应商是否满足你的组织必须满足的合规要求(例如 SOC 2、HIPAA、GDPR)?你们有什么漏洞检测流程?
性能与规模保证的 P95/P99 延迟是多少?子系统如何处理扩展(例如自动扩缩容、水平扩展)?正常运行时间保证是什么(是否在服务级别协议 SLA 中提供)?失败处罚是什么?
监控与日志是否有监控仪表盘?能否与你现有可观测性工具集成?
支持与维护支持渠道是什么(例如邮件、专用 Slack channel、电话)?关键生产问题的保证响应时间是多少?更新和新版本如何管理?

尤其是支持,可能会出乎意料地复杂。举例来说,当检测到 bug、延迟超过可接受阈值,或响应质量突然下降时,会发生什么?你可能会发现自己需要和多个供应商合作,每个供应商都有自己的支持人员和支持 SLA,而你则成了所有这些参与方之间的协调者。

这正是更交钥匙式解决方案极为有益的领域之一。当出现问题时,拥有单一责任方可以避免无休止的麻烦。一个集成平台会把这些组件打包成一个统一系统,简化架构和责任归属。

团队与专业能力

另一个重要挑战,是组建一支不仅能实现初始 RAG 应用,还能长期支持和升级它的团队,并进行必要变更,以支持更多企业使用场景。

如果你的 RAG 旅程从单一使用场景开始,那么一旦成功,更多使用场景的需求自然会增加。事实上,一些大型金融机构已经识别出多达 400 个生成式 AI 使用场景;虽然每个组织情况不同,但我们认为,大多数成熟企业在使用 RAG 的前两年内,至少能够识别出 30 个能为业务运营带来显著价值的生成式 AI 使用场景。

RAG 系统位于机器学习、软件工程和领域知识的交叉点,因此需要具备多样能力的团队。

主要挑战在于组建能弥合以下技能桶之间差距的专业人员:

机器学习工程

正确使用嵌入和重排序模型的专业能力;使用合适 GPU 进行 LLM 推理,以平衡成本和延迟;提示词工程;实现混合搜索技术;优化检索流水线;以及幻觉检测和修正方面的专门知识。

如果你在 RAG 流水线中集成知识图谱,那么构建它们又需要另一整套专业能力,包括图查询语言(见第 9 章)。

数据工程

熟练构建可扩展且高可用的 ETL 流水线,能够从多样数据源(PDF 文件、数据库、网站、文档站、SharePoint、Jira 等)高效摄取非结构化数据,包括处理这些数据、规范化这些数据,并让它们为 RAG 做好准备。

DevOps/MLOps

容器化、持续集成和持续交付(CI/CD)、编排、GPU 优化和自动扩缩容技能,以及监控复杂机器学习工作流的能力。

安全/合规

安全、提示词注入防护、PII 脱敏、数据治理、数据隐私,以及生成内容审计追踪方面的技能。

让事情更不容易的是,LLM 和 RAG 本身的知识还相对较新,并且以我们从未见过的速度持续变化。对于大多数有能力的团队来说,跟上所有最佳实践,并持续深入理解其中复杂性,可能相当具有挑战性。

组建和维护高技能 RAG 团队的挑战,源于底层技术的快速演进,以及所需技能的跨学科性质。每个团队成员都确实需要成为自己领域的专家,而围绕 AI 人才的激烈竞争预计只会进一步加剧,这使得它成为真正的挑战。

底线是:如果一个组织要自己构建完整 RAG 流水线,就必须准备好持续且积极地投资招聘、技能提升,并持续培养具备 AI engineering(AIE)、machine learning engineering(MLE)、DevOps 和安全专业能力的人才。

替代方案是,对非差异化组件采用交钥匙 RAG 服务,同时利用内部领域专业知识和人才来满足业务需求和要求。

总体拥有成本

当你将 RAG 应用从 POC 迁移到生产时,考虑所需的总体拥有成本(TCO)并规划预算很有帮助,这样不仅能确保初始部署成功,也能支持 RAG 和 agentic RAG 的更多使用场景。

接下来,我们会介绍 RAG 的 TCO 估算主要组成部分。

直接成本

这些是你直接支付给供应商的成本,用于 RAG 技术栈中包含的某项服务或组件许可证:

供应商管理

向量嵌入和 LLM 都需要考虑供应商管理。你几乎肯定需要供应商提供 RAG 流水线中的某些组件。如果你必须管理许多供应商、付款,并在不同供应商组件之间进行成本优化,那么管理成本本身就会成为一个因素。每增加一个供应商,都会带来安全、法务和 IT 开销。供应商锁定风险始终存在,这意味着未来涨价可能会增加 RAG 流水线整体成本。

检索流水线运行

需要纳入运行检索流水线的开销,这包括向量数据库、词法数据库(如果采用混合搜索),以及重排序。需要注意的是,向量数据库通常呈现非线性成本扩展,因为数据量增加会要求更强性能,例如低延迟和高正常运行时间。

计算和存储

staging 和生产环境都需要成本,通常同时组合 CPU 和 GPU 资源,以处理复杂处理任务。

间接和持续成本

除了前期支出外,还有若干间接因素会影响整体 TCO。

随着 RAG 技术栈增长,加入更多数据、扩展使用场景、增加查询量,你的计算和存储需求会随时间上升。此外,与每个供应商的持续支持合同、常规系统更新和基础设施监控,也会迅速增加不可忽视的额外成本。

根据你的 IT 组织及其要求,在将 RAG 技术栈与现有企业软件或第三方工具集成时,你可能会产生成本;在为数据摄取、测试、DevOps 和监控实现额外系统时,也可能产生成本;安全和隐私控制同样会带来成本。

其他总体拥有成本考虑因素

全面的 TCO 评估还应纳入网络安全控制,例如入侵检测、定期审计,以及组织需要遵守的任何额外要求。

不幸的是,没有任何系统能完全抵抗停机。生产规模下,系统通常需要遵守业务连续性要求和恢复方案,这会进一步增加成本。为解决这一点,你可以实现高可用(HA)架构,例如多区域部署,这会带来额外成本,因此会影响 TCO。多区域部署使用自动故障转移机制,在主位置故障时迅速将操作重定向到健康的备用位置,确保业务连续性。

总结来说,将 RAG 应用从 POC 成功迁移到生产,需要对资本支出(CAPEX)和运营支出(OPEX)进行详细且现实的评估。通过纳入直接供应商付款、计算和存储要求,以及广泛的间接成本,你可以更好地为扩展 RAG 方案的真实成本做好准备。

我们发现,DIY RAG 部署的初始成本估算往往出了名地不可靠,实际生产开销通常会超过预估 3–5 倍。因此要谨慎规划、现实预算,并考虑长期影响,以实现成功且具成本效益的生产部署。

成本监控

鉴于成本升级风险很高,尤其是按 token 付费的 LLM API 和自动扩缩容计算,你应该实现细粒度成本监控并配置硬性告警。在生产架构中,将云供应商和 LLM 供应商的计费与使用仪表盘直接集成进主监控系统,包括基于预算的告警和限流:

基于预算的告警会在成本接近预定义阈值时自动通知运维团队,例如达到月预算的 50%、80% 和 100%。

限流可以防止单个有故障服务、恶意用户,或“denial-of-wallet”攻击为你的团队制造灾难性账单。

另一种控制成本的策略是采用级联模型方法。也就是说,不要把每个查询都发送给最强大、也最昂贵的 LLM,而是实现一个“router”逻辑,如图 4-1 所示。该 router 会首先把查询发送给一个更小、更快、更便宜的模型;如果该模型提供高置信答案,或者查询被标记为“简单”,响应会立即返回。只有当查询复杂,或者便宜模型失败时,它才会被“升级”到昂贵前沿模型。这种分层方法可以显著降低每次查询的平均成本,同时为最困难的用户问题维持高质量。

图 4-1 展示了通过 LLM router 实现的级联模型方法:查询被路由到快速、低成本模型处理简单查询,或路由到更复杂、昂贵的模型处理困难查询,从而优化成本和效率。

image.png

图 4-1 通过 LLM router 实现的级联模型方法

你当然可以实现自己的 LLM router,也可以使用现有库或服务,例如 LiteLLM 或 Not Diamond。无论你如何实现,一如既往,重要的是确保它被集成进你的可观测性和日志系统,不仅报告所发起的调用类型,还要基于具体 LLM 成本自动计算成本。

总体拥有成本的复杂性,是许多公司选择交钥匙 RAG 方案的常见原因。在这些情况下,单一供应商提供 RAG 的全部功能。供应商提供显著简化的成本结构,使 TCO 无论初期还是长期,都更可预测、更易管理。

RAG 评估

正如我们在“响应质量与减少幻觉”中提到的,随着规模扩大,维持一条健康的 RAG 流水线,使其拥有高质量响应、低幻觉、低延迟和高可用,可能很棘手。一个可能有助于解决这个挑战的重要组件,是可靠的 RAG 评估框架,我们会在第 6 章讨论。

正如老话所说:“你无法修复你无法衡量的东西。” 如果你没有可靠框架来衡量响应质量并量化幻觉,随着生产规模扩大,质量可能会随时间下降,而你可能根本没有意识到。持续衡量 RAG 流水线通常需要可扩展且高效地实现检索指标和生成指标,以及整体端到端 RAG 响应质量评估。

到目前为止,本章已经强调了你在将 RAG 应用从 POC 推向生产时可能面临的许多挑战,但不要灰心。许多公司已经成功完成了这一过渡。在下一节中,我们会介绍一些成功应对它的策略。

参考生产架构

现在我们已经概述了将 RAG 从 POC 推向生产所面临的重大挑战,接下来从“是什么”转向“怎么做”。解决高延迟、安全、供应商混乱等问题,需要有意识的系统设计。

一条健壮、可扩展的 RAG 流水线,最好构建成一个解耦的、基于微服务的系统。正如我们在延迟讨论中简要描述过的,这种架构是管理复杂性和支持独立扩展的关键。构建这类系统有许多方式。图 4-2 展示了一种参考架构。

图 4-2 展示了 RAG 的生产架构示例,显示从数据源到安全且带引用答案的流程,包括文档抽取、嵌入、搜索和生成阶段。

image.png

图 4-2 RAG 生产架构示例

让我们回顾一下图中的一些细节。在摄取侧:

文档抽取

文档抽取微服务负责摄取过程中的文档处理。根据数据源和文档类型,它可以从二进制文件中抽取文本,执行表格或图片处理,并抽取元数据。对于元数据,如果需要 PII 脱敏,通常会在存储元数据之前完成。

切分

切分微服务将文档拆分成更小 chunks。它可以是文档抽取微服务的一部分,但将其作为单独服务,可以为语义切分等更复杂的切分策略提供更大灵活性。

文档和查询嵌入

文档嵌入和查询嵌入可以实现为一个托管嵌入模型的微服务,同时服务查询和文档;也可以实现为独立微服务。

摄取处理完成后,嵌入向量会被存储进向量数据库,而文本本身会被存储进词法搜索系统。如果抽取了元数据,它通常也会被存储进词法搜索系统,以便未来检索。

这些微服务中的每一个都可以实现高可用(多个实例),并且当然都需要实现端到端加密等安全最佳实践。

现在来看查询流程:

查询字符串会被发送到查询嵌入服务,然后进入使用向量数据库的语义搜索;与此同时,同一个查询字符串也会被发送到词法搜索系统。

来自向量搜索和词法搜索的相关 chunks 会被合并,并发送给重排序服务进行最终相关性排序。

在这个阶段,如果需要,可以在 chunks 通过提示词发送给 LLM 之前应用 PII 掩码,然后经过护栏——所有这些都在生成式微服务内部完成——最终产生响应。

正如本章前面“高延迟”中提到的,缓存可以贯穿这个架构,应用在检索、chunk 和完整响应层级。安全需要在每一层和每个组件中处理——包括静态和传输中——并且漏洞检测和缓解最佳实践应应用到每个组件和微服务中。

图中没有展示的是 MLOps 的三个关键方面:日志记录、监控和可观测性。它们需要根据组织最佳实践,可靠地集成进每个组件和微服务。

理解了主要问题,并对生产架构可能更详细长什么样有了概念之后,你就可以规划并执行从 POC 到生产的过渡了。

从概念验证成功过渡到生产

现在你已经了解风险和挑战,可以规划 RAG 生产部署了。

与任何复杂技术栈部署一样,仔细规划有助于缓解风险,生成式 AI 也不例外。大多数情况下,POC 已经提供了一些初步动手经验,因此你已经有了一组不错的问题可以询问,也很可能对什么重要有了不错判断。

总结你在概念验证中学到的内容

从创建一份总结 POC 所有收获的报告开始。下面是一些你可能纳入报告的问题和细节示例:

你在 POC 中使用了哪些组件:向量数据库、嵌入模型、重排序器、LLM 等?

数据是如何从源数据存储中收集和摄取的?你是否为表格或图片实现了任何特殊处理?是否有任何数据源需要特别关注?

RAG POC 使用了什么提示词?它在生成适当响应和最小化幻觉方面表现如何?

你测试了哪些高级 RAG 能力,例如混合搜索、知识图谱?

响应质量是否满足你对 POC 的预期?

延迟是如何衡量的?

你如何评估响应质量?

发现了哪些意外问题?

POC 中缺少哪些你本想包含的功能,为什么?

写下这份报告之后,你就可以定义实际生产实现的目标了。

定义目标和要求

在开始实际实现之前,先定义生产部署的目标和要求会非常有帮助。事实上,你可能还需要重新审视业务目标,以确保 POC 目标与生产目标一致。

在适用情况下,使用关键绩效指标(KPI)以数字形式定义要求。你可以填入 POC 报告中的结果,并定义你希望生产部署相比 POC 改善多少。

表 4-2 展示了一些我们在与许多 Vectara 客户合作时看到的 KPI 和要求——你可以直接使用这份列表,也可以根据需要调整。我们填入了一些示例值用于演示;当然,你的 POC 结果或生产目标可能不同。

表 4-2 RAG 生产系统考虑事项示例列表

KPI/要求定义POC生产
查询延迟查询响应时间的均值和中位数(秒),基于 50 个样本查询测量均值:7.5;中位数:8.5均值:4.5;中位数:4
正常运行时间和可用性系统可运行的时间百分比未测量Uptime >= 99.99%
响应质量RAG 评估指标,例如 context precision、context recall、hallucination、answer relevance,以及平均 UMBRELA 分数(第 6 章会进一步讨论)未测量CP >= 0.9;CR >= 0.8;% Hallucination <= 0.05;AR >= 0.9;UMBRELA > 2.5
数据摄取支持的数据源、支持的文件类型、刷新要求仅本地 PDF 文件文件类型:PDF、DOCX、PPTX、HTML;来源:网页、S3、Snowflake、Notion;每日刷新
检索流水线支持的检索技术仅向量搜索向量搜索;混合搜索;相关性重排序;多样性重排序
切分支持的切分策略固定固定;语义
LLM 选择支持用于生成的 LLMOpenAI GPT-4oOpenAI GPT-5.1;Anthropic Claude 4.5;Llama 3.3 70B;Deepseek-R1
嵌入模型选择支持的嵌入模型Hugging Face 上任意模型Hugging Face 上任意模型;OpenAI 和 Cohere
知识图谱系统是否包含知识图谱?

除了表 4-2 中列出的项目之外,在生产部署中还需要规划其他系统考虑事项:

硬件

考虑你需要哪些机器,包括 CPU 和 GPU 机器,以及内存容量和网络要求。还要考虑高可用要求和 staging 环境,它们通常需要额外硬件。

开发环境和流程

代码将托管在哪里?你会使用哪个 CI/CD 系统?你想实现哪些单元测试、集成测试或回归测试?

数据连接

RAG 应用需要连接哪些企业系统进行数据摄取?凭证将如何提供?考虑在 RAG 中实现 RBAC,以防止数据泄漏。

数据安全与治理

系统如何遵守审计要求、SOC-2 合规、HIPAA 合规或 GDPR(取决于你的组织适用什么)?所有数据是否在所有组件之间端到端加密?

监控

你将如何实现监控?考虑系统正常运行时间监控、延迟监控,以及用户满意度。RAG 评估指标见第 6 章。

预算

为 RAG 应用分配的预期月度预算是多少?当预算超支时,性能会如何退化?

规划完成后,你就可以进入实现阶段。这需要项目管理、敏捷开发和强团队协作方面的传统卓越执行能力。企业中的成功技术实现高度依赖研发和 IT 实践,而这些实践在不同组织之间差异很大,因此超出了本书范围。

那么让我们快进一下——你的第一个 RAG 使用场景的第一次生产部署还有两周,就要在整个组织范围内推出了。接下来做什么?

确保 RAG 持续成功

首先也是最重要的,你希望确保顺利、成功上线。这通常需要培训员工或客户使用新的 RAG 应用,确保他们充分了解所有能力,并理解什么时候使用它,以及如何以最有效方式使用它。

当用户使用 RAG 应用时,仔细关注你已经构建好的指标非常关键——不仅包括用户对查询响应的满意度,也包括延迟和系统性能。

例如,你可能会看到查询量在最初几天达到峰值,但两到三周后回落到小得多的每日查询量。这很可能说明某处存在问题——也许系统没有向用户提供有用响应,因此他们又回到旧方式解决问题。也许是延迟问题,用户不想等待。有了良好的日志和监控,你应该能够精准定位问题并进行修复;例如,如果延迟增加,这很容易在监控系统中被检测到。类似地,如果为每个查询详细记录响应,并提供点赞/点踩指标,你可以快速定位用户认为质量低的问题查询,并调查问题到底是不准确检索、生成问题、幻觉,还是缺失数据。

部署后的前两周出现一些问题并不少见——这些可能是你没有预料到的事情,也可能是上线前测试中没有暴露的问题。因此,你需要确保查看所有指标,并快速响应以解决问题。

在实现中包含强监控和可观测性能力,会大幅提高成功概率。通过查看用户查询和响应、理解延迟指标,并记录日常运行中出现的任何问题,你可以快速识别真实应用问题并迅速修复。

假设初始上线顺利(除了某些你会快速修复的问题,这类问题是应该预期到的),后续仍然会有大量工作。这些工作范围从系统维护这类琐碎任务,到随着查询量和使用量增长进行计算升级,再到修复系统可用性问题。你可能需要不时升级组件——例如,如果你使用向量数据库,一旦发现安全漏洞,它可能就需要升级。

将新技术集成进 RAG 流水线是更大的挑战。

例如,假设发布了一个新的嵌入模型,并且显示它在你所有使用场景中都有稳定 5% 的质量提升。你难道不想采用它吗?当然会。为了做到这一点,你必须在 RAG 流水线中实现这个新模型,包括摄取时和查询时,端到端测试所有内容,更新任何系统依赖,并运行 RAG 评估(见第 6 章),比较新旧模型,以证明一切运行良好,并且确实看到 5% 的提升。

这可能说起来容易做起来难。例如,这个新模型可能有高得多的延迟。或者它可能需要另一种类型的 GPU 机器,你可能需要从超大规模云厂商处采购或租用。等等。

而这只是一个例子。你可能想引入一种新 LLM、一种更好的混合搜索算法、一个新的重排序器,或一个改进后的幻觉检测或修正组件。在每次升级 RAG 技术栈时,都要确保遵循与初始生产部署类似的流程:规划、测试、部署和监控。

总结

将企业级 RAG 从 POC 推向生产部署并不容易。

它需要完整理解所有要求,包括安全、治理、数据隐私、系统运维,同时还需要维护一支具备多样专业能力的高技能团队。

重要的是要记住,你不仅需要实现 RAG 应用的第一个版本,还需要支持持续维护、升级,以及可能出现的任何问题。这包括在源头维护“数据卫生”;组织必须具备成熟流程,确保被送入 RAG 流水线的文档是干净的、去重的,并且定期更新。随着生成式 AI 领域不断发展,新的技术会提升响应质量并减少幻觉,更好的 LLM 和嵌入模型会出现,更高效的组件和硬件也会出现;让你的系统保持最新可能很有挑战性,并需要大量投入。

最重要的是,不仅要为持续改进做计划,还要为组织未来想要实现的新使用场景做计划,以便从 RAG 技术栈中获得更多收益。

交钥匙 RAG 平台正在迅速成为自建方案的强大替代。在这种情况下,供应商承担质量实现、升级、改进、安全、隐私和持续监控的负担,让开发者专注于 RAG 应用本身——它应该基于什么数据,以及应当集成到业务工作流的哪里。

在下一章中,你将学习交钥匙 RAG 平台,它们相比 DIY 系统有什么优势,以及它们的一些局限。

注 1:如果你想深入了解 MLOps,Noah Gift 和 Alfredo Deza 的《Practical MLOps》(O’Reilly)可能会有帮助。
注 2:这是 acetaminophen 或 paracetamol 的一个品牌名。