使用Aspire优雅的进行全栈开发——WinUI使用Semantic Kernel调用智普清言LLM总结Asp.Net Core通过Playwright解析的网页内容
前言
这算是一篇学习记录博客了,主要是学习语义内核(Semantic Kernel)的实践,以及Aspire进行全栈开发的上手体验,我是采用Aspire同时启动API服务,Blazor前端服务以及WinUI的桌面端项目,同时进行三个项目的代码修改,整体感觉很方便,如果代码都修改了只需要启动Aspire项目,不用每个项目单独起一遍了,而且速度很快,即使是有用容器服务的情况下。
技术方案
1. 框架选型
- WebApi使用Asp.Net Core WebApi实现。
- Bing搜索结果获取,以及网页解析内容提取使用的是PlayWright库。
- 网页内容总结使用的是WinUI编写的客户端,结合语义内核(Semantic Kernel)调用国产智普清言LLM。
- 后台管理页面使用的Blazor,不过只是一个demo页面。

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结果展示如下:

3. 网页结果总结
这部分代码在WinUI项目中实现,WinUI调用接口获取到结果,并通过Microsoft.SemanticKernel.PromptTemplates.Liquid库进行消息模板动态生成消息,调用语义内核(Semantic Kernel)进行内容总结。
语义内核(Semantic Kernel)注入代码如下:
//测试token被删除 已经无效 请换成自己的智普token
builder.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;
}
效果如下:

到此总结就已经完成了,大家可以去看看代码,看看有没有帮助。
个人心得体会
在进行一段时间的学习之后,对大语言模型有了一些全面的认识,意识到大语言模型并不是万能的,但是它能够很轻松的做到我们之前要很复杂才能做到的事情。轻松做到的前提就是要给出很好的提示词。
如果把大语言模型比作战斗机,那提示词就可以比作是驾驶员了,提示词的好坏直接决定大语言模型输出的准确度。
作为软件开发人员,对于提示词的编写一定要多学习,多总结才行了。
参考推荐文档项目如下:
使用Aspire优雅的进行全栈开发——WinUI使用Semantic Kernel调用智普清言LLM总结Asp.Net Core通过Playwright解析的网页内容的更多相关文章
- .NET全栈开发工程师学习路径
PS:最近一直反复地看博客园以前发布的一条.NET全栈开发工程师的招聘启事,觉得这是我看过最有创意也最朴实的一个招聘启事,更为重要的是它更像是一个技术提纲,能够指引我们的学习和提升,现在转载过来与各位 ...
- 学习笔记之Python全栈开发/人工智能公开课_腾讯课堂
Python全栈开发/人工智能公开课_腾讯课堂 https://ke.qq.com/course/190378 https://github.com/haoran119/ke.qq.com.pytho ...
- 一文读懂NodeJS全栈开发利器:CabloyJS(万字长文)
目录 0 修订 0.1 修订说明 0.2 修订历史 1 基本概念 1.1 CabloyJS是什么 1.2 CabloyJS核心解决什么问题 1.3 CabloyJS的开发历程 2 数据版本与开发流程 ...
- CabloyJS全栈开发之旅(1):NodeJS后端编译打包全攻略
背景 毋庸置疑,NodeJS全栈开发包括NodeJS在前端的应用,也包括NodeJS在后端的应用.CabloyJS前端采用Vue+Framework7,采用Webpack进行打包.CabloyJS后端 ...
- Python全栈开发【面向对象进阶】
Python全栈开发[面向对象进阶] 本节内容: isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__geta ...
- Python全栈开发【面向对象】
Python全栈开发[面向对象] 本节内容: 三大编程范式 面向对象设计与面向对象编程 类和对象 静态属性.类方法.静态方法 类组合 继承 多态 封装 三大编程范式 三大编程范式: 1.面向过程编程 ...
- Python全栈开发【模块】
Python全栈开发[模块] 本节内容: 模块介绍 time random os sys json & picle shelve XML hashlib ConfigParser loggin ...
- Python全栈开发【基础四】
Python全栈开发[基础四] 本节内容: 匿名函数(lambda) 函数式编程(map,filter,reduce) 文件处理 迭代器 三元表达式 列表解析与生成器表达式 生成器 匿名函数 lamb ...
- Python全栈开发【基础三】
Python全栈开发[基础三] 本节内容: 函数(全局与局部变量) 递归 内置函数 函数 一.定义和使用 函数最重要的是减少代码的重用性和增强代码可读性 def 函数名(参数): ... 函数体 . ...
- Python全栈开发【基础二】
Python全栈开发[基础二] 本节内容: Python 运算符(算术运算.比较运算.赋值运算.逻辑运算.成员运算) 基本数据类型(数字.布尔值.字符串.列表.元组.字典) 其他(编码,range,f ...
随机推荐
- liquibase常用操作
1.概述 Liquibase是一个用于跟踪.管理和应用数据库变化的开源的数据库重构工具.它将所有数据库的变化(包括结构和数据)都保存在XML文件中,便于版本控制. 本文旨在将平时工作中常用的一些操作进 ...
- window10设置保护眼睛的颜色
1.调出运行菜单.右击开始键选择运行,或者同时按下键盘上的WIN+R打开运行框,输入 regedit 回车转到注册表编辑器.2.选择第二项 HKEY_CURRENT_USER 点击进入.进入后点击 C ...
- C# .NET core Avalonia 11.0版本,发布linux和MAC的简单记录
.net core 7.0+centos 7.0 cetnos目前运行在hyper V虚拟机里 虚拟机部署的注意事项 1 需要配置网络环境, 确保在同一局域网下 如果sftp无法连接 ctrl+shi ...
- 简单理解IOC控制反转和DI依赖注入
用过.net core框架的同学都知道,框架默认支持"构造函数"注入引用对象的方式.使用.net core框架也有一段时间了,最近去了解了一下到底什么是"依赖注入&quo ...
- UIController转为SwiftUI
在UIKit转到SwiftUI的过渡时期中,项目中会遇到不得不用到二者混合使用的情景,苹果这时提供了相关API让iOSer更好地适应这个时期. UIViewControllerRepresentabl ...
- 新手指引:前后端分离的springboot + mysql + vue实战案例
案例说明: 使用springboot + mysql + vue实现前后端分离的用户查询功能. 1.mysql:创建test数据库 -> 创建user数据表 -> 创建模拟数据: 2.sp ...
- 支持TraceID、错误文件、错误行的第三方golang库:gerror
Gerr库简介 Golang第三方库 官方仓库:https://github.com/GuoFlight/gerror 特点: 兼容golang原生error库 gerror会自动生成traceID, ...
- 【论文阅读】RAL2020: UFOMap An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown
Last edited time: March 31, 2023 1:30 PM Reference and prenotes Paper link:https://ieeexplore.ieee.o ...
- Ubuntu下的NVIDIA显卡【驱动&CUDA 安装与卸载】
碎碎念:主要是把显卡相关的整合出来,基础知识后面再放上来 显卡安装后可以有效降低电脑开太多界面卡顿hhh现象,不过如果显卡不好的话或者是独显的话 问题也不大,主要是学习 learning 使用 参考资 ...
- 小组合作实现的基于 jsp,servlet,mysql 编写的学校管理系统
基本完成的页面--源代码在<文件>中可下载 文件地址:https://i.cnblogs.com/Files.aspx 学生管理模块各功能已实现 百度网盘下载地址: 链接:https:// ...