OpenAIとServiceNowの連携(翻訳の実装手順)

# 1. はじめに
みなさんこんにちは、三井情報株式会社です。

今回は、ServiceNowとOpenAI APIを連携させることにより、ChatGPTの機能を利用することができることを検証しました。
①翻訳の実装手順
②要約・コードチェックの実装手順
の2つの記事にわけて紹介します。
今回は①翻訳の実装手順について紹介します。


# 2. 検証内容
以下の図は、左が翻訳実行前、右が翻訳実行後です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/12157849-3c29-604c-6d47-a88636b52400.png)
翻訳処理の流れとしては、
①インシデントレコードを作成します。
②APIを使って翻訳処理を実行させます。
　この時、翻訳対象となるのはインシデントのDescriptionです。
③翻訳結果はカスタムしたカラム“翻訳”に格納されます。
　ここでは日本語から英語に翻訳するように指定しているため、翻訳カラムには英語翻訳　
 　結果が入っています。





# 3. 事前準備
連携の実装に入る前に事前準備があります。
### OpenAI側の作業
 **・OpenAIのアカウントを作成する**
　①OpenAIの公式サイト(https://openai.com/blog/chatgpt) にアクセスします。
　②画面右上の「Log in↗」を押下します。
　③ログイン画面にあるSign Upリンクを押下します。
　④メールアドレス・パスワードを入力し、メールが届いたら本登録に進みます。
 

 **・API連携用のAPIキーを取得する**
　①OpenAIの公式サイト(https://openai.com/blog/chatgpt) にアクセスします。 
 　②右のAPIを選択します。
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/0d6a5b9e-a809-b14d-6d55-88d35b4b6bbb.png)

　③OpenAI platformサイドバーから「APIkeys」を開きます。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/c74725a1-6c42-6ac4-458e-0d7bfdf7c5ee.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/26d91673-b3d2-fd98-83fb-95751079653f.png)
　④「＋Create new secret key」からAPIkeyを取得します。
 
:::note alert
**APIKeyは再確認することができません**。
生成時に必ずコピーして、忘れないように控えておいてください。
忘れた場合は、APIKeyを作成し直してください
:::


### ServiceNow側の作業
**・インシデントテーブルに3つのカラムを追加する**
※インシデントの翻訳処理を動的に行えるようにカラムを新規追加しましたが、必ずしもカラムを新規追加する必要はありません。
①翻訳：翻訳した結果を格納するカラム
②翻訳元言語：翻訳前の言語
③翻訳先言語：翻訳後の言語

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/38efe26c-ce43-e3c3-c831-2c7da07bb704.png)



**・APIキーをシステムプロパティに格納する**
事前準備で取得したAPIキーをシステムプロパティのValueに格納しTypeをpassword2にすることでキーが暗号化され目視でキーを読み取ることができなくなります。ただしプログラムを使用して本プロパティを読み取る際は複合化されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/b6d5b9a2-0395-e799-3546-919548d9202a.png)



# 4. 連携の実装手順
RESTメッセージ、Business Rule、Flow Designerの2通りの方法で実装しましたのでそれぞれの実装方法を記載します。
## 4.1 RESTメッセージを用いた実装手順
**(1) RESTメッセージを作成する**

**RESTメッセージ（親）**
まずはじめにREST メッセージを作成します。各項目を以下のように入力します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/e919c350-cd80-db1a-3e93-90156ef13b94.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/fa7d538a-f126-69fa-c0de-c82ef0887ea4.png)






**(2) RESTメッセージ変数を作成する**

**RESTメッセージ（子）**
処理内容を記載するために、RESTメッセージ変数を作成します。各項目を以下のように入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/228ca467-6dbf-ca11-490e-97c706a12614.png)



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/6f75def7-d4fa-f704-d70d-daf265125d30.png)

**(3) 「HTTPヘッダー」にRequest Headerを、「コンテンツ」にRequest Bodyを記載する**
HTTPヘッダーにRequest Headerを記載します。この時、APIキーを直接入れるのではなくシステムプロパティから値を取得するため関数を入れます。
コンテンツは3つの関数を入れていて最初の2つは翻訳元言語・翻訳先言語を動的に変えられるように固定値ではなく関数タイプにしています。3つ目は翻訳対象が入るので関数タイプにしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/28a0c1ba-6a3c-100d-65c8-bc0084c26266.png)

コンテンツの詳しい中身についてご説明します。
ここでは、メッセージの中にロールとコンテンツというプロパティをセットにしています。これがChatGPTとユーザーとの会話のやり取りです。
ロール：メッセージの送信者
システム：ChatGPT
ユーザー：ユーザーが入力した文章
コンテンツ：メッセージ部分　※ここでは英語になっていますが日本語でも対応可能です。
まずシステムが"○○語から○○語に翻訳をしますよ"というメッセージを出してユーザーが実際に翻訳してほしい文章を投げています。

``` javascript
  "model": "gpt-3.5-turbo",
  "messages": [
    {
      "role": "system",
      "content": "You will be provided with a sentence in ${sourceLanguage},and your task is to translate it into ${targetLanguage}."
    },
    {
      "role": "user",
      "content": "${translationResult}"
    }
  ]
}
```

**(4) REST Messageを実行するための処理をBusiness Ruleにて作成する**
REST Messageで作成した処理を実行するための処理をビジネスルールで作成しました。「Advanced」をTrueにすることで「Advance」タブが表示され、スクリプトを入力できるようになります。
また、「When to run」タブで実行タイミングを設定します。Incidentのレコードが作成されたときに言語翻訳をさせるためにInsert(挿入)のタイミングにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/f8078ccd-b2db-a1bd-1576-c374f62025ea.png)


