这是用户在 2024-6-5 10:26 为 https://medium.com/cyberark-engineering/an-llm-journey-from-poc-to-production-6c5ec6a172fb 保存的双语快照页面,由 沉浸式翻译 提供双语支持。了解如何保存?


LLM 旅程:从 POC 到生产

Adva Nakash Peleg
CyberArk Engineering
12 min read5 days ago

 图片由 DALL-E 创建


想象一下:您有一个很棒的项目构想,可以使用 LLM(大型语言模型)执行该构想,并快速实现有效的概念验证 (POC)。你为自己感到自豪,惊讶于它实际上只需要很少的工作就可以实现。 (五行提示的魔力☺)

 但现在怎么办?


您很快就会意识到,在使用 LLMs 时,编写 POC 很容易,但制作真正可行的产品却很困难。


如果您认同这种情况,您可能会发现这篇文章很有趣。


LLM 旅程的开始


了解这一旅程的最佳方式是查看我们当前的一个 LLM 项目。旅程的第一部分可以分为三个步骤。

 寻找动力


在产品复杂、信息量巨大的世界中,我们的客户经常会迷失方向。有时,即使在产品中进行基本操作,也需要阅读许多文档页面,费尽周折地导航产品的 UI 页面,或者分析大量信息,例如日志、报告和其他原材料。


客户通常会问:“我不能用自己的话说出我想要的东西,然后让系统为我做吗?”


答案是 - 使用 LLM - 是的,你可以!

 宣布目标


本例中的项目目标是使用客户提供的自然语言 (NL) 对我们的产品执行操作。


我们的项目首先学习我们产品的 API 规范文件(以标准格式声明 API 的文件,通常是 OpenAPI),然后使用 LLM 将 NL 请求转换为正确的 API。


实现有效的概念验证 (POC)


宣布项目目标后,我们就进入了POC阶段。


POC 的目的是确保我们的想法实际上是可行的。


我们需要构建一个基本系统,接收 API 规范文件作为输入,以及自然语言的用户请求,并作为结果执行用户的请求。


为此,我们选择 OpenAI GPT 作为我们的 LLM,并选择 LangChain 作为包装 LLM 用法的库。


我们编写了一个引擎,用于接收输入数据,将其处理为逻辑分组(服务),并将其加载到 LangChain 库中,并附带一个包含用户请求的提示以及一些如何执行的说明。我们使用 LangChain 链、工具和代理以及 OpenAI 函数调用功能来完成此操作。


您可以在此处阅读有关 LangChain 工具和代理的更多信息。


下图描述了我们 POC 中的主要参与者:

 POC基本流程图


恭喜,我们有了一个可以工作的 POC!现在真正的旅程开始了……


第一件事:准确性


在工作 POC 最初的兴奋消退之后,我们开始发现 LLM 决策和响应中存在一些缺陷。


在软件行业中,我们经常使用确定性算法(即对于相同的输入,算法总是产生相同的输出)。


我给你的第一个建议是把你习惯的确定性期望扔到窗外。是的,即使 OpenAI 温度为 0。


我们的目标是学习如何在这个新的、不确定的世界中航行。换句话说,我们如何才能使其更具可预测性以及如何处理不同的反应?

 以下是一些提示:


提示#1:确保您使用正确的提示


您需要一步一步告诉 LLM 需要做什么。这包括描述您的输入、其格式、含义、预期输出、其格式、含义等。有时,这意味着使用比您希望的更多的提示行,或者在提示中包含示例。


在下面的少样本学习示例中,您可以看到向提示添加用例的优势。


接受以下请求:“获取用户‘user1’的用户详细信息,并使用这些详细信息创建一个新用户”。


非常基本和简单,对吧?错误的!您不会相信 LLM 试图采取的奇怪操作 - 包括在新用户详细信息中添加“那些”一词。在提示中为 LLM 提供以下示例,效果就像一个魅力:


问题:获取“User1”实体的详细信息,并使用相同的详细信息创建一个名为“User2”的新实体


想法:我应该首先使用“获取实体详细信息”工具获取“User1”的详细信息。那么我应该使用“实体编写器”工具创建一个名为“User2”且具有相同详细信息的新实体。


提示#2:为您的LLM提供一个工具


有时 LLM 不知道如何自行实现正确的操作。在这种情况下,您可以为您的LLM配备一套工具,当LLM不确定要做什么时可以使用这些工具。这可以通过 LangChain 工具或 OpenAI 函数调用轻松完成。


以下是您可以使用的不同工具的示例:


  • 帮助计算日期的日期工具。你可以问它:“给我昨天的所有日志。”由于 LLM 可能难以理解昨天是什么,因此日期工具可以帮助它将“昨天”转换为可以使用的时间戳。

  • 用于从用户那里获取说明的用户/人类工具。你可以问它:“创建一个新用户。”由于LLM可能需要其他信息,例如用户名,因此它可以使用用户工具询问用户所需的用户名是什么。


提示#3:限制您的LLM的创造力


