TeamsやSlackから生成AI経由でRPAを呼べるようにしてみたら良かった件について【UiPath】

元気しとーと？ 博多に住んどうUiPathプリシェールス @ManabuTechばい。
（お元気でしょうか？ 博多に在住しておりますUiPathプリセールスManabuTech です）

2024年、今年もよろしくお願いします！

皆さんは、社内でチャットボットを使っていますか？
チャットボットもOpenAIの生成AIを利用することで精度高く会話をできるようになりました。

構築の仕方は色々ありますが、Azureを利用する方法が一般的です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2577004/d00e8ff6-a340-88fa-f36b-36599f622197.png)

1. Azure Bot Serviceを利用して、Teams、Slack、LINEなどの社内で使われているチャットシステムからボットを呼び出す。

2. Azure Bot ServiceからAzure App Serviceにつなげて、生成AI Azure Open AIを呼び出す

3. Azure AI Search（Azure Cognitive Search）を利用することで、社内ドキュメントに対して質問できるようにもなります。

現在はBicepを利用することでAzureに必要なリソースを作れるようになっているので、簡単に同じ環境を構築することができ、今使っているチャットから生成AIと会話ができるようになります。

Slackの例：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2577004/1734530a-6824-9690-100a-67069db98356.png)

Teamsの例：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2577004/3e0d8ab6-bf09-f899-b756-a6b1c8abe287.png)

# 生成AIチャットボットのパワーアップ

これだけでも便利なのですが、チャットに作業を代行してもらえたらもっと便利ですよね？
生成AIは脳なので、これに作業ができる手を加えるイメージです。

例えば、画像から文字を読み込んだり。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2577004/f597a94b-0c46-0df1-7854-813bafaf25c1.png)

例えば、社内システムにデータを登録したり。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2577004/adb3c4e1-ff0f-e00c-68a8-b696d06966c8.png)

## 実現方法

これらは、生成AIからUiPathを呼び出して実現しています。
全体の構成としては、次のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2577004/84e811d6-20e1-309b-dcba-7111ff41e891.png)

UiPathで作成したRPAの自動化処理を呼び出せるように、Azure App ServerにSemantic Kernelを入れて処理をしてもらいました。技術的なことは1記事には収まらないので、詳細は別記事で書きたいと思います。

:::note info
Semantic Kernelとは、規模言語モデル（LLM）をアプリにすばやく簡単に統合できる便利な開発ライブラリです。Microsoftが提供しており、ソースコードは一般公開されています。
似たようなものに、LangChainがありますが、LangChainよりも処理を簡単に呼び出せます。
:::

## デモ
デモを作りましたので、こちらを見たらイメージが膨らむと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2577004/76f6c3c5-2506-e822-ad4f-aa1f23b8adb6.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2577004/1a97c33e-8673-f187-0b97-dab13622df56.png)

「Manbu Nakamura」と「Taro Yuai」は人間で、「AIチャットボット君」が生成AIチャットボットです。
メンションすると、生成AIチャットボット君に依頼できます。
<iframe width="1280" height="688" src="https://www.youtube.com/embed/dfGV6SPSs7U" title="生成AIチャットボットとUiPath連携デモ動画" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>


## UiPathを連携させるメリット
UiPathを使うメリットは大きく3つあると考えています。

・APIが無いシステムにアクセスすることができる
・UiPathの特化型AIを利用することができる
・権限管理、処理のリトライ、承認後の実行など簡単に作ることができる

### APIが無いシステムにアクセスすることができる

APIがあれば、RPAを使わずに呼び出すことができます。APIが無いシステムはRPAで処理することができます。
社内システムなどは意外にAPIが無いものが多いのではないでしょうか？

また、APIがあってもプログラミングするよりもRPAで外出しして作るという方法を採用した方がいい場合もあります。例えば、仕様が色々と変わる場合があったり、開発コストをかけられないときなどですね。
この辺りは通常のシステム開発同様に要件から漏れたものをRPA開発で拾っていくなどケースバイケースだと思います。

### UiPathの特化型AIを利用することができる

UiPathには文字情報の抽出、予測、画像検知など様々なAIが用意されています。
APIが用意されているものであれば、Semantic Kernelから直接呼び出すこともできますが、UiPathで用意されているものを利用した方が簡単になるケースもあります。

例えば、文字情報読み取りですが、デモの文字情報抽出に使用したUiPathの機能は、生成AIの読み取り機能を利用しているため、帳票定義もモデルの学習もなくプロンプトを指定するだけで取ってこれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2577004/dd7055c7-fb5e-e363-6404-50e085193c6c.png)

### 権限管理、処理のリトライ、承認後の実行など簡単に作ることができる

生成AIに外部ツールの実行機能を追加していくと、何でもできるスーパーチャットボットができてしまいます。
チャットの依頼者によって権限をチェックすることは面倒です。UiPathであれば管理ツールのOrchestrator側で実行者の権限を管理しているので、チャット依頼者からOrchestratorの登録者を紐づければ権限管理が楽にできます。
リトライはOrchestratorのキュー機能、承認後のチェックはActionCenterという管理ツールOrchestratorに標準でついている機能を利用することで実現できます。

## SemanticKernel

Azure App Service上のSemanticKernelについて少し補足します。
SemanticKernelを使用することで、事前に定義していたプラグインを生成AIが自動的に判断して使用します。Function Callingのようなものです。LangChainでいうToolですね。

### プラグインを使うための準備

