Published on

【译】构建生成式 AI 平台概述

简介

在研究公司如何部署生成式 AI 应用程序后,我注意到它们的平台有许多相似之处。本文概述了生成式 AI 平台的共同组件、它们的功能以及如何实现。我尽量保持架构的通用性,但某些应用可能会有所偏离。这是整体架构的样子。

Overview of a genai platform

这是一个相当复杂的系统。本文将从最简单的架构开始,逐步添加更多组件。在最简单的形式中,您的应用接收查询并将其发送到模型。模型生成响应,并将其返回给用户。没有保护措施,没有增强上下文,也没有优化。Model API 框指的是第三方 API(例如,OpenAI、Google、Anthropic)和自托管 API。

Overview of a genai platform

从这里,您可以根据需要添加更多组件。本文讨论的顺序是常见的,尽管您不需要严格遵循相同的顺序。如果您的系统在没有某个组件的情况下运行良好,可以跳过该组件。在开发过程的每一步都需要进行评估。

  1. 通过让模型访问外部数据源和信息收集工具来增强输入模型的上下文。
  2. 设置保护措施以保护您的系统和用户。
  3. 添加模型路由器和网关以支持复杂的管道并增加安全性。
  4. 通过缓存优化延迟和成本。
  5. 添加复杂逻辑并编写操作以最大化系统的能力。

可观察性使您能够获得对系统的可见性,以便进行监控和调试,而编排则涉及将所有组件连接在一起,这两个是平台的基本组件。我们将在本文末尾讨论它们。

» 这篇文章不是关于什么 «

这篇文章专注于部署 AI 应用程序的整体架构。它讨论了构建这些组件所需的组件和考虑因素。它不是关于如何构建 AI 应用程序,因此不讨论模型评估、应用评估、提示工程、微调、数据注释指南或 RAG 的分块策略。所有这些主题将在我即将出版的书籍 AI Engineering 中涵盖。

第一步。增强上下文

平台的初步扩展通常涉及添加机制,以允许系统为每个查询增添必要的信息。收集相关信息称为上下文构建。

许多查询需要上下文来回答。上下文中相关信息越多,模型就越不需要依赖其内部知识,而这些知识可能由于其训练数据和训练方法而不可靠。研究表明,访问上下文中的相关信息可以帮助模型生成更详细的响应,同时减少幻觉 (Lewis et al., 2020)。

例如,给定查询“Acme 的 fancy-printer-A300 能打印 100pps 吗?”,如果提供 fancy-printer-A300 的规格,模型将能够更好地响应。(感谢 Chetan Tekur 提供的例子。)

基础模型的上下文构建相当于经典 ML 模型的特征工程。它们具有相同的目的:为模型提供处理输入所需的信息。

上下文学习,即从上下文中学习,是一种持续学习的形式。它使模型能够不断地纳入新信息以做出决策,防止其变得过时。例如,训练于上周数据的模型将无法回答有关本周的问题,除非新信息包含在其上下文中。通过用最新信息更新模型的上下文,例如 fancy-printer-A300 的最新规格,模型保持最新状态,并能够响应超出其截止日期的查询。

RAGs

最著名的上下文构建模式是 RAG,检索增强生成。RAG 由两个组件组成:生成器(例如语言模型)和检索器,它从外部来源检索相关信息。

Overview of a genai platform

检索并不是 RAG 的独特之处。它是搜索引擎、推荐系统、日志分析等的基础。许多为传统检索系统开发的检索算法可以用于 RAG。

外部记忆源通常包含非结构化数据,例如备忘录、合同、新闻更新等。它们可以统称为 文档。文档可以是 10 个标记或 100 万个标记。简单地检索整个文档可能会导致上下文任意长。RAG 通常要求将文档拆分为 可管理的块,这可以根据模型的最大上下文长度和应用程序的延迟要求来确定。要了解有关分块和最佳块大小的更多信息,请参见 PineconeLangchainLlamaindexGreg Kamradt 的教程。

一旦从外部记忆源加载并分块数据,就可以使用两种主要方法进行检索。

  1. 基于术语的检索
    这可以简单到关键词搜索。例如,给定查询“transformer”,获取所有包含该关键词的文档。更复杂的算法包括 BM25(利用 TF-IDF)和 Elasticsearch(利用倒排索引)。 基于术语的检索通常用于文本数据,但它也适用于具有文本元数据(如标题、标签、说明、评论等)的图像和视频。
  2. 基于嵌入的检索(也称为向量搜索)
    您使用嵌入模型(例如 BERTsentence-transformers 和 OpenAI 或 Google 提供的专有嵌入模型)将数据块转换为嵌入向量。给定查询,向量搜索算法确定与查询嵌入最接近的向量的数据被检索。 向量搜索通常被构建为最近邻搜索,使用近似最近邻(ANN)算法,例如 FAISS(Facebook AI 相似性搜索)、Google 的 ScaNN、Spotify 的 ANNOYhnswlib分层可导航小世界)。
    ANN-benchmarks 网站 比较了多个数据集上不同的 ANN 算法,使用四个主要指标,考虑了索引和查询之间的权衡。
    • 召回率: 算法找到的最近邻的比例。
    • 每秒查询数 (QPS): 算法每秒可以处理的查询数量。这对于高流量应用至关重要。
    • 构建时间: 构建索引所需的时间。这个指标尤其重要,如果您需要频繁更新索引(例如,因为数据发生变化)。
    • 索引大小: 算法创建的索引的大小,这对于评估其可扩展性和存储要求至关重要。
    这不仅适用于文本文档,还适用于图像、视频、音频和代码。许多团队甚至尝试总结 SQL 表和数据框,然后使用这些摘要生成用于检索的嵌入。