另一个建议是通过让 LLM 要求澄清而不是猜测它不知道的事情来限制您的 LLM 的创造力。


例如,当要求“创建一个名为‘user1’的新用户”时,API 的参数之一是密码。


LLM将尝试使用生成的密码Password123创建用户。这可能不是您想要的。您可以指示您的 LLM 在这些用例中要求澄清,而不是猜测。


总是期待意想不到的事情


我们现在已经创建了一个支持我们所有用例的引擎,并且在“快乐路径”中表现良好。但是错误流呢?边缘情况又如何呢?


以下是一些如何处理它们的想法:


  • 您可以指导 LLM 如何处理意外响应:让 LLM 知道可能发生错误,描述如何识别错误以及发生错误时应采取的措施。

  • 发生错误时向用户提供清晰的消息,以便用户了解发生了什么以及如何修复它。

  • 定义自动恢复行为:在某些情况下,LLM可以从故障中自动恢复。


示例#1:自动更正错误


“创建一个名为‘user1’的用户”。


LLM 可以请求创建此用户,并自动生成用户描述,例如“这是管理员用户!”。在这种情况下,产品可能会返回错误,指出“‘!’的使用是非法的。只允许使用字母数字值”。然后,我们可以指示 LLM 读取此错误消息并使用不包含无效字符的描述自动更正。


示例#2:绕过身份验证问题


“获取名为‘user1’的用户”。


我们用于向产品系统进行身份验证的令牌可能已过期。在这种情况下,产品会返回错误“令牌过期”。


我们可以指示 LLM 处理身份验证问题并返回特殊消息,以便我们可以自动刷新令牌并重试请求。


部署、托管和主要决策


构建引擎并微调模型后,您需要做出有关部署的决策。需要做出许多选择,各有利弊,包括:


在哪里托管引擎


这对我们来说是理所当然的。由于我们正在推动 SaaS,因此这是我们的首选。


由于我们的主要云提供商是 AWS,因此这也是我们的首选。


在哪里托管 LLM 模型


我们使用了 OpenAI GPT 模型,它提供了最好的结果(主要是因为我们大量使用了该模型本身支持的函数调用功能)。经过一番比较,我们得出的结论是,托管此模型的最佳方式是使用 Azure OpenAI。这意味着这个项目是建立在两个云提供商的基础上的——但这根本不是问题。


选择部署策略


作为无状态和无服务器解决方案的倡导者,我始终倾向于朝这个方向发展。


这并不意味着引擎不能托管在静态、有状态的机器上。但我相信无状态和无服务器在弹性、扩展、部署和管理方面的优点是难以与之竞争的。


这引导我们迎接下一个挑战……


使您的LLM引擎无状态和无服务器


在编写 LLM 引擎时,您可能会遇到“会话”或“对话”的概念。在这种情况下,我们需要将此会话/对话的状态提取到可以在需要时加载的外部位置。外部位置可以是分布式缓存或数据库,可以从多个引擎工作人员访问。


以下是提取会话或对话状态的步骤。


步骤 1) 提取会话/对话历史记录。为了使 LLM 考虑过去的消息,我们需要向其传递对话历史记录。对话历史记录是一种状态,需要保存在单独的位置,并在必要时加载以包含在提示中。


步骤2)提取LangChain状态。 LangChain 库及其代理和工具在设计上是有状态的。它在记忆中保留了许多事情,这些事情提供了有关如何继续、到目前为止发生的事情等等的提示。我们遇到的一个实际例子是“用户澄清工具”的使用:当LangChain决定使用该工具时,需要出去澄清,然后从完全相同的位置、完全相同的状态继续。


为了解决这个问题,我们需要深入分析LangChain代码,并重写一些部分以将状态序列化和反序列化到外部位置。


这不是最容易做的事情,但好消息是它可以做到!


步骤 3) 提取您的项目/应用程序非 LLM 状态。如果您有一个与 LLM 无关的状态,则也需要在需要时提取并加载它。


例如,这是引擎工作流程:


获取用户请求 -> 从外部缓存资源加载状态 -> 处理请求 -> LLM -> 将状态保存到外部缓存资源 -> 将响应返回给用户。


您还可以在下图中看到这一点:


发动机工作主流程图


安全,安全,安全


好的,现在我们已经到达了可怕的部分。


如何保护您无法控制的东西?


答案是:通过获得尽可能多的控制权!


首先,熟悉 LLMs 的 OWASP Top 10 始终是个好主意。


以下是我的经验中的一些额外提示:

 提示#1:避免越狱


在 LLMs 中,越狱概念讨论了绕过 LLM 项目的内置功能和保护措施,并将其用于用户自身的利益。这实际上是我们发现很难防范的事情。您会惊讶地发现 LLM 很容易忽略您的规则以满足用户的请求。根据您的项目,您可能需要创造性地采取措施来防止越狱。


以下是一些想法:


使用系统提示说明和规则


使用系统提示功能向您的LLM提供规则,以避免越狱。系统提示规则比用户提示规则更受到重视,也更少被忽视。不幸的是,根据我们的测试,这并不能提供 100% 的保护。


