トピック

生成AIがアシストしてくれるテキストエディターが爆誕! 「EmEditor」のマクロで実現

マクロの処理を徹底解説! 魔改造で関西弁変換機能も実現!

 ChatGPTの登場以来、生成AIは、あっという間に実用的に使われるようになってきた。たとえば多くのオフィスワーカーが使うMicrosoftの「Office 365」でも、生成AI機能を搭載して文書作成を効率化しようとしている。

 さて、日本語の文章を毎日書いている「窓の杜」編集部でも愛用者の多いWindows用テキストエディターが「EmEditor」だ。この「EmEditor」が、v24(2月29日公開)より、AIマクロを標準搭載した。「EmEditor」で編集しているテキストに対して、ChatGPTを使って校正や要約などの処理を実行できるものだ。

「EmEditor」のAIマクロに文章を校正してもらったところ

 実は、AIマクロのための技術要素は段階的に取り入れられてきた。v23.0で「EmEditor」の中にWebブラウザーを表示する機能を追加した。そしてv23.1では、JavaScriptから非同期に(≒バックグラウンドで)Web APIを呼び出して結果を受け取る「fetch()」関数において、そのままでは非同期で返事が来る前にマクロが終了してしまうため、マクロを終了せずに結果を待つ「KeepRunning」プロパティを追加した(Professional版のみ)。

 また、GitHub上のライブラリでは、「EmEditor」内のWebブラウザーを使ってChatGPTを呼び出すマクロのサンプル「ChatGPT.jsee」や、 fetch() 関数を使ってChatGPTをAPIで呼び出すマクロのサンプル「ChatOpenAI.jsee」が公開されていた。

 今回v24で標準搭載された「AI.jsee」は、この「ChatOpenAI.jsee」を元に、完成度を高めて同梱されたものだ。

「EmEditor」から生成AIで文章を校正してみる

 さっそくAIマクロを使ってみよう。なお、ChatGPTのAPIを呼び出すために、事前にOpenAIのWebページからAPIキーを取得しておく必要がある。また、前述のとおり、「EmEditor」はProfessional版とする。

 まず、なんらかの文章のテキストを「EmEditor」で開いておく。テキストを選択しておくとその部分が、選択していなければテキスト全体が対象になる。

 ここで、メニューから[マクロ]-[AI.jsee]を選ぶと、OpenAIのAPIキーの入力を求められるので、入力する。

「AI.jsee」を呼び出す
Open AIのAPIキーを入力

 すると、コンテキストメニューで[校正してください]、[意味を調べてください]、[間違いや問題を見つけてください]、[英語に翻訳してください]といった項目が表示される。これは、ChatGPTにしてもらいたい、よくある作業のプロンプト(指示)をテンプレート化したものだ。

 ちなみに、この中には、OpenAIの画像生成AI「Dall-E」を呼び出す[画像を生成]という項目や、指示を直接入力する[質問を指定]という項目もある。[画像を生成]を選ぶと、テキストを元にDall-Eが生成した画像が、EmEditor内蔵のWebブラウザーで表示される。

[画像を生成]でDall-Eに生成してもらった画像

 さて、ここでは[校正してください]を選んでみる。「EmEditor」のステータスバーに『OpenAI APIからのレスポンスを待っています。しばらくお待ちください』とのメッセージが表示されるので少し待つ。筆者の環境で試したときには、10秒ちょっと待った。

ChatGPTにしてもらいたい作業を選ぶ
少し待つ

 やがて、画面が2つに分割され、ChatGPTが校正した結果が「無題」のドキュメントに表示される。校正によって変更された部分は、変更のあった行に色が付けられ、該当部分はさらに濃い色で明示されていることがわかる。

 この校正結果から、特定の変更部分を元の文章に反映するには、コンテキストメニューから、これもv24から追加された[他へコピー]を使えばいい。また、すべて反映するには、分割ウィンドウ「無題」のタイトル部分に表示された[すべて他へコピー]をクリックすることもできる。