基于术语的检索比基于嵌入的检索要快得多且便宜。它可以开箱即用,成为一个吸引人的起点。BM25 和 Elasticsearch 在行业中被广泛使用,并作为更复杂检索系统的强大基准。基于嵌入的检索虽然计算成本高,但随着时间的推移可以显著改进,以超越基于术语的检索。

生产检索系统通常结合几种方法。将基于术语的检索和基于嵌入的检索结合起来称为 混合搜索

一种常见的模式是顺序模式。首先,一个便宜且不太精确的检索器,例如基于术语的系统,获取候选项。然后,一个更精确但更昂贵的机制,例如 k 最近邻,找到这些候选项中最好的。第二步也称为重新排序。

例如,给定术语“transformer”,您可以获取所有包含单词 transformer 的文档,无论它们是关于电器、神经网络架构还是电影。然后,您使用向量搜索在这些文档中找到与您的 transformer 查询实际相关的文档。

上下文重新排序与传统搜索重新排序的不同之处在于项目的确切位置不那么关键。在搜索中,排名(例如,第一或第五)至关重要。在上下文重新排序中,文档的顺序仍然很重要,因为它影响模型处理文档的效果。正如论文 Lost in the middle(Liu et al., 2023)所建议的,模型可能更好地理解上下文开头和结尾的文档。然而,只要文档被包含,其顺序的影响相较于搜索排名就不那么显著。

另一种模式是集成。请记住,检索器通过根据其与查询的相关性评分对文档进行排名来工作。您可以同时使用多个检索器获取候选项,然后将这些不同的排名结合在一起生成最终排名。

带有表格数据的 RAGs

外部数据源也可以是结构化的,例如数据框或 SQL 表。 从 SQL 表中检索数据与从非结构化文档中检索数据有显著不同。 给定一个查询,系统的工作流程如下。

  1. 文本到 SQL: 根据用户查询和表模式,确定需要什么 SQL 查询。
  2. SQL 执行: 执行 SQL 查询。
  3. 生成: 根据 SQL 结果和原始用户查询生成响应。

概述一个 genai 平台

对于文本到 SQL 步骤,如果有许多可用的表,其模式无法全部适应模型上下文,您可能需要一个中间步骤来预测每个查询要使用哪些表。 文本到 SQL 可以由用于生成最终响应的相同模型或许多专门的文本到 SQL 模型之一完成。

Agentic RAGs

一个重要的数据来源是互联网。 像 Google 或 Bing API 这样的网络搜索工具可以让模型访问丰富的、最新的资源,以收集每个查询的相关信息。 例如,给定查询“今年谁获得了奥斯卡奖?”,系统会搜索有关最新奥斯卡的信息,并使用这些信息生成对用户的最终响应。

基于术语的检索、基于嵌入的检索、SQL 执行和网络搜索是模型可以采取的增强其上下文的操作。 您可以将每个操作视为模型可以调用的函数。 可以包含外部操作的工作流也称为 agentic。 该架构看起来如下。

概述一个 genai 平台

» 操作与工具 «

工具允许一个或多个操作。 例如,一个人员搜索工具可能允许两个操作:按名称搜索和按电子邮件搜索。 然而,区别很小,因此许多人将 操作工具 互换使用。

» 只读操作与写操作 «

从外部来源检索信息但不改变其状态的操作称为只读操作。赋予模型写操作,例如更新表中的值,使模型能够执行更多任务,但也带来了更多风险,这将在后面讨论。

查询重写

通常,用户查询需要被重写,以增加获取正确信息的可能性。考虑以下对话。

User: When was the last time John Doe bought something from us?
AI: John last bought a Fruity Fedora hat from us two weeks ago, on January 3, 2030.
User: How about Emily Doe?

最后一个问题,“Emily Doe怎么样?”,是模糊的。如果你直接使用这个查询来检索文档,你可能会得到无关的结果。你需要重写这个查询,以反映用户实际想问的内容。新的查询应该独立成句。最后一个问题应该重写为“Emily Doe上次从我们这里购买东西是什么时候?”

查询重写通常使用其他AI模型进行,使用类似于“鉴于以下对话,重写最后的用户输入以反映用户实际想问的内容。”的提示。

概述一个genai平台

查询重写可能会变得复杂,特别是当你需要进行身份解析或整合其他知识时。如果用户问“他的妻子怎么样?”,你首先需要查询你的数据库以找出他的妻子是谁。如果你没有这个信息,重写模型应该承认这个查询是不可解决的,而不是虚构一个名字,导致错误的答案。

第2步. 设置保护措施

保护措施有助于减少AI风险,保护不仅是你的用户,还有你,开发者。它们应该在潜在失败的地方设置。本文讨论了两种类型的保护措施:输入保护措施和输出保护措施。

