作为 DevOps 工程师,如何测试基于 LLM 的应用程序
测试是软件工程的宝贵工具——适用于应用程序和底层基础架构配置。虽然DevOps工程师可能不会编写测试,但他们通常负责实施和自动化测试逻辑。
随着生成式人工智能 (GenAI)和大型语言模型 (LLM) 越来越受欢迎,组织已开始投入大量资源来构建和部署基于这些系统的软件。与任何其他软件应用程序一样,测试对于成功部署至关重要。然而,生成式人工智能在测试和验证方面提出了独特的挑战。与输出是确定性和可预测的传统软件不同,生成式人工智能模型产生的输出本质上是概率性的和非确定性的。这使得测试故事变得更加模糊。
DevOps 工程师应该如何考虑设计和部署对 LLM 和基于生成式 AI 的应用程序有效的测试套件?在本文中,我们将介绍测试的类型以及测试的组织方式。我们将提供一个示例 GitHub 存储库,其中包含演示 LLM 和测试实现。
不同的测试及其目标
在实施测试之前,了解不同的测试及其用例非常重要。本节简要概述了单元测试和集成测试,并说明了测试在机器学习 (ML) 环境中的含义。
单元测试
单元测试的目的是确保软件应用程序的各个组件正常运行。编写单元测试时通常考虑以下目标:
功能:此代码是否按预期运行?业务逻辑在这里并不那么重要。测试可能会检查函数是否返回有效的 JSON 对象或数组是否永远不会为空。
避免回归:覆盖范围广的测试套件有助于确保新更改不会破坏现有功能。即使是简单的测试也经常会发现看似无关的更改导致的意外结果。
检测错误或缺陷:您可以使用测试来捕获意外空值、边界条件或语法错误等错误。
在某些情况下,单元测试还可以模拟集成测试的元素。开发人员可以使用Mock等工具来模拟对象或响应, 而不是依赖数据库或 API 端点等外部服务。
集成测试
集成测试是软件开发的另一个主要测试类别。集成测试的目的是验证软件不同组件与外部服务之间的交互。集成测试目标包括:
功能:功能在集成测试中涵盖的范围更广。确保各个组件的行为符合预期非常重要,但在集成环境中,它们通常依赖于外部组件。例如,检查 API 是否始终返回特定标头或数据库调用是否始终返回值。
数据一致性:现代软件应用程序通常需要在不同的系统之间传递数据,然后才能呈现最终结果或输出。确保数据在请求的整个生命周期内保持一致非常重要。例如,应用程序的一部分可能会从数据库读取数值,执行数学运算,然后将新值写回数据库。集成测试可以断言数据类型的一致性(确保数据始终是整数或浮点数而不是字符串)以及数据库对读写操作做出适当响应等。
性能:复杂的软件系统依赖于许多活动部件,如果任何单个组件的响应能力低于某个阈值,就会影响整个系统的性能。测试可能会检查以确保 API 调用完成所需的时间不超过 n 毫秒;如果超过,则可能表明查询逻辑效率低下或上游端点存在问题。
集成测试通常用于评估应用程序的业务逻辑。虽然这些测试非常有价值,但它们通常需要更长的测试周期才能返回有用的信息。设计和实施时要小心谨慎。
测试机器学习
区分本文中介绍的测试类型与机器学习模型的实际测试非常重要。测试和微调机器学习模型是一个复杂的过程,通常涉及大量并行的 GPU 驱动的工作负载。机器学习工程师和数据科学家通常需要应用最先进的研究和软件开发来训练和测试这些模型,这是一种不同于本文介绍的以操作为重点的测试类型的测试。
本节绝不是这些测试类型和应用的详尽列表。我建议参与软件开发和部署的任何人都花时间学习并获得软件测试和测试自动化的实践经验,以扩展他们的知识并帮助提高应用程序质量和性能。
组织和分类测试的策略
有效地组织和分类测试可以帮助工程师做出明智的决定,决定如何、在何处以及何时实施各种类型的测试。
确定性测试与非确定性测试
对测试进行分类的一种方法是按照其可预测性,将测试分为确定性测试和非确定性测试。确定性测试是指输入、过程和输出都是可预测和可重复的测试,在给定相同条件的情况下,每次运行时都会产生相同的结果。如果用户应用输入 A,他们将始终收到输出 B。在大多数情况下,单元测试被视为确定性测试,尽管一些集成测试也可以被视为确定性测试。
相反,非确定性测试具有多变的结果和潜在的随机性。即使所有其他测试条件相同,输入 A 也不一定总是产生输出 B。如前所述,集成测试可以是确定性的,但也可以产生非确定性的输出。请考虑以下场景:
测试对提供天气数据的端点的 API 调用:用户可以测试返回数据的结构,但数据本身几乎总是会变化。
响应能力:对外部系统的调用可能总是会返回相同的数据,但是响应时间会因测试运行而异。
唯一 ID 或键值:应用程序组件可能负责生成唯一的 UUID 或其他值作为元数据。您可以测试功能,但如果系统正常运行,则返回的值可能始终是唯一的。
基于 LLM 的系统通常会生成非确定性输出。例如,如果用户向 LLM 提出“埃菲尔铁塔在哪里?”的问题,则响应可能会有所不同。答案可能包括“巴黎”、“巴黎”、“法国巴黎”或“欧洲”。从技术上讲,所有这些答案都是正确的,但从数据验证的角度来看,它们在技术上并不是相同的值。随着提示的复杂性增加,这些测试变得越来越难以进行和评估。
早期单元测试
建议在集成测试之前部署单元测试。这些测试通常轻量、快速且独立,非常适合在开发周期的早期阶段捕获唾手可得的成果。开发人员可以在这些测试期间使用模拟或存根来模拟 API 或数据存储区。许多单元测试套件除了可以在 CI/CD 管道中运行外,还可以在开发机器上本地运行,使开发人员能够在提交代码并接受集成测试套件的较长测试周期之前,更快地收到有关其更改的可操作反馈。
CI/CD 管道中的集成测试
另一方面,您应该将集成测试纳入持续集成/持续部署 (CI/CD) 管道中。如果可能,请在管道的早期阶段进行 linting 和单元测试,并在将它们推送到上游存储库之前对所有提交运行它们。在执行单元测试后,将集成测试隔离在单独的阶段或管道作业中。您应该配置 CI/CD 管道阶段,特别是后面的阶段,以更紧密地反映将部署应用程序的环境和系统。
本文的其余部分将提供一个示例 GitHub repo 和 GitHub Actions 工作流程,其中包含一个基于 LLM 的应用程序,使用 Python 作为主要语言。
在 GitHub Actions 中设置 Python 环境
我们将在 GitHub 和 GitHub Actions 中设置一个基于 Python 的基本 LLM 应用程序和测试环境。本节假设您对 GitHub 和 GitHub Actions、CI/CD 和 Python 开发有一定的了解。我将在 GitHub 存储库中提供完整的示例。如果您想完整复制此示例,则需要访问 OpenAI API 和付费帐户。
配置 Python 和 GitHub Actions
对于应用程序本身,我们将使用出色的LangChain库来处理对 LLM 的调用,在本例中,LLM 将是 OpenAI 的GPT-4模型。`main.py` 脚本将使用LangChain 文档中的这个示例。我使用Poetry来处理本地依赖项和虚拟环境管理。
在 GitHub Actions 工作流中,我们将根据文档中提供的示例来配置工作流并进行一些额外的调整。
为了进行测试,我们将使用pytest、ruff进行 linting 以及pyspellchecker来测试提示和输入。
完整的 GitHub Actions 工作流程可以在这里找到。
LLM 提示的基本测试
基于 LLM 的软件应用程序高度依赖于输入数据和提示的质量。提示本质上是针对 LLM 的基于文本的指令,用于指示给定问题的所需语气、结构和上下文以及所需的答案。保持提示的质量和一致性非常重要。这时测试就派上用场了。
确定性测试
本例中的单元测试旨在具有确定性,并且不依赖于外部 API 调用来验证是否成功。这些测试将在独立的工作流步骤中运行。如果出现任何类型的测试失败,则不会在集成测试中浪费更多时间进行可能代价高昂的外部 API 调用。
检查应用程序的语法正确性:Lint 会查找常见的格式问题和其他经常阻止代码运行的错误。
验证输入和提示:使用`unit_tests.py` 模块,我们验证输入和输出解析是否正确处理预期值,并且提示不包含任何可能影响 LLM 输出质量的拼写错误的单词。
非确定性测试
集成测试示例可能具有不确定性,即使在一致的测试条件下也可能产生不同的结果或测试失败。
评估响应能力:测试装置包含对响应时间的简化评估。根据此处设置的值,如果响应时间(以秒为单位)超过该值,则测试将失败。如果 OpenAI API 遇到问题或需求量很大,即使代码正确,也可能会发生失败。
验证输出格式:尽管 LLM 已提供明确的指示以列表形式返回输出,但它可能会提供格式错误的输出。此断言验证了格式。
验证响应数据:提示指示 LLM 回答符合某一类别(在本例中为颜色)的“对象”列表。这里定义了原色和次色以帮助实现此断言,但 LLM 完全有可能回答“浅灰色”之类的内容,从而导致测试失败。
评估测试结果
代码和测试示例经过精心设计,并且经过了极大的简化。基于 LLM 的生产应用程序通常包含更复杂的逻辑和行为,并且需要更全面的测试套件。但是,这些测试为测试的评估和部署提供了良好的起点和基准。
您可以在此处查看成功运行的 GitHub Actions 工作流程的示例。
应对 linting 和单元测试失败通常是一个简单的建议:修复有问题的代码并再次运行测试。应对集成测试失败通常需要更多思考。如果 LLM 持续提供格式不正确的输出,或者输出与查询无关,则可能需要重写提示以提高效率。拥有 OpenAI 帐户的用户可以利用操场快速测试各种输入和提示组合以对抗实时模型,而无需每次进行更改时都经历测试周期。
包起来
将传统软件测试方法应用于生成式 AI 软件需要以不同的方式思考测试。DevOps 工程师可能会开始被分配路线图目标,以帮助部署基于 LLM 的软件应用程序和基础设施。
了解如何利用 CI/CD 和测试自动化来帮助实现它们的操作,对于组织在新的环境中保持竞争力以及对于 DevOps 工程师保持未来的相关技能组合来说,都非常重要。
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~