使用Aspire优雅的进行全栈开发——WinUI使用Semantic Kernel调用智普清言LLM总结Asp.Net Core通过Playwright解析的网页内容

news/2025/3/18 9:39:14/文章来源:https://www.cnblogs.com/GreenShade/p/18341903

前言

这算是一篇学习记录博客了,主要是学习语义内核(Semantic Kernel)的实践,以及Aspire进行全栈开发的上手体验,我是采用Aspire同时启动API服务,Blazor前端服务以及WinUI的桌面端项目,同时进行三个项目的代码修改,整体感觉很方便,如果代码都修改了只需要启动Aspire项目,不用每个项目单独起一遍了,而且速度很快,即使是有用容器服务的情况下。

技术方案

1. 框架选型

  • WebApi使用Asp.Net Core WebApi实现。
  • Bing搜索结果获取,以及网页解析内容提取使用的是PlayWright库。
  • 网页内容总结使用的是WinUI编写的客户端,结合语义内核(Semantic Kernel)调用国产智普清言LLM。
  • 后台管理页面使用的Blazor,不过只是一个demo页面。

img

2. 为什么这样选

作为一个.Net开发,肯定优先使用.Net相关的技术了,也为了能实践最新的技术,就进行了一些新技术的选择。

主要说明一下选择这几个技术框架的原因:

  • Playwright 原因是通过测试发现它的表现最好,其他类型的库也有测试,比如Selenium,HtmlAgilityPack,HtmlAgilityPack对静态网页解析比较好,但是如果遇到js渲染的数据很多的页面就不好了,Selenium比Playwright提取的内容差了一些,Playwright是通过模拟用户操作启动浏览器,然后获取内容,感觉如果一次性处理很多的页面应该也会负载很大。

  • Aspire 这个是因为这是微软最新的专门给开发人员开发的工具,那既然是给开发人员做的,那肯定要体验一把了,体验完感觉是真的不错,能够节省很多的步骤。

  • 语义内核(Semantic Kernel)选择它是因为这算是.Net社区对接大语言模型最流行的框架了,提供了很多的开箱即用的功能,对于开发智能APP帮助很大,而且社区热度也很高。

  • 智普清言LLM 选择它是多方面考虑的结果,第一是它兼容OpenAI的接口,这样语义内核就可以通过配置就能使用它,第二是它是支持Function Call的,也就是说它可以作为OpenAI的国内平替,用它开发一些智能APP是很好的。

  • WinUI 选择它是个人对客户端开发主要使用的是WinUI,而且用它对接大语言模型不把对接放到后端也是为了后面对接离线大语言模型做基础,比如微软的Phi3之类的。

代码讲解

本博客涉及的代码链接如下:

https://github.com/GreenShadeZhang/BingSearchSummary

1. 搜索结果获取

示例代码如下:

先创建Playwright实例,然后进行用户操作模拟。

   var playwright = await Playwright.CreateAsync();var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions { Headless = true });var page = await browser.NewPageAsync();// 设置 User-Agent 和视口大小var js = @"Object.defineProperties(navigator, {webdriver:{get:()=>false}});";await page.AddInitScriptAsync(js);await page.GotoAsync("https://www.bing.com");// 模拟用户输入搜索关键词await page.FillAsync("input[name=q]", keyword);await page.Keyboard.PressAsync("Enter");// 等待搜索结果加载await page.WaitForLoadStateAsync(LoadState.NetworkIdle);// 获取搜索结果内容var content = await page.ContentAsync();var dataList = BingSearchHelper.ParseHtmlToJson(content);var result = new List<BingSearchItem>();

将搜索结果解析成json数据如下:

这一步是因为我没有bing搜索的订阅,所以只能解析页面,如果有bing搜索的订阅这一步可以省略。

using BingSearchSummary.ApiService.Models;
using HtmlAgilityPack;namespace BingSearchSummary.ApiService;public class BingSearchHelper
{public static List<BingSearchItem> ParseHtmlToJson(string htmlContent){var htmlDocument = new HtmlDocument();htmlDocument.LoadHtml(htmlContent);var results = new List<BingSearchItem>();foreach (var node in htmlDocument.DocumentNode.SelectNodes("//li[@class='b_algo']")){var titleNode = node.SelectSingleNode(".//h2/a");var snippetNode = node.SelectSingleNode(".//p");var urlNode = node.SelectSingleNode(".//cite");var title = titleNode?.InnerText.Trim();var snippet = snippetNode?.InnerText.Trim();var url = urlNode?.InnerText.Trim();if (string.IsNullOrEmpty(title)){continue;}var searchItem = new BingSearchItem{Title = title,Snippet = snippet ?? "",Url = url ?? ""};results.Add(searchItem);}return results;}
}

