本图使用Dall-E3创建
本文将向你介绍构建一款LLM原生应用程序开发所需要的所有步骤——从最初的想法到实验、评估和生产的所有过程。

大型语言模型(LLM)正在迅速成为现代人工智能的基石。然而,目前还没有出现公认的最佳实践,而且先驱们往往没有明确的开发路线图。因此,这种状况急需要有人重新发明有关轮子;否则,将会使人陷入困境。

在过去的两年里,我帮助一些组织利用LLM构建了创新型应用程序。通过这次经历,我开发了一种经得住考验的方法来创建创新型LLM应用解决方案(受LLM.org.il社区的启发而形成),我将在本文中与大家共同分享。

具体来说,本文将为读者朋友在LLM原生开发的复杂环境中导航提供一幅清晰的路线图。你将学习如何从构思转变为实验、评估和产品化的全过程,从而有助于释放出你创建开创性应用程序的所有潜力。

本图使用Dall-E3创建本图使用Dall-E3创建

为什么标准化流程至关重要

当今,LLM领域是如此充满活力,以至于我们几乎每天都能够听到新的突破性创新。这很令人兴奋,但也让人陷于迷茫——你可能会发现自己在这个过程中迷失了方向,不知道该做什么,或者如何将你的新颖的想法付诸实践。

长话短说,如果你是一位想要有效地构建LLM原生应用程序的人工智能创新者(经理或从业者),本文正是你的选择。

实施标准化流程有助于启动新项目,并提供以下几个关键好处:

  1. 标准化流程——有助于调整团队成员,并确保新成员顺利入职(尤其是在这种混乱的情况下)。
  2. 定义明确的里程碑——这是一种简单的方法,用于跟踪和衡量你的工作,并确保你沿着正确的道路前行。
  3. 确定决策点——LLM原生开发充满了未知和“小实验”(见下文)。清晰的决策点使我们能够轻松地降低风险,并始终使我们的开发努力保持清晰而高效。

LLM工程师的基本技能

与软件研发中的任何其他既定角色不同,LLM原生开发绝对需要一个新角色:LLM工程师或AI工程师。

LLM工程师是一种独特的综合技能成员,涉及不同(既定)角色的技能:

  • 软件工程技能——像大多数软件工程师一样,大多数工作都涉及到将工程组件拼合在一起,并将其粘成一体。
  • 研究技能——正确理解LLM的原生实验性质至关重要。虽然构建一些简易的酷的演示程序非常容易,但这些酷的演示程序和实际解决方案之间的距离需要实验证明并具备一定的灵活性。
  • 深入的业务/产品理解——由于模型的脆弱性,理解业务目标和过程至关重要,而不是拘泥于我们定义的架构。对LLM工程师来说,建立手动流程模型的能力是一项黄金技能。

在写这篇文章的时候,LLM工程仍然是全新的,相应的岗位招聘可能非常具有挑战性。因此,企业较早些时间招聘具有后端/数据工程或数据科学背景的候选人可能是个好主意。

软件工程师可能会期待一个更平稳的过渡,因为实验过程更“工程师化”,而不是“科学化”(与传统的数据科学工作相比)。话虽如此,我看到许多数据科学家也在做这种转变。只要你对自己必须接受新的“软技能”这一事实感到满意,那么你就已经走在了正确的道路上!

LLM原生开发的关键要素

与经典的后端应用程序(如CRUD)不同,这里没有循序渐进的开发方案。像“人工智能”中的其他一切一样,LLM原生应用程序需要开发人员具备研究和实验的心态。

要“驯服一头巨兽”,你必须分而治之,把你的工作划分成更小的实验模块,测试其中的一些,最后再选择最有希望的实验组件。

我再怎么强调研究心态的重要性也不为过。这意味着,你可能会花时间探索一项研究内容,发现它“不可能”、“不够好”或“不值得”。这完全没关系——这意味着我们已经走在了正确的道路上。

使用LLM进行实验是构建LLM原生应用程序的唯一方法(并避免前进中的“陷阱”)(使用Dall-E3创建)

