Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails以外の開発一般

Unity: 国産の言語解析APIを使ってユニティちゃんと会話したい

ライセンス表記

ユニティちゃんライセンス

この作品はユニティちゃんライセンス条項の元に提供されています

まえがき

BPSの協力会社として横浜を拠点に活動しております、最近記事のスタイルを模索中の株式会社ECNのFuseです。
突然ですが、みなさんはAPI、使ってますか?今日は前回作ったデスクトップマスコットを改修し、
みなさんご存じUnityちゃんと会話できるように作り変えたいと思います!

目次

  1. まえがき
  2. 今回使うAPIについて
  3. とりあえず会話できるようにしたい
  4. 雑談以外ははぐらかすようにしたい
  5. 独自の会話パターンを作りたい
  6. あとがき

今回使うAPIについて

今回は文章の解析と自然な返答の生成のためにYahoo!JAPANさんの自然言語理解(V2)を使っていきます。

ドキュメント: 自然言語理解(V2) - Yahoo!デベロッパーネットワーク

こちらはサイトやアプリの下部に指定の表示を行うことで非商用に限り誰でも無料で使えてしまう優れもの!!
1分間に300回(5秒に1回)という制限こそあるものの、無料でこれだけ使える会話用APIはかなりレア!
ありがたく使わせてもらいましょう!

とりあえず会話できるようにしたい

▲APIの仕様上若干塩対応気味

できました。
というわけで、ボタンが押された際にAPIを呼び出す関数を呼び出すクラスChatSystemを作りました。
GUI周りの機能も簡単に扱えるのもUnityの魅力ですね。

// ChatSystem.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using System.Threading.Tasks;
using System.Text;
using UnityEngine.UI;
public class ChatSystem : MonoBehaviour//チャットシステム用クラス
{
    private const string token="取得したクライアントIDをここに入れる";//APIトークン
    [SerializeField] Text tex;//出力用テキスト
    [SerializeField] InputField field;//入力フォーム
    public void Request(){//外部から呼び出す用メソッド
        tex.text="思考中…";
        string req="{\"id\": \"任意の値\",\"jsonrpc\" : \"2.0\",\"method\" : \"jlp.nluservice.analyze\",\"params\" : {\"q\" : \""+field.text+"\"}}";//リクエスト用クエリ
        StartCoroutine(Upload(req,"https://jlp.yahooapis.jp/NLUService/V2/analyze"));//送信開始
    }
    private IEnumerator Upload(string query,string url)
    {
        var req = new UnityWebRequest(url, "POST");//リクエスト生成
        byte[] bodyRaw = Encoding.UTF8.GetBytes(query);//ボディー用配列
        req.SetRequestHeader("Content-Type", "application/json");//ヘッダー
        req.SetRequestHeader("User-Agent", "Yahoo AppID: "+token);//ヘッダー
        req.uploadHandler = (UploadHandler) new UploadHandlerRaw(bodyRaw);
        req.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer();
        yield return req.SendWebRequest();//レスポンスが返ってくるまで待つ
        if(req.responseCode==200){//200が帰ってきたら
            var json=req.downloadHandler.text;//レスポンスを引っ張り出す
            ResponseData resp = JsonUtility.FromJson<ResponseData>(json);//パースする
            tex.text=resp.result.PARAM_TEXT;//返答を表示する
        }else{//何らかのエラーが起きたら
            tex.text="何らかのエラーが起きているみたいだね…";
        }
    }
}
[System.Serializable]
public class ResponseData{//レスポンス用クラス
    public ResultData result;
}
[System.Serializable]
public class ResultData{
    public string METHOD;
    public string PARAM_TEXT;
}

関数が呼び出された際にテキストフィールドの値からJSONを組み立て、APIに送り付けます。
その際ヘッダにトークンとContent-Type:application/jsonを入れるのを忘れずに。
成功を示す200番が帰ってきたらレスポンスからPARAM_TEXTを取り出して、
格納されている返答を表示します。

雑談以外ははぐらかすようにしたい

そういえば、このプログラムには実は問題があります。
ドキュメントを読んだ皆さんはお気づきのことでしょう。
このプログラムが正常に動作するにはレスポンスにPARAM_TEXTが必要なのですが、
APIがレスポンスにPARAM_TEXTを含めるのは文章が雑談と解釈された時だけなのです。
なので、天気や乗り換えについて質問するとプログラムが返答を取り出せず、ユニティちゃんが沈黙してしまいます。

