book
book copied to clipboard
誤字、脱字、誤植をLLMで一括で見つける仕組みを構築したいです。
解決したい課題
ドキュメント誤字、脱字が多くISSUEでも度々指摘されるため、誤字、脱字が発生しない仕組みを構築したい
解決策の方針
- LLMを活用して誤字、脱字を自動で検知する仕組みを構築する。
- 検知された誤字、脱字を自動で修正する。
やり方について
テキストの自動修正についてはLLMをどれだけ信用するによる。予期しないテキストに自動修正されて、気づかずにドキュメントがリリースされる危険性もあるので、フェーズを分けて初回では誤字、脱字を検知するだけの仕組みを構築して、修正するのは人が行うのが良さそう。
実験
OpenAI Playground で試してみたら、普通にできそうな気配を感じました。
探せばいくらでもこの課題を解決できるツールは見つかりそうな気もします。
以下のMarkdownテキストについて実験的にChat-GPTで指摘したら良い結果が得られなかった。👀
# 従来の関数とアロー関数の違い
JavaScriptの関数は、[関数宣言]、[関数式]、[アロー関数]の3通りの方法で作れます。
[関数宣言]: ./function-declaration.md
[関数式]: ./function-expression.md
[アロー関数]: ./arrow-functions.md
## 従来の関数とアロー関数の使い分け
アロー関数が特に相性がいいところはコールバック関数です。たとえば、配列オブジェクトの`Array`には、各要素に対して処理をかけるメソッドがいくつかあります。これらのメソッドは引数に関数を渡す必要があります。次の例は、数値の配列に対して`filter`メソッドを用い、偶数だけを抽出するコードです。このコードでは関数式をコールバック関数に渡しています。
\`\`\`js twoslash
const nums = [1, 2, 3, 4];
const even = nums.filter(function (n) {
return n % 2 === 0;
});
console.log(even);
// @log: [2, 4]
\`\`\`
従来の関数も出番がないわけではありません。HTMLのボタンがクリックされたときに何らかの処理をしたい場合、`addEventListener`メソッドを使います。任意の処理をコールバック関数としてこのメソッドに渡すことで、好きな処理が行なえます。
#746 で指摘いただたドキュメントを簡易化して実験
指摘された表記を修正して問題が無い場合のパターン
誤字、脱字、誤植を検知するタスクだと、誤りか人によって判断が分かれるようのな指摘が多く出力されツールとして利用しづらくなる。 「単語の誤植」というタスクに絞れば、ツールとして精度は高められそう。
触発されて試したら誤植の発見に繋がりました。
- #748
用いたプロンプト
与えられた文章の単語の誤用を指摘しなさい。 誤用とするもの:
- スペルミス
- 誤ったチョイス 単語の誤用以外は指摘しないこと。
GPTの回答
「1977年に廃止されたJavaのDateクラスを基にして10日でJavaScriptエンジンに含まれました。」という表現は誤っています。Javaが公開されたのは1995年であり、そのDateクラスがJavaScriptエンジンに組み込まれることもありません。また、何を意味するのか不明瞭な「10日で」という表現も問題です。
正しくは、JavaScriptのDateオブジェクトはJavaのDateクラスを基に設計されたものであり、それぞれ異なる歴史と経緯があります。
類似の指示として次のプロンプトも有用でした。
あなたは、与えられた原稿の問題点を指摘してください。
指摘すること:
- 単語の誤植
- 単語のスペルミス
- 事実と反する説明
試していて気づいたのですが、一旦「問題点はありません」という回答が出ると、文脈バイアスがかかるようで、その後のスレッドでは問題点の発見精度が下がる感じがしました。逆に言うと、毎回文脈をリセットして(新たなチャットとして開始して)原稿を与えていったほうが発見精度が高い気がしました。
仕組み的にはAPIを呼ぶだけにはなるので、プロンプトの調整が重要そうですね。👀
#750 について対象のMarkdownファイル内の全てのテキストを与えて誤植チェックを試したが、誤植が検知されなかった。
自然言語処理の仕組みとして、テキストをベクトルに変換して認識している?ので、リターン
と リータン
の微妙な違いの検知は苦手なのかもしれない。
一度に与えるテキスト量も精度に影響してるかもです?
直接誤植や脱字を検知させようとすると、誤植じゃない部分も誤植として判定されて精度が微妙になるパターンが多い。
{
"count": 2,
"misspelling": [
{
"line": 5,
"misspelled_word": "ユニオン型",
"correct_word": "ユニオン型"
},
{
"line": 19,
"misspelled_word": "リータン",
"correct_word": "リターン"
}
]
}
Markdownファイルのテキスト1行単位に分割して、「スペルミスを修正してください」という形で依頼を投げる方がツールとして利用しやすい形になりそうでした。👀
与えらたMarkdown形式の日本語のテキストの誤字、脱字、誤植を修正してください。
-: これは制御フロー分析により`month`変数が`string`型の場合は早期リータンにより関数が終了し、`month.toFixed()`が実行されるタイミングでは`month`変数は`number`型のみであるとTypeScriptが判断するためです。
+: これは制御フロー分析により`month`変数が`string`型の場合は早期リターンにより関数が終了し、`month.toFixed()`が実行されるタイミングでは`month`変数は`number`型のみであるとTypeScriptが判断するためです。
仕組みの仕様(仮)
処理フロー
- Makrdownファイルを読み込む
- 改行ごとにテキストを分割
- 1行ごとにOpenAI API で修正のタスク依頼を投げる
- テキストの差分が存在する場合だけユーザーに結果を出力する
- 3~4を分割したテキストの数だけ繰り返す
ChatGPTモデル
モデル: gpt-3.5-turbo-0613 templature: 0.0
プロンプト
与えられたMarkdown形式の日本語のテキストを`修正ルール`に従い修正してください。
## 修正ルール
- 単語のスペルミスを修正する
プロトタイプの結果
対象のテキスト: 制御フロー分析と型ガードによる型の絞り込み
句読点の有無については、プロンプトで制御が難しかった。もし句読点を無視するなら、テキスト比較で句読点を除いて比較するなどをする必要がありそうです。
- これは`month`の変数が`string`or`number`型のどちらかになる可能性があり`number`型が渡された時に未定義なメソッドへのアクセスが発生する危険があるためです。
+ これは`month`の変数が`string`または`number`型のどちらかになる可能性があり、`number`型が渡された時に未定義なメソッドへのアクセスが発生する危険があるためです。
- ## 制御フロー分析
+ ## 制御フロー解析
- 先ほどの例に`month`変数が`string`型であることを条件判定を追加することで`month.padStart()`の実行時は`month`が`string`型であるとTypeScriptが判断し型エラーを解消することができます。
+ 先ほどの例に`month`変数が`string`型であることを条件判定を追加することで`month.padStart()`の実行時は`month`が`string`型であるとTypeScriptが判断し、型エラーを解消することができます。
- これは制御フロー分析により`month`変数が`string`型の場合は早期リータンにより関数が終了し、`month.toFixed()`が実行されるタイミングでは`month`変数は`number`型のみであるとTypeScriptが判断するためです。
+ これは制御フロー分析により`month`変数が`string`型の場合は早期リターンにより関数が終了し、`month.toFixed()`が実行されるタイミングでは`month`変数は`number`型のみであるとTypeScriptが判断するためです。
- ### in
+ ### out
- この名称(user-defined type guard)は英語としても長いらしく、型ガード関数(type guarding function, guard's function)と呼ばれることもあります。
+ この名称(user-defined type guard)は英語としても長いらしく、型ガード関数(type guard function, guard function)と呼ばれることもあります。
- 型ガードに変数を使うこともできます。
+ 型ガードで変数を使うこともできます。
- ただし、この文法は TypeScript4.4 以降のみで有効なため、使用する場合はバージョンに注意してください。
+ ただし、この文法は TypeScript 4.4 以降のみで有効なため、使用する場合はバージョンに注意してください。
- ## 関連情報
+ ## 修正ルール
chatgpt-4ならどうでしょう?
@suin
gpt-4-0613
モデルで実験した例です。
gpt-4-0613
モデルの方が 判定 => 判断
をスペルミスとしていたりして、修正内容が多くなる印象でした。😌
プロンプトの書き方の問題もあるかもですが。
同じプロンプトで実験
- ## ユニオン型と曖昧さ
+ ## ユニオン型と曖昧性
- ユニオン型で変数の型注釈を書いた時に、片方の型でしか定義されていないメソッドやプロパティにアクセスをすると型エラーが発生します。
+ ユニオン型で変数の型注釈を書いた時に、片方の型でしか定義されていないメソッドやプロパティにアクセスすると型エラーが発生します。
- これは`month`の変数が`string`or`number`型のどちらかになる可能性があり`number`型が渡された時に未定義なメソッドへのアクセスが発生する危険があるためです。
+ これは`month`の変数が`string`or`number`型のどちらかになる可能性があり、`number`型が渡された時に未定義なメソッドへのアクセスが発生する危険があるためです。
- 先ほどの例に`month`変数が`string`型であることを条件判定を追加することで`month.padStart()`の実行時は`month`が`string`型であるとTypeScriptが判断し型エラーを解消することができます。
+ 先ほどの例に`month`変数が`string`型であることを条件判定に追加することで、`month.padStart()`の実行時には`month`が`string`型であるとTypeScriptが判断し、型エラーを解消することができます。
- この関数の最初の条件分岐の中に`return`を追記して早期リターンで関数の処理を終了させてみます。
+ この関数の最初の条件分岐の中に`return`を追加して早期リターンで関数の処理を終了させてみます。
- これは制御フロー分析により`month`変数が`string`型の場合は早期リータンにより関数が終了し、`month.toFixed()`が実行されるタイミングでは`month`変数は`number`型のみであるとTypeScriptが判断するためです。
+ これは制御フロー分析により`month`変数が`string`型の場合は早期リターンにより関数が終了し、`month.toFixed()`が実行されるタイミングでは`month`変数は`number`型のみであるとTypeScriptが判断するためです。
- ## 型ガード
+ ## タイプガード
- 制御フローの説明において、型の曖昧さを回避するために`if(typeof month === "string")`という条件判定で変数の型を判定して型の絞り込みを行いました。
+ 制御フローの説明において、型の曖昧さを回避するために`if(typeof month === "string")`という条件判断で変数の型を判断して型の絞り込みを行いました。
- 次の例では`typeof`で`month`変数の型を`string`型と判定しています。
+ 次の例では`typeof`で`month`変数の型を`string`型と判断しています。
- JavaScriptにおいて`null`はオブジェクトであるため、次の型ガードを書いた場合は`date`変数は`Date | null`に絞り込まれ`null`となる可能性が残ってしまい型エラーが発生します。
+ JavaScriptにおいて`null`はオブジェクトであるため、次の型ガードを書いた場合、`date`変数は`Date | null`に絞り込まれ、`null`となる可能性が残ってしまい、型エラーが発生します。
- 特定のクラスのインスタンスであることを明示せず、`in`演算子でオブジェクトが特定のプロパティを持つかを判定する型ガードを書くことで型を絞り込むこともできます。
+ 特定のクラスのインスタンスであることを明示せず、`in`演算子でオブジェクトが特定のプロパティを持つかを判断する型ガードを書くことで型を絞り込むこともできます。
- 型ガードはインラインで記述する以外にも関数として定義することもできます。
+ 型ガードはインラインで記述するだけでなく、関数として定義することもできます。
- この名称(user-defined type guard)は英語としても長いらしく、型ガード関数(type guarding function, guard's function)と呼ばれることもあります。
+ この名称(user-defined type guard)は英語としても長いらしく、型ガード関数(type guarding function, guard function)と呼ばれることもあります。
- ただし、この文法は TypeScript4.4 以降のみで有効なため、使用する場合はバージョンに注意してください。
+ ただし、この文法は TypeScript 4.4 以降のみで有効なため、使用する場合はバージョンに注意してください。
- [any型](../values-types-variables/any.md)
+ [Any型](../values-types-variables/any.md)
句読点を無視するようにお願いしたパターン
句読点もスペルミスという扱いで修正が入ってました。
与えらたMarkdown形式の日本語のテキストを`修正ルール`に従い修正してください。
## 修正ルール
- 単語のスペルミスを修正する
- 句読点は無視してください
$ poetry run python linter.py
- ## ユニオン型と曖昧さ
+ ## ユニオン型と曖昧性
- ユニオン型で変数の型注釈を書いた時に、片方の型でしか定義されていないメソッドやプロパティにアクセスをすると型エラーが発生します。
+ ユニオン型で変数の型注釈を書いた時に、片方の型でしか定義されていないメソッドやプロパティにアクセスすると型エラーが発生します。
- これは`month`の変数が`string`or`number`型のどちらかになる可能性があり`number`型が渡された時に未定義なメソッドへのアクセスが発生する危険があるためです。
+ これは`month`の変数が`string`or`number`型のどちらかになる可能性があり、`number`型が渡された時に未定義なメソッドへのアクセスが発生する危険があるためです。
- 先ほどの例に`month`変数が`string`型であることを条件判定を追加することで`month.padStart()`の実行時は`month`が`string`型であるとTypeScriptが判断し型エラーを解消することができます。
+ 先ほどの例に`month`変数が`string`型であることを条件判定に追加することで、`month.padStart()`の実行時には`month`が`string`型であるとTypeScriptが判断し、型エラーを解消することができます。
- この関数の最初の条件分岐の中に`return`を追記して早期リターンで関数の処理を終了させてみます。
+ この関数の最初の条件分岐の中に`return`を追加して早期リターンで関数の処理を終了させてみます。
- これは制御フロー分析により`month`変数が`string`型の場合は早期リータンにより関数が終了し、`month.toFixed()`が実行されるタイミングでは`month`変数は`number`型のみであるとTypeScriptが判断するためです。
+ これは制御フロー分析により`month`変数が`string`型の場合は早期リターンにより関数が終了し、`month.toFixed()`が実行されるタイミングでは`month`変数は`number`型のみであるとTypeScriptが判断するためです。
- 制御フローの説明において、型の曖昧さを回避するために`if(typeof month === "string")`という条件判定で変数の型を判定して型の絞り込みを行いました。
+ 制御フローの説明において、型の曖昧さを回避するために`if(typeof month === "string")`という条件判断で変数の型を判断して型の絞り込みを行いました。
- 次の例では`typeof`で`month`変数の型を`string`型と判定しています。
+ 次の例では`typeof`で`month`変数の型を`string`型と判断しています。
- JavaScriptにおいて`null`はオブジェクトであるため、次の型ガードを書いた場合は`date`変数は`Date | null`に絞り込まれ`null`となる可能性が残ってしまい型エラーが発生します。
+ JavaScriptにおいて`null`はオブジェクトであるため、次の型ガードを書いた場合、`date`変数は`Date | null`に絞り込まれ、`null`となる可能性が残ってしまい、型エラーが発生します。
- `typeof`でインスタンスを判定した場合はオブジェクトであることまでしか判定ができません。
+ `typeof`でインスタンスを判定した場合、オブジェクトであることまでしか判定ができません。
- 特定のクラスのインスタンスであることを明示せず、`in`演算子でオブジェクトが特定のプロパティを持つかを判定する型ガードを書くことで型を絞り込むこともできます。
+ 特定のクラスのインスタンスであることを明示せず、`in`演算子でオブジェクトが特定のプロパティを持つかを判断する型ガードを書くことで型を絞り込むこともできます。
- 型ガードはインラインで記述する以外にも関数として定義することもできます。
+ 型ガードはインラインで記述する以外にも、関数として定義することもできます。
- この名称(user-defined type guard)は英語としても長いらしく、型ガード関数(type guarding function, guard's function)と呼ばれることもあります。
+ この名称(user-defined type guard)は英語としても長いらしく、型ガード関数(type guarding function, guard function)と呼ばれることもあります。
- ただし、この文法は TypeScript4.4 以降のみで有効なため、使用する場合はバージョンに注意してください。
+ ただし、この文法は TypeScript 4.4 以降のみで有効なため、使用する場合はバージョンに注意してください。
- [any型](../values-types-variables/any.md)
+ [Any型](../values-types-variables/any.md)
ネクストアクション
- 新しいプロンプトで、指摘箇所がない場合に「なし」と回答させる指示をしてなかったので追加してみる
- 改行単位だと細かすぎるかもなので、セクション単位などで分割してチェックさせてみる
- MarkdownのAST解析はライブラリ使うと良い
- awesome-syntax-tree