校正結果が表示された
[他へコピー]で一部を元の文章に反映
[すべて他へコピー]ですべて元の文章に反映

 なお、OpenAIのAPIキーは、「AI.jsee」マクロを実行するたびに入力を求められる。このAPIキーは50文字以上のランダムな文字列なので、毎回入力するのはとても煩雑だ。

 これを避けるために、APIキーをWindowsの環境変数に設定しておく方法がある(OpenAIによる解説)。Windowsのユーザー環境変数の設定から新規追加に進み、変数名「OPENAI_API_KEY」に対して値としてAPIキーの文字列を設定する。そして「EmEditor」を閉じて開き直せば、APIキーの入力なしに「AI.jsee」マクロを利用できるようになる。

OpenAIのAPIキーを環境変数に設定しておく

生成AI搭載に向け準備を進めていた「EmEditor」

江村豊氏

 さて、こうしたAI機能を「EmEditor」に追加した理由はどのようなものだろうか。現在米国に在住する、「EmEditor」の開発者であるEmurasoft, Inc.の江村豊氏が来日した機会に、AIマクロを追加した理由や今後について話を聞いた。

――「EmEditor」でAIマクロを標準搭載したわけですが、その背景や理由について教えてください。

[江村氏]まず、v23.0ではWebブラウザーを「EmEditor」に内蔵しました。これも、ChatGPTなどのWebを「EmEditor」の中で完結して呼び出し、マクロを使って、その結果をまた「EmEditor」に読み込めるようにしたものです。この方法ではChatGPTのフリー版でも使えます。

 ただ、ChatGPTのWebのフォーマットが将来変わる可能性などもありますし、OpenAIのAPIキーがあればAPIを呼んだほうが確実で新しい機能も使えます。そこでv23.1では、fetch()関数を使ってOpenAIのAPIを呼べるようにしました。

 fetch()自体は前から使えたんですが、非同期処理の終了を待つ指定ができなかったので、マクロが最後まで実行され、マクロが終了してしまうと、非同期処理も終了してしまい、そのあとで結果が返ってきても取得できなくなっちゃうんですね。そこで新しいバージョンでは、KeepRunningというプロパティを追加して、これを「true」にしておくとマクロを終了せずに結果を待つようにしました。

 そしてv24でAIマクロを標準で搭載しました。

――すると、Webブラウザー内蔵やfetch()についての変更も、ChatGPTを呼ぶという目的ありきだったんですね。

[江村氏]そういう感じですね。マクロを標準で搭載したのも、初心者ではマクロをどう使うかわからない方もいるので、そういう方でも簡単に使えるように、マクロをクリックすれば使えるようにもしました。

――もともとご自身でChatGPTなどの生成AIを使っていたのでしょうか?

[江村氏]ときどき使っています。「EmEditor」v24以降では、自分の作ったマクロを使っています(笑)。いちばんよく使うのは、文章の校正や翻訳ですね。

 あと、JavaScriptで簡単な処理を作ってもらうぐらいであれば、ChatGPTからまともな回答が得られると思っています。

 実は、今回標準で入れたAIマクロも、ChatGPTに聞いて作った部分があるんですよ。OpenAIのAPIからは、元の質問の改行コードがCR+LFでも、それに関係なく改行がLFのみで回答が返ってくるんです。それを比較すると、改行だけの違いなのに、全行が違うことになってしまって、ちょっと使いにくいんですね。それを元の改行に合わせる処理をChatGPTに作ってもらいました。

 もちろん自分でもすぐ作れるんですが、もっと早く作れますね。ただAIが間違えることもあるので、そこは質問する側が見分けられる必要があります。また、あいまいに質問すると思ったような回答にならないので、具体的に指示する必要があるようですね。

――ChatGPTはWebなどからも使えますが、それをテキストエディターである「EmEditor」から使えるようにしたのはどのような理由でしょうか?