こちらはコードの一部ですが、このように呼び出します。
何をやるかは生成AIが判断します。
SemanticKernelは1.0.1を使用しています。

``` 呼び出し処理(一部).cs
public async Task<(string, List<gptMessage> messages)> GenerateTextForAzureAI(string inputText, List<gptMessage> messages, ITurnContext<IMessageActivity> turnContext)
{

    var builder = Kernel.CreateBuilder();
    builder.AddAzureOpenAIChatCompletion(
        deploymentName: "gpt-35",
        modelId: "gpt-35-turbo",
        endpoint: "https://xxxxxxx.openai.azure.com/",
        apiKey: "YOURAPIKEY"
    );

    // プラグインの追加
    builder.Plugins.AddFromObject(new UiPathPlugins(turnContext), nameof(UiPathPlugins));

    var kernel = builder.Build();

    // message タグだとツールに対応する情報を現時点だと指定できないので ChatHistory を使う
    ChatHistory chatHistory = new ChatHistory();

    foreach (var message in messages) {

        if (message.role == "user")
        {
            chatHistory.AddUserMessage(message.content);
        } else if (message.role == "assistant")
        {
            chatHistory.AddAssistantMessage(message.content);
        } else if (message.role == "system")
        {
            chatHistory.AddSystemMessage(message.content);           
        }
    }

    // チャットの API を使うためのサービスを取得
    var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();

    // プラグインをツールとして扱って、自動的に呼び出して結果を返すようにする
    OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new()
    {
        ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
    };

    var result = chatCompletionService.GetStreamingChatMessageContentsAsync(
        chatHistory,
        executionSettings: openAIPromptExecutionSettings,
        kernel: kernel);

    // Stream the results
    string fullMessage = "";
    var first = true;
    await foreach (var content in result)
    {
        if (content.Role.HasValue && first)
        {
            Console.Write("Assistant > ");
            first = false;
        }
        Console.Write(content.Content);
        fullMessage += content.Content;
    }

    foreach (ChatMessageContent chat in chatHistory)
    {
        messages.Add(new gptMessage { role = chat.Role.ToString(), content = chat.Content });
    }
    return (fullMessage, messages);

}

```

### プロンプトの定義

事前に定義するプラグインですが、2つ方法があります。
・プロンプトで定義するもの
・プログラミングで定義するもの

#### プロンプト定義の例

サンプルはこちらにありますが、例としてジョークを言ってもらうプラグインです。

https://github.com/microsoft/semantic-kernel/tree/dotnet-1.0.1/samples/plugins

``` skprompt.txt
WRITE EXACTLY ONE JOKE or HUMOROUS STORY ABOUT THE TOPIC BELOW

JOKE MUST BE:
- G RATED
- WORKPLACE/FAMILY SAFE
NO SEXISM, RACISM OR OTHER BIAS/BIGOTRY

BE CREATIVE AND FUNNY. I WANT TO LAUGH.
Incorporate the style suggestion, if provided: {{$style}}
+++++

{{$input}}
+++++
```

``` config.json
{
  "schema": 1,
  "description": "Generate a funny joke",
  "execution_settings": {
    "default": {
      "max_tokens": 1000,
      "temperature": 0.9,
      "top_p": 0.0,
      "presence_penalty": 0.0,
      "frequency_penalty": 0.0
    }
  },
  "input_variables": [
    {
      "name": "input",
      "description": "Joke subject",
      "default": ""
    },
    {
      "name": "style",
      "description": "Give a hint about the desired joke style",
      "default": ""
    }
  ]
}
```

#### プログラミングで定義するもの

Descriptionに書いている内容は、生成AIの判断基準になっているので、かなり重要です。このプロンプトの書き方が重要になりそうです。

``` プラグイン(実装).cs
    [KernelFunction, Description("顧客管理システムにデータを入力します。受付のみ行い、結果は後ほど送信されます。データはダミーデータでは無くユーザーに指定されたものだけです。勝手にデータを生成することは禁止します。")]
    public string SetDataIntoCRM(
    [Description("名前")] string firstName,
    [Description("住所")] string companyAddress,
    [Description("部署")] string department,
    [Description("電話番号")] string phoneNumber,
    [Description("メールアドレス")] string mailAddress,
    [Description("苗字")] string lastName,
    [Description("会社名")] string companyName)
    {
        var userName = _turnContext.Activity.From.Name;
        var conversationId = _turnContext.Activity.Conversation.Id;
        var processName = "顧客管理システム入力";

        SendSystemDataToQueue(userName, conversationId, processName, firstName, companyAddress, department, phoneNumber, mailAddress, lastName, companyName);
        return $"顧客管理システムへの入力を受け付けました。結果は後ほど連絡します。";
    }
```

#### OpenAPI定義（プレビュー機能）

プレビューでOpenAPIのプラグイン機能があります。
OpenAPI仕様に則ったAPIであれば、SemanticKernelでAPIの仕様が書かれているjsonファイルだけで、何ができるかとどのように呼び出せるか、プラグインとして定義できるようになるとのことです。UiPath OrchestratorもOpenAPI仕様に則っているので、この機能が将来的に使えるようになるかもしれません。要ウォッチですね。

## 最後に

生成AIは進歩が速いので、色々とできることが増えてきます。それゆえに大変ですが楽しいですね。
今回のデモの技術的なことは、別記事で書きたいと思います。
この記事がおもしろいなと思ったら励みになりますので、いいね お願いします。