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($"![alt 图片]({imageList[index]})");
                        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();
        }
    }
}
复制代码

  运行结果一:

   运行结果二:

   文章来源微信公众号

  想要更快更方便的了解相关知识,可以关注微信公众号 

posted @   刘靖凯  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
历史上的今天:
2019-02-27 WHERE 子句操作符
2019-02-27 不同的数据库查询行数的方式
点击右上角即可分享
微信分享提示