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.除了 ...
随机推荐
- IValueConverter的基础用法
1.我们在做工控项目的时候通常设置配方的上下限 这个时候要求OK数在上下限范围之内,否则NG 首先我们绑定一个简单的List用来展示数据,我这里用学生Age来展示 <ListView Items ...
- 记录--手把手教你Vue+ECharts+高德地图API实现天气预报数据可视化
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前言 所谓数据可视化,我们可以理解为从宏观角度来看一眼就能看出来整个数据的占比,走向.对于数据可视化,很多互联网公司是很看重这一块的,包括 ...
- 测试开发之系统篇-Docker容器安装
前面文章我们讲到,容器是运行在宿主机上的一个进程,多个容器之间使用同一个宿主机上的操作系统内核.此处以Ubuntu20.04系统为例,介绍Docker容器引擎的安装过程. 安装 安装依赖. sudo ...
- Apache服务器打开网页是乱码解决方案
当 Apache 服务器显示乱码时,可以使用两种方法解决: 1. 服务器端 可以在 Apache 的配置文件中添加以下内容来设置默认编码为UTF-8: AddDefaultCharset utf-8 ...
- #线段树#洛谷 4588 [TJOI2018]数学计算
题目传送门 分析 由于曾经做过原题 所以就直接说了,因为每个数最多被除掉一次 所以可以用线段树维护区间乘,也就很简单了,删除就单点修改就行了 代码 #include <cstdio> #i ...
- Kafka原理剖析之「位点提交」
一.背景 Kafka的位点提交一直是Consumer端非常重要的一部分,业务上我们经常遇到的消息丢失.消息重复也与其息息相关.位点提交说简单也简单,说复杂也确实复杂,没有人能用一段简短的话将其说清楚, ...
- Blazor OIDC 单点登录授权实例7 - Blazor hybird app 端授权
目录: OpenID 与 OAuth2 基础知识 Blazor wasm Google 登录 Blazor wasm Gitee 码云登录 Blazor OIDC 单点登录授权实例1-建立和配置IDS ...
- 开发指导—利用组件&插值器动画实现HarmonyOS动效
一. 组件动画 在组件上创建和运行动画的快捷方式.具体用法请参考通用方法. 获取动画对象 通过调用animate方法获得animation对象,animation对象支持动画属性.动画方法和动画事件. ...
- 定了!12支队伍进入HarmonyOS极客马拉松2023决赛
12支队伍将在8月初,华为开发者大会(HDC.Togerther)上展开巅峰对决!
- Pytorch DistributedDataParallel(DDP)教程一:快速入门理论篇
一. 写在前面 随着深度学习技术的不断发展,模型的训练成本也越来越高.训练一个高效的通用模型,需要大量的训练数据和算力.在很多非大模型相关的常规任务上,往往也需要使用多卡来进行并行训练.在多卡训练中, ...