乍一看,将GPT-4这样的大型语言模型(LLM)构建到您的代码中可能看起来很简单。该API是一个单一的REST调用,接收文本并根据输入返回响应。但在实践中,事情变得比这复杂得多。API也许被认为是一个领域的边界,你在那里提供提示,定义模型用来提供其输出的格式。但这是一个关键点:LLMs可以像你希望的那样简单或复杂。
当我们将人工智能模型整合到我们的代码中时,我们正在跨越两种不同的计算方式之间的界限,就像量子计算机的编程方式很像设计硬件。在这里,我们在写一个模型的行为方式的描述,期望它的输出能够符合我们的提示所定义的格式。就像在量子计算中,像Q#语言这样的结构提供了传统计算和量子计算机之间的桥梁,我们需要工具来包装LLM的API,并提供管理输入和输出的方法,确保模型仍然专注于我们定义的提示,并确保输出保持相关。
这是需要强调的一个重要观点:我们与LLM的互动方式与传统的编程方式有很大不同。我们需要的是一种与Q#相当的东西,一种能够在不同领域之间进行转换的更高层次的抽象,帮助我们获取数据并利用它来制作提示,同时提供一种方法来管理调用之间的基本上下文,避免耗尽对话中的可用标记,同时仍然保持输出以源数据为基础。
引入语义内核
几周前,我考察了微软的第一个LLM包装器,即开源的Prompt Engine。此后,微软又发布了一个更大、更强大的C#工具,用于与Azure OpenAI(以及与OpenAI自己的API)合作,即Semantic Kernel。它也是开源的,可以在GitHub上获得,同时还有一些样本应用程序,以帮助你开始使用。Semantic Kernel的Python版本也正在开发之中。
这个名字的选择很耐人寻味,因为它显示出对LLM的用途有了更好的理解。Prompt Engine是关于管理对API的输入的,而Semantic Kernel的范围更广,侧重于自然语言输入和输出。微软将其方法描述为 "以目标为导向",利用用户的初始请求("问")来指导模型,通过与模型相关的资源来协调传递,以满足请求,并返回对请求的响应("得")。
因此,将语义内核称为内核是有道理的。它就像是LLM API的一个操作系统,接受输入,通过与模型协作来处理输入,并返回输出。内核作为协调者的作用在这里很关键,因为它不仅能够与当前的提示及其相关的标记一起工作,而且还能够与记忆(键值对、本地存储以及矢量或 "语义 "搜索)一起工作,与其他信息服务的连接器一起工作,以及与混合提示和常规代码(想想LLM函数)的预定义技能一起工作。
语义内核(Semantic Kernel)工具提供了更有效的方法来构建和使用您需要包裹在提示引擎周围的构造类型,简化了可能变得相当复杂的编程任务,尤其是在涉及到处理上下文以及支持包括多次调用LLM API的行动时。
矢量和语义记忆
处理用户询问的一个关键要素是记忆的概念。这就是Semantic Kernel管理上下文的方式,与熟悉的文件和键值存储一起工作。不过,还有第三种选择,即语义内存。这种方法接近于LLM处理数据的方式,将内容视为向量,或者说嵌入,这是LLM用来表示文本含义的数字数组。类似的文本在与你的模型及其内容相关的整体空间中会有类似的向量,很像搜索引擎生成排名结果的方式。
像GPT这样的LLM使用这些嵌入向量来提取提示的上下文,帮助底层模型保持相关性和一致性。嵌入越好,模型就越不可能产生纯粹的随机输出。通过将大型提示分解成可由LLM总结的文本块,我们可以为每个总结生成一个嵌入向量,然后使用这些向量来创建复杂的提示,而不会耗尽一个请求的可用标记(例如,GPT-4对每个输入有8192个标记的限制)。
这些数据可以存储在一个矢量数据库中,以便快速检索。可以为专门的知识创建特定的矢量数据库,使用总结的内容来帮助保持LLM的进度。因此,例如,一个使用GPT-4进行医学病例摘要的应用程序,可以使用来自医学论文、合适的匿名笔记和其他相关文本的嵌入矢量数据库,以确保其输出是连贯的和符合背景的。这种方法在一定程度上解释了为什么微软的第一个基于GPT的大型应用是其Bing搜索引擎,因为它已经有适当的矢量数据库可供使用。
连接到外部数据源
连接器是语义内核的一个有趣的功能,因为它们是将现有的API与LLM整合起来的一种方式。例如,您可以使用微软图形(Microsoft Graph)连接器,在电子邮件中自动发送请求的输出,或者在您的组织结构图中建立关系描述。在Semantic Kernel应用程序中,进行调用并没有设定点;它可以是输入的一部分,也可以是输出的一部分,甚至可以是往返于LLM的调用序列的一部分。你可以从API调用中建立提示,而这些调用本身又建立了进一步的API调用,也许是通过使用基于Codex代码的模型来将所产生的输出注入到运行时中。
连接器的一个更有趣的特点是,它对LLM应用某种形式的基于角色的访问控制。如果你使用,比如说,微软图形查询来构建一个提示,这些将是在运行应用程序的用户的上下文中,使用他们的凭证来提供数据。将凭证传递给连接器可以确保输出结果是根据用户自己的数据定制的。
培养技能以混合提示模板和代码
Semantic Kernel的第三个主要组成部分是技能,它是混合LLM提示和传统代码的功能容器。这些函数在概念和操作上与Azure Functions类似,可以用来将专门的提示连锁起来。一个应用程序可以有一组函数,使用GPT生成文本,然后将该文本作为Codex和DALL-E的提示,从描述到网络应用原型(类似于微软的低代码和无代码Power Platform中自然语言编程工具的工作方式)。
一旦你有了你的技能、记忆和连接器,你就可以开始建立一个由LLM驱动的应用程序,使用技能将请求变成提示,并传递给底层模型。这种方法可以让您建立灵活的技能,让您的代码可以根据需要选择和使用。语义内核(Semantic Kernel)区分了语义功能、模板化的提示和本地功能,即处理数据以供LLM的语义功能使用的本地计算机代码。一个函数的输出可以与另一个函数连锁,允许你建立一个混合了本地处理和LLM操作的函数管道。
仅仅从对Semantic Kernel的这一简要了解中就可以看出,这是一个强大的工具,但是需要仔细思考和规划。您可以利用Semantic Kernel来构建和管理复杂的提示以及与输入和输出链一起工作的管道,以提供有趣和有用的结果。自然,当您适当地使用每一个元素的时候,您就会得到最好的结果,用本机代码来处理计算,用模型来关注定向目标(或者像文档中以非常微软的方式称之为 "要求")。
使用Semantic Kernel(语义内核)这样的工具来调集和协调输入和函数,肯定会使与LLM的工作变得更加有效,而不是简单地将提示传递给输入。它将允许您对输入进行消毒,引导LLM产生有用的输出。为了帮助你开始工作,微软提供了一份从其业务范围内构建LLM应用程序中学到的最佳实践指南(语义AI的Shillace法则)的清单。它们是如何围绕GPT等LLM构建代码的一个有用的入门指南,并且应该帮助你从这些新工具和技术中获得尽可能多的价值,同时避免不切实际的期望。