拥抱实验——过程的核心

有时,你的“实验”会失败,然后你稍微调整一下你的工作,从而使得另一项实验取得更好的效果。

这正是为什么,在设计最终解决方案之前,我们必须从简单开始,尽可能规避风险。

  1. 定义“预算”或时间表。让我们看看在X周内我们能做什么,然后决定如何做或是否继续。通常,2-4周的时间来了解基本的PoC(Proof of Concept:为观点提供证据)就足够了。如果它看起来很有希望——继续投入资源来改善它。
  2. 实验——无论你在实验阶段选择自下而上还是自上而下的方法,你的目标都是最大限度地提高结果的连续性。在第一次实验迭代结束时,你应该有一些PoC(与你的工作相关的其他成员可以使用)和你已经实现的基本内容。
  3. 回顾性——在我们的研究阶段结束时,我们可以了解构建这样一个应用程序的可行性、局限性和成本。这有助于我们决定是否将其产品化,以及如何设计最终产品及其用户体验。
  4. 产品化——通过遵循标准软件工程师最佳实践并实施反馈和数据收集机制,开发项目的最终商业版本,并将其与解决方案的其余部分集成。

LLM原生应用程序开发生命周期(图片由作者提供)LLM原生应用程序开发生命周期(图片由作者提供)

为了很好地实施以实验为导向的过程,我们必须在处理和构建这些实验时做出明智的决定:

精益起步——自下而上的方法

【译者注】精益即“lean”,起源于日本丰田的创作理念。

虽然许多早期采用者很快就进入了具有成熟Langchain或类似功能的“最先进”多链代理系统,但我发现采用“自下而上的方法”通常会产生更好的结果。

开始精益,非常精益,信奉“one prompt to rule them all(一个提示,统治他们所有人)”的哲学吧。尽管这种策略看起来可能是非常规的,而且一开始可能会产生糟糕的结果,但它可以为你的系统打开基础。

从那里开始,然后不断迭代和完善提示,采用提示工程技术来优化结果。当你发现精益解决方案中的弱点时,通过添加分支来解决这些缺点,从而对流程进行拆分。

在设计我的LLM工作流程图或LLM原生架构的每一片“叶子”时,我都会遵循魔幻三角形(The Magic Triangle)原则来确定何时何地修剪树枝、拆分树枝或加厚根部(通过提示工程技术),并挤出更多的柠檬。

自下而上方法示意图(作者图片)自下而上方法示意图(作者图片)

例如,要用自下而上的方法实现“原生语言SQL查询”,我们可以从以原生方式将模式发送到LLM并要求它生成查询开始。

自下而上的方法示例(图片由作者提供)自下而上的方法示例(图片由作者提供)

通常,这与“自上而下的方法”并不矛盾,而是在它之前迈出的又一步。这种方案有利于我们能够快速获胜,吸引更多的项目投资。

大局在前:自上而下的战略

“我们知道LLM工作流并不容易,为了实现我们的目标,我们可能最终会采用一些工作流或LLM原生架构。”

自上而下的方法认识到了这一点,并从第一天开始就着手设计LLM原生架构,并探索实现其不同的步骤/链。

通过这种方式,你可以将工作流架构作为一个整体进行测试,并挤压整个“柠檬”,而不是单独精炼每一片“叶子”。

自上而下的方法过程:设计一次架构,然后实现架构并进行测试和测量(作者图片)

例如,为了用自上而下的方法实现“原生语言SQL查询”,我们将在开始编码之前就开始设计体系结构,然后跳到完整的实现:

自上而下方法的一个例子(作者图片)自上而下方法的一个例子(作者图片)

找到正确的平衡

当你开始试验LLM时,你可能会从其中一个极端开始(过于复杂的自上而下或超级简单的一次性)。事实上,没有这样的赢家。

理想情况下,在对模型进行编码和实验之前,你将定义一个好的SoP(标准操作流程),并为专家建模。在现实中,建模是非常困难的;有时,你可能无法接触到这样的专家。

