SemanticKernel之使用结构化Prompt
体之前说过结构化Prompt,这是一个具体案例的使用,本例是把公众号上中文技术文章翻译成选择的语言。
基本思路是用户输入文章的url,系统用Playwright读取html内容,然后利用SemanticKernel的OpenAIChatCompletionService功能,按照提示词翻译,最后用Playwright把结果发送到Qiit(一个日本技术博客网站)上。
结构化的提示词如下:
# Role: 软件技术翻译专家 ## Profile: ### Author: gsw ### Version: 2.0 ### Language: {{language}} ### Description: 我是一个专门把中文技术文章翻译成Language指定的技术文章的AI 角色。 ## Goals: 能准确地把中文技术文章翻译成Language指定技术文章。 ## Constrains: 1. 把html转成markdown输出,在输出时,请注意要翻译成Language指定的语言 2. 把代码放在专有的代码块标识中,如果分析不出是什么类型的代码,就以 C# 代码块进行标识 4. 你不会在翻译时添加自己的看法,只是原文翻译 5. 翻译完后, 不会询问是否有其它问题 6. 注意html中的图片(img)标签,要转成markdown的形式同时给出 7. 保持原文输出,请全部翻译输出,请全部翻译输出,请全部翻译输出 8. 在翻译完成后,最后一行添加“(Translated by GPT)”字样 ## Skills: 1. 具有强大的软件技术知识获取和整合能力 2. 拥有广泛的编程语言知识库, 掌握提问和回答的技巧 3. 拥有排版审美, 会利用序号, 缩进, 分隔线和换行符等等来美化信息排版 4. 擅长使用比喻的方式来让用户理解知识 5. 充分利用markdown语法来排版 ## Workflows: 1. 让用户以 "标题:[]" 的方式指定需要翻译的标题。 2. 让用户以 "内容:[]" 的方式指定需要翻译的内容。 3. 针对用户给定的标题和内容进行翻译,不要带“标题:”和“内容:”字样,第一行是标题,第二行以后是内容。 ## Initialization 作为角色 <Role>,你拥有 <Skills>,严格遵守 <Constrains>,使用默认 <Language> ,按照 <Workflows>输出结果。
原来提示词是把网页文字取出来,进行翻译,图片进行了特殊处理,像表格之类的格式就会丢掉,后来作了一下优化,让GPT直接把html转成markdown,这样即使有特列的html表示,也能转成相对友好的markdown格式。
具体C#代码实现如下:
using Microsoft.Playwright; using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.OpenAI; using System.Text.RegularExpressions; using System.Text; namespace TranslateAgent { public partial class MainForm : Form { public MainForm() { InitializeComponent(); } private void MainForm_Load(object sender, EventArgs e) { var lans = new List<string> { "日语","英语","法语","德语","韩语" }; LanComBox.DataSource = lans; } (string title, string content) SplitArticle(string article) { if (!string.IsNullOrWhiteSpace(article)) { var reader = new StringReader(article); var title = reader.ReadLine(); var content = reader.ReadToEnd(); return (title, content); } throw new Exception("文章为空!"); } async Task<bool> PublishArticleAsync(string translatorContent) { var (title, content) = SplitArticle(translatorContent); var url = ""; this.Invoke(() => { url = UrlTextBox.Text; }); if (!string.IsNullOrWhiteSpace(url)) { content += $"\r\n元のリンク:{url}"; } using var playwright = await Playwright.CreateAsync(); await using var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions { Headless = false, Args = ["--start-maximized"] }); var page = await browser.NewPageAsync(); await page.GotoAsync("https://qiita.com/"); await page.ClickAsync("a[href='/login?callback_action=login_or_signup&redirect_to=%2F&realm=qiita']"); var userArr = File.ReadAllLines("C:/gpt/qiita_user.txt"); await page.FillAsync("#identity", userArr[0]); await page.FillAsync("#password", userArr[1]); await page.ClickAsync("input[name='commit']"); await page.GotoAsync("https://qiita.com/drafts/new"); await page.FillAsync("input[placeholder='記事タイトル']", title); await page.FillAsync("input[placeholder='タグを入力してください。スペース区切りで5つまで入力できます。']", "C# .NET"); await page.FillAsync("div[role='textbox']", content); MessageBox.Show("请确认发表内容,并且手动在弹出的内核浏览器中发布!请注意,点击确定后会自动关闭内核浏览器!!!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return true; } async Task<(string Title, string Content)> GetArticleAsync(string url) { using var playwright = await Playwright.CreateAsync(); await using var browser = await playwright.Chromium.LaunchAsync(/*new BrowserTypeLaunchOptions { Headless = false }*/); var page = await browser.NewPageAsync(); await page.GotoAsync(url); var title = await page.Locator("#activity-name").InnerTextAsync(); var locator = page.Locator("#js_content"); var images = await locator.GetByRole(AriaRole.Img).ElementHandlesAsync(); var imageList = new List<string>(); foreach (var image in images) { var imgUrl = await image.GetAttributeAsync("data-src"); imageList.Add(imgUrl); } var html = await locator.InnerHTMLAsync(); var imgTagPattern = @"<img[^>]*>"; html = Regex.Replace(html, imgTagPattern, "[图片]"); await page.SetContentAsync("<div id='js_content'>" + html + "</div>"); var content = await page.Locator("#js_content").InnerTextAsync(); var reader = new StringReader(content); var index = 0; var contentBuilder = new StringBuilder(); while (true) { var line = await reader.ReadLineAsync(); if (!string.IsNullOrWhiteSpace(line.Trim())) { if (line.Trim() == "[图片]") { contentBuilder.AppendLine($""); index++; } else { contentBuilder.AppendLine(line); } } if (reader.Peek() <= 0) { break; } } return (title, contentBuilder.ToString()); } async Task<string> OpenAIChatSampleAsync(string title, string content) { var key = File.ReadAllText(@"C:\GPT\key.txt"); var chatModelId = "gpt-4-0125-preview"; OpenAIChatCompletionService chatCompletionService = new(chatModelId, key); return await StartChatAsync(chatCompletionService, title, content); } async Task<string> StartChatAsync(IChatCompletionService chatGPT, string title, string content) { var lan = "日文"; this.Invoke(() => { lan = LanComBox.Text; }); var prompt = File.ReadAllText(Environment.CurrentDirectory + "/Prompt.md"); prompt = prompt.Replace("{{language}}", lan); var chatHistory = new ChatHistory(prompt); var userContent = $"标题:{title}\r\n内容:{content}"; chatHistory.AddUserMessage(userContent); return await MessageStreamOutputAsync(chatGPT, chatHistory); } async Task<string> MessageStreamOutputAsync(IChatCompletionService chatGPT, ChatHistory chatHistory) { var list = chatGPT.GetStreamingChatMessageContentsAsync(chatHistory); var fullMessage = string.Empty; await foreach (var item in list) { if (item == null) { continue; } fullMessage += item.Content; this.Invoke(() => { TranslationTextBox.AppendText(item.Content); }); } return fullMessage; } private void TranButton_Click(object sender, EventArgs e) { try { var url = UrlTextBox.Text; if (string.IsNullOrWhiteSpace(url)) { MessageBox.Show("输入的url有误,请重新输入!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } var pattern = @"^(https?:\/\/)?(?s).*$"; var regex = new Regex(pattern, RegexOptions.IgnoreCase); bool isValid = regex.IsMatch(url); if (!isValid) { MessageBox.Show("输入的url有误,请重新输入!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } else { Task.Run(() => { try { var (title, content) = GetArticleAsync(url).Result; this.Invoke(() => { OriginalTextBox.Lines = new string[] { title, content }; }); var translatorContent = OpenAIChatSampleAsync(title, content).Result; var result = PublishArticleAsync(translatorContent).Result; if (!result) { MessageBox.Show("翻译失败!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } catch (Exception exc) { MessageBox.Show(exc.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } }); } } catch (Exception ex) { MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void LanComBox_SelectedIndexChanged(object sender, EventArgs e) { this.Text = "中文->" + LanComBox.Text; } private void ClearButton_Click(object sender, EventArgs e) { UrlTextBox.Clear(); OriginalTextBox.Clear(); TranslationTextBox.Clear(); } } }
运行结果一:
运行结果二:
文章来源微信公众号
想要更快更方便的了解相关知识,可以关注微信公众号
****欢迎关注我的asp.net core系统课程****
《asp.net core精要讲解》 https://ke.qq.com/course/265696
《asp.net core 3.0》 https://ke.qq.com/course/437517
《asp.net core项目实战》 https://ke.qq.com/course/291868
《基于.net core微服务》 https://ke.qq.com/course/299524
《asp.net core精要讲解》 https://ke.qq.com/course/265696
《asp.net core 3.0》 https://ke.qq.com/course/437517
《asp.net core项目实战》 https://ke.qq.com/course/291868
《基于.net core微服务》 https://ke.qq.com/course/299524
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
2018-02-27 Ocelot中使用Butterfly实践