输入保护措施

输入保护措施通常是针对两种类型风险的保护:向外部API泄露私人信息,以及执行可能危害你系统的坏提示(模型越狱)。

向外部API泄露私人信息

这一风险特指在需要将数据发送到组织外部时使用外部模型API。例如,员工可能会将公司的机密或用户的私人信息复制到提示中,并发送到模型托管的地方。

最引人注目的早期事件之一是三星员工将三星的专有信息放入ChatGPT中,意外地泄露了公司的机密。尚不清楚三星是如何发现这一泄漏的,以及泄露的信息是如何被用于对抗三星的。然而,这一事件严重到三星在2023年5月禁止使用ChatGPT

在使用第三方API时,没有万无一失的方法来消除潜在的泄漏。然而,你可以通过保护措施来减轻这些风险。你可以使用许多可用的工具之一,这些工具可以自动检测敏感数据。要检测哪些敏感数据由你指定。常见的敏感数据类别包括:

  • 个人信息(身份证号码、电话号码、银行账户)。
  • 人脸。
  • 与公司知识产权或特权信息相关的特定关键词和短语。

许多敏感数据检测工具使用AI来识别潜在的敏感信息,例如确定一个字符串是否类似于有效的家庭地址。如果查询被发现包含敏感信息,你有两个选择:阻止整个查询或从中删除敏感信息。例如,你可以用占位符[PHONE NUMBER]来掩盖用户的电话号码。如果生成的响应包含这个占位符,请使用一个PII可逆字典,将这个占位符映射到原始信息,以便你可以解密,如下所示。

Overview of a genai platform

Model jailbreaking

尝试破解 AI 模型已经成为一种在线运动,让它们说出或做出不当行为。虽然有些人可能会觉得让 ChatGPT 发表争议性言论很有趣,但如果您的客户支持聊天机器人带着您的名字和标志也这样做,那就没那么有趣了。这对于可以访问工具的 AI 系统尤其危险。想象一下,如果用户找到方法让您的系统执行一个破坏您数据的 SQL 查询。

为了解决这个问题,您应该首先在系统上设置保护措施,以便不允许自动执行任何有害操作。例如,任何可以插入、删除或更新数据的 SQL 查询都必须经过人工批准才能执行。这种额外安全性的缺点是可能会减慢系统的速度。

为了防止您的应用程序发表不应有的荒谬言论,您可以为您的应用程序定义超出范围的话题。例如,如果您的应用程序是一个客户支持聊天机器人,它不应该回答政治或社会问题。一个简单的方法是过滤掉包含通常与争议话题相关的预定义短语的输入,例如“移民”或“反疫苗”。更复杂的算法使用 AI 来分类输入是否属于预定义的限制话题之一。

如果有害提示在您的系统中很少,您可以使用异常检测算法来识别不寻常的提示。

输出保护措施

AI模型是概率性的,使得它们的输出不可靠。你可以设置保护措施来显著提高你应用程序的可靠性。输出保护措施有两个主要功能:

  1. 评估每次生成的质量。
  2. 指定处理不同失败模式的政策。

输出质量测量

为了捕捉未达到你标准的输出,你需要了解失败的表现。以下是失败模式的示例以及如何捕捉它们。

  1. 空响应
  2. 格式错误的响应,即不符合预期输出格式的响应。例如,如果应用程序期望 JSON,而生成的响应缺少闭合括号。某些格式有验证器,例如正则表达式、JSON 和 Python 代码验证器。还有用于 受限采样 的工具,例如指导、提纲和讲师。
  3. 有毒响应,例如种族主义或性别歧视的响应。这些响应可以通过许多毒性检测工具之一来捕捉。
  4. 事实不一致的响应,由模型幻觉产生。幻觉检测是一个活跃的研究领域,解决方案包括 SelfCheckGPT(Manakul 等,2023)和 SAFE,搜索引擎事实评估器(Wei 等,2024)。通过为模型提供足够的上下文和提示技术(例如思维链)可以减轻幻觉。幻觉检测和减轻将在我即将出版的书籍 AI Engineering 中进一步讨论。
  5. 包含敏感信息的响应。这可能发生在两种情况下。
    1. 您的模型是在敏感数据上训练的,并且将其重新输出。
    2. 您的系统从内部数据库检索敏感信息以丰富其上下文,然后将这些敏感信息传递给响应。 这种失败模式可以通过不在敏感数据上训练模型以及不允许其首先检索敏感数据来防止。输出中的敏感数据可以使用与输入保护措施相同的工具进行检测。
  6. 品牌风险响应,例如错误描述您的公司或竞争对手的响应。一个例子是,当 Grok(由 X 训练的模型)生成的响应 暗示 Grok 是由 OpenAI 训练的,导致互联网怀疑 X 偷了 OpenAI 的数据。这种失败模式可以通过关键字监控来减轻。一旦您识别出与您的品牌和竞争对手相关的输出,您可以选择阻止这些输出,将其传递给人工审核,或使用其他模型检测这些输出的情感,以确保只返回正确的情感。
  7. 通常糟糕的响应。例如,如果您要求模型写一篇论文,而那篇论文就是糟糕的,或者如果您要求模型提供低卡路里蛋糕的食谱,而生成的食谱包含过量的糖。使用 AI 评审来评估模型响应的质量已成为一种流行做法。这些 AI 评审可以是通用模型(例如 ChatGPT、Claude)或专门的评分模型,旨在根据查询为响应输出具体分数。