我发现从一开始就要确定一个好的架构/SoP很有挑战性,所以在投入使用之前,值得先进行一些尝试。然而,这并不意味着一切都必须过于精简。如果你已经有了一个预先的理解,即某些东西必须被分解成更小的部分——那么就这样做吧。

在任何情况下,在设计解决方案时,你都应该利用“魔幻三角形”原则,正确地对手动流程进行建模。

优化你的解决方案——挤压“柠檬”

在实验阶段,我们不断挤压“柠檬”,增加更多的复杂“层”:

  • 提示工程技术——比如少样本、角色分配,甚至动态少样本。
  • 将上下文窗口从简单的变量信息扩展到复杂的RAG流可以帮助改进结果。
  • 用不同的模型进行实验——不同的模型在不同的任务上表现不同。此外,大型LLM通常不是很划算,值得尝试更多特定于任务的模型。
  • 提示节约——我了解到,在“节约”中使用标准操作流程(特别是提示和要求的输出)通常会提高延迟。通过减少提示词大小和模型需要经历的步骤,我们可以减少模型需要生成的输入和输出。你会感到惊讶,但提示节约(prompt dieting)有时甚至可以提高质量!请注意,提示节约也可能导致质量下降。因此,在这样做之前进行理智测试很重要。
  • 将流程拆分为更小的步骤也非常有益,并使优化SOP的子流程变得更容易和可行。

请注意,这可能会增加解决方案的复杂性或损害性能(例如,增加处理的符号数量)。为了缓解这种情况,请使用简洁的提示和较小的模型。

根据经验,当系统提示的显著变化为SOP的这一部分产生更好的结果时,通常最好进行拆分。

挤压AI柠檬(使用Dall-E3创建)挤压AI柠檬(使用Dall-E3创建)

LLM实验的剖析

就我个人而言,我更喜欢从一个简单的Jupyter笔记本开始,使用Python、Pydantic和Jinja2:

  1. 使用Pydantic从模型中定义我的输出模式。
  2. 使用Jinja2编写提示模板。
  3. 定义结构化输出格式(在YAML文件中)。这将确保模型遵循“思维步骤”,并以我的SOP为指导。
  4. 通过Pydantic验证确保此输出;如果需要,请重试。
  5. 稳定你的工作——使用Python文件和包将代码组织成工作单元。

在更广泛的范围内,你可以使用不同的工具,如OpenAI流媒体来轻松利用流媒体(和工具),LiteLLM在不同的提供商之间拥有标准化的LLM SDK,或vLLM来服务开源LLM。

通过健全测试和评估确保质量

健全性测试评估项目的质量,并确保项目质量不会低于你预先定义的成功率基准线。

把你的解决方案/提示词库想象成一条毯子——如果你把它拉得太长,它可能会突然无法覆盖它曾经覆盖的一些使用场景。

要做到这一点,可以先定义一组你已经成功覆盖的案例,并确保你保持这种状态(或者至少值得这样做)。将其视为表驱动的测试可能会更易于操作。

请注意,评估“生成”解决方案(例如,编写文本)的成功与否比使用LLM执行其他任务(例如分类、实体提取等)要复杂得多。对于这类任务,你可能希望使用更智能的模型(如GPT4、Claude Opus或LLAMA3-70B)来进行评价。

尝试使输出在“生成”输出之前包含“确定性部分”可能也是一个好主意,因为这些类型的输出更容易测试:

cities:
  - New York
  - Tel Aviv
vibes:
  - vibrant
  - energetic
  - youthful
target_audience:
  age_min: 18
  age_max: 30
  gender: both
  attributes:
    - adventurous
    - outgoing
    - culturally curious
# ignore the above, only show the user the `text` attr.
text: Both New York and Tel Aviv buzz with energy, offering endless activities, nightlife, and cultural experiences perfect for young, adventurous tourists.
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

当前,已经存在一些尖端的比较有前景的解决方案值得研究。我发现它们在评估基于RAG的解决方案时特别有用:你可以进一步了解DeepChecks、Ragas或ArizeAI。