▲ユニティちゃん、困惑

なので、METHODSAYじゃなかったら適当にはぐらかすように改造します。

// ChatSystem.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using System.Threading.Tasks;
using System.Text;
using UnityEngine.UI;
public class ChatSystem : MonoBehaviour
{
    private const string token="dj00aiZpPWJPTWVWQlp2ZEF3dSZzPWNvbnN1bWVyc2VjcmV0Jng9OTY-";
    [SerializeField] Text tex;
    [SerializeField] InputField field;
    public void Request(){
        tex.text="思考中…";
        string req="{\"id\": \"unitychankawaii\",\"jsonrpc\" : \"2.0\",\"method\" : \"jlp.nluservice.analyze\",\"params\" : {\"q\" : \""+field.text+"\"}}";
        StartCoroutine(Upload(req,"https://jlp.yahooapis.jp/NLUService/V2/analyze"));
    }
    IEnumerator Upload(string query,string url)
    {
        var req = new UnityWebRequest(url, "POST");
        byte[] bodyRaw = Encoding.UTF8.GetBytes(query);
        req.SetRequestHeader("Content-Type", "application/json");
        req.SetRequestHeader("User-Agent", "Yahoo AppID: "+token);
        req.uploadHandler = (UploadHandler) new UploadHandlerRaw(bodyRaw);
        req.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer();
        yield return req.SendWebRequest();
        Debug.Log(req.responseCode);
        if(req.responseCode==200){
            var json=req.downloadHandler.text;
            ResponseData resp = JsonUtility.FromJson<ResponseData>(json);
            if(resp.result.METHOD.Equals("SAY")){         //追加
                tex.text=resp.result.PARAM_TEXT;
            }else{                                        //追加
                tex.text="その質問には答えられそうにないみたい…";//追加
            }                                             //追加

        }else{
            tex.text="何らかのエラーが起きているみたいだね…";
        }
    }
}
[System.Serializable]
public class ResponseData{
    public ResultData result;
}
[System.Serializable]
public class ResultData{
    public string METHOD;
    public string PARAM_TEXT;
}

▲はぐらかし(?)

できました。
これで気まずい沈黙が流れることもなくなりそうです。
今日は枕を高くして眠れそうです。

独自の会話パターンを作りたい

これでもデスクトップマスコットとして十分かわいいのですが、もうひと手間加えてみます。
現在、このアシスタントさんはユニティちゃんのことを何も知りません。
なので名前を聞かれても素直にアシスタントとして答えてしまいます。

▲これはこれでかわいいけども

ここは最後に、リクエストにルールのデータを追加し、ユニティちゃんとして答えてもらいましょう!
リクエストのparams/context/に"sample0...99":"任意のメソッド名,入力ルール"の形で記述し、ルールを追加します。
ここでは、名前を聞かれたらMETHODをagent_nameにするルールを追加しました。

// ChatSystem.cs(15行目)
string req="{\"id\": \"unitychankawaii\",\"jsonrpc\" : \"2.0\",\"method\" : \"jlp.nluservice.analyze\",\"params\" : {\"q\" : \""+field.text+"\",\"context\":{\"sample0\":\"agent_name,{あなた|お前|君}の?名前\"}}}";

そのあとはレスポンスを受け取った後の処理にMETHODがagent_nameだった時の処理を記述し完成です!

// ChatSystem.cs(31~41行目)
            switch(resp.result.METHOD){//今後の拡張を考えswitch式に変更
                case "SAY":
                    tex.text=resp.result.PARAM_TEXT;
                    break;
                case"agent_name":
                    tex.text="私の名前は大鳥こはく!\nみんなからは「ユニティちゃん」って呼ばれてるよ!";
                    break;
                default:
                    tex.text="その質問には答えられそうにないみたい…";
                    break;
            } 

▲そうそうこれこれ

ばっちりユニティちゃんとして答えてくれました!

あとがき

今回はYahoo!JAPANさんが提供しているAPIを使ってユニティちゃんと話せるようになりました!
こういったAPIを組み合わせ、いつしか友達を0から作れるようになる日も、そう遠くはないのかもしれませんね。
以上、Fuseでした!


株式会社ECNはPHP、JavaScriptを中心にお客様のご要望に合わせたwebサービス、システム開発を承っております。
ビジネスの最初から最後までサポートを行い
お客様のイメージに合わせたWebサービス、システム開発、デザインを行います。



CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。