失败管理

AI模型是概率性的,这意味着如果你再次尝试一个查询,你可能会得到不同的响应。许多失败可以通过基本的重试逻辑来缓解。例如,如果响应为空,可以尝试X次,直到获得非空响应。类似地,如果响应格式错误,可以继续尝试,直到模型生成正确格式的响应。

然而,这种重试策略可能会导致额外的延迟和成本。一次重试意味着API调用数量增加2倍。如果重试是在失败后进行,用户体验的延迟将加倍。为了减少延迟,可以并行进行调用。例如,对于每个查询,而不是等待第一个查询失败后再重试,可以同时将该查询发送给模型两次,获取两个响应,并选择更好的一个。这增加了冗余API调用的数量,但保持了可管理的延迟。

依赖人类处理棘手查询也是很常见的。例如,如果查询包含特定的关键短语,可以将其转交给人工操作员。一些团队使用专门的模型,可能是内部训练的,以决定何时将对话转交给人类。例如,一支团队在他们的情感分析模型检测到用户变得愤怒时,将对话转交给人工操作员。另一支团队在经过一定数量的轮次后转交对话,以防止用户陷入无限循环。

防护措施权衡

可靠性与延迟的权衡:虽然承认防护措施的重要性,但一些团队告诉我,延迟更为重要。他们决定不实施防护措施,因为这可能会显著增加他们应用程序的延迟。然而,这些团队是少数。大多数团队发现,增加的风险比增加的延迟更为昂贵。

输出防护措施在流式完成模式下可能效果不佳。默认情况下,整个响应在显示给用户之前生成,这可能需要很长时间。在流式完成模式下,新令牌在生成时流式传输给用户,减少了用户等待查看响应的时间。缺点是很难评估部分响应,因此不安全的响应可能在系统防护措施确定应该阻止之前就被流式传输给用户。

自托管与第三方API的权衡: 自托管您的模型意味着您不必将数据发送给第三方,从而减少了对输入保护措施的需求。然而,这也意味着您必须自己实现所有必要的保护措施,而不是依赖第三方服务提供的保护措施。

我们的平台现在看起来是这样的。保护措施可以是独立的工具或模型网关的一部分,如后面所讨论的。如果使用评分器,则将其归类于模型API,因为评分器通常也是AI模型。用于评分的模型通常比用于生成的模型更小且更快。

概述一个genai平台

第 3 步. 添加模型路由器和网关

随着应用程序复杂性的增加以及涉及更多模型,出现了两种工具来帮助您处理多个模型:路由器和网关。

路由器

应用程序可以使用不同的模型来响应不同类型的查询。针对不同查询拥有不同解决方案有几个好处。首先,这使您能够拥有专业化的解决方案,例如一个模型专注于技术故障排除,另一个模型专注于订阅。专业化模型的性能可能优于通用模型。其次,这可以帮助您节省成本。您可以将简单的查询路由到更便宜的模型,而不是将所有查询都路由到一个昂贵的模型。

路由器通常由 意图分类器 组成,该分类器预测用户的意图。根据预测的意图,查询被路由到适当的解决方案。例如,对于客户支持聊天机器人,如果意图是:

  • 重置密码 –> 将此用户路由到关于密码重置的页面。
  • 更正账单错误 –> 将此用户路由到人工操作员。
  • 故障排除技术问题 –> 将此查询路由到针对故障排除进行微调的模型。

意图分类器还可以帮助您的系统避免超出范围的对话。例如,您可以拥有一个意图分类器来预测查询是否超出范围。如果查询被认为不合适(例如,如果用户询问您在即将到来的选举中会投票给谁),聊天机器人可以礼貌地拒绝参与,使用其中一个标准回复(“作为一个聊天机器人,我没有投票的能力。如果您对我们的产品有疑问,我很乐意提供帮助。”),而不会浪费 API 调用。

如果您的系统可以访问多个操作,路由器可以涉及 下一个动作预测器 来帮助系统决定下一步采取什么行动。一个有效的操作是如果查询模糊则请求澄清。例如,针对查询“冻结”,系统可能会问:“您是想冻结您的账户,还是在谈论天气?”或者简单地说:“抱歉,您能详细说明一下吗?”

意图分类器和下一步预测器可以是通用模型或专门的分类模型。专门的分类模型通常比通用模型小得多且速度更快,使您的系统能够在不产生显著额外延迟和成本的情况下使用多个模型。

在将查询路由到具有不同上下文限制的模型时,查询的上下文可能需要相应调整。考虑一个计划用于具有4K上下文限制的模型的1,000个标记的查询。系统随后采取行动,例如网络搜索,返回8,000个标记的上下文。您可以选择截断查询的上下文以适应原本打算使用的模型,或者将查询路由到具有更大上下文限制的模型。

网关