[江村氏]私の考えでは、今、生成AIでよく使われている用途の一つは文章の校正や翻訳かと思います。その目的ではテキストエディターからが使いやすいと思うんですね。最近はAIを使いたい人が増えているので、AIを使うハードルを下げるために使っていただければと思っています。

 たとえば「EmEditor」のAIマクロで校正を実行すると、分割ウィンドウで結果と元の文章と並べて表示されます。変更された部分も、色分けして表示されるので、そこから取捨選択して反映できます。そのためにv24では[他へコピー]の機能も追加しました。

――このタイミングで[他へコピー]の機能が入ったのは、そのためだったんですね。

[江村氏]そうなんです。それで、校正に使いやすくなったと思います。本当は、矢印ボタンなどを追加してより直感的にコピーできるようにしようかとも思ったのですが、ちょっと時間がなくてそこまではできませんでした(苦笑)。将来のバージョンではそのようなことも考えています。

――「EmEditor」で、特にAI関連での今後の機能改良の方向性について教えてください。

[江村氏]まだちょっと考えはまとまっていませんが、たとえばChatGPTを使って文章を作るときに、慣れていない人はどう質問のプロンプトを書いたらいいかわからないんですね。そこで「EmEditor」から、ウィザード形式で文章の種類や条件などの質問を次々に出して、その答からChatGPTのプロンプトを作って文章を書いてもらう、ということもできないかな、とは思っています。ほかの会社も同じようなことを考えているかとは思いますが。

――たとえば、いま書いている文章からその先を補完してくれる機能などはどうでしょうか。

[江村氏]実はそれも試してみたんですが、うまくいかなくて今は「ボツ」にしています。プロンプトがよくなかったのか、でたらめな予測が返ってきて。APIの利用料を湯水のように使っていいかという問題もあります。ただ、システムロール設定などプロンプトのコツもわかってきたので、またもう一回チャレンジしたいとは思っています。

(注:インタビュー後、「EmEditor」プレビュー版で補完機能がテスト中で、次期正式版で追加される予定とのこと)

――ありがとうございました。

AIマクロの中身を解剖してみる

 AIマクロがどのような作りになっているか、実際のコードでざっくり見てみよう。以下、「AI.jsee」の内容は、執筆時点で最新の「EmEditor」v24.0.1のものを元にしている。

 「EmEditor」のメニューから[マクロ]-[カスタマイズ]を選び、マクロのカスタマイズのダイアログで「AI.jsee」を選択して、[編集]をクリックすると、「EmEditor」で「AI.jsee」が開かれる。

 なお、標準添付されている「AI.jsee」を変更してしまうと、動作がおかしくなってしまう可能性もあり、また今後のアップデートのときに更新ファイルがあって上書きされるなど、問題になる可能性もある。うっかり変更が加わってしまうことを考え、また後ほど実際に変更してみることもあり、見る前に[ファイル]-[名前を付けて保存]から、同じフォルダーに別の名前(「MyAI.jsee」など)で保存しておくとよいだろう。

