Semantic Kernel dotnet 1.0 RC1 发布了,这是正式迈向 1.0 正式版的第一个候选版本。这个版本相对于1.0 Beta 8 变化非常的大。而且可能是因为发布的比较匆忙,Semantic Kernel 开发者的先行者们已经发现多个明显的 Bug。建议大家谨慎升级。
以下是部分我们认为比较重要的变更:
实现自动函数调用
实现自动 OpenAI(其他带函数调用的模型也可以)函数调用
自动调用行为是根据 autoInvoke 是否为 true 来处理的。通过在
PromptExecutionSettings
基类的字典中设置命名属性,还可以以与服务无关的方式选择加入FunctionCallBehavior.AutoInvokeKernelFunctions
; 如果未来的服务实现具有内置的函数调用概念(其他一些 LLM 也是如此),或者如果想使用嵌入到连接器实现中的规划器来模拟它,那么它们也可以尊从这个逻辑。
代码例子:
Kernel kernel = new KernelBuilder()
.WithOpenAIChatCompletion("gpt-3.5-turbo-1106", apiKey)
.ConfigurePlugins(plugins => plugins.AddPluginFromObject<TimePlugin>())
.Build();
OpenAIPromptExecutionSettings settings = new() { FunctionCallBehavior = FunctionCallBehavior.AutoInvokeKernelFunctions };
Console.WriteLine(await kernel.InvokePromptAsync("What is the current time?", settings));
这个部分的功能表现,简直太酷了。这是一个相当大的新变化,连接器可以自行触发功能,使 LLM 成为实际的计划执行者。
FunctionCallBehavior
中有几个选项可用:
FunctionCallBehavior.EnableKernelFunctions
: 将发送所提供内核中的函数,但如果请求,不会自动调用;FunctionCallBehavior.AutoInvokeKernelFunctions
: 将发送所提供内核中的函数,并在请求时自动调用;FunctionCallBehavior.EnableFunctions(functionsList, autoInvoke)
: 指定的函数将被发送,它们是否会被自动调用取决于autoInvoke
参数以及它们在提供的内核中是否可用;FunctionCallBehavior.RequireFunction(function, autoInvoke)
: 指定的函数将作为函数调用请求发送到服务
流式/非流式处理的平等性
ChatCompletion
以前的
public interface IChatCompletion : IAIService
{
ChatHistory CreateNewChat(string? instructions = null);
Task<IReadOnlyList<IChatResult>> GetChatCompletionsAsync(ChatHistory chat, ...);
Task<IReadOnlyList<IChatResult>> GetChatCompletionsAsync(string prompt, ...);
IAsyncEnumerable<T> GetStreamingContentAsync<T>(ChatHistory chatHistory, ...);
}
public static class ChatCompletionExtensions
{
public static async Task<string> GenerateMessageAsync(ChatHistory chat, ...);
}
变更为
public interface IChatCompletionService : IAIService
{
Task<IReadOnlyList<ChatContent>> GetChatContentsAsync(ChatHistory chat, ..> tags)
IAsyncEnumerable<StreamingChatContent> GetStreamingChatContentsAsync(ChatHistory chatHistory, ...);
}
public static class ChatCompletionServiceExtensions
{
// v Single vv Standardized Prompt (Parse <message> tags)
public static async Task<ChatContent> GetChatContentAsync(string prompt, ...);
// v Single
public static async Task<ChatContent> GetChatContentAsync(ChatHistory chatHistory, ...);
public static IAsyncEnumerable<StreamingChatContent> GetStreamingChatContentsAsync(string prompt, ...);
}
TextCompletion
以前:
public interface ITextCompletion : IAIService
{
Task<IReadOnlyList<ITextResult>> GetCompletionsAsync(string prompt, ...);
IAsyncEnumerable<T> GetStreamingContentAsync<T>(string prompt, ...);
}
public static class TextCompletionExtensions
{
public static async Task<string> CompleteAsync(string text, ...);
public static IAsyncEnumerable<StreamingContent> GetStreamingContentAsync(string input, ...);
}
变更为:
public interface ITextCompletionService : IAIService
{
Task<IReadOnlyList<TextContent>> GetTextContentsAsync(string prompt, ...);
IAsyncEnumerable<StreamingTextContent> GetStreamingTextContentsAsync(string prompt, ...);
}
public static class TextCompletionServiceExtensions
{
public static async Task<TextContent> GetTextContentAsync(string prompt, ...);
}
函数调用 stepwise planner 改进
- 将
kernel
参数从构造函数移动到ExecuteAsync
中 - 使用语义函数生成初始计划,并从 YAML 加载提示和设置
- 根据为 planner 指定的最大令牌数检查估计的令牌计数
FunctionCallingStepwisePlannerConfig
增加了几个属性来支持这些改进:
/// <summary>
/// The ratio of tokens to allocate to the completion request. (prompt / (prompt + completion))
/// </summary>
public double MaxTokensRatio { get; set; } = 0.1;
internal int MaxCompletionTokens { get { return (int)(this.MaxTokens * this.MaxTokensRatio); } }
internal int MaxPromptTokens { get { return (int)(this.MaxTokens * (1 - this.MaxTokensRatio)); } }
HandleBars Planer 的遥测支持
重命名
Planners.Handlebars
独立包形式存在,在 nuget 上的完整名是Microsoft.SemanticKernel.Planners.Handlebars
将类、方法中的
SK
前缀改为Kernel
,
比如:
SKFunction
改为KernelFunction
SKException
改为KernelException
ISKPlugin
改为IKernelPlugin
- 等等
AIRequestSettings
改名为PromptExecutionSettings
引入
KernelFunctionArguments
作为Kernel.InvokeAsync
和KernelFunction.InvokeAsync
方法的参数。奇葩的是在之后的修改中又将KernelFunctionArguments
重命名为KernelArguments
了,以便额外支持KernelFunctions
以外的其他组件(prompt template、planner 等);也因此相关的一些参数也从IDictionary<string, string>
类型变更为KernelArguments
。且KernelArguments
开始支持非字符串参数:
InvokeAsync(Kernel kernel, SKContext context, AIRequestSettings? requestSettings, CancellationToken cancellationToken)
变为InvokeAsync(Kernel kernel, KernelArguments arguments, CancellationToken cancellationToken)
InvokeAsync(this Kernel kernel, KernelFunction function, ContextVariables variables, CancellationToken cancellationToken)
变为InvokeAsync(this Kernel kernel, KernelFunction function, KernelArguments arguments, CancellationToken cancellationToken)
KernelFunction.GetMetadata
从方法变为属性Function.Metadata
重构了
PromptTemplateConfig
:
requestSettings
重命名为executionSettings
- 合并了
PromptTemplateConfig
和PromptFuncitonModel
- prompt 函数的 config.json 中的
models
改名为execution_settings
KernelFunctionFromPrompt
的Create
方法增加了promptTemplateFactory
AzureTextCompletion
重名为AzureOpenAITextCompletion
RunStreamingAsync
更名为InvokeStreamingAsync
,并连同InvokeAsync
一起变为Kernel
的自有方法,而不再是扩展方法。包
Microsoft.SemanticKernel.TemplateEngine.Handlebars
改名为Microsoft.SemanticKernel.PromptTemplate.Handlebars
将
ModelResult
从Microsoft.SemanticKernel.Orchestration
移动到Microsoft.SemanticKernel.AI
将
KernelBuilder
的方法重命名为With
而不是Configure
。为了保持一致性,还将参数的顺序更改为WithPlugins
优先,以便所有重载都首先具有插件集合,并且将集合的类型更新为类型更强的KernelPluginCollection
而不是ICollection<IKernelPlugin>
命名空间中的
OpenAPI
重命名为OpenApi
IChatCompletion
接口及其所有实现都重命名为具有Service
后缀ITextCompletion
重命名为ITextGenerationService
ImageGeneration
重命名为TextToImage
有两个重要的称呼正式变更了
- 正式将
Semantic Function
改称为Prompt Function
。 - 正式将
Native Function
改称为Method Function
。
其他
- AIServices 将必须使用模型 ID 进行配置
- 修复了发出多个函数调用导致 OpenAI 返回 500 内部服务器错误的问题
- 更改事件处理程序取消的方式,并简化了
FunctionResult
,开发者可以通过检查可为空字典来检查是否有元数据。 - 将
ExperimentalAttribute
添加到 Semantic Kernel 的 .NET SDK 中的包和类,以标记Semantic Kernel v1.0.0 中尚未准备好的内容。 - Chat Completion API 的标准化提示输入支持(例如
<message>
) - 提取了一个
TypeConverterFactory
实用工具类,统一为FunctionResult
和KernelFunctionFromMethod
提供相同的类型转换功能。 - 移除了
KernelNameAttribute
,并为KernelFunctionAttribute
增加了一个 Name 属性。 原来需要在一个方法上加[KernelName("email_addresses")]
和[KernelFunction]
的合并为一个[KernelFunction("email_addresses")]
即可 - 恢复了
ChatHistory
流式消息输入的支持 - 修复 OpenAI JSON manifest 解析问题(下划线小写)
- 增加了一个用于使用
System.Text.Json
的序列化方法序列化自定义类型的 ADR。旨在简化自定义类型的使用,允许开发者使用任何可以通过System.Text.Json进行序列化的类型。在 JSON 可序列化类型上进行标准化是必要的,以便使用 JSON 模式在 Planner 的函数手册中描述函数。使用 JSON 模式来描述函数的输入和输出类型将允许 Planner 验证函数的使用是否正确。 - 清理了旧的流式处理 API
- 删除了
SKContext
类 - 新的 Planner 不再以来 Planner Core,以便可以独立发布
- Assistant Experimental 功能做了更新
- 清理一些扩展方法类
- 除了
Microsoft.SemanticKernel.Connectors.Memory.Postgres.PostgresMemoryEntry
以外所有其他record
都改回了class
有时开发者希望对可变数据或行为数据进行建模,这些数据具有复杂或自定义逻辑,用于更改其状态、验证其不变量或与其他对象交互。此类数据的示例包括 UI 控件、服务、存储库、工厂等。在这些情况下,类可以为数据及其行为的实现和封装提供更大的灵活性和控制。也希望避免创建和复制 record 实例的潜在性能开销,尤其是当它们具有许多或大型属性时,或者如果它们经常更新或传递。record 在后台作为引用类型实现,因此它们仍然会产生内存分配和垃圾回收的成本,并且在与表达式或解构一起使用时,它们还可能生成比类更多的中间对象和副本。此外,record 使用反射来实现其某些方法,例如 ToString 和 equality,这也可能会影响性能。亦或希望依赖基于类的语义的现有代码或框架进行互操作,例如序列化、反射、数据绑定、依赖关系注入等。其中一些方案可能不完全兼容或支持 record,或者可能需要额外的配置或自定义才能按预期工作。例如,某些库可能不容易序列化或反序列化记录,或者可能不支持某些数据绑定功能,如更改通知或验证。
结语
Semantic Kernel dotnet 的这个版本变更很大,也存在一些比较明显的 bug,在跟最新的 .NET 8 兼容上也存在一些潜在的问题,预计官方在本周内会发布一个新的修正版。建议大家先不要着急升级到这个版本,等待下个更稳定的版本发布后再跟进升级。当然了尝鲜研究者除外啊