通过上面的代码操作,关键词搜索的网页URL就已经拿到了,然后就可以继续进行页面内容的解析了。

2. 网页内容解析

客户端通过调用接口,然后获取关键词的前三条的搜索结果和网页内容。

    // 获取搜索结果内容var content = await page.ContentAsync();var dataList = BingSearchHelper.ParseHtmlToJson(content);var result = new List<BingSearchItem>();foreach (var data in dataList){if (result.Count >= 3){break;}//只处理三条数据await page.GotoAsync(data.Url);var divContent = await page.QuerySelectorAsync(".content");divContent ??= await page.QuerySelectorAsync("body");if (divContent != null){var pageContent = await divContent.InnerTextAsync();result.Add(new BingSearchItem{Title = data.Title,Url = data.Url,Snippet = data.Snippet,PageContent = pageContent});}

swagger结果展示如下:

img

3. 网页结果总结

这部分代码在WinUI项目中实现,WinUI调用接口获取到结果,并通过Microsoft.SemanticKernel.PromptTemplates.Liquid库进行消息模板动态生成消息,调用语义内核(Semantic Kernel)进行内容总结。

语义内核(Semantic Kernel)注入代码如下:

            //测试token被删除 已经无效 请换成自己的智普tokenbuilder.AddOpenAIChatCompletion(modelId: "GLM-4-Air", apiKey: "4827638425a6b9d48bea3b0599246ff2.pFjhEKShPOZE8OFd", httpClient: GetProxyClient("https://open.bigmodel.cn/api/paas/v4/chat/completions"));builder.Plugins.AddFromType<TimeInformationPlugin>();services.AddSingleton(builder.Build());#pragma warning disable SKEXP0040 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。services.AddSingleton<IPromptTemplateFactory, LiquidPromptTemplateFactory>();
#pragma warning restore SKEXP0040 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。

内容总结代码如下:

  [RelayCommand]private async Task SummaryAndUploadAsync(BingSearchItem item){_chatHistory.Clear();SummaryProcessRingStatus = true;try{var arguments = new KernelArguments{["startTime"] = DateTimeOffset.Now.ToString("hh:mm:ss tt zz", CultureInfo.CurrentCulture),["userMessage"] = item.PageContent};var systemMessage = await _promptTemplateFactory.Create(new PromptTemplateConfig(_systemPromptTemplate){TemplateFormat = "liquid",}).RenderAsync(_kernel, arguments);var userMessage = await _promptTemplateFactory.Create(new PromptTemplateConfig(_userPromptTemplate){TemplateFormat = "liquid",}).RenderAsync(_kernel, arguments);_chatHistory.AddSystemMessage(systemMessage);_chatHistory.AddUserMessage(userMessage);var chatResult = await _chatCompletionService.GetChatMessageContentAsync(_chatHistory, _openAIPromptExecutionSettings, _kernel);SummaryResult = chatResult.ToString();await _apiClient.PostContentsAsync(new BingSearchSummaryItem{Title = item.Title,Summary = chatResult.ToString(),Url = item.Url});}catch (Exception ex){System.Diagnostics.Debug.WriteLine(ex.Message);SummaryProcessRingStatus = false;}SummaryProcessRingStatus = false;}

效果如下:

img

到此总结就已经完成了,大家可以去看看代码,看看有没有帮助。

个人心得体会

在进行一段时间的学习之后,对大语言模型有了一些全面的认识,意识到大语言模型并不是万能的,但是它能够很轻松的做到我们之前要很复杂才能做到的事情。轻松做到的前提就是要给出很好的提示词。

如果把大语言模型比作战斗机,那提示词就可以比作是驾驶员了,提示词的好坏直接决定大语言模型输出的准确度。

作为软件开发人员,对于提示词的编写一定要多学习,多总结才行了。

参考推荐文档项目如下:

  • 博客代码

  • BotSharp

  • Rodel Agent

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/777759.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序3

