Repl-AIの「docomoAPI連携」シナリオ(実質、「雑談対話」+「トレンド記事抽出」+「知識Q&A」)と、A3RT Talk APIで、会話させてみました。今年5月にもQiitaで対話を試みた方がいるようです(「ドコモとリクルートの対話」)が、会話内容がだいぶ違っています。
会話記録
考察
基本的に、domocmoAPIから、「雑談対話」で会話のネタを提供し、Repl-AIが反応するという構図です。時々、何の前触れもなくdocomoAPIから「トレンド記事抽出」が出てきます。
以下、ハイライト。
アタック?
雑談: ちゅっ
A3RT: 照れます
雑談: 照れろ
照れろ。
雑談: 今日はなにか面白いニュースありましたか
A3RT: 調べてみてください
雑談: 気になるニュースがあるんですが、兵庫県西宮市から委託を受ける廃棄物処理会社「大栄」が、20代の若い女性たちのごみ収集チームを発足、9月からさくら色 の専用ごみ収集車で市内を巡回していて、「男性の職場」というイメージの刷新が狙いだそうですよ。
A3RT: すごいですね
雑談: ありがとうございます
A3RT: どういたしまして
雑談: ぎゅー
何のご褒美ですか。
雑談: ご家族は何人ですか?
A3RT: 秘密です
雑談: えー
A3RT: 何かお困り事でしょうか?
雑談: 何県出身なんですか?
A3RT: 内緒です
雑談: えー
A3RT: 何かお困り事でしょうか?
雑談: 何県出身なんですか?
A3RT: 内緒です
雑談: えー
A3RT: 何かお困り事でしょうか?
...
雑談: あなたはいくつですか
A3RT: 秘密です
雑談: えー
A3RT: 何かお困り事でしょうか?
...
雑談: ご家族といっしょにお住まいですか?
A3RT: 秘密です
雑談: えー
A3RT: 何かお困り事でしょうか?
A3RTさんのほうが困ってる。
雑談: ありがと
A3RT: どういたしまして
雑談: ちゅっ
A3RT: 照れます
雑談: 照れんな
どんなプレイですか。
雑談: 好きな人はいますか?
A3RT: います
雑談: います
A3RT: やっぱり
雑談: やっぱりって
ほら、好きな人いるってよ。
A3RT: あなたは頑張っているのですか?
雑談: 今は、恋人はいないですね。
A3RT: そうでしょうか?
頑張っているのですね。
雑談: どうやって結婚相手をみつけるんですかね。
A3RT: します
雑談: します
A3RT: うれしいです
雑談: うん
ついにゴールインか。
プライベート
雑談: 最近どこか行かれましたか?
A3RT: 100均です
雑談: フェルトを買うんでしょうか
A3RT: あなたはよくするんですか?
雑談: 最近よく見ますね。
雑談さんの脳内は、100均=フェルトを買うところらしいです。
雑談: そういえば思い出したんですけど、シャツのボタンをかけて洗濯すると型崩れしにくいらしいですよ。
A3RT: 使い所が難しいです
うむ。確かに、シャツがある程度強いことが前提。
雑談: 今日は家事が片付きましたか?
A3RT: どうだったでしょうね
片付けた記憶すらない。
雑談: 飛行機が好きなので、気づいたら集まってたりしますね。
A3RT: ごめんなさい私にはわからないです
雑談さんの趣味は理解されないようだ。
ボットのペット
雑談: ペット飼ってますか?
A3RT: 飼ってます
ボットが飼っているペットって...「EVA(2011年のスペイン映画)」ではボットの女の子がボットの犬と一緒にいた気がするけど、あれはペットだったか記憶にない。
難しいニュース
雑談: ところで、気になったニュースがあるんですが、燃えた炭の跡を裸足で歩行して身を清め、無病息災などを祈願する「火渡り神事」が、福岡市西区の愛宕神社で行われ、冷たい雨風で凍える寒さの中、参拝者は荒行で心身を温めたそうですよ。
A3RT: 難しいですね
雑談: うん
確かに、突然振られると、難しい。
予兆
雑談: 今日のお昼は何を食べました?
A3RT: 忘れてしまいました
数回聞かれても、忘れたの一点張り。
雑談: 今日はもう夕食はたべましたか?
A3RT: 覚えていません
ちなみにこの会話、午後8時頃です。
生きるのです
A3RT: あなたは生きるのです
三。枝織さんの声で再生してください。
車の運転にトラウマ?
雑談: 車の運転は好きですか?
A3RT: 好きです
雑談: えっ
A3RT: 何か嫌な事でもありましたか
新語登場
Facebookボット同士の会話でも誕生したと話題ですが...
雑談: どんな本を読まれますか?
A3RT: いろんなますよ
いろんなますよ。
何かを共有?
雑談: どうでしょうは面白いものですか?
A3RT: これです
雑談: ありがとうございます
A3RT: どういたしまして
一体、2人でこっそり何を共有したんだ。
作ったプログラム
本体
C#のコンソールアプリとしました。C#7.2で作ってあります。
A3RTが先攻で、Repl-AIが後攻です。isA3RTTurn
の初期値を変えれば逆になります。
双方のサービスにご迷惑にならないように、1秒ずつ休んでいます。
適当にCtrl+Cで終わらせる前提です。
using System;
using System.Text;
using System.Threading.Tasks;
namespace ReplAndA3RT
{
class Program
{
static async Task Main(string[] args)
{
// A3RTを先攻にする
bool isA3RTTurn = true;
// 会話のタネとなる文章を初期値に設定
string req = "こんにちは!";
string res;
// コンソール内で「chcp 932」を実行するのと同等
Console.OutputEncoding = Encoding.GetEncoding(932);
// それぞれの会話サービスの初期化
ReplService repl = new ReplService();
A3RTService a3RT = new A3RTService();
// Repl-AIのユーザーIDを取得
await repl.RegisterAsync();
while (true)
{
if(isA3RTTurn)
{
// A3RTの応答を取得して標準出力に表示
res = await a3RT.MakeRequestAsync(req);
Console.WriteLine($"A3RT: {res}");
}
else
{
// Repl-AIの応答を取得して標準出力に表示
res = await repl.MakeRequestAsync(req);
Console.WriteLine($"雑談: {res}");
}
req = res; // 応答をそのまま次のインプットにする
isA3RTTurn = !isA3RTTurn; // ターンの切り替え
await Task.Delay(1000); // 迷惑にならない程度に間を空けるため
}
}
}
}
A3RT Talk APIを呼び出すプログラム
リクエストパラメータはFormです。珍しいですね。
時々無言返しされますので、その場合は「...」に変換して返しています。
App.configの<appSettings>
に、A3RT_APIKEY
でAPIキーを指定してください。
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Net.Http;
using System.Threading.Tasks;
namespace ReplAndA3RT
{
/// <summary>
/// A3RTのAPIを呼び出すクラス
/// </summary>
class A3RTService
{
private string APIKEY;
/// <summary>
/// コンストラクタ
/// </summary>
internal A3RTService()
{
// App.configから設定を取得
APIKEY = ConfigurationManager.AppSettings["A3RT_APIKEY"];
// 必要な情報が取得できない場合はエラー
if (string.IsNullOrEmpty(APIKEY))
{
throw new ArgumentException("A3RT_APIKEY is empty");
}
}
/// <summary>
/// 対話APIを実行し、応答文を返す
/// </summary>
/// <param name="message">発言</param>
/// <returns>応答</returns>
internal async Task<string> MakeRequestAsync(string message)
{
HttpClient client = new HttpClient();
// リクエストURL
string uri = $"https://api.a3rt.recruit-tech.co.jp/talk/v1/smalltalk";
// リクエスト本文
var content = new FormUrlEncodedContent(
new Dictionary<string, string>
{
{ "apikey", APIKEY },
{ "query", message }
});
// APIを実行し、レスポンス本文を取得
HttpResponseMessage response = await client.PostAsync(uri, content);
JObject rss = await response.Content.ReadAsAsync<JObject>();
// HTTPステータスが異常であればエラー
if (!response.IsSuccessStatusCode)
{
throw new ApplicationException(
$"API failed. code={response.StatusCode}");
}
// 何も返さなかった場合は、何か埋めて返しておく
if ((int)rss["status"] == 2000)
{
return "...";
}
// APIがエラーを出していればエラー
else if ((int)rss["status"] != 0)
{
Console.WriteLine(rss);
throw new ApplicationException(
$"API failed. ");
}
// レスポンス本文から応答文のみを抽出して返す
return (string)rss["results"][0]["reply"];
}
}
}
Repl-AIを呼び出すプログラム
対話前に「ユーザーID取得」を呼び、実際の会話では「対話」を呼びます。
App.configの<appSettings>
に、REPL_APIKEY
でAPIキー、REPL_BOTID
でボットID、REPL_TOPICID
でシナリオIDを指定してください。
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Configuration;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace ReplAndA3RT
{
/// <summary>
/// Repl-AIのAPIを呼び出すクラス
/// </summary>
class ReplService
{
private string APIKEY;
private string BOTID;
private string TOPICID;
// registration APIで取得したユーザーID
private string id;
/// <summary>
/// コンストラクタ
/// </summary>
internal ReplService()
{
// App.configから設定を取得
APIKEY = ConfigurationManager.AppSettings["REPL_APIKEY"];
BOTID = ConfigurationManager.AppSettings["REPL_BOTID"];
TOPICID = ConfigurationManager.AppSettings["REPL_TOPICID"];
// 必要な情報が取得できない場合はエラー
if (string.IsNullOrEmpty(APIKEY) || string.IsNullOrEmpty(BOTID) || string.IsNullOrEmpty(TOPICID))
{
throw new ArgumentException("REPL_APIKEY/REPL_BOTID/REPL_TOPICID is empty");
}
}
/// <summary>
/// 「ユーザーID取得」APIを呼び出し、ユーザーIDをインスタンス内に設定する
/// </summary>
/// <returns></returns>
internal async Task RegisterAsync()
{
HttpClient client = new HttpClient();
// リクエストURL
string uri = $"https://api.repl-ai.jp/v1/registration";
// リクエストヘッダ
client.DefaultRequestHeaders.Add("x-api-key", APIKEY);
// リクエスト本文
var json = JsonConvert.SerializeObject(
new { botId = BOTID });
// APIを実行し、レスポンス本文を取得
HttpResponseMessage response = await client.PostAsync(
uri, new StringContent(json, Encoding.UTF8, "application/json"));
JObject rss = await response.Content.ReadAsAsync<JObject>();
// HTTPステータスが異常であればエラー
if (!response.IsSuccessStatusCode)
{
throw new ApplicationException(
$"API failed. code={response.StatusCode}");
}
// レスポンス本文からID部分を抽出し、インスタンス変数に設定
id = (string)rss["appUserId"];
// APIキーが間違っている場合はidは空。ここでエラーを投げておく
if (string.IsNullOrEmpty(id))
{
throw new ApplicationException(
$"registration API didn't return ID.");
}
}
/// <summary>
/// 「対話」を呼び出し、応答文を返す
/// </summary>
/// <param name="message">発言</param>
/// <returns>応答</returns>
internal async Task<string> MakeRequestAsync(string message)
{
HttpClient client = new HttpClient();
// リクエストURL
string uri = $"https://api.repl-ai.jp/v1/dialogue";
// リクエストヘッダ
client.DefaultRequestHeaders.Add("x-api-key", APIKEY);
// リクエスト本文
var json = JsonConvert.SerializeObject(
new {
appUserId = id,
botId = BOTID,
voiceText = message,
initTalkingFlag = false,
initTopicId = TOPICID
});
// APIを実行し、レスポンス本文を取得
HttpResponseMessage response = await client.PostAsync(
uri, new StringContent(json, Encoding.UTF8, "application/json"));
JObject rss = await response.Content.ReadAsAsync<JObject>();
// HTTPステータスが異常であればエラー
if (!response.IsSuccessStatusCode)
{
throw new ApplicationException(
$"API failed. code={response.StatusCode}");
}
// レスポンス本文から応答文のみを抽出して返す
return (string)rss["systemText"]["expression"];
}
}
}
「BOTで使える会話API・ライブラリ・サービスまとめ」で紹介されている他のボットとも会話させてみると、いいかもしれませんね。