看一下任何大型语言模型(LLM)的教学指南,其中推荐的用法包括调用API,向其发送提示(prompt),并使用返回的响应。如果你想让LLM生成一封感谢信,你可以这样做:
import openai``recipient_name = "John Doe"``reason_for_thanks = "helping me with the project"``tone = "professional"``prompt = f"Write a thank you message to {recipient_name} for {reason_for_thanks}. Use a {tone} tone."``response = openai.Completion.create("text-davinci-003", prompt=prompt, n=1)``email_body = response.choices[0].text
虽然这在概念验证(PoCs)中是可以接受的,但使用将大型语言模型(LLM)仅仅当作另一个文本到文本(或文本到图像/音频/视频)API的架构进行生产部署,会导致在风险、成本和延迟方面,应用程序的设计欠佳。
解决方案不是走向另一个极端,通过每次微调LLM并添加护栏等措施来过度设计你的应用程序。正如任何工程项目的目标一样,关键是在每个具体用例的复杂性、适用性、风险、成本和延迟方面找到正确的平衡。在这篇文章中,我将描述一个框架,帮助你找到这种平衡。
大型语言模型应用架构框架
以下是一个建议你用于确定你的生成式AI应用或智能体架构的框架。我将在接下来的部分中详细介绍如图所示的八种选择。
这里的坐标轴(即决策标准)是风险和创造性。对于每一个你打算使用大型语言模型(LLM)的用例,首先确定你需要从LLM中获得多少创造性,以及该用例所携带的风险程度。这能帮助你缩小选择范围,找到适合你的平衡点。
需要注意的是,是否使用智能体系统(Agentic Systems)与上述决策是完全独立的——当任务过于复杂,无法通过单次LLM调用完成,或者任务需要非LLM能力时,应使用智能体系统。在这种情况下,你需要将复杂任务分解为更简单的任务,并在智能体框架中进行编排。本文将向你展示如何构建一个生成式AI应用(或一个智能体)来完成这些简单任务之一。
为什么第一个决策标准是创造性
为什么决策标准是创造性和风险?LLM是一种非确定性技术,如果你不需要太多独特的内容生成,那么使用LLM可能得不偿失。
例如,如果你正在生成一系列的产品目录页面,它们之间真的需要有多大的不同?你的客户希望产品信息准确,可能并不真的在意所有单反相机页面是否用相同的方式解释单反技术的优势——事实上,一定程度的标准化可能更便于对比。这说明在这种情况下,对LLM的创造性要求是非常低的。
实际上,降低非确定性的架构也减少了对LLM的总调用次数,因此也具有降低使用LLM总体成本的副作用。由于LLM调用比典型的Web服务更慢,这也带来了降低延迟的美好副作用。这就是为什么y轴是创造性,以及为什么在该轴上我们也考虑了成本和延迟。
你可以参考上述图表中列出的示例用例,并讨论它们是否需要低创造力或高创造力。这真的取决于你的业务问题。如果你是一家杂志或广告公司,即使你的信息内容网页(不同于产品目录页面)也可能需要具备一定的创造力。
为什么第二个决策标准是风险
大规模语言模型(LLMs)倾向于生成虚构细节,并可能反映其训练数据中的偏见和毒性。鉴于此,直接将LLM生成的内容发送给最终用户存在一定的风险。为了解决这个问题,需要增加大量的工程复杂性——你可能需要引入人工审核环节来审查内容,或者在应用程序中添加防护措施以验证生成的内容是否违反了政策。
如果你的应用场景允许最终用户向模型发送提示,并且应用程序在后台生成面向用户的响应(在许多SaaS产品中这是一个常见的情况),那么与错误、虚构和毒性相关的风险会非常高。
同样的应用场景(如艺术生成)在不同的情境下可能携带不同层次和类型的危险,如下面的图所示。例如,如果你正在生成电影的背景音乐,风险可能涉及到无意中复制受版权保护的音符,而如果你正在生成将向数百万用户播放的广告图片或视频,你可能担心内容的毒性。这些不同的风险类型与不同的风险级别相关联。例如,如果你正在构建一个企业搜索应用程序,该程序返回来自你的公司文档库或技术文档的文档片段,与LLM相关的风险可能会非常低。如果你的文档库包括医学教科书,那么搜索应用程序返回的与上下文无关的内容可能带来的风险会很高。
当然,下面是翻译内容:
与按照创意排列的用例列表一样,你也可以对按风险排列的用例列表挑刺。但是,一旦你确定了与每个用例相关的风险及其所需的新颖性,所建议的架构就值得作为起点来考虑。然后,如果你理解了每种架构模式背后的“为什么”,你可以选择一种能够平衡你需求的架构。
在本文的其余部分,我将从图表中的#1开始描述这些架构。
- 每次生成(适用于高创意、低风险任务)
这是作为默认的架构模式——每次你想要生成内容时,都调用部署好的LLM的API。这是最简单的,但它也意味着每次都要调用LLM。
通常,你会使用一个PromptTemplate,并根据运行时参数来模板化你发送给LLM的提示。使用一种允许你替换LLM的框架是个好主意。
在我们基于提示发送邮件的例子中,我们可以使用langchain:
prompt_template = PromptTemplate.from_template(` `"""` `You are an AI executive assistant to {sender_name} who writes letters on behalf of the executive.` `Write a 3-5 sentence thank you message to {recipient_name} for {reason_for_thanks}.` `Extract the first name from {sender_name} and sign the message with just the first name.` `"""``)``...``response = chain.invoke({` `"recipient_name": "John Doe",` `"reason_for_thanks": "speaking at our Data Conference",` `"sender_name": "Jane Brown",``})
因为你每次都在调用LLM(大语言模型),这种方式仅适用于那些需要极高创造力的任务(例如,每次你都想得到一封不同的感谢信),并且你也不担心因此带来的风险(例如,最终用户在点击“发送”之前可以阅读并编辑这封信)。
这种模式常用于交互式应用程序中,这些应用程序需要响应各种提示,并且这些应用程序是面向内部用户的,因此风险较低。
- 响应/提示缓存(适用于中等创造力、低风险的任务)
你可能不希望向同一个人发送相同的感谢信。你希望每次都能得到不同的感谢信。
但是,如果你正在构建一个基于过去工单的搜索引擎,以协助内部的客户支持团队,那么在这种情况下,你希望重复的问题每次都能生成相同的答案。
一种能够大幅降低成本和延迟的方法是缓存过去的提示和响应。你可以使用langchain在客户端进行这样的缓存操作。
from langchain_core.caches import InMemoryCache``from langchain_core.globals import set_llm_cache`` ``set_llm_cache(InMemoryCache())`` ``prompt_template = PromptTemplate.from_template(` `"""` `What are the steps to put a freeze on my credit card account?` `"""``)``chain = prompt_template | model | parser
当我尝试时,缓存响应的时间仅是原来的千分之一,并且完全避免了与大型语言模型(LLM)的调用。
缓存不仅适用于客户端缓存精确的文本输入及其对应响应(见下图)。Anthropic支持“提示缓存”,这意味着你可以要求模型在服务器端缓存提示的一部分(通常是系统提示和重复的上下文),同时在每次后续查询中继续发送新的指令。使用提示缓存可以减少每次查询的成本和延迟,而不会影响生成内容的创造性。这在检索增强生成(RAG)、文档提取和少量样本提示中特别有用,尤其是在示例变得庞大时。
Gemini将这种功能分离为上下文缓存(这可以降低成本和延迟)和系统指令(这不会减少代币数量,但可以减少延迟)。OpenAI最近宣布支持提示缓存,其实现会自动缓存发送到API的提示中最长的前缀,只要提示超过1024个代币。这些服务器端缓存不会减少模型的能力,只会减少延迟和/或成本,因为你仍然会继续可能会收到与相同文本提示不同的结果。
内置的缓存方法需要完全匹配文本。但是,可以以利用您情况细微差别的方法来实现缓存。例如,您可以将提示重写为规范形式以增加缓存命中的机会。另一个常见的技巧是存储最常见的100个问题,对于任何足够接近的问题,您可以将提示重写为询问存储的问题。在多轮对话机器人中,您可以获取用户对这种语义相似性的确认。像这样的语义缓存技术会稍微减少模型的能力,因为您会获得相同问题的相同响应,即使这些问题略有不同。
- 预生成模板(适用于中等创造力,低至中等风险任务)
有时,您并不介意在相同情况下为每个人生成相同的感谢信。也许您正在写给购买产品的客户的感谢信,并且不介意为购买该产品的任何客户生成相同的感谢信。
同时,这种使用情况下存在较高的风险,因为这些通信会发送给最终用户,并且没有内部员工能够在发送之前编辑每个生成的信件。
在这种情况下,预生成模板响应会很有帮助。例如,假设您是一个旅行社,并提供5种不同的套餐。您只需要为这些套餐中的每一个准备一个感谢信息。也许您希望为单独旅行者、家庭和团体提供不同的消息。您仍然只需要比套餐数量多3倍的信息。
prompt_template = PromptTemplate.from_template(` `"""` `Write a letter to a customer who has purchased a tour package.` `The customer is traveling {group_type} and the tour is to {tour_destination}.` `Sound excited to see them and explain some of the highlights of what they will see there` `and some of the things they can do while there.` `In the letter, use [CUSTOMER_NAME] to indicate the place to be replaced by their name` `and [TOUR_GUIDE] to indicate the place to be replaced by the name of the tour guide.` `"""``)``chain = prompt_template | model | parser``print(chain.invoke({` `"group_type": "family",` `"tour_destination": "Toledo, Spain",``}))
结果是为给定的组类型和旅游目的地生成这样的消息:
Dear [CUSTOMER_NAME],`` ``We are thrilled to welcome you to Toledo on your upcoming tour! We can't wait to show you the beauty and history of this enchanting city.`` ``Toledo, known as the "City of Three Cultures," boasts a fascinating blend of Christian, Muslim, and Jewish heritage. You'll be mesmerized by the stunning architecture, from the imposing Alcázar fortress to the majestic Toledo Cathedral.` ` ``During your tour, you'll have the opportunity to:`` ``* **Explore the historic Jewish Quarter:** Wander through the narrow streets lined with ancient synagogues and traditional houses.` `* **Visit the Monastery of San Juan de los Reyes:** Admire the exquisite Gothic architecture and stunning cloisters.``* **Experience the panoramic views:** Take a scenic walk along the banks of the Tagus River and soak in the breathtaking views of the city.``* **Delve into the art of Toledo:** Discover the works of El Greco, the renowned painter who captured the essence of this city in his art.`` ``Our expert tour guide, [TOUR_GUIDE], will provide insightful commentary and share fascinating stories about Toledo's rich past.` ` ``We know you'll have a wonderful time exploring the city's treasures. Feel free to reach out if you have any questions before your arrival.`` ``We look forward to welcoming you to Toledo!`` ``Sincerely,`` ``The [Tour Company Name] Team` `
你可以生成这些消息,让一个人工审核员进行审核,然后将它们存储在数据库中。
正如你所见,我们要求大语言模型在消息中插入我们可以动态替换的占位符。每当需要发送回复时,从数据库中检索消息,并用实际数据替换占位符。
使用预生成的模板将一个每天需要审核数百条消息的问题转化为仅在添加新项目时审核几条消息的问题。
- 小型语言模型(低风险,低创造力)
最近的研究表明,由于大语言模型在学习我们所需的所有可计算函数时存在矛盾,因此无法完全消除幻觉。相较于太大以至于不适合特定任务的语言模型,为特定任务设计的小型语言模型出现幻觉的风险要低得多。如果你正在使用前沿语言模型来执行不需要其强大能力和广泛知识的任务,那么可能没有必要。
在一些任务非常简单,不需要太多创造力且风险容忍度非常低的使用场景中,你可以选择使用小型语言模型(SLM)。这确实是以准确性为代价的——在一项2024年6月的研究中,微软的一名研究员发现,对于从无结构文本中提取结构化数据(如发票信息),他们的基于文本的小型模型(Phi-3 Mini 128K)能达到93%的准确率,而GPT-4o的准确率可达99%。
LLMWare团队评估了各种小型语言模型。截至撰写本文时(2024年),他们发现Phi-3表现最佳,但随着时间的推移,越来越小的模型也开始达到这一水平的表现。
以图形形式表示这两个研究,小型语言模型(SLM)正在越来越小的模型尺寸下实现其准确性(因此幻觉越来越少),而大语言模型(LLM)则专注于提升任务处理能力(因此幻觉越来越多)。对于文档提取等任务,这两种方法之间的准确度差异已经趋于稳定(见图)。
如果这种趋势持续下去,预计会越来越多地使用SLM(小语言模型)和非前沿大语言模型(LLM)来处理企业任务,这些任务只需要低创造力且对风险容忍度低。例如,从文档中创建嵌入式数据,用于知识检索和主题建模,这些应用场景通常符合这种特征。使用小型语言模型来执行这些任务。
- 组装重构(中等风险,低创造力)
组装重构的核心思想是在生成动态内容之前通过预生成来降低风险,并仅使用大语言模型(LLM)进行提取和总结,即使这些任务是“实时”进行的,引入的风险也很低。
假设你是机器零部件的制造商,需要为产品目录中的每个项目创建一个网页。你当然会关心准确性。你不想宣称某个产品具有耐热性,但实际上它没有。你也不希望大语言模型虚构出安装该零件所需的工具。
你可能有一个数据库,描述每个零件的属性。一种简单的方法是使用大语言模型为每个属性生成内容。就像预生成模板(模式#3)一样,确保在将内容存储到内容管理系统之前由人工进行审核。
prompt_template = PromptTemplate.from_template(` `"""` `You are a content writer for a manufacturer of paper machines.` `Write a one-paragraph description of a {part_name}, which is one of the parts of a paper machine.` `Explain what the part is used for, and reasons that might need to replace the part.` `"""``)``chain = prompt_template | model | parser``print(chain.invoke({` `"part_name": "wet end",``}))
然而,简单地将所有生成的文本附加在一起会产生一些不太易于阅读的内容。你可以将所有这些内容组装到提示的上下文中,并要求大语言模型将内容重新格式化为所需的网站布局:
class CatalogContent(BaseModel):` `part_name: str = Field("Common name of part")` `part_id: str = Field("unique part id in catalog")` `part_description: str = Field("short description of part")` `price: str = Field("price of part")`` ``catalog_parser = JsonOutputParser(pydantic_object=CatalogContent)`` ``prompt_template = PromptTemplate(` `template="""` `Extract the information needed and provide the output as JSON.` `{database_info}` `Part description follows:` `{generated_description}` `""",` `input_variables=["generated_description", "database_info"],` `partial_variables={"format_instructions": catalog_parser.get_format_instructions()},``)`` ``chain = prompt_template | model | catalog_parser
如果你需要总结评论或交换关于该物品的文章,可以将其整合到批量处理流水线中,并将总结内容也加入上下文中。
- 模板的机器学习选择(中等创造力,中等风险)
组装好的重新格式化方法适用于内容相对静态的网页(如产品目录页面)。然而,如果你是电子商务零售商,并希望创建个性化推荐,内容将是高度动态的。你需要从大型语言模型(LLM)中获得更高的创造力。你对准确性的风险容忍度仍然大致相同。
在这种情况下,你可以继续为每种产品使用预先生成的模板,并使用机器学习来选择将要使用的模板。
例如,对于个性化推荐,你可以使用传统的推荐引擎来选择将向用户展示的产品,并根据所选产品获取相应的预生成内容(图像+文本)。
这种结合预生成+机器学习的方法也可以用于为客户旅程定制网站。你会预先生成着陆页,并使用倾向模型来选择下一步的最佳行动。
- 微调(高创造力,中等风险)
如果你的创造力需求很高,那么没有其他方法可以避免使用LLM来生成所需的内容。但是,每次生成内容意味着你无法扩大人工审查的规模。
有两种方法可以解决这个难题。从工程复杂性角度来看,较简单的方法是教会LLM生成你想要的内容类型,而不是生成你不想要的内容类型。这可以通过微调来实现。
有三种方法可以微调基础模型:适配器调优、蒸馏和人工反馈。每种微调方法都针对不同的风险:
-
适配器调优保留了基础模型的全部能力,但允许你选择特定的风格(如符合公司声音的内容)。这里解决的是品牌风险。
-
蒸馏在有限的任务集上近似基础模型的能力,并使用较小的模型部署在本地或防火墙后面。这里解决的是保密性风险。
-
通过强化学习的人类反馈(RLHF)或通过差异隐私优化(DPO),使模型能够从合理的准确度开始,并通过人类反馈变得更好。这里解决的是适用性风险。
微调的常见用例包括创建品牌内容、总结机密信息和个性化内容。
- 防护措施(高创造力,高风险)
如果你希望拥有全部能力,并且需要缓解多种风险(例如,担心品牌风险,机密信息泄露,或者对通过反馈持续改进感兴趣)?
此时,唯一的办法就是全力以赴构建防护措施。防护措施可能涉及在模型输入信息之前进行预处理,对模型输出进行后处理,或根据错误条件迭代提示。
市场上有一些预构建的防护措施(比如Nvidia的NeMo),用于常见功能,如检查越狱、在输入中屏蔽敏感数据以及自我事实核查。
然而,你可能需要自己实现其中的一些护栏(见上图)。与可编程护栏一起部署的应用程序是你选择实现一个GenAI应用的最复杂的方式。在选择这条路之前,请确保这种复杂性是必要的。
总结
我建议你使用一个平衡创造力和风险的框架来决定你的GenAI应用或智能体的架构。创造力指的是生成内容所需的独特性水平。风险则关乎如果LLM生成不准确、有偏见或有害内容时的影响。处理高风险场景需要工程上的复杂性,比如人工审查或护栏。
该框架由八种架构模式组成,分别对应不同创造力和风险的组合:
-
每次生成:每次内容生成请求都调用LLM API,提供最大的创造力,但成本和延迟也较高。适用于风险较小的交互式应用,如内部工具。
-
响应/提示缓存:适用于中等创造力、低风险的任务。缓存过去的提示和响应以减少成本和延迟。当一致的答案被需要时(如内部客户支持搜索引擎),此方法非常有用。提示缓存、语义缓存和上下文缓存等技术可以提高效率,同时不牺牲创造力。
-
预生成模板:通过使用预生成且经过审核的模板来处理重复任务,减少持续的人工审查需求。适用于中等创造力、低到中等风险的情况,如需要标准化但又个性化的内容,例如旅行社的客户沟通。
-
小型语言模型(SLMs):使用较小的模型来减少与大型LLM相比的错觉和成本。适合低创造力、低风险的任务,如知识检索或主题建模的嵌入创建。
-
组装重组:使用LLM进行重组和摘要,同时使用预生成的内容以确保准确性。适用于像产品目录这样的内容,其中部分内容的准确性至关重要,而另一些部分则需要创造性写作。
-
机器学习选择模板:利用机器学习根据用户上下文选择适当的预生成模板,平衡个性化与风险管理。适用于个性化推荐或动态网站内容。
-
微调:包括对LLM进行微调以生成所需内容,同时尽量减少不需要的输出,解决与品牌声音、机密性或准确性相关的风险。适配器调优侧重于风格调整,蒸馏针对特定任务,而人工反馈则用于持续改进。
-
护栏:高创造力、高风险的任务需要护栏来缓解多重风险,包括品牌风险和机密性,通过预处理、后处理和迭代提示来实现。现成的护栏可以解决诸如越狱和敏感数据屏蔽等常见问题,而针对特定行业/应用需求的自定义护栏可能也是必要的。
通过使用上述框架来架构GenAI应用,你可以为每个用例平衡复杂性、适用性、风险、成本和延迟。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。