1.样式文件1.小程序中不需要主动来引入样式文件2.px是写死的像素,不会变的,需要把页面中某些元素的单位由 px 变为 rpx1.设计稿 750px750px=750rpx 1px=1rpx2.把屏幕宽度变为375px375px=750rpx 1px=2rpx 1rpx=0.5px3.存在一个设计稿 宽度不和750成比例或者未知,…

Java流程控制02:顺序结构

JAVA的基本结构就是顺序结构,除非特别指明,否则就按照顺序一句一句执行。 顺序结构是最简单的算法结构。语句与语句之间,框与框之间是按从上到下的顺序进行的,它是由若干个依次执行的处理步骤组成的, 它是任何一个算法都离不开的一种基本算法结构。 顺序结构在程序流程图中…

6-2训练模型的三种方法

Pytorch通常需要用户编写自定义训练循环,训练循环的代码风格因人而异。Pytorch通常需要用户编写自定义训练循环,训练循环的代码风格因人而异。 有三类典型的训练循环代码风格:脚本形式训练循环,函数形式训练循环,类型是训练循环。 下面以minis数据集的多分类模型的训练为例…

kibana报错:Your basic license does not support watcher. Please upgrade your license.

kibana突然遇到以下错误,特地记录下:错误内容如下:   解决方法:删除,然后启动:

Python逆向

参考链接: https://www.bilibili.com/video/BV1JL4y1p7Tt/?spm_id_from=333.999.0.0 https://bbs.kanxue.com/thread-282542.htm https://blog.csdn.net/weixin_35967330/article/details/114390031?spm=1001.2014.3001.5501 https://0xd13a.github.io/ctfs/0ctf2017/py/前言…

浏览器插件监听元素变动-用于直播自动回复

直播获取评论区的原理 MutationObserver 是一个强大的浏览器API,它可以监听DOM的变化,包括元素的添加、删除、属性的更改等。 开发需求可联系vx:llike620 步骤:创建一个MutationObserver实例,并提供一个回调函数。 使用observe方法指定要监控的DOM节点和具体的变动类型。 …

redis+xxl-job初步设计点赞功能

一般情况下点赞业务涉及以下下几个方面: 1.我们肯定要知道一个题目被多少人点过赞,还要知道,每个人他点赞了哪些题目。 2.点赞的业务特性,频繁。用户一多,时时刻刻都在进行点赞,收藏等等处理,如果说我们采取传统的数据库的模式啊,这个交互量是非常大的,很难去抗住这个…

易优CMS友情链接列表标签

{eyou:flink type=all row=30 titlelen=15} <!--type:image图片类型--> <a href={$field.url} {$field.target}><img src={$field.logo} /></a> <!--type:text文本类型--> <a href={$field.url} {$field.target}>{$field.title}</a>…

如何选择PHP和MySQL的版本?

建议优先使用 php5.6 + MySQL5.7 的组合。扫码添加技术【解决问题】专注中小企业网站建设、网站安全12年。熟悉各种CMS,精通PHP+MYSQL、HTML5、CSS3、Javascript等。承接:企业仿站、网站修改、网站改版、BUG修复、问题处理、二次开发、PSD转HTML、网站被黑、网站漏洞修复等。…

易优cms获取栏目id的方法,调用栏目id实现不同栏目banner图替换

直接{$eyou.field.typeid}.jpg{$eyou.field.typeid}调用当前栏目ID , 可以在前面加上路径如 /skin/images/{$eyou.field.typeid}.jpg。 效果: 图片上传 images文件夹, 根据不同ID, 图片命名为1.jpg/2.jpg扫码添加技术【解决问题】专注中小企业网站建设、网站安全12年。熟…

帝国cms忘记后台登陆密码怎么办?

使用MySQL数据库管理软件phpmyadmin,修改数据库中的phome_enewsuser数据表进行密码重置操作: 修改phome_enewsuser表里的记录:将password字段的内容改为:“322d3fef02fc39251436cb4522d29a71”;将salt字段的内容改为:“abc”. 密码就重置为:123456扫码添加技术【解决问题…

易优CMS模板标签uiarclist文档列表

【基础用法】标签:uiarclist描述:文档列表编辑,比uitext、uihtml、uiupload标签多了一个typeid属性,使用时结合html一起才能完成可视化布局,只针对具有可视化功能的模板。用法: <div class="eyou-edit" e-id="文件模板里唯一的数字ID" e-page=文件…