探索构建生成式人工智能平台的关键组件与实现策略,从基础架构到高级功能,提供全面的指导和深入分析。
阅读原文请转到:https://jimmysong.io/trans/building-a-generative-ai-platform/
在研究了各公司部署生成式人工智能应用程序的方式后,我注意到他们的平台之间有许多相似之处。本文概述了生成式人工智能平台的常见组件、其功能及其实现方式。我尽量保持架构的通用性,但某些应用可能会有所不同。以下是整体架构的外观。
![null 19f3eeadd57971dfc194073d32bf194b.jpeg](https://img-blog.csdnimg.cn/img_convert/19f3eeadd57971dfc194073d32bf194b.jpeg)
这是一个相当复杂的系统。本文将从最简单的架构开始,逐步添加更多组件。在最简单的形式中,你的应用程序接收一个查询并将其发送到模型。模型生成响应,该响应被返回给用户。没有防护措施、没有增强上下文,也没有优化。模型 API框指的是第三方 API(例如 OpenAI、Google、Anthropic)和自托管 API。
![null 55f91a458e9b336b59a1a96fa21eff00.jpeg](https://img-blog.csdnimg.cn/img_convert/55f91a458e9b336b59a1a96fa21eff00.jpeg)
从这里开始,你可以根据需要添加更多组件。本文讨论的顺序是常见的,尽管你不需要完全按照相同的顺序操作。如果你的系统运行良好,可以跳过某个组件。开发过程中的每一步都需要进行评估。
1. 通过让模型访问外部数据源和信息收集工具,增强模型的上下文输入。
2. 设置防护措施,以保护你的系统和用户。
3. 添加模型路由器和网关,以支持复杂的管道并增加更多安全性。
4. 通过缓存优化延迟和成本。
5. 添加复杂逻辑和写操作,以最大化系统的功能。
可观察性和编排是平台的两个重要组件。我们将在本文末尾讨论它们。
» 本文不涉及的内容 «
本文重点讨论部署 AI 应用程序的总体架构。它讨论构建这些组件时需要哪些组件和考虑因素。它不涉及如何构建 AI 应用程序,因此不讨论模型评估、应用程序评估、提示工程、微调、数据注释指南或用于 RAG 的分块策略。所有这些主题都包含在我即将出版的书籍 AI Engineering[1] 中。
第一步:增强上下文
平台的初步扩展通常涉及添加机制,允许系统用必要的信息增强每个查询。收集相关信息称为上下文构建。
许多查询需要上下文来回答。上下文中的相关信息越多,模型就越少依赖其内部知识,这可能因其训练数据和训练方法而不可靠。研究表明,访问上下文中的相关信息可以帮助模型生成更详细的回应,同时减少幻觉 (Lewis et al.[2], 2020)。
例如,给定查询“Acme 的精美打印机 A300 是否能打印 100 页每秒?”,如果给出了精美打印机 A300 的规格,模型将能够更好地响应。(感谢 Chetan Tekur 举的例子)
上下文构建对于基础模型来说等同于传统 ML 模型的特征工程。它们的目的相同:为模型提供处理输入所需的信息。
在上下文中学习,从上下文中学习,是一种连续学习的形式。它使模型能够持续地整合新信息以做出决策,防止其过时。例如,一个在上周数据上训练的模型,除非将新信息包含在其上下文中,否则无法回答有关本周的问题。通过使用最新信息更新模型的上下文,例如精美打印机 A300 的最新规格,模型保持最新状态,并可以回应超出其截止日期的查询。
RAGs
上下文构建的最著名模式是 RAG,检索增强生成。RAG 由两个组件组成:一个生成器(例如语言模型)和一个检索器,后者从外部来源检索相关信息。
![null b74d57380b1d29682995a7e5f5d45cc6.jpeg](https://img-blog.csdnimg.cn/img_convert/b74d57380b1d29682995a7e5f5d45cc6.jpeg)
检索不仅限于 RAG。它是搜索引擎、推荐系统、日志分析等的支柱。许多为传统检索系统开发的检索算法可以用于 RAG。
外部记忆源通常包含非结构化数据,如备忘录、合同、新闻更新等。它们可以统称为_文档_。一个文档可以有 10 个标记,也可以有 100 万个标记。简单地检索整个文档可能会导致你的上下文长度过长。RAG 通常要求将文档分割成_可管理的块_,这可以根据模型的最大上下文长度和应用程序的延迟要求确定。要了解更多有关分块和最佳块大小的信息,请参见 Pinecone[3]、Langchain[4]、Llamaindex[5] 和 Greg Kamradt[6] 的教程。
一旦外部记忆源的数据被加载并分块后,检索主要通过以下两种方式执行:
1. 基于术语的检索
这可以简单到像关键字搜索。例如,对于查询“transformer”,检索包含此关键词的所有文档。更复杂的算法包括 BM25(利用 TF-IDF)和 Elasticsearch(利用倒排索引)。基于术语的检索通常用于文本数据,但它也适用于包含文本元数据(如标题、标签、字幕、评论等)的图像和视频。2. 基于嵌入的检索(也称为向量搜索)
你将数据块转换为嵌入向量,使用嵌入模型如 BERT[7],sentence-transformers[8],以及 OpenAI 或 Google 提供的专有嵌入模型。根据查询,检索与查询嵌入最接近的数据,这一过程由向量搜索算法确定。向量搜索通常被视为最近邻搜索,使用近似最近邻 (ANN) 算法,如 FAISS[9](Facebook AI 相似性搜索),Google 的 ScaNN[10],Spotify 的 ANNOY[11],和 hnswlib[12](层次化可导航小世界[13])。ANN-benchmarks 网站[14] 在多个数据集上比较了不同 ANN 算法,考虑了索引和查询之间的权衡。这种方法不仅适用于文本文档,还适用于图像、视频、音频和代码。许多团队甚至尝试总结 SQL 表和数据框,然后使用这些总结来生成检索用的嵌入。
• 召回率:算法找到的最近邻居的比例。
• **每秒查询数 (QPS)**:算法每秒可以处理的查询数。这对高流量应用至关重要。
• 构建时间:构建索引所需的时间。这个指标特别重要,尤其是如果你需要频繁更新你的索引(例如因为你的数据发生了变化)。
• 索引大小:算法创建的索引的大小,这对评估其可扩展性和存储需求至关重要。
基于术语的检索比基于嵌入的检索快得多、成本也更低。它可以开箱即用,是一个吸引人的起点。BM25 和 Elasticsearch 在行业中得到了广泛使用,为更复杂的检索系统提供了强大的基线。尽管基于嵌入的检索在计算上开销较大,但随着时间的推移可以显著改进,以超越基于术语的检索。
生产检索系统通常结合了几种方法。结合基于术语的检索和基于嵌入的检索被称为 _混合搜索_。
一个常见的模式是顺序的。首先,一个便宜、精度较低的检索器,如基于术语的系统,获取候选项。然后,一个更精确但更昂贵的机制,如 k-最近邻,找到这些候选中的最佳选项。第二步也称为重排。
例如,给定术语“transformer”,你可以检索所有包含单词 transformer 的文档,无论它们是关于电器设备、神经结构还是电影。然后你使用向量搜索,在这些文档中找到与你的 transformer 查询实际相关的文档。
上下文重排与传统的搜索重排不同,因为项目的确切位置不那么重要。在搜索中,排名(例如第一或第五)至关重要。在上下文重排中,文档的顺序仍然重要,因为它影响模型如何处理它们。模型可能更好地理解上下文开始和结束时的文档,正如论文 Lost in the middle[15] (Liu et al., 2023) 所示。然而,只要文档被包含在内,其顺序的影响就比在搜索排名中的影响要小。
另一种模式是集成。记住,检索器通过对查询的相关性评分对文档进行排名。你使用多个检索器同时获取候选项,然后将这些不同的排名结合起来生成最终的排名。
带表格数据的 RAGs
外部数据源也可以是结构化的,如数据框或 SQL 表。从 SQL 表检索数据与从非结构化文档检索数据有显著不同。系统的工作流程如下:
1. 文本到 SQL:根据用户查询和表结构,确定需要什么 SQL 查询。
2. SQL 执行:执行 SQL 查询。
3. 生成:根据 SQL 结果和原始用户查询生成响应。
![null 2528dd011120b384a123a5afa278b4e0.jpeg](https://img-blog.csdnimg.cn/img_convert/2528dd011120b384a123a5afa278b4e0.jpeg)
对于文本到 SQL 步骤,如果有很多可用的表格,其架构不能全部适应模型上下文,你可能需要一个中间步骤来预测每个查询使用哪些表格。文本到 SQL 可以由生成最终响应的同一模型或许多专门的文本到 SQL 模型完成。
具有代理能力的 RAGs
互联网是一个重要的数据来源。像 Google 或 Bing API 这样的网络搜索工具可以使模型访问丰富、最新的资源,以收集每个查询的相关信息。例如,给定查询“今年谁赢得了奥斯卡?”,系统会搜索有关最新奥斯卡的信息,并使用这些信息为用户生成最终响应。
基于术语的检索、基于嵌入的检索、SQL 执行和网络搜索是模型可以采取的行动,以增强其上下文。你可以将每个动作视为模型可以调用的函数。可以整合外部动作的工作流程也被称为 _具有代理能力_。其架构如下所示。
![null 0f759d4f47d679943d878ab6c53aa839.jpeg](https://img-blog.csdnimg.cn/img_convert/0f759d4f47d679943d878ab6c53aa839.jpeg)
» 动作与工具 «
一个工具允许一个或多个动作。例如,一个人员搜索工具可能允许两个动作:按姓名搜索和按电子邮件搜索。然而,差别很小,所以很多人将 动作 和 工具 混为一谈。
» 只读动作与写入动作 «
从外部源检索信息但不改变其状态的动作是只读动作。赋予模型写入动作,例如更新表中的值,使模型能够执行更多任务,但也带来了更多风险,稍后将进行讨论。
查询重写
通常,需要重写用户查询以增加获取正确信息的可能性。考虑以下对话。
用户:John Doe上次从我们这里购买东西是什么时候?
AI:John最后一次从我们这里购买是两周前,即2030年1月3日购买的FruityFedora帽。
用户:那EmilyDoe 呢?
最后一个问题,“那 Emily Doe 呢?”是模糊的。如果你逐字使用这个查询来检索文档,你可能会得到无关的结果。你需要重写这个查询,以反映用户实际在询问什么。新查询应该本身就有意义。最后一个问题应该被重写为“Emily Doe 上次从我们这里购买东西是什么时候?”
查询重写通常使用其他 AI 模型完成,使用类似于“给定以下对话,重写最后一个用户输入以反映用户实际在询问什么”的提示。
![null 99000f7296ee18ae68b663e8685c5c47.jpeg](https://img-blog.csdnimg.cn/img_convert/99000f7296ee18ae68b663e8685c5c47.jpeg)
查询重写可能会变得复杂,特别是如果你需要进行身份解析或纳入其他知识。如果用户问“他的妻子怎么样?”,你首先需要查询你的数据库来找出他的妻子是谁。如果你没有这个信息,重写模型应该承认这个查询是无法解决的,而不是臆造一个名字,导致错误的答案。
第二步:设置防护栏
防护栏有助于减少 AI 风险,保护的不仅是用户,还有开发者。在存在失败潜力的地方应设置防护栏。本文讨论两种类型的防护栏:输入防护和输出防护。
输入防护
输入防护通常用于防范两种风险:将私人信息泄露给外部 API,以及执行可能危及系统的不良提示(模型越狱)。
向外部 API 泄露私人信息
当需要将数据发送到组织外部时,使用外部模型 API 会特别有这种风险。例如,员工可能会将公司的秘密或用户的私人信息复制到提示中,并发送到托管模型的位置。
没有万无一失的方法来消除使用第三方 API 时的潜在泄露。然而,你可以通过防护栏来减轻这些风险。你可以使用许多可用的工具之一来自动检测敏感数据。要检测的敏感数据由你指定。常见的敏感数据类别包括:
• 个人信息(身份证号、电话号码、银行账户)。
• 人脸。
• 与公司的知识产权或特权信息相关的特定关键词和短语。
许多敏感数据检测工具使用 AI 来识别潜在的敏感信息,例如判断一个字符串是否像一个有效的家庭地址。如果发现一个查询包含敏感信息,你有两个选择:阻止整个查询或从中移除敏感信息。例如,你可以使用占位符 [PHONE NUMBER] 来掩盖用户的电话号码。如果生成的响应包含此占位符,请使用 PII 可逆字典将此占位符映射回原始信息,以便你可以取消屏蔽它,如下图所示。
![null 7116a9e08390dc645d2fd961a3545faf.jpeg](https://img-blog.csdnimg.cn/img_convert/7116a9e08390dc645d2fd961a3545faf.jpeg)
模型越狱
试图越狱 AI 模型,让它们说出或做出不良行为,已经成为一种在线运动。尽管有些人可能觉得让 ChatGPT 发表争议性言论很有趣,但如果你的客户支持聊天机器人,带有你的品牌和标志做同样的事情就没那么有趣了。这对于有权访问工具的 AI 系统尤其危险。想象一下,如果用户找到一种方法让你的系统执行一个会破坏你的数据的 SQL 查询。
为了对抗这一点,你应该首先在你的系统上设置防护栏,以便不会自动执行任何有害的操作。例如,没有人工批准,不得执行可以插入、删除或更新数据的 SQL 查询。这种增加的安全性的缺点是它可能会减慢你的系统。
为了防止你的应用程序发表不应该发表的离谱言论,你可以为你的应用程序定义范围外的话题。例如,如果你的应用程序是一个客户支持聊天机器人,它不应该回答政治或社会问题。一个简单的方法是过滤掉包含通常与争议话题相关的预定义短语的输入,如“移民”或“反疫苗”。更复杂的算法使用 AI 来分类输入是否关于预定义的受限话题之一。
如果你的系统中有害提示很少,你可以使用异常检测算法来识别不寻常的提示。
故障管理
AI 模型是概率性的,这意味着如果你再次尝试一个查询,你可能会得到不同的响应。许多故障可以通过基本的重试逻辑来缓解。例如,如果响应为空,尝试再次查询 X 次或直到你得到一个非空响应。同样,如果响应格式错误,再试一次直到模型生成一个格式正确的响应。
然而,这种重试策略可能会增加额外的延迟和成本。一次重试意味着 API 调用次数翻倍。如果在失败后进行重试,用户体验的延迟将会加倍。为了减少延迟,你可以并行进行调用。例如,对于每个查询,不是等待第一个查询失败后再重试,而是同时向模型发送两次查询,获取两个响应,并选择较好的一个。这会增加冗余的 API 调用次数,但保持延迟在可管理的范围内。
处理棘手查询时常常需要人类介入。例如,如果查询包含特定关键短语,你可以将查询转给人工操作员。有些团队使用专门的模型(可能是内部训练的)来决定何时将对话转交给人类。例如,有一个团队在他们的情感分析模型检测到用户开始生气时,会将对话转给人工操作员。另一个团队在一定的对话轮数后转交对话,以防用户陷入无限循环。
防护栏权衡
可靠性与延迟的权衡:虽然承认防护栏的重要性,但一些团队告诉我延迟更为重要。他们决定不实施防护栏,因为这会显著增加应用的延迟。然而,这些团队属于少数。大多数团队发现增加的风险比增加的延迟更为昂贵。
输出防护栏可能在流式完成模式中不太有效。默认情况下,整个响应在显示给用户之前生成,这可能需要很长时间。在流式完成模式中,新的令牌在生成时即时传输给用户,减少了用户等待看到响应的时间。缺点是,很难评估部分响应,因此不安全的响应可能在系统防护栏判定应该阻止之前被传输给用户。
自托管与第三方 API 的权衡:自托管模型意味着你不必将数据发送给第三方,减少了输入防护栏的需求。然而,这也意味着你必须自己实施所有必要的防护栏,而不是依赖第三方服务提供的防护栏。
我们的平台现在看起来是这样的。防护栏可以是独立工具或模型网关的一部分,稍后将进行讨论。如果使用,评分器通常被归类在模型 API 下,因为评分器通常也是 AI 模型。用于评分的模型通常比用于生成的模型小且快。
![null 00e44cf782f1000df3e557309e4a42fb.jpeg](https://img-blog.csdnimg.cn/img_convert/00e44cf782f1000df3e557309e4a42fb.jpeg)
第三步:添加模型路由器和网关
随着应用变得越来越复杂并涉及更多模型,出现了两种类型的工具以帮助你处理多个模型:路由器和网关。
路由器
应用程序可以使用不同的模型来响应不同类型的查询。对不同的查询有不同的解决方案有几个好处。首先,这允许你拥有专门的解决方案,如一个专门处理技术故障的模型,另一个专门处理订阅的模型。专门的模型可能比通用模型表现更好。其次,这可以帮助你节省成本。你可以将简单的查询路由到更便宜的模型,而不是所有查询都路由到昂贵的模型。
路由器通常包括意图分类器,用于预测用户试图做什么。根据预测的意图,将查询路由到合适的解决方案。例如,对于客户支持聊天机器人,如果意图是:
• 重置密码 -> 将用户路由到关于密码重置的页面。
• 纠正账单错误 -> 将用户路由到人工操作员。
• 排查技术问题 -> 将查询路由到为排查故障而微调的模型。
意图分类器还可以帮助你的系统避免超出范围的对话。例如,你可以有一个意图分类器来预测查询是否超出范围。如果查询被认为是不适当的(例如,如果用户询问你在即将到来的选举中会投票给谁),聊天机器人可以礼貌地拒绝参与,使用一个标准回复(“作为一个聊天机器人,我没有投票的能力。如果你有关于我们产品的问题,我很乐意帮助。”)而不浪费一个 API 调用。
如果你的系统有权访问多个动作,路由器可以包括一个下一步动作预测器来帮助系统决定下一步采取什么动作。一个有效的动作是如果查询含糊不清,要求澄清。例如,对于查询“Freezing”,系统可能会问,“你是想冻结你的账户还是在谈论天气?”或者简单地说,“对不起,请你详细说明一下。”
意图分类器和下一步动作预测器可以是通用模型或专门的分类模型。专门的分类模型通常比通用模型小且快,允许你的系统使用多个这样的模型,而不会引入显著的额外延迟和成本。
当将查询路由到具有不同上下文限制的模型时,可能需要相应地调整查询的上下文。考虑一个设定为使用 4K 上下文限制的模型的 1,000 令牌查询。然后系统采取一个动作,例如网络搜索,带回 8,000 令牌的上下文。你可以截断查询的上下文以适应最初的模型,或者将查询路由到具有更大上下文限制的模型。
网关
模型网关是一个中间层,允许你的组织以统一且安全的方式与不同模型进行交互。模型网关的基本功能是使开发者能够以相同的方式访问不同的模型——无论是自托管模型还是商业 API(如 OpenAI 或 Google)背后的模型。模型网关简化了代码的维护。如果模型 API 发生变化,你只需要更新模型网关,而不是更新所有使用该模型 API 的应用程序。
模型网关是访问控制和成本管理。与其将组织的令牌分发给每个需要访问 OpenAI API 的人,不如只允许他们通过模型网关访问,这样可以创建一个集中和受控的访问点。网关还可以实施细粒度的访问控制,指定哪些用户或应用程序应该访问哪个模型。此外,网关还可以监控和限制 API 调用的使用,有效防止滥用和管理成本。
模型网关还可以用来实施回退策略,以克服速率限制或 API 失败(后者不幸很常见)。当主要 API 不可用时,网关可以将请求路由到备用模型,短暂等待后重试,或以其他优雅的方式处理失败。这确保了你的应用程序可以平稳运行,不受中断。
由于请求和响应已经通过网关,这是实施其他功能(如负载平衡、日志记录和分析)的好地方。一些网关服务甚至提供缓存和防护栏。
鉴于网关的实施相对简单,市面上有许多现成的网关。示例包括 Portkey 的网关[16],MLflow AI Gateway[17],WealthSimple 的llm-gateway[18],TrueFoundry[19],Kong[20],和 Cloudflare[21]。
随着网关和路由器的添加,我们的平台变得更加令人兴奋。与评分一样,路由也在模型网关中进行。用于路由的模型通常比用于生成的模型小且快。
![null 22cfdc44772fd70c13f485bdc302b3c8.jpeg](https://img-blog.csdnimg.cn/img_convert/22cfdc44772fd70c13f485bdc302b3c8.jpeg)
第四步:通过缓存减少延迟
当我与我的朋友 Eugene Yan 分享这篇帖子时,他说缓存可能是 AI 平台中最被低估的组件。缓存可以显著减少应用程序的延迟和成本。
缓存技术也可以在训练期间使用,但由于这篇帖子是关于部署的,我将重点讨论用于推理的缓存。一些常见的推理缓存技术包括提示缓存、精确缓存和语义缓存。提示缓存通常由你使用的推理 API 实现。在评估推理库时,了解它支持哪种缓存机制是有帮助的。
用于注意力机制的 KV 缓存超出了本讨论的范围。
提示缓存
许多应用中的提示存在重叠的文本段。例如,所有查询可以共享相同的系统提示。提示缓存存储这些重叠段以便重用,因此你只需要处理一次。对于有长系统提示的应用,提示缓存可以显著减少延迟和成本。如果你的系统提示是 1000 个令牌,而你的应用今天生成了 100 万个模型 API 调用,提示缓存将每天为你节省大约 10 亿个重复输入令牌的处理!然而,这并不是完全免费的。与 KV 缓存一样,提示缓存的大小可能相当大,需要显著的工程努力。
提示缓存还适用于涉及长文档的查询。例如,如果许多用户查询与同一长文档(如一本书或代码库)相关,这个长文档可以被缓存以便跨查询重用。
自从 2023 年 11 月 Gim et al.[22] 引入以来,提示缓存已经被纳入模型 API。Google 宣布将在 2024 年 6 月提供名为 context cache[23] 的功能。缓存的输入令牌与常规输入令牌相比可获得 75% 的折扣,但你需要为缓存存储支付额外费用(编写时为每小时 1 美元/100 万令牌)。考虑到提示缓存的明显好处,我不会感到惊讶如果它变得和 KV 缓存一样流行。
而 llama.cpp 也有 prompt cache[24],但它似乎只缓存整个提示,并且仅适用于同一聊天会话中的查询。其文档有限,但从阅读代码的情况来看,在一次长对话中,它会缓存之前的消息并只处理最新的消息。
精确缓存
如果提示缓存和 KV 缓存是基础模型独有的,那么精确缓存则更为通用和直接。你的系统存储处理过的项目以便稍后在请求确切项目时重用。例如,如果用户要求模型总结一个产品,系统会检查缓存中是否有该产品的总结。如果有,就获取这个总结。如果没有,就总结该产品并缓存总结。
精确缓存也用于基于嵌入的检索,以避免重复的向量搜索。如果传入的查询已经在向量搜索缓存中,就获取缓存的搜索结果。如果没有,就为这个查询执行向量搜索并缓存结果。
缓存对于需要多个步骤(例如思维链)和/或耗时操作(例如检索、SQL 执行或网络搜索)的查询特别有吸引力。
精确缓存可以使用内存存储来实现快速检索。然而,由于内存存储有限,也可以使用数据库如 PostgreSQL、Redis 或分层存储来平衡速度和存储容量。拥有一个逐出政策对于管理缓存大小和维护性能至关重要。常见的逐出政策包括最近最少使用(LRU)、最少频繁使用(LFU)和先进先出(FIFO)。
缓存一个查询多长时间取决于这个查询被再次调用的可能性有多大。特定于用户的查询,如“我的最近订单的状态如何”,不太可能被其他用户重用,因此不应该被缓存。同样,缓存时间敏感的查询,如“天气如何?”也没有多大意义。有些团队训练一个小型分类器来预测是否应该缓存一个查询。
语义缓存
与精确缓存不同,语义缓存不要求传入查询与任何缓存的查询完全相同。语义缓存允许重用相似的查询。想象一下,一个用户问“越南的首都是什么?”模型生成答案“河内”。后来,另一个用户问“越南的首都**城市**是什么?”这是同一个问题,但增加了“城市”这个词。语义缓存的思想是系统可以重用答案“河内”,而不是从头计算新查询。
语义缓存只有在你有可靠的方式确定两个查询在语义上是否相似时才有效。一种常见的方法是基于嵌入的相似性,具体操作如下:
1. 对每个查询生成其嵌入,使用嵌入模型。
2. 使用向量搜索找到与当前查询嵌入最接近的缓存嵌入。假设这个相似度得分是 X。
3. 如果 X 小于你设置的相似度阈值,认为缓存的查询与当前查询相同,并返回缓存结果。如果不是,处理这个当前查询并将其与其嵌入和结果一起缓存。
这种方法需要一个向量数据库来存储缓存查询的嵌入。
与其他缓存技术相比,语义缓存的价值更为可疑,因为它的许多组件容易失败。其成功依赖于高质量的嵌入、功能性的向量搜索和可靠的相似度度量。设置正确的相似度阈值也可能很棘手,需要大量的试验和错误。如果系统错误地将传入查询视为与另一个查询相似,从缓存中获取的响应将是错误的。
此外,语义缓存可能耗时且计算密集,因为它涉及向量搜索。这种向量搜索的速度和成本取决于你的缓存嵌入数据库的大小。
如果缓存命中率很高,即大部分查询可以通过利用缓存结果有效地回答,那么语义缓存可能仍然值得。然而,在纳入语义缓存的复杂性之前,请确保评估与之相关的效率、成本和性能风险。
可观测性
虽然我将可观测性单独分为一节,但它应该从平台构建之初就被整合进去,而不是事后才加入。对于所有规模的项目,可观测性都至关重要,其重要性随着系统复杂性的增加而增加。
指标
讨论监控时,大多数人会想到指标。你想追踪的指标取决于你希望了解系统的哪些方面,这是特定于应用程序的。一般而言,有两类指标你可能会追踪:模型指标和系统指标。
系统指标反映了你整个系统的状态。常见的系统指标包括吞吐量、内存使用情况、硬件利用率和服务可用性/运行时间。本帖将重点介绍模型指标。
模型指标评估你的模型性能,如准确性、毒性和幻觉率。应用程序流程中的不同步骤也有自己的指标。例如,在 RAG 应用中,检索质量常用上下文相关性和上下文精确度来评估。向量数据库的评估可以通过它存储数据所需的存储空间和查询数据所需的时间来进行。
日志
关于日志,我的哲学很简单:记录一切。记录系统配置、查询、输出和中间输出。记录组件开始、结束、崩溃等事件。录制日志时,请确保为其标记和 ID,以帮助你了解日志来自系统中的哪里。
记录所有内容意味着你拥有的日志量可能会迅速增长。许多用于自动日志分析和日志异常检测的工具都是由 AI 驱动的。
虽然手工处理日志是不可能的,但每天手工检查你的生产数据,了解用户如何使用你的应用程序是有用的。Shankar 等人(2024)发现,随着开发者与更多数据的互动,他们对什么构成良好和不良输出的看法发生了变化,使他们能够重写他们的提示以增加良好响应的机会,并更新他们的评估流程以捕捉不良响应。
跟踪
跟踪是详细记录请求通过各种系统组件和服务的执行路径。在 AI 应用中,跟踪揭示了从用户发送查询到返回最终响应的整个过程,包括系统采取的动作、检索的文档以及发送给模型的最终提示。它还应显示每个步骤所需的时间及其相关成本(如果可测量)。例如,这是 Langsmith[25] 跟踪的可视化表示。
![null 2b5b2253a769146a46c1a2d18bf7c6f0.jpeg](https://img-blog.csdnimg.cn/img_convert/2b5b2253a769146a46c1a2d18bf7c6f0.jpeg)
理想情况下,你应该能够逐步追踪每个查询在系统中的转换。如果查询失败,你应该能够确定出错的确切步骤:是处理不当、检索的上下文不相关,还是模型生成了错误的响应。
AI 流水线编排
AI 应用可以变得相当复杂,涉及多个模型、从多个数据库检索数据,并使用多种工具。编排器帮助你指定如何将这些不同组件组合(链接)在一起,创建端到端的应用流程。
在高层次上,编排器分两步工作:组件定义和链接(也称为流水线)。
1. 组件定义
你需要告诉编排器你的系统使用哪些组件,如模型(包括用于生成、路由和评分的模型)、系统可以从中检索数据的数据库以及系统可以采取的操作。与模型网关的直接集成可以简化模型引入,一些编排工具也想成为网关。许多编排器还支持与评估和监控工具的集成。2. 链接(或流水线)
你告诉编排器你的系统从接收用户查询到完成任务的步骤顺序。简而言之,链接就是函数组合。以下是流水线的一个示例。编排器负责在步骤之间传递数据,并可以提供工具来确保当前步骤的输出符合下一步骤的预期格式。1. 处理原始查询。
2. 根据处理后的查询检索相关数据。
3. 将原始查询和检索到的数据组合起来,创建模型预期格式的提示。
4. 模型根据提示生成响应。
5. 评估响应。
6. 如果响应被认为是好的,返回给用户。如果不是,将查询路由到人工操作员。
设计流程时,尤其对于有严格延迟要求的应用,尽可能并行处理各个步骤。例如,如果你有一个路由组件(决定将查询发送到哪里)和一个 PII 移除组件,它们可以同时进行。
有许多 AI 编排工具,包括 LangChain[26], LlamaIndex[27], Flowise[28], Langflow[29], 和 Haystack[30]。每个工具都有自己的 API,所以我不会在这里展示实际代码。
虽然在开始一个项目时直接使用编排工具很诱人,但首先应该在没有工具的情况下开始构建应用。任何外部工具都会带来额外的复杂性。编排器可能会抽象掉系统工作的关键细节,使系统难以理解和调试。
随着应用开发过程的深入,你可能会发现编排器能让你的工作更轻松。在评估编排器时需要考虑三个方面:
1. 集成和扩展性
评估编排器是否支持你已经使用或可能在未来采用的组件。例如,如果你想使用 Llama 模型,检查编排器是否支持它。鉴于存在许多模型、数据库和框架,编排器不可能支持所有内容。因此,你还需要考虑编排器的扩展性。如果它不支持特定组件,更改难度如何?2. 支持复杂流水线
随着应用复杂性的增加,你可能需要管理涉及多个步骤和条件逻辑的复杂流水线。支持高级功能如分支、并行处理和错误处理的编排器将帮助你有效管理这些复杂性。3. 易用性、性能和可扩展性
考虑编排器的用户友好性。寻找直观的 API、全面的文档和强大的社区支持,这些可以显著降低你和你团队的学习曲线。避免使用会启动隐藏 API 调用或引入应用延迟的编排器。此外,确保编排器可以随着应用、开发者和流量的增长有效扩展。
结论
这篇文章从基础架构开始,逐步添加组件来应对日益增长的应用复杂性。每次添加都带来了自己的好处和挑战,需要仔细考虑和实施。
虽然组件的分离对于保持系统的模块化和可维护性很重要,但这种分离是流动的。组件之间有许多重叠。例如,模型网关可以与防护栏共享功能。缓存可以在不同的组件中实施,如在向量搜索和推理服务中。
这篇文章比我预期的要长,但仍有许多细节我没有能够进一步探讨,尤其是在可观测性、上下文构建、复杂逻辑、缓存和防护栏方面。我将在即将出版的书籍《AI 工程》中更深入地探讨这些组件。
这篇文章也没有讨论如何服务模型,假设大多数人将使用第三方 API 提供的模型。《AI 工程》还将专门介绍推理和模型优化。
参考文献和致谢
特别感谢 Luke Metz[31], Alex Li[32], Chetan Tekur[33], Kittipat “Bot” Kampa[34], Hien Luu[35], 和 Denys Linkov[36] 对本文早期版本的反馈。他们的见解极大地改善了内容。任何剩余的错误都是我的。
我阅读了许多公司分享的案例研究,了解它们如何采用生成式 AI,以下是我最喜欢的一些。
• 构建生成式 AI 产品的思考[37] (LinkedIn, 2024)
• 我们是如何在 Pinterest 构建 Text-to-SQL 的[38] (Pinterest, 2024)
• 从想法到现实:通过生成式 AI 提升我们的客户支持[39] (Vimeo, 2023)
• 深入探究世界上最智能的电子邮件 AI[40] (Shortwave, 2023)
• LLM-powered 数据实体分类的大规模实施[41] (Grab, 2023)
• 从预测到生成 - 如何加速 Uber 的 AI 之旅[42] (Uber, 2024)
引用链接
[1]
AI Engineering: https://learning.oreilly.com/library/view/ai-engineering/9781098166298/[2]
Lewis et al.: https://arxiv.org/abs/2005.11401[3]
Pinecone: https://www.pinecone.io/learn/chunking-strategies/[4]
Langchain: https://js.langchain.com/v0.1/docs/modules/data_connection/document_transformers/[5]
Llamaindex: https://docs.llamaindex.ai/en/stable/optimizing/production_rag/[6]
Greg Kamradt: https://www.youtube.com/watch?v=8OJC21T2SL4[7]
BERT: https://arxiv.org/abs/1810.04805[8]
sentence-transformers: https://github.com/UKPLab/sentence-transformers[9]
FAISS: https://arxiv.org/abs/1702.08734[10]
ScaNN: https://research.google.blog/announcing-scann-efficient-vector-similarity-search/[11]
ANNOY: https://github.com/spotify/annoy[12]
hnswlib: https://github.com/nmslib/hnswlib[13]
层次化可导航小世界: https://arxiv.org/abs/1603.09320[14]
ANN-benchmarks 网站: https://ann-benchmarks.com/[15]
Lost in the middle: https://arxiv.org/abs/2307.03172[16]
网关: https://github.com/Portkey-AI/gateway[17]
MLflow AI Gateway: https://mlflow.org/docs/latest/llms/gateway/index.html[18]
llm-gateway: https://github.com/wealthsimple/llm-gateway[19]
TrueFoundry: https://docs.truefoundry.com/docs/ai-gateway[20]
Kong: https://konghq.com/products/kong-ai-gateway[21]
Cloudflare: https://developers.cloudflare.com/ai-gateway/[22]
Gim et al.: https://arxiv.org/pdf/2311.04934[23]
context cache: https://ai.google.dev/gemini-api/docs/caching[24]
prompt cache: https://github.com/ggerganov/llama.cpp/blob/master/examples/main/README.md#prompt-caching[25]
Langsmith: https://blog.langchain.dev/announcing-langsmith/[26]
LangChain: https://github.com/langchain-ai/langchain[27]
LlamaIndex: https://github.com/run-llama/llama_index[28]
Flowise: https://github.com/FlowiseAI/Flowise[29]
Langflow: https://github.com/langflow-ai/langflow[30]
Haystack: https://github.com/deepset-ai/haystack[31]
Luke Metz: https://x.com/luke_metz[32]
Alex Li: https://www.linkedin.com/in/findalexli/[33]
Chetan Tekur: https://www.linkedin.com/in/chetantekur/[34]
Kittipat “Bot” Kampa: https://www.linkedin.com/in/kittipat-bot-kampa-1b1965/[35]
Hien Luu: https://www.linkedin.com/in/hienluu/[36]
Denys Linkov: https://www.linkedin.com/in/denyslinkov/[37]
构建生成式 AI 产品的思考: https://www.linkedin.com/blog/engineering/generative-ai/musings-on-building-a-generative-ai-product?_l=en_US[38]
我们是如何在 Pinterest 构建 Text-to-SQL 的: https://medium.com/pinterest-engineering/how-we-built-text-to-sql-at-pinterest-30bad30dabff[39]
从想法到现实:通过生成式 AI 提升我们的客户支持: https://medium.com/vimeo-engineering-blog/from-idea-to-reality-elevating-our-customer-support-through-generative-ai-101a2c5ea680[40]
深入探究世界上最智能的电子邮件 AI: https://www.shortwave.com/blog/deep-dive-into-worlds-smartest-email-ai/[41]
LLM-powered 数据实体分类的大规模实施: https://engineering.grab.com/llm-powered-data-classification[42]
从预测到生成 - 如何加速 Uber 的 AI 之旅: https://www.uber.com/blog/from-predictive-to-generative-ai/
获取更多云原生社区资讯,加入微信群,请加入云原生社区,点击阅读原文了解更多。