模型网关是一个中间层,允许您的组织以统一和安全的方式与不同模型进行接口。模型网关的最基本功能是使开发人员能够以相同的方式访问不同的模型——无论是自托管模型还是像OpenAI或Google这样的商业API后面的模型。模型网关使维护代码变得更容易。如果模型API发生变化,您只需更新模型网关,而不必更新所有使用该模型API的应用程序。

Overview of a genai platform

在其最简单的形式中,模型网关是一个统一的包装器,类似于以下代码示例。此示例旨在让您了解模型网关可能如何实现。它并不意味着是功能性的,因为它不包含任何错误检查或优化。

import google.generativeai as genai
import openai

def openai_model(input_data, model_name, max_tokens):
    openai.api_key = os.environ["OPENAI_API_KEY"]
    response = openai.Completion.create(
        engine=model_name,
        prompt=input_data,
        max_tokens=max_tokens
    )
    return {"response": response.choices[0].text.strip()}

def gemini_model(input_data, model_name, max_tokens):
    genai.configure(api_key=os.environ["GOOGLE_API_KEY"])
    model = genai.GenerativeModel(model_name=model_name)
    response = model.generate_content(input_data, max_tokens=max_tokens)
    return {"response": response["choices"][0]["message"]["content"]}

@app.route('/model', methods=['POST'])
def model_gateway():
    data = request.get_json()
    model_type = data.get("model_type")
    model_name = data.get("model_name")
    input_data = data.get("input_data")
    max_tokens = data.get("max_tokens")

    if model_type == "openai":
        result = openai_model(input_data, model_name, max_tokens)
    elif model_type == "gemini":
        result = gemini_model(input_data, model_name, max_tokens)
    return jsonify(result)

一个模型网关是 访问控制和成本管理。与其将组织的令牌轻易泄露给每个想要访问OpenAI API的人,不如只给人们访问模型网关的权限,从而创建一个集中且受控的访问点。网关还可以实施细粒度的访问控制,指定哪个用户或应用程序应该访问哪个模型。此外,网关可以监控和限制API调用的使用,防止滥用并有效管理成本。

模型网关还可以用于实施回退策略,以克服速率限制或API故障(后者不幸是常见的)。当主要API不可用时,网关可以将请求路由到替代模型,经过短暂等待后重试,或以其他优雅的方式处理故障。这确保了您的应用程序可以顺利运行而不受干扰。

由于请求和响应已经通过网关流动,因此这是实现其他功能的好地方,例如负载均衡、日志记录和分析。一些网关服务甚至提供缓存和保护措施。

鉴于网关相对容易实现,市面上有许多现成的网关。示例包括Portkey的 gatewayMLflow AI Gateway、WealthSimple的 llm-gatewayTrueFoundryKongCloudflare

随着添加的网关和路由器,我们的平台变得更加令人兴奋。与评分一样,路由也在模型网关中。与用于评分的模型类似,用于路由的模型通常比用于生成的模型小。

Overview of a genai platform

第4步。通过缓存减少延迟

当我与我的朋友Eugene Yan分享这篇文章时,他说缓存可能是AI平台中最被低估的组件。缓存可以显著减少应用程序的延迟和成本。

缓存技术也可以在训练期间使用,但由于这篇文章是关于部署的,我将重点讨论推理的缓存。一些常见的推理缓存技术包括提示缓存、精确缓存和语义缓存。提示缓存通常由您使用的推理API实现。在评估推理库时,了解其支持的缓存机制是很有帮助的。

KV缓存对于注意机制的讨论超出了本讨论的范围。

提示缓存

应用程序中的许多提示具有重叠的文本片段。例如,所有查询可以共享相同的系统提示。提示缓存存储这些重叠的片段以供重用,因此您只需处理一次。不同提示中的一个常见重叠文本片段是系统提示。如果没有提示缓存,您的模型需要在每个查询中处理系统提示。使用提示缓存时,它只需在第一个查询中处理系统提示一次。

对于具有长系统提示的应用程序,提示缓存可以显著减少延迟和成本。如果您的系统提示是1000个标记,而您的应用程序今天生成了100万个模型API调用,提示缓存将使您避免每天处理大约10亿个重复输入标记!然而,这并不是完全免费的。与KV缓存一样,提示缓存的大小可能相当大,并且需要大量的工程努力。

提示缓存对于涉及长文档的查询也很有用。例如,如果您的许多用户查询与同一长文档(例如一本书或一个代码库)相关,则可以缓存该长文档以供查询重用。

自2023年11月由Gim et al.引入以来,提示缓存已经被纳入模型API。谷歌宣布,Gemini API将在2024年6月以 上下文缓存 的名称提供此功能。缓存的输入标记与常规输入标记相比享有75%的折扣,但您需要为缓存存储支付额外费用(截至撰写时,$1.00 / 100万个标记每小时)。考虑到提示缓存的明显好处,我不会感到惊讶,如果它变得像KV缓存一样受欢迎。

虽然 llama.cpp 也有 prompt cache,但它似乎只缓存整个提示,并且仅适用于同一聊天会话中的查询。它的文档有限,但我从阅读代码中猜测,在长时间的对话中,它会缓存之前的消息,并仅处理最新的消息。

精确缓存

