ChatGPTを活用したブログレビューシステム
はじめに
株式会社QualiArtsでバックエンドエンジニアをしている高嶋です。
現在は開発推進室で弊社のAI利用を推し進めています。
現在ご覧いただいているように、弊社では技術ブログを運営しており、このブログは皆様に弊社の活動や技術について発信する重要な役割を担っています。
そのため、誤りがなくわかりやすい記事にしなければなりませんが、それにはレビューが欠かせません。
しかし、レビューにはどうしてもコストがかかってしまいますし、人間が確認するため漏れも発生してしまいます。
そこで今回、AI推進の施策の一つとして、ChatGPTを利用したブログレビューシステムを実装・導入しましたので、その内容を紹介します。
ChatGPTを利用したシステムを構築しようと考えている方の一助となれば幸いです。
システム概要
システムの概要を以下に示します。
QualiArtsエンジニアブログはGitHubで管理されており、プルリクエストを用いて記事のレビューを行っています。
このシステムは、ChatGPTを利用してレビュー文言を生成し、プルリクエストに対してレビューのコメントを行います。
基本的なレビューの流れを以下に示します。
- ブログ執筆者がGitHubにプルリクエストを作成(または
/review
から始まるコメントを投稿) - GitHub Actionsが1のイベントをトリガーとして起動
- GitHubのAPIを介してプルリクエストの差分を取得
- 差分をもとにプロンプトを作成し、ChatGPTにリクエスト
- ChatGPTのレスポンスを元に、コメントの内容を作成
- GitHubのAPIを介してコメントをプルリクエストに投稿
これが一連の流れとなります。
ブログ執筆者は、このコメントを見て修正を行い、必要に応じて/review
から始まるコメントを投稿し、再度レビューを実行できます。
ChatGPTの選定理由
ChatGPTはこの分野の最前線にいるシステムであり、自然言語処理の能力が高いことが知られています。
弊社でも、すでにバックオフィスの支援を行うシステムにChatGPTを採用していました。
その経験から、ChatGPTは信頼性が高いと判断し、今回のシステムにも採用することにしました。
他のLLM(Large Language Model: 大規模言語モデル)として、ClaudeやGeminiなども候補としてありました。
以下に、各モデルの比較としてChatBotArenaでの現在(2024年7月末)のランキングを示します。
なお、ChatBotArenaは、様々なLLMを比較するためのプラットフォームです。人間に対して2つのモデルが生成した文章を提示し、どちらがより自然な文章を生成できるかを評価し、ランク付けをしています。
このように、ChatGPTは他のLLMと比較しても高い性能を持っていることがわかります。
一方で、それらの高性能モデルと比較したとき、ChatGPT(gpt-4o)は入力では100万トークンあたり2ドル、出力では100万トークンあたり5ドル程度費用が高くなってしまいます。(2024年7月末現在)
(なお、トークンとは、テキストを小さな単位に分割して処理するための概念であり、基本的にLLMの利用料金は入出力のトークン数に応じて決まります)
しかし、
- 今回のケースでは分量も1記事程度なので、トークン数はそれほど多くならない
- プルリクエストが出た際 と、再チェックの際にのみ利用される想定のため、頻度は高くない
といった想定から、この程度の費用の差はChatGPT以外のLLMを利用する理由にはならないと判断しました。
システム導入による効果・メリット
システムを導入することで以下のような効果・メリットがあると考えています。
- レビューの人的コストを削減できる
- 誤字脱字の見逃しを低減できる
- 本質的な問題に集中したレビューを行えるようになる
レビューをしたことがある方であればわかると思いますが、誤字脱字などの細かいミスが多いと、本質的な問題に目がいかなくなることがあります。
このシステムを導入することで、そのようなミスの指摘はAIに任せ、人間はより本質的な問題に集中できるようになります。
これにより人によるレビューの大変さが低減できるとともに、記事の品質向上につながると期待しています。
実装の詳細
このシステムはTypeScriptで実装し、JavaScriptに変換してGitHub Actionsで動作するようにしています。
使用しているライブラリは以下のとおりです。
- @actions/core: 1.10.1
- @actions/github: 6.0.0
- openai: 4.38.2
システムが指摘する内容について説明します。
システムは以下の内容について、それぞれ別のプロンプトを用いてレビューを行います。
- 構成に関する指摘
- 誤字脱字などの文章の問題点の指摘
- セキュリティに関する問題点の指摘
- コンプライアンスに関する問題点の指摘
流れとしては、まず差分全体を用いて、全体的・構成的なレビューを行います。
その後、差分のチャンクごとに、文章の問題点、セキュリティ、コンプライアンスの問題点がないかレビューします。
なお、ここでいうチャンクとは、プルリクエストを見たときに、差分として表示されているかたまりを指しています。
構成のレビュー
構成のレビューを行う際のプロンプト例を以下に示します。
system:
あなたは一流の技術記事の校正・校閲エキスパートで、とても丁寧かつ優しいレビュアーです。
あなたの役割は、提出された記事に対して価値ある簡潔なフィードバックを提供することです。
提示された記事の内容に対して、一般的なフィードバックや提案を行ってください。
これには、記事の全体的な構成、その他の側面に関する提案を含みます。
以下のガイドラインに注意してください:
- レビュアーに対する補足の提案は避けてください:
レビュアーに対する補足の提案は、このレビューの範囲外ですので、提案しないでください。
system:
レビュアーに対する補足: ~~~
user:
(差分全文)
なお、プロンプト中に「レビュアーに対する補足」とありますが、これは、コメントをトリガーとしてレビューを行う際、そのコメントの内容を補足としてプロンプトに追加しているものになります。
構成のレビューでは、ChatGPTのレスポンスをそのままコメントとして採用しています。
また、コメントの投稿の際には、Create an issue commentのAPIを利用しています。
差分のチャンクごとのレビュー
差分のチャンクごとのレビューを行う際のプロンプト例を以下に示します。
system:
あなたは一流の技術記事の校正・校閲エキスパートで、とても丁寧かつ優しいレビュアーです。
あなたの役割は、提出された記事に対して価値ある簡潔なフィードバックを提供することです。
あなたのレビューは、以下の点を優先してください:
- 誤字脱字の修正: 誤字脱字を見つけた場合は、それらを修正してください
- 誤解を解消する: 誤解を招く可能性のある箇所を見つけた場合は、それを修正してください
- 一貫性の確保: 一貫性のない表現を見つけた場合は、それを修正してください
- 表記揺れの修正: 単語の大文字小文字、漢字の閉じ開きなど、統一されていない表記があった場合は、それを修正してください
- 冗長な提案は避けてください: 既存の変更を注意深く確認し、すでに対処されている変更を提案しないようにしてください
以下のガイドラインに注意してください:
- originalの改変は避けてください: originalには元の文章そのままをコピーし、絶対に改変しないでください。
- レビュアーに対する補足の提案は避けてください:
レビュアーに対する補足の提案は、このレビューの範囲外ですので、提案しないでください。
- 問題が見受けられない場合は、何も提案しないでください。
- 追加されたコードの指摘に集中してください: 追加されたコード(先頭が+になっている行)に対する指摘に集中してください。
- 削除されたコードの指摘は避けてください:
削除されたコード(先頭が-になっている行)に対する修正は、このレビューの範囲外ですので、提案しないでください。
system:
レビュアーに対する補足: ~~~
user:
(差分のチャンク)
加えて、差分のチャンクごとのレビューでは、最終的にどの箇所の指摘か明示的にするために、ファンクションコーリングを利用してJSON形式でレスポンスを受けるようにしています。
ファンクションコーリングとは、ChatGPTに対して特定の関数を呼び出すためのJSONを生成させる機能です。(OpenAI Platform Docs: Function calling)
ファンクションコーリングのパラメータとして送っているJSONを以下に示します。
{
"type": "function",
"function": {
"name": "send_review",
"description": "レビュー結果を送る",
"parameters": {
"type": "object",
"properties": {
"reviews": {
"type": "array",
"items": {
"type": "object",
"properties": {
"original": {
"type": "string",
"description": "記事の元のテキスト。このテキストを参照して、提案を行ってください。絶対に元のテキストから改変しないでください。"
},
"suggestion": {
"type": "string",
"description": "記事を有意義に改善するための具体的な提案。"
},
"comment": {
"type": "string",
"description": "なぜこの提案をしたか、この提案を受け入れることにより何が改善されるかを丁寧に説明してください。"
}
},
"required": ["original", "suggestion", "comment"]
}
}
},
"required": ["reviews"]
}
}
}
このように、レビュー結果として、原文(original)、提案(suggestion)、コメント(comment)を出力するようにしました。
なぜこのような定義にしているかというと、GitHubのSuggestion機能を利用するためです。
Suggestion機能を利用することで、ブログ執筆者は簡単に修正が必要な箇所がわかり、また簡単に提案を採用できるため、ただ指摘のコメントを投稿するよりブログ執筆者の負担を軽減できます。
システムはChatGPTからレスポンスを受け取ると、差分の中からoriginalと一致する箇所を検索、どの行に対する指摘かを特定し、suggestionとして投稿します。
ファンクションコーリングの定義にそのまま行番号を含めれば良いと考える方もいらっしゃるかもしれませんが、現状の生成AIは何かをカウントするなど、正確な数値を必要とする処理を苦手としているため、このような作りにしています。
コメントを投稿する際のAPIとしては、構成的なレビューとは違い、Create a review comment for a pull requestを利用しています。
テストとフィードバック
私はこのシステムが有用か確認するため、前回、および前々回のQualiArtsエンジニアブログでこのシステムを試しま した。
以下に、その際に得られた結果を示します。
このように、誤字脱字の検出はできており、一部の問題点を指摘できていることが確認できました。
実際に著者やレビュアーの方からも、句読点や表記揺れの指摘はありがたいという声をいただきました。
一方で、いくつかの問題点・改善点が挙がりました。
不完全なJSON形式
はじめ、私はファンクションコーリングを利用せずに、プロンプト中に結果として求めるJSONの定義を記述していました。
それでもある程度は問題なく結果を得られていたのですが、ChatGPTのレスポンスが不完全なJSON形式で返ってくることも度々ありました。
不完全な場合、その内容を補完してから次の処理を行う実装も試みましたが、それにも限界がありました。
そこで、ファンクションコーリングを利用することで、この問題を解決しました。
ファンクションコーリングを用いても、稀に不完全なJSONが返ってくることもあると聞いていますが、自分の観測範囲では現状は問題なく動いているようです。
ですので、レスポンスをそのまま利用するのではなく、構造化した結果を取得し、システムの結果として利用する場合は、ファンクションコーリングを利用することをお勧めします。
開発コードの混入
弊社では、サービスの名称とは別に開発コードが存在します。
社内では開発コードでそのサービスを呼んでいるのですが、それが社外に出ることは情報漏洩につながるため避けないといけません。
プロンプトに開発コードを列挙し、それが含まれていれば指摘するように記述していたのですが、見逃してしまうことも度々ありました。
開発コードのように絶対に漏洩を避けるべきものであり、完全一致で調べられるものに関しては、通常の検索で対応したほうが良さそうということで、現在はそのような実装になっています。
部分修正後の全体レビューの需要
一度プルリクエストがマージされた後、その後の修正で想定されていない文字が含まれてしまうことがありました。
その際に、差分のある箇所のみではなく、差分のあったファイル全体をレビューしてほしいという需要があることがわかりました。
実装当初からファイル全文のレビューができないことはわかっていましたが、GitHubのpull requestを取得するAPIでは、差分のあった箇所のみを取得するため、ファイル全文をレビューするには、別途ファイルを取得するAPIにアクセスする必要があり、それほどその必要性もないと考えていたので実装はしていませんでした。
しかし、今回のテストを通して、やはり全文のレビューは必要であることがわかったので、追加で実装しました。
簡単に実装した内容について説明します。システムは以下の流れでファイル全文のレビューを行います。
- プルリクエストの差分を取得
- 差分のあったファイルのパスをリストアップ
- リストアップされたファイルパスごとに、ファイルを取得するAPIを使用してファイルを取得
- ChatGPTにファイル全文を含むプロンプトを送信してレビューを行う
このようにして、ファイル全文のレビューを行えるようにしました。
今後の展望
以下に、現在解決できていない問題点と今後の展望に関して記します
セキュリティ・コンプライアンスの問題についての指摘
現在、セキュリティ・コンプライアンスの問題についての指摘は、あまり正確とは言えません。
その理由として、ChatGPTの考えるセキュリティ・コンプライアンスの基準と、我々の考える基準が異なることが挙げられます。
例えば、ChatGPTが指摘した内容に以下のようなものがあります。
このように、我々としては著者名と社名を入れることは問題ないと考えているものの、ChatGPTの基準としては問題となっているため不要な指摘が生成されてしまっています。
また、似たような問題として、ブログの主目的であるツールの説明があると、社内で使われているツールを書くことはセキュリティ的な問題があるとして指摘される問題が発生しました。
これは一般 的には正しい指摘です。使っているツールがわかれば、そこからセキュリティホールを突かれる可能性があるためです。
しかし、このツールの説明を書くことがその記事のそもそもの主目的であるため、この指摘は不適切です。
この問題への対応は簡単ではなく、具体的に「自己紹介の指摘は不要」とプロンプトに記載しても、ChatGPTはその指摘を生成してしまうことがありました。
一般的な基準と、我々の基準の違いをどのようにChatGPTに理解させ、有用な指摘のみを行えるようにするか考える必要があります。
存在しない文言に対する指摘
プロンプトにはoriginalを絶対に変えないようにと記載していますが、これでも度々改変し、存在しない文言に対して指摘を行うことがあります。
これは、ChatGPTがoriginalを厳密には元の文章を参照して指摘しているわけではなく、originalを元に新たな文章(指摘)を生成しているために発生している問題です。
現状は原文に存在しないoriginalの場合は、その指摘を無視 するようにしていますが、このような指摘が多いと有用な指摘が得られなくなってしまいます。
そのため、originalに存在しない文言に対する指摘を抑制する方法を考える必要があります。
一度に入力する文章が長ければ長いほどその問題は顕著に現れていると感じるため、差分のチャンクを更に分解してChatGPTにわたすなどの方法も考えられますが、そうすると前後の文脈を考慮したチェックが行えなくなるため単純に短くすればよいというものでもありません。
内容の正しさの検証
AIにどこまでのレビューを求めるかにもよりますが、書かれている内容が正しいかの検証は行いたいと思っています。
これには大きく分けて以下の2種類があると考えています。
- 表記の誤り
- 内容の誤り
前者は、例えば、弊社QualiArtsであれば、先頭のQと間のAが大文字になっているのが正しいのですが、現状のシステムではそれを指摘できません(他の箇所では正しく表記されているのであれば表記揺れとして検出できるかもしれませんが)。
弊社 の名前を間違えるぐらいであれば大きな問題にはなりませんが、他社の名前を出す場合は表記の誤りは大きな問題になりえます。
後者は、例えば「2024年にリリースされたガールフレンド(仮)は…」という誤った文言があったとき、これに対してリリース年が誤っていることを指摘するのは現状のシステムでは難しいです。
これらについて、すべて解決することはおそらく不可能ですが、「この内容はあなたの持つ知識から正しいといえるか」というプロンプトを投げることで、ある程度は可能かもしれないと考えています。
一方で、それを全文言でやるのは流石に無理があるため、どのような単位で検証を行うかも検討する必要があります。
このように難しい問題ではありますが、内容の正しさの検証ができれば、このシステムはさらに有用なものになるため、なんとか良い方法を見つけたいものです。
おわりに
この記事では、ChatGPTを利用したブログレビューシステムの導入と実装について紹介しました。
また、実際のプロンプトの一部と、より有用な指摘を出力するための工夫について述べました。
今後は、このシステムを改善しつつ、このシステムを実装した経験をもとに、より業務を効率化するためのシステムを構築していきたいと考えています。
この記事が皆様の今後の開発において少しでも参考になれば幸いです。
なお、この記事の構成はChatGPTにより提案された内容をもとに作成され、この記事で紹介したシステムによってレビューされました。