使用白名单技术来限制 LLM 的功能


如果您的LLM功能使用LangChain工具,您可以确保每个请求都使用您的工具之一。这可确保请求与您的内置功能相关。

 下面是两个例子:

 有效的提示


“创建一个名为‘user1’的用户”:


这使用负责用户功能 API 的工具。


我们可以允许这个请求。

 无效的提示


“谁是美国第一任总统?”:


我们项目中没有任何工具可以用来回答这个问题,但LLM知道如何根据自己的知识来回答这个问题。我们必须阻止这个请求。


提示#2:强化模型访问


托管和提供LLM服务可能会很昂贵,并且也有配额限制。尽可能强化对 LLM 的访问可能是明智之举,不仅可以使用 OpenAI 密钥,还可以强化网络并限制访问。


在我们的项目中,当使用 Azure OpenAI 时,我们可以将 LLM 部署在隔离网络(Azure 虚拟网络)中,并将其仅限于引擎的使用 - 在我们的示例中是 AWS 引擎 Lambda,即在专用 VPC(亚马逊虚拟私有云)中。 LLM 和引擎之间的连接也通过 VPN 进行保护。


提示#3:阻止攻击性反应


想象一下您的 LLM 项目就会开始咒骂和冒犯您的客户。看起来像一场噩梦,对吧?我们应该尽一切努力避免这种情况的发生。


可以使用的两种方法是:


提示 #4:向您的 LLM 引擎添加控制点/挂钩


假设您有一个 LLM 需要一个接一个地执行多个操作。添加“检查点”以确保操作有意义可能是个好主意。


这是一个具体的例子:


在我们的项目中,LLM需要使用NL输入生成API和参数,执行产品的API,分析响应并在需要时重试,然后返回格式化结果。


在这种情况下,一个主要控制点出现在生成 API 之后和执行之前。我们可能想要验证 API、参数,甚至清理输入以避免提示注入。在将结果返回给客户之前可能会发生另一个控制点 - 验证响应的内容和格式。


提示 #5:不要忽视常用的安全准则


请记住,该项目与任何其他项目具有相同的风险,甚至更多。许多人希望获得免费的 LLM 访问权限供自己使用,包括仍然想要访问被禁止的资产的攻击者等。

 专注于:


  • 身份验证和授权。验证访问您的新产品的人是否有权这样做。

  • 租户隔离。保持与以往相同的租户隔离保护。不要让您的新 LLM 项目成为访问敏感信息的后门。

  • 防火墙和节流。控制您收到的请求数量。不要让您的项目被滥用。确保您的 LLM 可以处理负载。


其他需要考虑的事情


我们几乎已经准备好生产了!


这里还有一些需要考虑的事情:

 反馈


您如何知道您的 LLM 项目在生产中是否表现良好?您如何知道您的客户对他们获得的结果是否满意?考虑一个反馈机制可能是明智的,这样您就可以跟踪结果并继续改进您的项目。

 模型评估


即使在发布之前,您如何知道您的模型正在做它应该做的事情?您如何才能对应用于模型的更改充满信心?模型评估是一个大而棘手的话题。


您应该考虑以下事项:


  • 添加测试以涵盖 LLM 项目的基本功能。这些测试可能与您习惯的不同(还记得我们提到过探索一个新的非确定性世界吗?)。例如,您将无法使用精确的单词匹配。相反,您应该测试核心功能。在我们的项目中,我们可以给出一个用例,然后验证是否使用正确的主要参数选择了正确的 API。

  • 使用 LLM 评估您自己的 LLM 引擎。 LLM 评估器可以生成用例,提供预测,然后将其与实际结果进行比较。很酷,对吧?

 合法的


请记住,当开始一项新的LLM计划时,您可能需要敲开法律团队的大门,以确保您正在做的事情......嗯......合法。


这包括获得客户使用 AI/Gen-AI 的同意、避免使用客户数据来训练 LLM、如何强制您的 LLM 托管平台不保留以下任何信息对话以及如何满足合规性(例如 GDPR 等)。


旅程的结束


这是显示我们的 LLM 产品架构的图表,后面是本博客中描述的所有内容:

 完整解决方案图


以下是主要参与者:


  • GPT 模型。托管在 Azure OpenAI 上,仅通过我们的 AWS VPC 进行保护和访问。

  • 预处理工人。托管在 AWS Lambda 上。在会话开始时准备数据(API 规范文件)并将处理后的数据保存在外部存储中。

  • 发动机工人。托管在 AWS Lambda 上。加载处理后的数据和状态,使用我们的 LLM 生成相关的 API 请求并对产品执行操作。

  • 反馈员。托管在 AWS Lambda 上。收集并存储会话反馈并清除会话状态。


这就是我们旅程的终点​​。或者这只是一个开始? ☺

Adva Nakash Peleg
CyberArk Engineering

Principal Software Architect at CyberArk. With 15+ years of software development experience.