如果提示缓存和 KV 缓存是基础模型特有的,精确缓存则更为通用和直接。您的系统存储处理过的项目,以便在请求相同项目时进行重用。例如,如果用户要求模型总结一个产品,系统会检查缓存中是否有该产品的摘要。如果有,则获取该摘要。如果没有,则总结该产品并缓存摘要。

精确缓存也用于基于嵌入的检索,以避免冗余的向量搜索。如果传入的查询已经在向量搜索缓存中,则获取缓存的搜索结果。如果没有,则对该查询执行向量搜索并缓存结果。

缓存对于需要多个步骤(例如,思维链)和/或耗时操作(例如,检索、SQL 执行或网络搜索)的查询尤其有吸引力。

精确缓存可以使用内存存储来实现快速检索。然而,由于内存存储是有限的,缓存也可以使用 PostgreSQL、Redis 或分层存储等数据库来实现,以平衡速度和存储容量。拥有驱逐策略对于管理缓存大小和维持性能至关重要。常见的驱逐策略包括最近最少使用(LRU)、最少使用(LFU)和先进先出(FIFO)。

缓存查询的时间长短取决于该查询再次被调用的可能性。用户特定的查询,例如“我最近的订单状态如何”,不太可能被其他用户重用,因此不应该被缓存。同样,缓存时间敏感的查询,例如“天气怎么样?”也没有太大意义。一些团队训练一个小分类器来预测查询是否应该被缓存。

语义缓存

与精确缓存不同,语义缓存不要求传入的查询与任何缓存的查询完全相同。语义缓存允许重用相似的查询。想象一下,一个用户问“越南的首都是什么?”模型生成的答案是“河内”。后来,另一个用户问“越南的首都 城市 是什么?”,这是同样的问题,但多了一个“城市”这个词。语义缓存的理念是,系统可以重用答案“河内”,而不是从头计算新的查询。

语义缓存只有在你有可靠的方法来确定两个查询在语义上是否相似时才能工作。一种常见的方法是基于嵌入的相似性,其工作原理如下:

  1. 对于每个查询,使用嵌入模型生成其嵌入。
  2. 使用向量搜索找到与当前查询嵌入最接近的缓存嵌入。假设这个相似性得分是 X。
  3. 如果 X 超过你设定的相似性阈值,则缓存查询被视为与当前查询相同,并返回缓存结果。如果没有,则处理当前查询,并将其与嵌入和结果一起缓存。

这种方法需要一个向量数据库来存储缓存查询的嵌入。

与其他缓存技术相比,语义缓存的价值更为可疑,因为它的许多组件容易出现故障。 它的成功依赖于高质量的嵌入、功能正常的向量搜索和可靠的相似性度量。设定正确的相似性阈值也可能很棘手,并且需要大量的反复试验。如果系统错误地将传入查询视为与另一个查询相似,则从缓存中获取的返回响应将是错误的。

此外,语义缓存可能耗时且计算密集,因为它涉及向量搜索。这种向量搜索的速度和成本取决于你缓存嵌入的数据库的大小。

如果缓存命中率很高,语义缓存可能仍然值得,这意味着相当一部分查询可以通过利用缓存结果有效回答。然而,在引入语义缓存的复杂性之前,请确保评估与之相关的效率、成本和性能风险。

随着缓存系统的增加,平台的结构如下所示。KV缓存和提示缓存通常由模型API提供者实现,因此在此图中未显示。如果我必须将它们可视化,我会将它们放在模型API框中。现在有一个新的箭头将生成的响应添加到缓存中。

Overview of a genai platform

第5步:添加复杂逻辑并编写操作

到目前为止,我们讨论的应用程序流程相对简单。基础模型生成的输出大多返回给用户(除非它们未通过保护措施)。然而,应用程序流程可以更复杂,包含循环和条件分支。模型的输出也可以用于调用写操作,例如撰写电子邮件或下订单。

复杂逻辑

模型的输出可以有条件地传递给另一个模型,或作为下一步输入的一部分反馈给同一模型。这一过程持续进行,直到系统中的某个模型决定任务已完成,并且应该将最终响应返回给用户。

当你赋予系统规划和决定下一步该做什么的能力时,这种情况就会发生。例如,考虑查询“为巴黎规划一个周末行程”。模型可能首先生成一个潜在活动的列表:参观埃菲尔铁塔、在咖啡馆吃午餐、游览卢浮宫等。然后,这些活动可以反馈到模型中,以生成更详细的计划。例如,“参观埃菲尔铁塔”可能会促使模型生成子任务,如检查开放时间、购买门票和寻找附近的餐厅。这个迭代过程持续进行,直到创建出一个全面且详细的行程。

我们的基础设施现在有一个箭头将生成的响应指向上下文构建,这又反馈给模型网关中的模型。

Overview of a genai platform

写操作

用于上下文构建的操作是 只读操作。它们允许模型从其数据源中读取以收集上下文。但系统也可以进行 写操作,对数据源和世界进行更改。例如,如果模型输出:“向 X 发送一封消息为 Y 的电子邮件”,系统将调用操作 send_email(recipient=X, message=Y)

