SemanticFunction 融合 LLM 和传统编程
本文将继续和大家介绍 SemanticKernel 神奇的魔法,将使用 LLM 大语言模型编写的自然语言函数和传统的编程语言编写的函数融合到一起的例子。通过本文的例子,大家可以看到 SemanticKernel 框架所推荐的一个工作模式,同时可以更好的理解 SemanticKernel 框架的用法
本文属于 SemanticKernel 入门系列博客,更多博客内容请参阅我的 博客导航 或 博客园的合集
开始之前先来聊聊本文的背景,本文将给出一个使用 SemanticKernel 框架连接魔法的 LLM 和传统的编程语言编写的函数的应用例子。这个例子所解决的问题是:我拿到了多个不同的数据集,我需要编写代码将数据集里面所提到的日期提取出来
更具体一点的实现是我拿到的多个不同的数据集里面,每个数据集对于日期的定义可能是不相同的格式,如以下的两个数据例子。如果不依靠 LLM 魔法的话,那我只能写多个正则表达式去适配多个不同的数据集了。但在本文的设定里面,我是不擅长写正则表达式的
var data1 =
"""
在2023年9月1号开始上课
在2023年9月2号开始准备教材
在2023年9月3号完成作业
""";
var data2 =
"""
在9.1.2023开始上课
在9.2.2023开始准备教材
在9.3.2023完成作业
""";
那如果全部都给 LLM 处理呢?理论上是可以的,但是存在两个问题,第一个是 LLM 可能不够擅长做比较大的数据集的处理,无论是成本方面还是本身 Token 长度限制方面。第二个就是我选用的 GPT 3.5 模型本身难以完成这项任务,当前的执行效率也不够高,需要跑半天才能完成,且即使完成之后后续对接解析结果也需要额外的工作量
那是否有比较完美的方案,同时规避了传统编程函数和 LLM 的缺点,且拥有两者的优势?这是当然有的,通过 SemanticKernel 框架的辅助下,咱可以各取两者的优点,让传统编程难以完成的事情交给 LLM 去实现,将 LLM 不擅长做的交给传统编程语言去完成
总的技术实现是,先对每个数据集分别进行处理。编写传统 C# 函数,取出数据集里面的代表数据,也就是第一行的字符串。接着将取出的代表数据给到使用自然语言编程函数的 GPT 进行处理,让 GPT 给出正则表达式字符串。再根据 GPT 给出的正则表达式字符串,传入到 C# 的正则类里面,让 C# 代码高效稳定处理数据集
如此实现既可以让开发者不用编写复杂的正则表达式,同时也可以使用一套代码处理多个不同的数据格式的数据集
接下来让大家看看 SemanticKernel 将 LLM 自然语言函数和传统编程融合到一起的威力
在开始编写代码之前,期望大家已经对 SemanticKernel 和 C# 语言有了入门的了解
新建一个 .NET 7 的控制台应用,编辑 csproj 项目文件,按照 dotnet 的习俗安装好各个 NuGet 库,修改之后的 csproj 项目文件代码大概如下。放心,在本文末尾将给出本文所有代码的下载方法
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
<PackageReference Include="Microsoft.SemanticKernel" Version="0.20.230821.4-preview" />
</ItemGroup>
</Project>
接着进行配置和初始化,本文这里用到了 AzureAI 的 GPT 3.5 模型,需要提前申请好。申请地址:https://aka.ms/oai/access
using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Orchestration;
using Microsoft.SemanticKernel.SkillDefinition;
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Debug);
});
var logger = loggerFactory.CreateLogger("SemanticKernel");
// 这里的演示代码需要用到 AzureAI 的支持,需要提前申请好,申请地址:https://aka.ms/oai/access
var endpoint = "https://lindexi.openai.azure.com/"; // 请换成你的地址
var apiKey = args[0]; // 请换成你的密钥
IKernel kernel = new KernelBuilder()
.WithLogger(logger)
.WithAzureChatCompletionService("GPT35", endpoint, apiKey)
.Build();
关于以上配置,详细请参阅 我的 SemanticKernel 合集博客
按照本文的技术实现设计,先编写 C# 函数,这里需要有两个函数,分别是取出数据集的第一行作为代表数据,以及使用 GPT 给到的正则表达式字符串进行处理数据集。根据 SemanticKernel 自定义技能 博客提供的方法,以下直接给出这两个 C# 函数编写的 TextSkill 技能代码
class TextSkill
{
[SKFunction]
public string TakeFirstLine(string input, SKContext context)
{
context.Variables["text"] = input;
var stringReader = new StringReader(input);
return stringReader.ReadLine() ?? string.Empty;
}
[SKFunction]
public void RegexMatchText(string input, string text, SKContext context)
{
foreach (Match match in Regex.Matches(text,input))
{
Console.WriteLine(match.Value);
}
}
}
上面代码里面的 TakeFirstLine 就是取出数据集的第一行作为代表数据,其中一个细节就是将输入的内容放入到 text 变量里面,而不是丢掉。放入到变量里面就可以方便让后续的 RegexMatchText 函数使用
在 RegexMatchText 就是根据 GPT 给出的正则表达式字符串,也就是 input 变量对应的值,对 TakeFirstLine 放入的 text 变量,也就是原始数据集进行处理。处理之后直接打印
完成 C# 函数编写之后,将 TextSkill 技能导入到 SemanticKernel 里
kernel.ImportSkill(new TextSkill());
接下来请出百万炼丹师进行编写自然语言函数,让 GPT 可以可以从代表数据里面输出提取日期的正则表达式
const string FunctionDefinition =
"""
我有这样一段文本:
{ {$input} }
请你写一个正则表达式字符串,用来提取出日期
正则表达式字符串:
""";
kernel.CreateSemanticFunction(FunctionDefinition, maxTokens: 200, temperature: 0.5, functionName: "BuildRegexText");
调用 CreateSemanticFunction 时传入函数名,则可以自动注册到 SemanticKernel 框架里。现在咱拥有了三个函数,分别是两个 C# 代码编写的 TakeFirstLine 和 RegexMatchText 函数,以及使用自然语言编写的 BuildRegexText 魔法函数
按照 SemanticKernel 管道式调用函数 博客提供的方法,咱可以使用管道将以上几个函数排列组合放入到 SemanticKernel 执行
为了方便调用,这里编写了一个 C# 内部方法,方法的入参就是数据集
async Task RunAsync(string data)
{
await kernel.RunAsync
(
data,
kernel.Skills.GetFunction("TakeFirstLine"),
kernel.Skills.GetFunction("BuildRegexText"),
kernel.Skills.GetFunction("RegexMatchText")
);
}
以上代码就是按照本文开始的设计,先使用 TakeFirstLine 取出数据集里面的代码数据,接着调用 BuildRegexText 魔法函数让 GPT 生成正则表达式字符串,最后调用 RegexMatchText 函数使用 GPT 的正则表达式字符串处理数据集
这就是本文的实现的所有代码了,这个代码可以适配非常多的不同格式的数据,只要 GPT 魔法函数 BuildRegexText 能够正常输出正确的正则表达式的,那以上代码都能符合预期工作
接下来测试一下,看看以上代码能否符合预期工作
var data1 =
"""
在2023年9月1号开始上课
在2023年9月2号开始准备教材
在2023年9月3号完成作业
""";
var data2 =
"""
在9.1.2023开始上课
在9.2.2023开始准备教材
在9.3.2023完成作业
""";
Console.WriteLine($"开始执行解析 data1");
await RunAsync(data1);
Console.WriteLine($"开始执行解析 data2");
await RunAsync(data2);
可以看到,以上代码给出的是两个日期格式不相同的数据集,这时候就可以看到 LLM 的威力了。运行代码,可以看到控制台有以下输出
开始执行解析 data1
2023年9月1号
2023年9月2号
2023年9月3号
开始执行解析 data2
9.1.2023
9.2.2023
9.3.2023
可以看到正确输出了两个数据集的日期
也就是说尽管两个数据集采用不同的日期表达形式,但都在咱以上代码能工作的范围内,大家也可以试试更加奇怪的数据集,看是否能够符合预期工作
这就是 SemanticKernel 威力,使用 LLM 配合传统编程语言函数完成工作,发挥 LLM 和传统编程语言的优势
可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin db13740804d16b3c56e8c24ab5a9ddf40962ecec
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin db13740804d16b3c56e8c24ab5a9ddf40962ecec
获取代码之后,进入 WalhocebarkairQalbehoreho 文件夹
SemanticFunction 融合 LLM 和传统编程的更多相关文章
- 10Spring_AOP编程(传统编程)
注意我写这篇文章的思路,要想做切面编程,包含两个部分,通知和切点,通知是你要做哪些增强,切点是指你要拦截哪些方法.先介绍通知的定义再去介绍切点的定义.这篇文章我取名叫做Spring_AOP编程(传统编 ...
- 传统编程和IoC的对比
ref:http://www.importnew.com/13619.html 传统编程:决定使用哪个具体的实现类的控制权在调用类本身,在编译阶段就确定了. IoC模式:调用类只依赖接口,而不依赖具体 ...
- *nix下传统编程入门之GCC
准备工作 注意:本文可能会让你失望,如果你有下列疑问的话:为什么要在终端输命令啊? GCC 是什么东西,怎么在菜单中找不到? GCC 不能有像 VC 那样的窗口吗?…… 那么你真正想要了解的可能是 a ...
- Python交互式编程导论----事件驱动编程
传统的编程是如下线性模式的: 开始--->代码块A--->代码块B--->代码块C--->代码块D--->......--->结束 每一个代码块里是完成各种各样事情 ...
- Scalaz(43)- 总结 :FP就是实用的编程模式
完成了对Free Monad这部分内容的学习了解后,心头豁然开朗,存在心里对FP的疑虑也一扫而光.之前也抱着跟大多数人一样的主观概念,认为FP只适合学术性探讨.缺乏实际应用.运行效率低,很难发展成现实 ...
- Scalaz(10)- Monad:就是一种函数式编程模式-a design pattern
Monad typeclass不是一种类型,而是一种程序设计模式(design pattern),是泛函编程中最重要的编程概念,因而很多行内人把FP又称为Monadic Programming.这其中 ...
- Python装饰器与面向切面编程
今天来讨论一下装饰器.装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数 ...
- iOS 并发编程指南
iOS Concurrency Programming Guide iOS 和 Mac OS 传统的并发编程模型是线程,不过线程模型伸缩性不强,而且编写正确的线程代码也不容易.Mac OS 和 iOS ...
- NodeJS的异步编程风格
NodeJS的异步编程风格 http://www.infoq.com/cn/news/2011/09/nodejs-async-code NodeJS运行环境因其支持Javascript语言和异步编程 ...
- 通过实例让你真正明白mapreduce---填空式、分布(分割)编程
本文链接:http://www.aboutyun.com/thread-8303-1-1.html 问题导读: 1.如何在讲mapreduce函数中的字符串等信息,输出到eclipse控制台?2.除了 ...
随机推荐
- 记录--webpack和vite原理
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前言 每次用vite创建项目秒建好,前几天用vue-cli创建了一个项目,足足等了我一分钟,那为什么用 vite 比 webpack 要快 ...
- 记录--Threejs-着色器实现一个水波纹
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 hree.js 是一个基于 WebGL 的 JavaScript 3D 库,用于创建和渲染 3D 图形场景. 一. 图像渲染过程 1.we ...
- 记录--微信调用jssdk--Invalid Signature, updateAppMessageShareData: denied等问题
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 最近在做安卓内嵌入H5活动页拉新活动,遇到的棘手问题记录下, 一是为了日后遇到同样问题好回顾,二是希望能帮到有同样问题的兄弟. 废话不多说 ...
- hdfs的透明加密记录
1.背景 我们知道,在hdfs中,我们的数据是以block块存储在我们的磁盘上的,那么默认情况下,它是以密文存储的,还是以明文存储的呢?如果是明文存储的,那么是否就不安全呢?那么在hdfs中是如何做才 ...
- 二.安装ifconfig命令
二.安装ifconfig命令 1.ifconfig命令是设置或显示网络接口的程序,可以显示出我们机器的网卡信息,可是有些时候最小化安装CentOS等Linux发行版的时候会默认不安装ifconfig等 ...
- KingbaseES 使用sys_bulkload远程导入
前言 sys_bulkload 常见场景是本地导入数据,也可以在远程运行 sys_bulkload ,对数据库上的CSV 文件进行导入.远程导入数据时候需要注意,csv文件和ctl文件所在服务器.以下 ...
- KingbaseES Json 系列十二:Json其他函数
KingbaseES Json 系列十二--Json其他函数(JSONB_TYPEOF,JSON_SCALAR,JSON_SERIALIZE,JSON_TYPEOF,JSON_VALUE) JSON ...
- 抗噪液晶屏驱动芯片VK2C22A/B适用于单相电表段码驱动,水瓦斯表段码表、驱动等
产品型号:VK2C22A/B 产品品牌:永嘉微电/VINKA 封装形式:LQFP52/48.DICE(COB邦定片).COG(邦定玻璃用) 产品年份:新年份 (C21-285) VK2C22A/B概述 ...
- JavaScript语法-字符串模板
目录 JavaScript 模板字符串 代码 问题 初学者容易出现的错误 调用函数的情况 JavaScript 模板字符串 代码 以下是index.js的部分代码: onShareAppMessage ...
- Jetty的http3模块
启用http3模块,执行如下命令: java -jar $JETTY_HOME/start.jar --add-modules=http3 命令的输出,如下: ALERT: There are ena ...