继续上周末基于 Semantic Kernel 对接星火大模型之后,我还想继续对接它的function call能力。而我们作为大模型应用开发者,最核心的需求就是将大模型应用到我们的日常业务场景中,参与到企业应用的方方面面。
而大模型的function call能力就是实现这一目标的关键。本文将介绍利用Sk和星火大模型的function call调用C#的本地方法(注意这里说的“本地”是跟“远程”区分。不是指只C#的本地函数特性,而是委托或者方法都支持)。
function call的原理就是在给大模型发送消息时,带上可调用的函数描述,包括函数名和描述、参数名、描述和是否必须,返回值名和描述,然后大模型会根据提问和上下文分析是否匹配某个函数,匹配后参数是否满足,如果满足了就会在响应里返回函数名和参数值,否则就会再次跟用户提问缺少的参数。
于是我在我的应用本地写了这样一个简单的方法,传入一个整型订单ID,返回拼好的订单信息字符串,并且加上了注释。最后为了精确找到这个方法,加了个自定义特性AntSkFunction。
然后我写了个方法查询,就是普通的扫描程序集的类和方法。然后从xml读取注释,获得这个方法和参数的描述。
简单的扫描程序集寻找要调用的方法以及读取注释
然后在构建kernel实例时,构建KernelFunction 为SK插件,可以CreateFunctionFromMethod 方法传入MethodInfo,这里也能直接传委托。下文能看到如何调用。
到构建星火大模型客户端的地方,发送请求前从kernel.Plugins.GetFunctionMetadata()方法能获取所有配置的sk function,通过前面导入时的命名,找到我们的本地方法,构建星火sdk的函数参数。
接着,在模型的响应中获取被识别的函数,并解析参数,然后传入sk的函数调用方法。调用是利用SkFunction.InvokeAsync方法。待获得结果后,重新组织一次消息,发送给大模型,最后把结果反馈到UI。
可以看到这里有个递归,这是因为我是在IAsyncEnumerable也就是流式响应的类型中处理的。星火响应了function call之后,流就结束了,但是函数调用的结果是需要返回给大模型的。返回的方式就是在对话历史记录中,最后多加一个记录把函数结果告知,等它组织好语言再返回内容。所以这里是需要递归,组装好方法的执行结果后,把前面的流程再走一遍。
最终的效果如下:
可以看到连贯的两次对话,第一句是调了我们开头所说的本地方法,对比我们返回的参数,能看到答复是经过整理的。而且要知道这个过程向大模型调用过两次。而第二句则让它从历史记录中提取信息再根据自己的训练数据生成答案。
这里是最基本的实现用大模型调c#本地方法,我们还需要兼容sk的其他sdk,如openai,azure 的,还有sk自带的插件。
下一步我想就可以在这个方法中调用UI元素,在与大模型的交流中呈现不同的交互,这才是Blazor在大模型应用的优势啊。