写操作使系统的能力大大增强。它们可以使您自动化整个客户外展工作流程:研究潜在客户,寻找他们的联系方式,起草电子邮件,发送第一封电子邮件,阅读回复,跟进,提取订单,使用新订单更新您的数据库等。

然而,赋予 AI 自动改变我们生活的能力的前景令人恐惧。就像您不应该给实习生删除生产数据库的权限一样,您也不应该允许不可靠的 AI 发起银行转账。对系统能力及其安全措施的信任至关重要。您需要确保系统受到保护,以防坏人试图操纵它执行有害操作。

AI 系统像其他软件系统一样容易受到网络攻击,但它们还有另一个弱点:提示注入。提示注入发生在攻击者操纵输入提示以使模型表现出不良行为时。您可以将提示注入视为对 AI 而非人类进行的社会工程。

许多公司担心的一个场景是,他们给 AI 系统访问其内部数据库的权限,而攻击者欺骗该系统泄露这些数据库中的私人信息。如果系统对这些数据库具有写入权限,攻击者可以欺骗系统破坏数据。

任何希望利用 AI 的组织都需要认真对待安全和保障。然而,这些风险并不意味着 AI 系统永远不应该被赋予在现实世界中行动的能力。AI 系统可能会失败,但人类也可能会失败。如果我们能让人们信任机器将我们带入太空,我希望有一天,安全措施将足以让我们信任自主 AI 系统。

Overview of a genai platform

可观察性

虽然我将可观察性放在了自己的部分,但它应该从一开始就集成到平台中,而不是作为事后考虑而添加。可观察性对于所有规模的项目都是至关重要的,随着系统复杂性的增加,其重要性也在增长。

与其他部分相比,这一部分提供的信息最少。在一篇博客文章中不可能涵盖可观察性的所有细微差别。因此,我将仅简要概述监控的三个支柱:日志、跟踪和指标。我不会深入细节或涵盖用户反馈、漂移检测和调试。

指标

在讨论监控时,大多数人会想到指标。要跟踪哪些指标取决于您想要跟踪系统的哪些方面,这与应用程序相关。然而,通常来说,您想要跟踪的指标有两种类型:模型指标和系统指标。

系统指标告诉您整体系统的状态。常见的指标包括吞吐量、内存使用、硬件利用率和服务可用性/正常运行时间。系统指标是所有软件工程应用程序共有的。在这篇文章中,我将重点关注模型指标。

模型指标评估您的模型性能,例如准确性、毒性和幻觉率。应用程序管道中的不同步骤也有自己的指标。例如,在RAG应用程序中,检索质量通常使用上下文相关性和上下文精确度进行评估。向量数据库可以通过索引数据所需的存储量和查询数据所需的时间进行评估。

模型输出可能以多种方式失败。识别这些问题并制定监控它们的指标至关重要。例如,您可能想要跟踪模型超时、返回空响应或生成格式错误的响应的频率。如果您担心模型泄露敏感信息,也要找到跟踪该问题的方法。

与长度相关的指标,如查询、上下文和响应长度,对于理解模型的行为非常有帮助。一个模型是否比另一个模型更冗长?某些类型的查询是否更可能导致冗长的回答?它们在检测应用程序中的变化时尤其有用。如果平均查询长度突然减少,这可能表明需要调查的潜在问题。

与长度相关的指标对于跟踪延迟和成本也很重要,因为较长的上下文和响应通常会增加延迟并产生更高的成本。

跟踪延迟对于理解用户体验至关重要。常见的延迟指标包括:

  • 首次令牌时间 (TTFT):生成第一个令牌所需的时间。
  • 令牌间隔时间 (TBT):每个令牌生成之间的间隔。
  • 每秒令牌数 (TPS):生成令牌的速率。
  • 每个输出令牌时间 (TPOT):生成每个输出令牌所需的时间。
  • 总延迟:完成响应所需的总时间。

您还需要跟踪成本。与成本相关的指标是查询数量和输入输出令牌的数量。如果您使用具有速率限制的 API,跟踪每秒请求的数量对于确保您保持在分配的限制内并避免潜在的服务中断非常重要。

在计算指标时,您可以选择抽样检查和全面检查。抽样检查涉及对数据子集进行采样,以快速识别问题,而全面检查则评估每个请求以获得全面的性能视图。选择取决于系统的要求和可用资源,结合两者提供了平衡的监控策略。

在计算指标时,确保它们可以按相关轴进行细分,例如用户、版本、提示/链版本、提示/链类型和时间。这种细粒度有助于理解性能变化并识别特定问题。

日志

由于这篇博客文章变得很长,我已经详细写过关于 日志 的内容,在 设计机器学习系统 中,我会在这里简要说明。日志记录的理念很简单:记录所有内容。记录系统配置。记录查询、输出和中间输出。记录组件何时开始、结束、何时崩溃等。当记录一条日志时,请确保为其提供标签和 ID,以帮助您了解该日志来自系统的哪个部分。

记录所有内容意味着日志的数量可以迅速增长。许多用于自动化日志分析和日志异常检测的工具都由AI驱动。

虽然手动处理日志是不可能的,但每天手动检查您的生产数据是有用的,以了解用户如何使用您的应用程序。Shankar et al. (2024) 发现,开发者对什么构成良好和不良输出的看法会随着他们与更多数据的互动而变化,这使他们能够重写提示以增加良好响应的机会,并更新评估流程以捕捉不良响应。