做出明智的决策:复盘的重要性

在每个主要的/时间框架的实验或里程碑之后,我们应该停下来,就如何以及是否继续采用这种方法做出明智的决策。

在这一点上,你的实验将有一个明确的成功率基准线,你将知道需要改进什么。

这也是开始讨论该解决方案的产品化含义并从“产品工作”开始的一个好观点:

  1. 在产品中会是什么样子?
  2. 限制/挑战是什么?你将如何缓解这些问题?
  3. 你当前的延迟是多少?它足够好吗?
  4. 用户体验应该是什么?你可以使用哪些UI技巧?流媒体有帮助吗?
  5. 代币的预计支出是多少?我们能用更小的模型来减少开支吗?
  6. 优先级事项是什么?这些挑战中的任何一项都是引人注目的吗?

假设我们达到的基准线“足够好”,并且我们相信我们可以缓解我们提出的问题。在这种情况下,我们将继续投资和改进该项目,同时确保它永远不会降级并使用健全性测试。

本图使用Dall-E3创建本图使用Dall-E3创建

从实验到产品:将你的解决方案变为现实

最后,但同样重要的是,我们必须将我们的工作产品化。与任何其他生产级解决方案一样,我们必须实现生产工程概念,如日志记录、监控、依赖关系管理、容器化、缓存等。

这是一个庞大的世界,但幸运的是,我们可以借鉴经典的生产工程中的许多机制,甚至可以采用许多现有的工具。

话虽如此,还需要格外注意LLM原生应用程序的细微差别:

  • 反馈循环——我们如何衡量成功?它只是一个“同意/反对”的机制,还是考虑采用我们的解决方案的更复杂的机制?收集这些数据也很重要;今后,这可以帮助我们重新定义我们的理智“基线”,或者通过动态少样本微调我们的结果,或者微调模型。
  • 缓存——与传统的软件工程不同,当我们在解决方案中涉及生成方面时,缓存可能非常具有挑战性。为了缓解这种情况,探索缓存类似结果的选项(例如,使用RAG技术)和/或减少生成输出(通过具有严格的输出模式)。
  • 成本跟踪——许多公司发现从“强大的模型”(如GPT-4或Opus)开始非常诱人;然而,在生产中,成本可能会迅速上升。避免对最终账单感到惊讶,并确保始终计算输入/输出符号,并跟踪你的工作流程影响。
  • 可调试性和跟踪——确保你已经设置了正确的工具来跟踪“带有错误”的输入并在整个过程中跟踪它。这通常包括保留用户输入以备日后调查和建立跟踪系统。记住:“与过渡性软件不同,人工智能会悄无声息地失败!”

结语:你在推进LLM原生技术方面的作用

这可能是本文的结束,但肯定不是我们工作的结束。LLM原生开发是一个迭代过程,它涵盖了更多的用例、挑战和功能,并不断改进我们的LLM产品。

当你继续你的人工智能开发之旅时,保持敏捷,大胆地进行实验,并牢记最终用户。与社区分享你的经验和见解,我们可以一起推动LLM原生应用程序的发展。不断探索、学习和建设——可能性是无穷的。

我希望本文能够成为你LLM原生开发之旅中的宝贵伴侣!

本文使用缩略语

【1】SoP/SOP:标准操作流程,这是从魔幻三角形文章借鉴的概念。

【2】YAML:我发现使用YAML来构建输出在LLM中效果更好。为什么?我的理论是,它去掉了不相关的标记,表现得很像原生语言。

【3】魔幻三角形(The Magic Triangle):LLM原生开发蓝图;请继续关注,并在它的蓝图发布时关注我已进行阅读。

附:

原文标题:Building LLM Apps: A Clear Step-By-Step Guide,作者:Almog Baku

译者介绍

朱先忠,51CTO社区编辑,51CTO专家博客、讲师,潍坊一所高校计算机教师,自由编程界老兵一枚。

Loading

作者 yinhua

发表回复