LoginSignup
1
0

SemanticKernelの構造化プロンプトの使用

Posted at

以前に構造化プロンプトについて話しましたが、これは具体的な使用例です。この例では、公式アカウントの中国語技術記事を選択した言語に翻訳します。

基本的なアイデアは、ユーザーが記事のURLを入力し、システムがPlaywrightを使用してHTMLコンテンツを読み取り、SemanticKernelのOpenAIChatCompletionService機能を使用してプロンプトに従って翻訳し、最終的にPlaywrightを使用して結果をQiita(日本の技術ブログサイト)に送信することです。

構造化されたプロンプトは以下の通りです:

# 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元のリンク:<span class="code-snippet__subst">{url}</span>";
            }
            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 图片](<span class="code-snippet__subst">{imageList[index]}</span>)");
                        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 = $"标题:<span class="code-snippet__subst">{title}</span>\r\n内容:<span class="code-snippet__subst">{content}</span>";
            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();
        }
    }
}

運行結果一:
图片

運行結果二:
图片

(Translated by GPT)

元のリンク:https://mp.weixin.qq.com/s?__biz=MzA3NDM1MzIyMQ==&mid=2247488059&idx=1&sn=d8743ddc50d1c50b21e14e223d378a48&chksm=9f004d11a877c40757aacf1fde4bf3fc12103b1ffff575ca56abe4b688dc7be3b259d5c82d5a&token=1666706056&lang=zh_CN#rd&wt.mc_id=MVP_325642

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0