「AI.jsee」を開く

 「AI.jsee」を実行すると、まずその中のmain()が呼び出されるようになっている。main()の最初のほう、「AI.jsee」の160行には以下のように書かれている。コンテキストメニューに表示される、ChatGPTへの指示の項目を、配列に入れている。

            asCommands = ["校正してください", "意味を調べてください", "要約してください", "間違いや問題を見つけてください", "日本語に翻訳してください", "英語に翻訳してください"];

 353行目からの箇所では、環境変数OPENAI_API_KEYをチェックしている。環境変数が設定されていればその値を、設定されていなければダイアログを開いてAPIキーを入力してもらった内容を、変数「apiKey」に入れている。

    let apiKey = shell.GetEnv("OPENAI_API_KEY");
    if (apiKey.length === 0) {
        apiKey = prompt(sAskKey, "", 0);

 コンテキストメニューを表示して指示が選択されたら、プロンプトのテキストを作る。[校正してください]が選ばれたときには、まず459行の以下のコードにより、メニュー項目がそのままプロンプトの文字列として変数「sPrompt」に入る。

            sPrompt = menu.GetText(result);

 続いて、477行の以下のコードにより、変数「sPrompt」に、「###」で区切って元のテキストが追加される。

                sPrompt = sPrompt + "###" + sText + "###";

 479行目では、v23.1で追加された KeepRunning プロパティを「true」にして、マクロが終了しないようにしている。

            shell.KeepRunning = true;

 そのうえで488行からの以下のコードで、同じ「AI.jsee」で定義されているcallOpenAI()関数を呼び出している。

                const endpoint = "https://api.openai.com/v1/chat/completions";
                const response = await callOpenAI(endpoint, apiKey, sPrompt, sSystemRole);   // Call OpenAI, get response

 callOpenAI()関数の中では、33行の以下のようなコードで、Web APIを呼び出すJavaScriptの fetch() 関数を呼び出している。

    const response = await fetch(
        endpoint,

 結果が返ったら、504行からの以下のコードのように、「EmEditor」で新しいファイルを作り、そこに結果を書き込んでいる。

                        editor.NewFile();
                        document.write(sCleanedResponse);

 最後に517行で、 KeepRunning プロパティを「false」に戻して、マクロが終了できるようにする。

            shell.KeepRunning = false;

AIマクロを魔改造して大阪弁の文章を書かせる

 ここまでで、同梱されている「AI.jsee」の構造はだいたい理解いただけたかと思う。

 次に、このマクロを改造して、新たな機能を追加してみたい。ここでは、[大阪弁に翻訳する機能]を追加してみる。

 「AI.jsee」でのAIへの指示は、コンテキストメニューの項目で表示されるテキストがそのままChatGPTに送られている。ここをいじってみるのが、最初の一歩として誰にでもわかりやすいだろう。コンテキストメニューにない指示は[質問を指定]から直接入力することもでき、入力履歴から呼び出すこともできるが、よく使うAIへの指示はメニューに追加できると便利だ。

 対象にするマクロファイルは、「AI.jsee」を別名で保存した「MyAI.jsee」だ。

 さきほど取り上げた「MyAI.jsee」の160行のメニュー項目定義で、“[”と“]”で囲まれた中に、以下のように「, "大阪弁に翻訳してください"」という項目を追加し、ファイルを保存する。

            asCommands = ["校正してください", "意味を調べてください", "要約してください", "間違いや問題を見つけてください", "日本語に翻訳してください", "英語に翻訳してください", "大阪弁に翻訳してください"];

 そしてメニューの[マクロ]-[これを選択]を実行すると[マクロ]メニューのマクロリストに「MyAI.jsee」が表示されるようになる。

 文章のウィンドウから、この「MyAI.jsee」を呼び出すと、普通の「AI.jsee」と同じようなコンテキストメニューが表示される。ただし、さきほどの[大阪弁に翻訳してください]という項目が追加されていることがわかる。

[大阪弁に翻訳してください]が追加された

 この項目を選んで、やはり少し待つと、分割ウィンドウにChatGPTが作った大阪弁バージョンの文章が表示された。

大阪弁バージョンの文章が表示された

 以上、「EmEditor」のAIマクロ機能を見てきた。

 テキストエディターに書かれているテキストを元に、指示を選ぶことで、生成AIが処理を実行してくれるというのはわかりやすい。江村氏も言うように、校正や翻訳のような決まったケースであれば、このようにテキストエディターから呼び出すのが便利だ。

 また、マクロの内容も、指示とテキストをセットにしてプロンプトにしているわけだ。マクロ全体はそれなりに複雑だが、テキスト+指示の組み合わせで実行するという考えは単純なため、アイデアしだいでいろいろ応用できそうだ。

 そのほか、マクロをカスタマイズするケースとしては、OpenAIのモデルを、コード中で指定されている「gpt-4-turbo-preview」から変更する場合が考えられる。それには、AI.jseeの19行目で設定しているsModelの値を変更すればよい。

 ChatGPTなどの生成AIは急速に進化しており、使う側のテクニックもどんどん新しくなっている。EmEditorのAIマクロがさらにアップデートされていくのも楽しみだし、JavaScriptの書ける人なら自分でカスタマイズしてみるのも面白いだろう。