构建可靠AI应用的LLM三角原则 原创
本文将介绍一种经精心设计的开发可靠的高性能LLM应用程序所遵循的软件设计原则——LLM三角原则。基于这一原则开发LLM应用,有助于缩短应用LLM的巨大潜力和现实世界实施面临的挑战之间的差距。
引言
大型语言模型(LLM)存在巨大的潜力,但是想开发出高可靠性的生产级LLM应用程序仍然存在相当大的挑战。在经历过构建了数十个LLM系统的实践后,我将成功开发LLM应用的公式提炼为任何团队都可以遵循的3+1基本原则。
“LLM原生应用程序有10%是训练复杂模型相关的工作,而另外90%则是实验数据驱动的工程开发工作。”
构建生产级实用型LLM应用程序需要非常细致的工程实践。当用户无法直接与LLM交互时,必须精心编写有关提示,以涵盖所有细微差别,因为迭代用户的反馈信息可能是不可用的。
LLM三角原则介绍
LLM三角原则概括了构建有效LLM原生应用程序的基本指导方针。这一原则提供了一个坚实的概念框架,指导开发人员构建健壮可靠的LLM原生应用程序,并提供指导和支持。
从SOP视角优化三个突出的原则,从而实现最佳的LLM使用
关键点
总体来看,LLM三角原则引入了四种编程原则,以帮助你设计和构建LLM原生应用程序。
其中,第一个原则是标准操作程序(SOP:Standard Operating Procedure)。SOP提出我们三角形的三个顶点:模型、工程技术和上下文数据。
通过SOP的视角优化上述三角形的三个顶点的原则是确保高性能LLM原生应用程序的关键。
1.标准操作程序(SOP)
标准操作程序(SOP)是工业界众所周知的术语。这是一套由大型组织编写的分步骤说明,旨在帮助员工进行日常操作,同时每次都能保持高质量和相似的结果。通过编写详细的说明,这将缺乏经验或低技能的工人变成了专家。
LLM三角原则借鉴了SOP范式,并鼓励你将该模型视为缺乏经验/不熟练的工人。我们可以通过向模型“讲授”专家应该如何执行这项任务,来确保更高质量的结果。
SOP指导原则
“如果没有SOP,即使是最强大的LLM也无法提供始终如一的高质量结果。”
在思考SOP指导原则时,我们应该确定哪些技术将帮助我们最有效地实施SOP。
(1)认知建模
为了创建SOP,我们需要选取表现最佳的员工(领域专家),模拟他们如何思考和工作,以实现相同的结果,并记录下他们所做的一切。
在经过精心编辑和正式确定之后,我们将提供详细的说明来帮助每一位缺乏经验或低技能的工人取得成功,并做出优秀的工作业绩。
与人类一样,通过简化或拆分任务来减少任务的认知难度至关重要。遵循简单的循序渐进的指导比冗长复杂的程序更直接。
在这个过程中,我们识别出隐藏于其中的隐式认知“跳跃”——专家们采取的小而无意识的步骤,这些步骤会显著影响结果。这些微妙的、无意识的、往往不言而喻的假设或决定会对最终结果产生重大影响。
“隐式认知跳跃”的一个例子
假设我们想为SQL分析师建模。我们将首先采访他们,并问他们几个问题,例如:
- 当要求你分析一个业务问题时,你会怎么做?
- 你如何确保你的解决方案满足要求?
- <向受访者反映我们所理解的过程>
- 这能准确地捕捉到你所理解的过程吗<获得更正>
- 等等……
一个分析师所经历的认知过程及对其进行建模的举例
隐式认知过程呈现出多种形态和形式;一个典型的例子是“特定领域的定义”。例如,“畅销书”可能是我们领域专家的一个突出术语,而不是其他所有人都了解的术语。
在我们的SQL分析示例中扩展隐式认知过程
最终,我们将获得一个完整的SOP“配方”,让我们能够模仿我们表现最佳的分析师。
在绘制这些复杂的过程时,将其可视化为图形可能会有所帮助。当整个过程微妙且涉及许多步骤、条件和拆分时,这尤其有用。
图形形式显示的“SQL Analyst SOP”包括所有必需的技术步骤
我们的最终解决方案应该模仿SOP中定义的步骤。在此阶段,请尽量忽略最终的实现部分。稍后,你可以在整个解决方案中的一个或多个步骤/链上实现它。
与其他原则不同,认知建模(SOP写作)是唯一独立的过程。强烈建议你在编写代码之前先对流程进行建模。话虽如此,在实施它的同时,你可能会根据你获得的新见解或理解来加以改变。
现在,我们了解了创建一个定义良好的SOP的重要性,该SOP指导我们对问题的业务理解,让我们探索如何使用各种工程技术有效地实施它。
2.工程技术
工程技术可帮助你实际实施SOP并充分利用模型。在考虑工程技术原则时,我们应该考虑工具箱中的哪些工具(技术)可以帮助我们实施和塑造我们的SOP,并帮助模型实现与我们进行良好的沟通。
工程技术原理示意图
值得注意的是,一些工程技术仅在提示层中实现,而许多技术需要软件层才能有效,还有些技术需要将两层结合在一起。
工程技术层架构
虽然几乎每天工程技术人员都会发现与发明一些细微差别和技术改进,在此我将介绍两种主要技术:工作流/链和代理。
(1)LLM原生架构(又名“流工程”或“链”)
LLM原生架构描述了应用程序为生成任务结果而经历的代理执行流程。
我们流程中的每一步都是一个独立的过程,必须发生该过程才能完成我们的任务。其中,一些步骤将会以简单的确定性代码方式执行;而对于另外某些步骤,我们会使用LLM(代理)方式来执行。
为此,我们可以进一步反思我们绘制的标准操作程序(SOP)并思考如下问题:
- 我们应该将哪些SOP步骤分配到同一代理上?作为不同的代理,我们应该采取哪些步骤?
- 哪些SOP步骤应该以独立的方式执行(但它们可能会收到之前步骤的信息)?
- 在确定性代码执行方式中,我们可以执行哪些SOP步骤?
- 等等……
一个基于给定SOP的“维基百科作者”应用的LLM原生架构示例
在分析我们前面所给出的架构/图的下一个步骤之前,我们首先定义此步骤的关键属性:
输入和输出——这一步骤的特征是什么?在我们采取行动之前需要准备些什么?(这也可以作为代理的输出格式)
- 质量保证——是什么让响应“足够好”?是否有需要人为干预的情况?我们可以配置哪些类型的断言?
- 自主水平——我们需要对结果的质量进行多少控制?这个阶段可以处理哪些用例?换句话说,在这一点上,我们能在多大程度上相信模型能够独立工作?
- 触发器——下一步骤是什么?下一步骤的定义是什么?
- 非功能性——所需的延迟是多少?我们需要特殊的业务监控吗?
- 故障转移控制——可能发生哪些类型的故障(系统性和代理性)?我们的退路是什么?
- 状态管理——我们需要一个特殊的状态管理机制吗?我们如何检索/保存状态(定义索引键)?我们需要持久性存储吗?这种状态的不同用法是什么(例如缓存、日志记录等)?
- 等等……
(2)什么是代理?
LLM代理是LLM原生架构中的一个独立组件,其中会涉及到LLM调用。
这里给出的仅是LLM用法的一个实例,其中的提示将包含上下文内容。并非所有代理都是同样的执行方式——有些会借助现成的“工具”执行,有些则不会,还有些可能在流程中“只使用一次”,而另一些则可能携带之前的输入和输出信息被递归调用或多次调用。
借助现成“工具”执行的代理
一些LLM代理可以使用现成的“工具”,这些工具其实是一些用于计算或网络搜索等任务的预定义函数。代理输出指令,这些指令指定应用程序执行的工具和输入数据,并将最终执行结果返回给代理。
为了理解这个概念,让我们来看一个简单的工具调用的提示实现。这种实现方案甚至可以用于未经过原生训练的调用工具的模型:
You are an assistant with access to these tools:
- calculate(expression: str) -> str - calculate a mathematical
expression
- search(query: str) -> str - search for an item in the inventory
Given an input, Respond with a YAML with keys: `func`(str) and
`arguments`(map) or `message`(str).Given input
区分具有工具的代理(因此是自主代理)和其输出可以导致执行某一动作的代理是非常重要的。
“自主代理是能够生成完成任务的方法的代理。”
自主代理有权决定是否采取行动以及采取何种行动。相比之下,一个(非自主)代理只是“处理”我们的请求(例如分类)。基于这个过程,我们的确定性代码执行一个动作,而模型对此没有控制权。
自主代理与触发动作的代理比较
随着我们增加代理在规划和执行任务方面的自主权,我们增强了其决策能力,但可能会降低对输出质量的控制。虽然这看起来像是一个让它更“智能”或“先进”的神奇解决方案,但这也伴随着会失去对质量控制的代价。
自主代理的利弊权衡
值得注意的是,要警惕完全自主代理的诱惑。虽然它们的架构可能看起来很吸引人,也更简单,但将其用于一切(或作为最初的PoC)可能会对“实际生产”情况产生很大的欺骗作用。另外,自主代理难以调试且不可预测(响应质量不稳定),这使得它们无法用于生产。
目前,代理(没有隐含的指导)还不太擅长规划复杂的流程,通常会跳过关键步骤。例如,在我们的“维基百科作者”应用示例中,它们只会开始写作,跳过系统化的过程。这使得代理(尤其是自主代理)仅与模型一样好,或者更准确地说,仅与它们相对于你的任务所训练的数据一样好。
与其让代理(或一群代理)自由地以端到端方式完成所有事情,不如尝试将它们的任务限制在需要这种敏捷性或创造力的流程/SOP的特定区域。这可以产生更高质量的结果,因为你可以同时享受这两个世界。
一个很好的例子是AlphaCodium(https://www.codium.ai/blog/alphacodium-state-of-the-art-code-generation-for-code-contests/):通过将结构化流与不同的代理(包括一个负责以迭代方式编写和测试代码的新型代理)相结合,它们提高了在CodeContest上GPT-4的准确性(pass@5),其准确率从19%上升到44%。
AlphaSodium的LLM架构
虽然工程技术为实施我们的SOP和优化LLM原生应用程序奠定了基础,但我们还必须仔细考虑LLM三角原则的另一个关键组成部分:模型本身。
3.模型
我们选择的模型是我们项目成功的关键组成部分。在这一方面,选择使用大型模型(如GPT-4或Claude Opus)可能会产生更好的结果,但相当昂贵,而较小的模型可能不那么“聪明”,但有助于资金预算。在思考模型原则这一方面时,我们应该着眼于确定我们的约束和目标,以及什么样的模型可以帮助我们实现它们。
模型原理
“并非所有LLM都是一样的;因此,请将选定的模型与要完成的任务相匹配即可。”
事实上,我们并不总是需要最大的模型;这取决于任务。为了找到正确的匹配,我们必须有一个实验过程,并尝试我们解决方案的多种不同版本。
不妨先让我们来看一个“缺乏经验的工人”的类比,这将会对作出决定有所帮助。例如,一个拥有许多学历的非常“聪明”的工人可能很容易在某些任务中取得成功。尽管如此,他们可能对这份工作有些资历过高;因此,雇佣一个“更便宜”的候选工人将更具成本效益。
在考虑模型时,我们应该根据我们愿意采取的权衡来定义和比较解决方案:
- 任务复杂性——较简单的任务(如摘要)更容易用较小的模型完成,而推理通常需要较大的模型。
- 推理基础设施——它应该在云端还是边缘设备上运行?模型大小可能会影响小型手机,但云服务可以容忍。
- 定价——我们能容忍什么价格?考虑到业务影响和预期使用情况,它是否具有成本效益?
- 延迟——随着模型变大,延迟也会变大。
- 标记数据——我们是否有可以立即使用的数据,用未经训练的样本或相关信息来丰富模型?
在许多情况下,在你拥有“内部专业知识”之前,为经验丰富的员工支付一点额外费用是有帮助的——LLM也是如此。
如果你没有标定好的数据,那么建议你从一个更强大(更大)的模型开始,收集数据,然后利用它通过一些小样本或微调来增强模型的能力。
(1)微调模型
在对模型进行微调之前,你必须考虑几个方面:
- 隐私——你的数据可能包括必须从模型中保留的私人信息。如果你的数据包含私人信息,你必须首先匿名处理你的数据以避免法律责任。
- 法律、合规性和数据权利——在训练模型时可能会提出一些法律问题。例如,OpenAI使用条款政策阻止你在没有OpenAI的情况下使用生成的响应来训练模型。另一个典型的例子是遵守GDPR(即《通用数据保护条例》,是欧盟的一项重要法律,旨在保护欧盟境内个人的数据隐私和安全)的法律,该法律要求“撤销权”,用户可以要求公司从系统中删除信息。这引发了关于该模型是否应该重新训练的法律问题。
- 更新延迟——在训练一个模型时,延迟(或数据截止)时间要比预计高得多。与通过上下文嵌入新信息不同(见下面的“上下文数据”一节)——这种方式能够提供即时延迟。训练模型则是一个漫长的过程,需要大量时间。因此,模型的再训练往往很少被采用。
- 开发和操作——在持续评估结果性能的同时,实施可重复、可扩展和可监控的微调流程至关重要。这个复杂的过程需要持续的维护。
- 成本——再训练因其复杂性和每次训练所需的高强度资源(GPU)而被认为是昂贵的。
LLM作为上下文学习者的能力,以及新模型支持更大的上下文窗口的事实,极大地简化了我们的实现;因此,即使没有微调,也能提供出色的结果。然而,由于微调的复杂性,建议将其作为最后手段或完全跳过。
相反,对特定任务(例如结构化JSON输出)或特定领域语言的模型进行微调可能是非常高效的。一个小型的、特定于任务的模型可能非常有效,并且在推理方面比大型LLM便宜得多。但无论如何,确保明智地选择你的解决方案,并在升级到LLM训练之前评估所有相关考虑因素。请牢记:
“即使是最强大的模型也需要相关且结构良好的上下文数据才能发光。”
4.上下文数据
LLM是上下文情境学习者。这意味着,通过提供特定任务的信息,LLM代理可以帮助我们在没有特殊训练或微调的情况下执行它。这使我们能够轻松地“讲授”新知识或技能。在考虑上下文数据原则时,我们应该致力于组织和建模可用数据,以及如何在提示中组合数据。
上下文数据原则
为了构建我们的上下文,我们需要在发送给LLM的提示中包含相关的(上下文)信息。归纳来看,我们可以使用两种上下文:
- 嵌入式上下文——作为提示的一部分提供的嵌入式信息。
You are the helpful assistant of , a at
- 附件上下文——提示开始/结束时粘合的信息块列表
Summarize the provided emails while keeping a friendly tone.
---
<email_0>
<email_1>
上下文通常使用“提示模板”来实现(例如jinja2或mustache,或者只是原生格式化文字字符串)。通过这种方式,我们便可以优雅地组合它们,同时保持提示信息的精髓:
# Embedded context with an attachment context
prompt = f"""
You are the helpful assistant of {name}. {name} is a {role} at {company}.
Help me write a {tone} response to the attached email.
Always sign your email with:
{signature}
---
{email}
"""
(1)小样本学习
小样本学习是一种通过示例“教导”LLM而不需要大量微调的强大方法。在提示中提供一些代表性示例可以指导模型理解所需的格式、风格或任务。
例如,如果我们希望LLM生成电子邮件回复,我们可以在提示中包含一些写得很好的回复示例。这有助于模型学习首选的结构和音调。
我们可以使用不同的例子来帮助模型捕捉不同的极端情况或细微差别,并从中学习。因此,必须包含各种样本示例,以涵盖应用程序可能遇到的一系列场景。
随着应用程序的增长,你可以考虑实现“动态小样本(https://arxiv.org/abs/1804.09458)”,这涉及以编程方式为每个输入选择最相关的示例。虽然它增加了你的实现方案的复杂性,但它确保了模型在每种情况下都能得到最合适的指导,从而有助于显著提高各种任务的性能,而无需进行昂贵的微调。
(2)检索增强生成
检索增强生成(RAG:https://www.promptingguide.ai/techniques/rag)是一种在生成响应之前为其他上下文检索相关文档的技术。这就像让LLM快速浏览特定的参考资料,以便帮助提供需要的答案一样。这种技术可以使响应保持最新和真实,而无需重新训练模型。
例如,在开发支持聊天机器人应用程序时,RAG可以提取相关的维基页面帮助,以告知LLM相关的答案。
这种方法通过将反应建立在检索到的事实基础上,帮助LLM保持最新状态并减少虚幻的信息。RAG对于需要更新的或专业化的知识而无需重新训练整个模型的任务特别方便。
例如,假设我们正在为我们的产品构建一个支持聊天。在这种情况下,我们可以使用RAG从我们的维基帮助中检索相关文档,然后将其提供给LLM代理,并要求其根据问题撰写答案并提供文档。
在实施RAG时,有三个关键部分需要注意:
- 检索机制——虽然RAG的传统实现涉及使用向量相似性搜索检索相关文档,但有时使用基于关键字的搜索(如BM-25)等更简单的方法会更好或更便宜。
- 索引数据结构——在没有预处理的情况下,对整个文档进行索引,可能会限制检索过程的有效性。有时,我们希望添加数据准备步骤,例如根据文档准备问题和答案列表。
- 元数据——存储相关元数据可以更有效地引用和过滤信息(例如,将维基页面缩小到仅与用户特定产品查询相关的页面)。这个额外的数据层简化了检索过程。
(3)提供相关背景
与你的代理相关的上下文信息可能会有所不同。虽然这似乎是有益的,但为模型(就好似一位“非技术工人”)提供过多的信息可能会让它不知所措。理论上,这会导致模型学习不相关的信息(或令牌连接),这可能会导致混淆和幻觉(https://en.wikipedia.org/wiki/Hallucination_(artificial_intelligence))。
例如,当模型Gemini 1.5作为LLM发布并投入使用时,可以处理多达1000万个符号,这导致一些从业者质疑上下文是否仍然是一个问题。虽然这是一项了不起的成就,特别是对于某些特定场景应用(如与PDF聊天),但它仍然有限,尤其是在推理各种文档时。
因此,浓缩一下提示信息并仅向LLM代理提供相关的信息至关重要。这降低了模型在无关符号上的处理能力,提高了质量,优化了延迟,并降低了成本。
当然,已经有许多技巧可以提高所提供上下文的相关性,其中大多数与你如何存储和编目数据有关。
例如,对于RAG应用程序来说,添加一个数据准备来格式化一下你存储的信息是很方便的(例如,基于文档的问题和答案,然后只向LLM代理提供答案;这样,代理就可以获得一个总结和更短的上下文),并在检索到的文档上使用重新排序算法来优化结果。
“数据为LLM原生应用程序的引擎提供动力。因此,上下文数据的有效性战略设计会有助于更高效地释放它们的真正潜力。”
结论
LLM三角原则提供了一种结构化的方法来开发高质量的LLM原生应用程序,它有助于缩短应用LLM的巨大潜力和现实世界实施面临的挑战之间的差距。开发人员可以通过专注于3+1关键原则(模型、工程技术和上下文数据)来创建更可靠、更有效的LLM驱动的解决方案,所有这些原则都以明确的SOP为指导。
LLM三角原则
关键点归纳
从一个明确的SOP开始:模拟你的专家的认知过程,为你的LLM应用程序创建一个循序渐进的指南。在思考其他原则时,将其作为根本指南。
- 选择正确的模型:在能力和成本之间取得平衡,并考虑从较大的模型开始,然后再考虑转向较小、微调的模型。
- 利用工程技术:实施LLM原生架构,并战略性地使用代理来优化性能和保持控制。尝试不同的提示技巧,为你的应用场景找到最有效的提示。
- 提供相关上下文:在适当的情况下,在上下文学习中使用,包括RAG技术,但要警惕切莫让不相关的信息淹没了模型。
- 迭代和实验:找到正确的解决方案通常需要不断地测试和改进你的工作。为此,我建议你阅读和实施文章《构建LLM应用程序:清晰的步骤指南》(https://towardsdatascience.com/building-llm-apps-a-clear-step-by-step-guide-1fe1e6ef60fd)”中强调的有关技巧,以获得详细的LLM原生开发过程指南。
通过应用LLM三角原则,开发公司就可以超越简单的概念验证,开发出强大的、可用于实战型的LLM应用程序,从而真正利用这项变革性技术的力量。
译者介绍
朱先忠,51CTO社区编辑,51CTO专家博客、讲师,潍坊一所高校计算机教师,自由编程界老兵一枚。
原文标题:The LLM Triangle Principles to Architect Reliable AI Apps,作者:Almog Baku
链接:https://towardsdatascience.com/the-llm-triangle-principles-to-architect-reliable-ai-apps-d3753dd8542e。