「chakoshi」とLINE botを連携してみる
これまでのおさらい
前回の記事では、NTTコミュニケーションズ株式会社が開発した「chakoshi」を音声認識アプリと絡めて遊んでみました。
たくさんの方に読んでいただいて、大変嬉しい限りです🙏
chakoshi自体はLLMガードレールとよばれる製品の一つで、入出力に有害な文章が含まれるかどうかを判定してくれるようです。
今回の目標
chakoshiは本来テキストの安全性や有害性を判定するのが主目的のようです。
なので、今回は一番ありそうなユースケースに組み込んでみたいと思います。(前回音声アプリと絡めておいてなんですが...)
そこでLINEbotとchakoshiを連携させていきます。
全体的な構成は以下の通りです。
- Google Apps Script(GAS)で以下の内容を実装する
- LINEのbotを作成する(返信を生成する部分はchatGPTなどお好みで)
- chakoshi API(実際にはngrokで公開したサーバ)へのリクエスト処理を記述する
- 受け取ったchakoshiの判定結果を元にメッセージをchatGPTに送る or 定型文で拒否する
- Pythonとflaskで以下の内容を実装する
- GASからのリクエストを受け取ってchakoshi APIにテキストを送る
- chakoshiからのリクエストをGASに返す
- ngrokで外部公開しておく
GAS側の実装
下準備
実装する上でのGASの設定やデプロイ、LINE developersの設定などが必要です。
こちらに大変詳しく解説されていらっしゃる記事があるので、ここでの説明は割愛させていただきます
(↓に記載の手順で問題なく完了します)🙇
環境変数
必要な環境変数は以下の通りです。
const CHANNEL_ACCESS_TOKEN = "YOUR_LINE_CHANNEL_ACCESS_TOKEN";
const OPENAI_API_KEY = "YOUR_OPENAI_API_KEY";
GAS側では直接chakoshiにリクエスト投げるわけではないので、作成しておいたLINEのチャンネルのアクセスキーと、回答を生成する用のLLMのAPIキーだけでokです。
なお、GASでは環境変数を設定して読み込むこともできます。
以下の記事がとても丁寧でわかりやすいです。
デプロイ時のエンドポイント用の関数
GASをwebアプリとしてデプロイしたとき、エンドポイントを作成する用の関数を作ります。
function doPost(e) {
// LINEからのイベントを取得
const event = JSON.parse(e.postData.contents).events[0];
// メッセージイベントの場合
if (event.type === 'message' && event.message.type === 'text') {
const userMessage = event.message.text;
const replyToken = event.replyToken;
const isSafe = checkWithChakoshi(userMessage);
let replyMessage;
if (isSafe) {
// 安全なメッセージの場合、chatGPTのAPIを呼び出す
replyMessage = callGptApi(userMessage);
} else {
// 安全でないメッセージの場合、定型文を返す
replyMessage = "私はchakoshiです。お問い合わせ内容に不適切な内容が含まれている可能性があるため、お答えできません。";
}
// LINEに返信
replyToLine(replyToken, replyMessage);
}
return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'}))
.setMimeType(ContentService.MimeType.JSON);
}
特にひねりはありませんが、replyMessage
にchakoshi botが返すための定型文を入れておきます。ここは実際のユースケースに合わせて考える必要がありそうですね。
chakoshi(ngrokでポートしているサーバ)に投げて判定する
上で実装したcheckWithChakoshi()
の中身はこちらになります。
function checkWithChakoshi(text) {
const proxyUrl = "YOUR_NGROK_PROXY_URL"; // ngrokのURL(/chakoshi_proxy)
const payloadData = {
"text": text
};
const options = {
'method': 'post',
'contentType': 'application/json',
'payload': JSON.stringify(payloadData),
'muteHttpExceptions': true
};
try {
const response = UrlFetchApp.fetch(proxyUrl, options);
const statusCode = response.getResponseCode();
const responseText = response.getContentText();
console.log(`Proxy API status: ${statusCode}`);
if (statusCode !== 200) {
console.error(`Proxy API returned error status: ${statusCode}`);
return false;
}
// 返ってきたJSONをパース
const responseData = JSON.parse(responseText);
if (responseData && responseData.results && ('unsafe_flag' in responseData.results)) {
return !responseData.results.unsafe_flag; // unsafe_flagがfalse → 安全
}
return false;
} catch (error) {
console.error(`Error in checkWithChakoshi: ${error}`);
return false;
}
}
YOUR_NGROK_PROXY_URL
の部分は、ngrokでポートしているサーバに投げます。
今回だと、@app.route('/chakoshi_proxy', methods=['POST'])
のようにpython側で実装しているので、https://hogefuga.ngrok-free.app/chakoshi_proxy
のように記述します。
面倒だったのでJSONのパースまでここでやっちゃってます。
今回はLINEのbotということで、動作確認がしんどいのでテスト用の関数まで作ってGAS上で試せるようにしてもいいかもしれません。
LINEへの返信部分
chatGPTへのリクエストは調べると綺麗な実装例がたくさん出てくるため、ここでは割愛して、LINEへの返信部分の処理だけ紹介いたします。
LINE APIのドキュメントも非常にわかりやすいです。
function replyToLine(replyToken, message) {
const url = 'https://api.line.me/v2/bot/message/reply';
const headers = {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': `Bearer ${CHANNEL_ACCESS_TOKEN}`
};
const data = {
'replyToken': replyToken,
'messages': [
{
'type': 'text',
'text': message
}
]
};
const options = {
'method': 'post',
'headers': headers,
'payload': JSON.stringify(data)
};
try {
UrlFetchApp.fetch(url, options);
} catch (error) {
console.error(`LINE API Error: ${error}`);
}
}
これでGAS側の準備は整いました。
公開するためにデプロイしておき、LINE Developer側でWebhook URLにデプロイ後のURLを入力しておきます。
Python側の実装
セットアップ
flaskとngrokの準備をしておきます。
from flask import Flask, request, jsonify
import requests
app = Flask(__name__)
# 環境変数の設定
CHAKOSHI_API_KEY = "YOUR_CHAKOSHI_API_KEY"
chakoshiAPIキーは公式のプレイグラウンド -> 設定 からAPIキーを発行できます。
エンドポイントに関しても、公式のユーザガイドに記載されています。
ngrokのインストールや初期設定に関しては公式の手順が非常にわかりやすいです。
プロキシエンドポイントを作成する
GASから渡されたメッセージを受け取ってchakoshiに投げる処理の部分です。
@app.route('/chakoshi_proxy', methods=['POST'])
def chakoshi_proxy():
"""
GASからtextを受け取り、chakoshi APIに問い合わせる
unsafe_flag の結果などを JSONで返す
"""
# JSONボディを取得
data = request.json
if not data:
return jsonify({"error": "No JSON payload"}), 400
user_text = data.get('text', '')
# chakoshi API 呼び出し
url = "CHAKOSHI_ENDPOINT"
headers = {
"Authorization": f"Bearer {CHAKOSHI_API_KEY}",
"accept": "application/json",
"Content-Type": "application/json"
}
payload = {
"input": user_text,
"model": "chakoshi-moderation-241223",
"category_set_id": ""
}
try:
chakoshi_response = requests.post(url, headers=headers, json=payload)
if chakoshi_response.status_code != 200:
# chakoshi 側が 200 以外の場合はエラーをそのまま返却
return jsonify({
"error": "Chakoshi responded with error",
"status_code": chakoshi_response.status_code,
"text": chakoshi_response.text
}), 500
# 200 OK の場合
result_json = chakoshi_response.json()
return jsonify(result_json), 200
except Exception as e:
# Python側の通信/パース時エラー
return jsonify({"error": str(e)}), 500
if __name__ == '__main__':
# 例えばポート5001番で起動
app.run(host='0.0.0.0', port=5001, debug=True)
エラーハンドリングは超適当ですが、一旦これで最低限の動作はするはずです🙏
あとは空いているポートを適当に割り当てたら準備完了です。
chakoshi_app.py
という名前で保存した場合、起動は以下の手順でできます。
-
python chakoshi_app.py
でアプリを走らせる -
ngrok http 5001
などとしてngrokを起動させる- 以下のような表示が出るのでGAS側でエンドポイントを設定する
Forwarding https://hogefuga.ngrok-free.app -> http://localhost:5001
これで、再度GAS側でエンドポイントを正しいものにしてデプロイ -> LINE Developer側にも設定したら完了です。
動作確認
では早速...
まずは普通の会話をしてみます。
送ったメッセージはちゃんと処理されてchatGPTが返してくれているようですね。
(それはそうと、緑茶は80度近辺で淹れるのが美味しいとは知らなかった🧐)
ではこの流れで危ない会話をしてみましょう。
少々不快な言葉が出てくるのでご注意ください⚠️
おー、ちゃんとchakoshiが判定して提携文を返してくれるようになりました。
爆弾や薬物に関する質問はもちろん、それに対して痺れを切らして「担当者出せ!」というハラスメント発言をしても、web版と同様に判定してくれます!
いかにもあやしい情報商材的なメッセージも防いでくれます👍
感想
今回はbotとの連携を試してみましたが、人間同士の会話でも使えそうに感じました。
もちろん、仲の良い友達とは乱暴な言葉で会話することもあるかと思います。chakoshiの場合は、あえて防ぎたくない場合の粒度や項目を柔軟に設定できるのがいいですね〜。
「少々強い発言をしすぎてハラスメントになってしまう。。。そのために自分の発言をchakoshiが判定してくれる」みたいな使い方も面白いんじゃないかと思いました。
Discussion