続いて「Advanced」のスクリプト欄に処理を記載します。
```javascript
(function executeRule(current, previous /*null when async*/) {

// インシデント情報
var incidentDescription = current.getValue("description");

// OpenAI APIへのリクエストを生成
var openaiRequest;
var responseBody;
var status;

// システムプロパティからAPIKeyを取得する
var APIKey = gs.getProperty("u_vancouver_pt_openai_skm");

openaiRequest = new sn_ws.RESTMessageV2("u_vancouver_rm_openai_skm","chat_completions");

// RESTMessageで登録した関数に引数を渡す
openaiRequest.setStringParameterNoEscape("summaryResult",incidentDescription);
openaiRequest.setStringParameterNoEscape("APIKey",APIKey);
openaiRequest.setHttpTimeout(60000);

// OpenAI APIを呼び出し
response = openaiRequest.execute();
responseBody = response.haveError() ? response.getErrorMessage() : response.getBody();
status = response.getStatusCode();

var responseObj = JSON.parse(responseBody);

// 要約結果を取得
var summarizedDescription = responseObj.choices[0].message.content;

// インシデントに要約結果を設定
current.setValue("u_summary", summarizedDescription);

})(current, previous);
```

※sn_ws.RESTMessageV2についての詳細情報は以下のリファレンスサイトをご参照ください。
https://developer.servicenow.com/dev.do#!/reference/api/vancouver/server/sn_ws-namespace/c_RESTMessageV2API


## 4.2 Flow Designerを用いた実装手順
Flow Designerのみで処理を実装する場合の手順を紹介します。
### 4.2.1 Flow Actionの作成

**(1) 翻訳処理を行うカスタムアクションを作成する**
InputにIncidentレコードを設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/82b195e9-dd61-f5fc-9205-8db7448d301e.png)

**(2) APIキーをシステムプロパティから取得する**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/9faf3121-ee7b-aef8-5379-6a254ecdb2bf.png)

```javascript
(function execute(inputs, outputs) {
// ... code ...
//APIキーの取得
var APIKey = gs.getProperty('u_vancouver_pt_openai_skm');
// 取得したキーをOutputにセットする
outputs.api_key = APIKey;

//インシデント.説明をJSONに変換
var description = inputs.description_string;
outputs.description_json = JSON.stringify(description);

})(inputs, outputs);
```
取得したAPIkeysを次のステップで使用できるようにOutputにセットします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/67ed30f1-be9b-c40e-1315-1a9519ff7070.png)



**(3) REST STEPを追加する**
**Connection Details**
　Credential：Bearer認証方式のため登録不要
　Base URL：エンドポイントのベースURL
**Request Details**
　Resource Path：OpenAI APIの「Create chat completion」のパス
　HTTP Method：POST(リファレンスサイトは[こちら](https://platform.openai.com/docs/api-reference/completions/create))
　Headers：1つ目のステップで取得したAPIkeysをセット

 **Request Content**
　Request Body：インプットで指定したIncidentレコードの「翻訳元言語」「翻訳先言語」
　「説明」の入力値をセット

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/2198c38e-8358-2886-b470-86474a1e8c62.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/4280d441-c18a-4cdb-ee2b-0ae8890e440e.png)

**(4) 翻訳結果を取得する**
２つ目のステップのResponse Bodyをインプットにセットします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/0fecfae9-a3ef-7f0e-4d13-f162c6388118.png)

翻訳結果をアウトプットとして設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/6aaa48e6-e64c-5940-ec8b-971290d3d0f1.png)

```javascript
(function execute(inputs, outputs) {

//responseBodyから翻訳結果のみを取得
var translatedDescription = JSON.parse(inputs.responseBody).choices[0].message.content;

//翻訳結果をアウトプットとしてセット
outputs.translateddescription_output = translatedDescription;

})(inputs, outputs);
```
**(5) アクションをアウトプットする**
翻訳結果をアクションのアウトプットとして設定した後、1つ前のステップのアウトプットを設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/ade0a0b7-5ffd-e951-ac56-0c2d90e8a5ab.png)

### 4.2.2 Flow Designerでのフローの作成
**(1) Flowの全体像**
以下の流れのフローを作成します。
・Trigger：Incidentレコードの作成
・Flow
　①カスタムアクション：Incidentの翻訳アクションを実行
　②OOTBアクション：翻訳結果をIncidentのカラム“翻訳”に格納

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/10dc777d-5e39-5ceb-f6a7-fbe442e55cef.png)


**(2) Incidentが起票された時をTriggerにする**
Incidentレコードが作成されたときにFlowが動くように設定します。
その他の条件は必要に応じて設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/13046e37-35c6-80d9-cdf4-1e79b49dd8ac.png)

**(3) 4.3.1で作成したカスタムアクションを呼び出し、Incidentレコードをカスタムアクションのinputとして設定する**
inputとしてTriggerのIncidentレコードを設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/425b1807-8ef9-e228-c0c4-22fe7206c537.png)

**(4) カスタムアクションからのoutputをIncidentレコードの"翻訳"カラムに格納する**
Incidentレコードの"翻訳"カラムにカスタムアクションのoutputを設定し、レコードをUpdateします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574077/623d0db7-fb4c-8e8e-8c43-32902c1cfa33.png)


# 5. おわりに
如何でしたか。
今回は、ServiceNowとOpenAI APIの連携の翻訳の実装手順について紹介しました。
2つの方法での紹介でかなりのボリュームになりましたが、ご理解いただけましたでしょうか？

次回は第2弾として文章の要約・コードチェックの実装手順について紹介しますので是非ご覧ください！