やりたいこと
Module-LLMとCardKBで入力して日本語で会話したい。
前回こちらの記事でローマ字ひらがな変換をローカルで実装してみたけれど、ひらがなだけだと、応答結果が塩なんですよね。
というわけで、かな漢字変換をしてみる。
材料
- Module-LLM
- M5Stack CoreS3
- CardKB
- Yahoo!かな漢字変換API
Yahoo!かな漢字変換API
- POSTでJSONリクエストすると変換結果が返ってくるAPI
- アプリケーションIDを登録すれば、RESTでリクエスト可能
- resultsの値はレスポンスされる候補の数。とりあえず第一候補だけでいいので1を指定する
リクエストJSON
{
"id": "1234",
"jsonrpc": "2.0",
"method": "jlp.jimservice.conversion",
"params": {
"q": "きしゃのきしゃがきしゃできしゃした。",
"format": "hiragana",
"mode": "kanakanji",
"option": ["hiragana", "katakana", "alphanumeric", "half_katakana", "half_alphanumeric"],
"dictionary": ["base", "name", "place", "zip", "symbol"],
"results": 1
}
}
レスポンス見ると、resultの中の[segment][candidate][0]の文字列を結合すればとりあえず第一候補で変換結果が得られそう。
レスポンスJSON
{
"id": "1234",
"jsonrpc": "2.0",
"result": {
"segment": [
{
"alphanumeric": "kishano",
"candidate": [
"貴社の"
],
"half_alphanumeric": "kishano",
"half_katakana": "キシャノ",
"hiragana": "きしゃの",
"katakana": "キシャノ",
"reading": "きしゃの"
},
{
"alphanumeric": "kishaga",
"candidate": [
"記者が"
],
"half_alphanumeric": "kishaga",
"half_katakana": "キシャガ",
"hiragana": "きしゃが",
"katakana": "キシャガ",
"reading": "きしゃが"
},
{
"alphanumeric": "kishade",
"candidate": [
"汽車で"
],
"half_alphanumeric": "kishade",
"half_katakana": "キシャデ",
"hiragana": "きしゃで",
"katakana": "キシャデ",
"reading": "きしゃで"
},
{
"alphanumeric": "kishashita.",
"candidate": [
"帰社した。"
],
"half_alphanumeric": "kishashita.",
"half_katakana": "キシャシタ。",
"hiragana": "きしゃした。",
"katakana": "キシャシタ。",
"reading": "きしゃした。"
}
]
}
}
というわけで
これに肉付けしていく。
convertKanji.h
#ifndef _CONVERT_KANJI_H_
#define _CONVERT_KANJI_H_
const char *ssid = "__ssid__";
const char *password = "__password__";
const char *api_url = "jlp.yahooapis.jp";
const char *api_endpoint = "/JIMService/V2/conversion";
const char *APPID = "__apikey__"; // Yahoo! APIキー
#define MODE_ROMAN "roman"
#define MODE_PREDICTIVE "predictive"
#define MODE_KANJI "kanakanji"
#define FORMAT_ROMAN "roman"
#define FORMAT_HIRAGANA "hiragana"
WiFiClientSecure client;
String parseResponse(String response) {
const size_t capacity = JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(1) + 100;
DynamicJsonDocument doc(capacity);
int jsonStart = response.indexOf("{");
if (jsonStart == -1) {
M5.Display.println("Invalid Response");
return "";
}
String jsonResponse = response.substring(jsonStart);
DeserializationError error = deserializeJson(doc, jsonResponse);
if (error) {
M5.Display.println("JSON Parse Failed");
return "";
}
String resultText = "";
JsonArray segments = doc["result"]["segment"];
for (JsonObject segment : segments) {
String candidate = segment["candidate"][0].as<String>();
resultText += candidate;
}
return (resultText);
}
String sendAPIRequest(String text, String mode = MODE_KANJI, String format = FORMAT_HIRAGANA) {
String jsonRequest = "{\"id\": \"1234\",\"jsonrpc\": \"2.0\",\"method\": \"jlp.jimservice.conversion\",\"params\": {\"q\": \"" + text + "\",\"format\": \"" + format + "\",\"mode\": \"" + mode + "\",\"option\": [\"hiragana\", \"katakana\", \"alphanumeric\", \"half_katakana\", \"half_alphanumeric\"],\"dictionary\": [\"base\", \"name\", \"place\", \"zip\", \"symbol\"],\"results\": 1}}";
if (!client.connect(api_url, 443)) {
M5.Display.println("API Connection Failed");
return "";
}
client.println("POST " + String(api_endpoint) + " HTTP/1.1");
client.println("Host: " + String(api_url));
client.println("User-Agent: Yahoo AppID: " + String(APPID));
client.println("Content-Type: application/json");
client.println("Content-Length: " + String(jsonRequest.length()));
client.println("Connection: close");
client.println();
client.println(jsonRequest);
String response = "";
while (client.connected() || client.available()) {
if (client.available()) {
response += client.readStringUntil('\n');
}
}
client.stop();
String result = parseResponse(response);
return result;
}
void initConvertKanji() {
client.setInsecure();
}
String convertKanji(String text, String mode = MODE_KANJI, String format = FORMAT_HIRAGANA) {
return sendAPIRequest(text, mode, format);
}
#endif _CONVERT_KANJI_H_
これで入力したひらがなをAPIに送ると「貴社の記者が汽車で帰社した。」に変換されるはず。
String result = convertKanji("きしゃのきしゃがきしゃできしゃした。");
おわりに
APIを利用する=オフラインLLMではなくなっちゃったのだけれど、まあ、可能性ということで。