やりたいこと
Module-LLMに対して日本語文字列でプロンプトを投げるとちゃんと日本語で返ってくるので、CardKBで日本語をポチポチと入力してModule-LLMと日本語でやりとりしたい。本当はかな漢字変換までやりたいのだけれど、形態素解析とか始めると大変なのでひらがなへの変換のみ。
材料
- Module-LLM
- CoreS3
- CardKB
やってること
- CardKBからの入力をreadしてEnterキーの入力時点でローマ字→ひらがなの変換を行い、Module-LLMへのプロンプトとして投げる
- 一応ローマ字入力時のDeleteキーも応答しますが、表示は書き換えてないのでちょっとおかしい。入力バッファは更新されている、はず
- 大元のソースは、SerialTextAssistant。シリアルモニタからの入力をCardKBのハンドリングとひらがな変換に置き換え
ローマ字かな変換
ローマ字っていろんなパターンがあってめんどい。ひと通り変換できそうだけれど、まだ足りないかも(今気がついたけれど「しぇ」とかない)。
convertHiraganaTable.h
#ifndef _CONVERT_HIRAGANA_TABLE_H_
#define _CONVERT_HIRAGANA_TABLE_H_
typedef struct {
char *romaji;
char *hiragana;
} RomajiKanaMap;
RomajiKanaMap table[] = {
{"a", "あ"}, {"i", "い"}, {"u", "う"}, {"e", "え"}, {"o", "お"},
{"ka", "か"}, {"ki", "き"}, {"ku", "く"}, {"ke", "け"}, {"ko", "こ"},
{"ga", "が"}, {"gi", "ぎ"}, {"gu", "ぐ"}, {"ge", "げ"}, {"go", "ご"},
{"sa", "さ"}, {"si", "し"}, {"su", "す"}, {"se", "せ"}, {"so", "そ"},
{"shi", "し"},
{"za", "ざ"}, {"zi", "じ"}, {"zu", "ず"}, {"ze", "ぜ"}, {"zo", "ぞ"},
{"ji", "じ"},
{"ta", "た"}, {"chi", "ち"}, {"tsu", "つ"}, {"te", "て"}, {"to", "と"},
{"ti", "ち"}, {"tu", "つ"},
{"da", "だ"}, {"di", "ぢ"}, {"du", "づ"}, {"de", "で"}, {"do", "ど"},
{"na", "な"}, {"ni", "に"}, {"nu", "ぬ"}, {"ne", "ね"}, {"no", "の"},
{"ha", "は"}, {"hi", "ひ"}, {"fu", "ふ"}, {"he", "へ"}, {"ho", "ほ"},
{"ba", "ば"}, {"bi", "び"}, {"bu", "ぶ"}, {"be", "べ"}, {"bo", "ぼ"},
{"pa", "ぱ"}, {"pi", "ぴ"}, {"pu", "ぷ"}, {"pe", "ぺ"}, {"po", "ぽ"},
{"ma", "ま"}, {"mi", "み"}, {"mu", "む"}, {"me", "め"}, {"mo", "も"},
{"ya", "や"}, {"yu", "ゆ"}, {"yo", "よ"},
{"ra", "ら"}, {"ri", "り"}, {"ru", "る"}, {"re", "れ"}, {"ro", "ろ"},
{"wa", "わ"}, {"wo", "を"}, {"nn", "ん"},
{"xya", "ょ"}, {"xyu", "ゅ"}, {"xyo", "ょ"}, {"xtu", "っ"},
{"kya", "きゃ"}, {"kyu", "きゅ"}, {"kyo", "きょ"},
{"gya", "ぎゃ"}, {"gyu", "ぎゅ"}, {"gyo", "ぎょ"},
{"sha", "しゃ"}, {"shu", "しゅ"}, {"sho", "しょ"},
{"ja", "じゃ"}, {"ju", "じゅ"}, {"jo", "じょ"},
{"cha", "ちゃ"}, {"chu", "ちゅ"}, {"cho", "ちょ"},
{"dya", "ぢゃ"}, {"dyu", "ぢゅ"}, {"dyo", "ぢょ"},
{"nya", "にゃ"}, {"nyu", "にゅ"}, {"nyo", "にょ"},
{"hya", "ひゃ"}, {"hyu", "ひゅ"}, {"hyo", "ひょ"},
{"bya", "びゃ"}, {"byu", "びゅ"}, {"byo", "びょ"},
{"pya", "ぴゃ"}, {"pyu", "ぴゅ"}, {"pyo", "ぴょ"},
{"mya", "みゃ"}, {"myu", "みゅ"}, {"myo", "みょ"},
{"rya", "りゃ"}, {"ryu", "りゅ"}, {"ryo", "りょ"},
{"kka", "っか"}, {"kki", "っき"}, {"kku", "っく"}, {"kke", "っけ"}, {"kko", "っこ"},
{"gga", "っが"}, {"ggi", "っぎ"}, {"ggu", "っぐ"}, {"gge", "っげ"}, {"ggo", "っご"},
{"ssa", "っさ"}, {"sshi", "っし"}, {"ssu", "っす"}, {"sse", "っせ"}, {"sso", "っそ"},
{"zza", "っざ"}, {"zzi", "っじ"}, {"zzu", "っず"}, {"zze", "っぜ"}, {"zzo", "っぞ"},
{"tta", "った"}, {"cchi", "っち"}, {"ttsu", "っつ"}, {"tte", "って"}, {"tto", "っと"},
{"dda", "っだ"}, {"ddi", "っぢ"}, {"ddu", "っづ"}, {"dde", "っで"}, {"ddo", "っど"},
{"nna", "っな"}, {"nni", "っに"}, {"nnu", "っぬ"}, {"nne", "っね"}, {"nno", "っの"},
{"hha", "っは"}, {"hhi", "っひ"}, {"ffu", "っふ"}, {"hhe", "っへ"}, {"hho", "っほ"},
{"bba", "っば"}, {"bbi", "っび"}, {"bbu", "っぶ"}, {"bbe", "っべ"}, {"bbo", "っぼ"},
{"ppa", "っぱ"}, {"ppi", "っぴ"}, {"ppu", "っぷ"}, {"ppe", "っぺ"}, {"ppo", "っぽ"},
{"mma", "っま"}, {"mmi", "っみ"}, {"mmu", "っむ"}, {"mme", "っめ"}, {"mmo", "っも"},
{"yya", "っや"}, {"yyu", "っゆ"}, {"yyo", "っよ"},
{"rra", "っら"}, {"rri", "っり"}, {"rru", "る"}, {"rre", "れ"}, {"rro", "ろ"},
{"wwa", "っわ"}, {"wwo", "っを"},
{"kkya", "っきゃ"}, {"kkyu", "っきゅ"}, {"kkyo", "っきょ"},
{"ggya", "っぎゃ"}, {"ggyu", "っぎゅ"}, {"ggyo", "っぎょ"},
{"ssha", "っしゃ"}, {"sshu", "っしゅ"}, {"ssho", "っしょ"},
{"jja", "っじゃ"}, {"jju", "っじゅ"}, {"jjo", "っじょ"},
{"ccha", "っちゃ"}, {"cchu", "っちゅ"}, {"ccho", "っちょ"},
{"ddya", "っぢゃ"}, {"ddyu", "っぢゅ"}, {"ddyo", "っぢょ"},
{"nnya", "っにゃ"}, {"nnyu", "っにゅ"}, {"nnyo", "っにょ"},
{"hhya", "っひゃ"}, {"hhyu", "っひゅ"}, {"hhyo", "っひょ"},
{"bbya", "っびゃ"}, {"bbyu", "っびゅ"}, {"bbyo", "っびょ"},
{"ppya", "っぴゃ"}, {"ppyu", "っぴゅ"}, {"ppyo", "っぴょ"},
{"mmya", "っみゃ"}, {"mmyu", "っみゅ"}, {"mmyo", "っみょ"},
{"rrya", "っりゃ"}, {"rryu", "っりゅ"}, {"rryo", "っりょ"},
{"-", "ー"}, {".", "。"}, {",", "、"}
};
#define TABLE_SIZE (sizeof(table) / sizeof(table[0]))
void convertRoma2Hiragana(const char *input, char *output) {
int i, j, len;
while (*input) {
int found = 0;
for (i = 0; i < TABLE_SIZE; i++) {
len = strlen(table[i].romaji);
if (strncmp(input, table[i].romaji, len) == 0) {
strcat(output, table[i].hiragana);
input += len;
found = 1;
break;
}
}
if (!found) {
strncat(output, input, 1);
input++;
}
}
}
#endif _CONVERT_HIRAGANA_TABLE_H_
メインループとキーボードハンドリング
KeyboardAssistant.ino
#include <Wire.h>
#include <Arduino.h>
#include <M5Unified.h>
#include <M5ModuleLLM.h>
#include "convertHiraganaTable.h"
M5ModuleLLM module_llm;
String llm_work_id;
String received_question;
String input_question;
bool question_ok;
void setup() {
// put your setup code here, to run once:
M5.begin();
Wire.begin();
M5.Display.clear();
M5.Display.setTextFont(0);
M5.Display.setTextSize(1);
M5.Display.setFont(&fonts::efontJA_12);
M5.Display.setTextScroll(true);
// init input string
input_question = "";
// set up
// Serial2.begin(115200, SERIAL_8N1, 16, 17); // Basic
// Serial2.begin(115200, SERIAL_8N1, 13, 14); // Core2
Serial2.begin(115200, SERIAL_8N1, 18, 17); // CoreS3
// llm start
module_llm.begin(&Serial2);
M5.Display.printf(">> Check ModuleLLM connection..\n");
while (1) {
if (module_llm.checkConnection()) {
break;
}
}
M5.Display.printf(">> Reset ModuleLLM..\n");
module_llm.sys.reset();
M5.Display.printf(">> Setup llm..\n");
m5_module_llm::ApiLlmSetupConfig_t llm_config;
llm_config.max_token_len = 1023;
llm_work_id = module_llm.llm.setup(llm_config);
M5.Display.printf(">> Setup finish\n");
M5.Display.printf(">> Try type your question via CardKB\n\n");
}
void loop() {
// put your main code here, to run repeatedly:
M5.update();
Wire.requestFrom(0x5F, 1);
question_ok = false;
while (Wire.available()) {
// handle keyboard input
question_ok = handleKeyboard();
if (question_ok) {
break;
}
}
if (question_ok) {
// send question string to llm
M5.Display.setTextColor(TFT_GREEN);
M5.Display.printf("\n<< %s\n", received_question.c_str());
M5.Display.setTextColor(TFT_YELLOW);
M5.Display.printf(">> ");
module_llm.llm.inferenceAndWaitResult(llm_work_id, received_question.c_str(), [](String& result) {
M5.Display.printf("%s", result.c_str());
});
/* Clear for next question */
received_question.clear();
M5.Display.println();
}
delay(20);
}
bool handleKeyboard() {
bool result = false;
char c_key;
char c = Wire.read();
if (c != 0) {
switch(c) {
case 0x0d:
// enter key
received_question = henkan(input_question);
input_question.clear();
input_question = "";
c_key = 0x00;
result = true;
break;
case 0x08:
// delete key
input_question.remove(input_question.length() - 1);
c_key = 0x00;
result = false;
break;
default:
c_key = c;
result = false;
break;
}
if (c_key != 0x00) {
input_question += c_key;
M5.Display.setTextColor(TFT_WHITE);
M5.Display.printf("%c", c_key);
}
}
return result;
}
String henkan(String input) {
const char *input_str = input.c_str();
char output_str[500] = "";
convertRoma2Hiragana(input_str, output_str);
return String(output_str);
}
終わりに
とりあえずは日本語で対話できるようにはなったけれど、全部ひらがななのでLLMとしてもちょっと何言ってるのかわからない応答を返す。やっぱりかな漢字変換が必要ですね。