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();
}
}
}
运行结果一:
运行结果二:
文章来源微信公众号
想要更快更方便的了解相关知识,可以关注微信公众号
分类:
.net
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
2019-02-27 WHERE 子句操作符
2019-02-27 不同的数据库查询行数的方式