跟踪

跟踪是指对请求在各种系统组件和服务中的执行路径的详细记录。在AI应用程序中,跟踪揭示了从用户发送查询到最终响应返回的整个过程,包括系统采取的操作、检索到的文档以及发送给模型的最终提示。它还应显示每个步骤所花费的时间及其相关成本(如果可测量)。例如,这是一个Langsmith跟踪的可视化。

Overview of a genai platform

genai平台概述

理想情况下,您应该能够逐步跟踪每个查询在系统中的转换。如果查询失败,您应该能够准确找出出错的步骤:是处理不正确、检索的上下文无关,还是模型生成了错误的响应。

AI管道编排

AI应用程序可能相当复杂,由多个模型组成,从许多数据库中检索数据,并访问广泛的工具。编排器帮助您指定如何将这些不同的组件组合(链接)在一起,以创建端到端的应用程序流程。

从高层次来看,编排器分为两个步骤:组件定义和链接(也称为管道化)。

  1. 组件定义
    您需要告诉调度器您的系统使用了哪些组件,例如模型(包括生成、路由和评分模型)、您的系统可以从中检索数据的数据库,以及您的系统可以采取的操作。与模型网关的直接集成可以帮助简化模型的接入,一些调度工具希望成为网关。许多调度器还支持与评估和监控工具的集成。
  2. 链式处理(或管道处理)
    您告诉调度器您的系统从接收用户查询到完成任务所采取的步骤顺序。简而言之,链式处理就是函数组合。以下是管道的示例。
    1. 处理原始查询。
    2. 根据处理后的查询检索相关数据。
    3. 将原始查询和检索到的数据结合起来,创建模型所期望格式的提示。
    4. 模型根据提示生成响应。
    5. 评估响应。
    6. 如果响应被认为是好的,则将其返回给用户。如果不是,则将查询路由到人工操作员。 调度器负责在步骤之间传递数据,并可以提供工具,帮助确保当前步骤的输出符合下一步骤所期望的格式。

在为具有严格延迟要求的应用程序设计管道时,尽量并行处理尽可能多的任务。例如,如果您有一个路由组件(决定将查询发送到哪里)和一个个人身份信息(PII)移除组件,它们可以同时执行这两个任务。

有许多AI调度工具,包括 LangChainLlamaIndexFlowiseLangflowHaystack。每个工具都有自己的API,因此我不会在这里展示实际代码。

虽然在开始项目时直接跳到调度工具是很诱人的,但请先在没有调度工具的情况下构建您的应用程序。 任何外部工具都会增加复杂性。调度器可以抽象掉您系统工作方式的关键细节,使得理解和调试您的系统变得困难。

随着您在应用程序开发过程中的推进,您可能会决定使用一个调度器来简化您的工作。评估调度器时,有三个方面需要考虑。

  1. 集成和可扩展性
    评估调度器是否支持您当前使用或将来可能采用的组件。例如,如果您想使用 Llama 模型,请检查调度器是否支持该模型。考虑到有这么多模型、数据库和框架,调度器不可能支持所有内容。因此,您还需要考虑调度器的可扩展性。如果它不支持特定组件,改变这一点有多困难?
  2. 对复杂管道的支持
    随着您的应用程序复杂性的增加,您可能需要管理涉及多个步骤和条件逻辑的复杂管道。支持分支、并行处理和错误处理等高级功能的调度器将帮助您高效管理这些复杂性。
  3. 易用性、性能和可扩展性
    考虑调度器的用户友好性。寻找直观的 API、全面的文档和强大的社区支持,因为这些可以显著降低您和您的团队的学习曲线。避免那些发起隐藏 API 调用或给您的应用程序引入延迟的调度器。此外,确保调度器能够在应用程序、开发人员和流量数量增加时有效扩展。

结论

这篇文章从基本架构开始,然后逐渐添加组件以应对日益增长的应用程序复杂性。每个添加都带来了自己的一系列好处和挑战,需要仔细考虑和实施。

虽然组件的分离对于保持系统的模块化和可维护性很重要,但这种分离是流动的。组件之间有许多重叠。例如,模型网关可以与保护措施共享功能。缓存可以在不同的组件中实现,例如在向量搜索和推理服务中。

这篇文章比我预期的要长得多,但仍有许多细节我未能进一步探讨,特别是在可观察性、上下文构建、复杂逻辑、缓存和保护措施方面。我将在即将出版的书籍 AI Engineering 中深入探讨所有这些组件。

这篇文章也没有讨论如何提供模型,假设大多数人将使用第三方 API 提供的模型。AI Engineering 还将有一章专门讨论推理和模型优化。

参考文献和致谢

特别感谢 Luke MetzAlex LiChetan TekurKittipat “Bot” KampaHien LuuDenys Linkov 对这篇文章早期版本的反馈。他们的见解极大地改善了内容。任何剩余的错误都是我自己的。

我阅读了许多公司分享的案例研究,关于他们如何采用生成性 AI,以下是我最喜欢的一些。

原文: https://huyenchip.com/2024/07/25/genai-platform.html