ヌーラボの@vvvatanabeと申します。
OpenAI APIのクライアントライブラリとして、Goで書かれた「Go OpenAI」というOSSが有ります。これはOpenAI APIを効率的に利用するためのもので、最近私がメンテナとしての役割を担うこととなりました。
OSSのメンテナとしての経験やコミュニティでの活動は多くの技術者にとって新しい挑戦や学びの場となることが多いです。本記事では、私がGo OpenAIのメンテナとして関わるようになった背景や経緯、そしてその過程で得られた知見や経験について詳しくお話しします。
※本記事は「DAIMYO Meetup #2」でのセッション「CODE TO COMMUNITY 〜 Journey Through OSS with Go OpenAI 〜」でお話した内容をブログ化したものです。時間の制約から深く触れることができなかった部分を中心に、詳細にわたってご紹介します。
目次
OpenAI APIの概要
OpenAI APIは、OpenAI社により開発された自然言語処理を行うAIモデルを利用するためのWeb APIです。以下のような多彩な機能を提供しています。
- 文章の自動生成
- 質問への対応
- 文章の要約
- 言語の翻訳
- コードの生成
- 画像の生成
- 音声データのテキスト変換
多くのプログラミング言語でライブラリが提供されており、既存のシステムやアプリケーションへのAI機能の組み込みが容易です。
OpenAI API:
https://platform.openai.com/overview
Go OpenAIの紹介
Go OpenAIは、Go言語で実装されたOpenAI APIのクライアントライブラリです。このライブラリは、OpenAI APIだけでなく、Azure OpenAI ServiceのAPIも同一のインターフェースでサポートしています。現在、約6000のスターを獲得しており、非常に高い人気を誇っています。OpenAI APIのコミュニティライブラリのページでも紹介されています。
Go OpenAI:
https://github.com/sashabaranov/go-openai
以下のチャートはGo OpenAIのスター数の遷移です。ビットコインみたいなチャートになってます。ChatGPTの有料版がでる少し前くらいに爆発したようです。
コントリビュートした経緯 〜 OSSのGive&Takeの精神 〜
OpenAI APIを利用して面白いツールを作りたいと思ったのが始まりでした。私はGoが得意なため、良いGoのAPIクライアントを探していたところ、OpenAI APIのコミュニティライブラリページで「Go OpenAI」を見つけたのが出会いです。スター数、IssueやPull Requestが作られる頻度、リリースの頻度を見て、信頼性を感じたのでこれを使用することに決めました。
Go OpenAIのコミュニティは実際にとても活発でしたが、一人のメンテナが数多くのIssueやPull Requestに応じるのは非常に大変そうでした。
最近の数年で、OSSのメンテナのリソースの不足により、継続的なメンテナンスが困難になるという問題がインターネット上でも頻繁に取り上げられています。本当はメンテナンスを継続したいけれど、徐々に疲弊してその意欲やモチベーションが削がれていくのは自然な流れだと感じます。その問題を解決する手段として、金銭的な支援はもちろんのこと、コードへの貢献、ドキュメントの整備、既存Issueの調査・回答など、さまざまな方法が考えられます。
私たちは自分にできることを見つけ、その範囲でサポートしていくことが重要だと信じています。そういった思いもあり、OSSコミュニティにおけるGive&Takeの精神を胸に、私もコントリビュートしていくことにしました
さて、一口にコントリビュートするといってもどのようにコントリビュートするのか考えるのはなかなか大変です。Go OpenAIへの最初のコントリビュートから継続的な活動に至るまでの私の経験と学びを、次に紹介していきます。
最初のコントリビュート〜 Good First Issue 〜
まず最初に、Good First Issueとラベル付けされたIssueを探しました。Good First Issueは一般的に以下のような特徴や意図を持っています。
- そのリポジトリに初めて貢献する人々を対象としている。
- 内容は比較的シンプルで、導入部分として適している。
- プロジェクトの全体的な構造や設計を深く理解していなくても、十分に対応しやすい。
- どのように問題を解決すれば良いか、何を期待されているのかなど、明確な指示や背景情報が提供されることが多い。
Go OpenAIも例外なくGood First Issueが存在していたので、そのリファクタリング系のIssueに取り組みました。
最初のIssue:
Move some code into internal directory #304
リファクタリング対象がTODOリストで分割されていたので、レビューのしやすさを考慮して、それぞれ適度な粒度でPull Requestを分割しました。
- move marshaller and unmarshaler into internal pkg (#304) (#325)
- move request_builder into internal pkg (#304) (#329)
- move error_accumulator into internal pkg (#304) (#335)
Pull Requestがマージされていくうちに、なんとなくコードの全体像が見えてきました。
その後のコントリビュート
サポートされていないAPIの実装やバグ修正
次に、まだサポートされていなかったAPIを実装しました。これにより、OpenAI APIが公式に提供しているすべてのAPIをサポートすることができるようになりました。
軽微なバグも修正しました。Go OpenAIは、OpenAI APIとAzure OpenAI Serviceの両方をサポートしています。しかし、両者のレスポンス形式に微妙な違いがあるため、予期せぬ不具合が生じることがあります。これらの挙動の差異を吸収するためのコードを書く際には、時として泥臭く対応しなければなりません。以下のPull Requestはその例です。
- fix parsing error of api error response (#381) (#384)
- fix json marshaling error response of azure openai (#343) (#345)
余談ですが、Go OpenAIでは、テストカバレッジが少しでも低下するとマージができないように設定されています。カバレッジがすべての問題を解決するわけではありませんが、コードの品質を維持する方法としてとても明瞭です。そして、レビュアーが「テストを追加してください」と指摘しなくていいので、精神的にも負担が軽減されます。
テストコードのボイラープレートの削減
その他、テストコードに大量のボイラープレートが存在したため、ボイラープレートを関数にまとめるリファクタリングを行い、重複しているコードを大幅に削減しました。
refactoring tests with mock servers (#30) (#356)
インテグレーションテストの整理
OpenAI APIへのリクエストを行うインテグレーションテストが、他のユニットテストと同じファイルに書かれていたため、どのテストが何のためのものかが分かりにくい状態でした。さらに、予期せずにインテグレーションテストが実行されるリスクもありました。そのため、インテグレーションテストは専用のファイルにまとめ、Goのビルドタグを使用して、インテグレーションテストを実行する際には明示的にビルドタグを指定するように変更しました。
コードの貢献以外のサポート活動
コードの貢献だけでなく、他の人が行ったPull Requestの議論に参加したり、Issueでの質問に答えるなど、サポート的な役割も果たしました。小さな質問のIssueに即座に答えることで、多くの感謝を受け、それがポジティブな気持ちに繋がりました。ただ、多くのIssueは情報が不足しているため、詳細を確認するためのコミュニケーションが増え、それがコストとなっていると感じました。
コントリビューターからメンテナへ 〜 作者からの招待 〜
1ヶ月程度継続してコントリビュートした結果、Go OpenAIの作者であるAlexさんからTwitterでフォローされ、ダイレクトメッセージで感謝の言葉をいただきました。さらに、共同メンテナとして参加してほしいとプロジェクトへの招待を受けました。
正直、コントリビュートを始めた時は、メンテナとして活動したいとまでは思っていなかったのですが、コントリビュートを継続して様々な問題を解決するうちに、たくさんの人達からの「Thanks」が積み重なり自然と責任感のようなものが芽生えてきました。
私はその招待をとても誇りに思い、コードを通じて世界中の人々と繋がることがOSSの醍醐味だとあらためて実感しました。
メンテナとしての最初の目標 〜 プロジェクトの運営の効率化とコミュニケーションコストの削減 〜
メンテナになって最初の目標は以下の2つでした。
- プロジェクトの運営効率を上げる
- メンテナンスのコミュニケーションコストを削減する
OSSのメンテナンスはしばしば非営利で、多くの場合、少数の人々、時には一人だけがそれを行うため、時間と労力が限られています。また、IssueやPullRequestのテキストコミュニケーションはある程度のフォーマットがないと情報がまとまりにくい傾向にあります。Go OpenAIのプロジェクトでもそれは例外なく課題として感じていました。
メンテナとしての取り組み
具体的には以下の4つのアクションを実施しました。
- IssueとPull Requestのテンプレートを導入する
- 全てのIssueにラベルを付ける
- 重複した課題や解決済みだがクローズしていない課題を整理する
- 非ActiveなIssueを自動で閉じるGitHub Actionsを導入する
IssueとPull Requestのテンプレートを導入する
GitHubのIssueテンプレートは、Issue作成者が必要な情報を一貫して正確に提供できるようにする機能です。Markdownで作成され、異なる種類のIssue(例:バグ報告、新機能リクエスト)ごとにカスタマイズされたテンプレートを設定できます。
このテンプレート機能を導入して、新たなIssueを作成したりPull Requestを提出したりする際のフォーマットが標準化され、内容を理解しやすくなりました。
全てのIssueにラベルを付ける
全てのIssueに適切なラベルを付けることで、特定のラベルを持つIssueの検索とフィルタリングが簡単になり、追跡しやすくなります。GitHubでは標準で以下のラベルがあります。
- bug
- documentation
- duplicate
- enhancement
- good first issue
- help wanted
- invalid
- question
- wontfix
※それぞれのラベルの説明については以下のGitHub公式ドキュメントを参照してください。
https://docs.github.com/ja/issues/using-labels-and-milestones-to-track-work/managing-labels#about-default-labels
すべてのIssueをラベル付けしたことで、Issueの種類を一目で理解することが可能になりました。その他、副次的な効果ですが、すべての課題を精査したことで問い合わせの傾向が把握できました。これにより、なにを優先的にやるべきかなんとなく見えてきました。
重複したIssueや解決済みだがクローズしていないIssueを整理する
Go OpenAIのIssue Trackerに、重複したIssueや解決済みだがクローズしていないIssueが散見されていました。ひとつひとつは些細なことでも、積み重なるとメンテナンスやコミュニケーションの妨げになります。これらのIssueを整理することで次のような効果を期待できます。
- 作業の重複の削減:
重複するIssueが整理されることで、同じIssueに対して複数の人が無駄に取り組むことを防ぎます。これにより、リソースの無駄を削減できます。 - 生産性の向上:
未解決のIssueと解決済みのIssueが明確に分かれることで、どの問題に取り組むべきか、次に何をすべきかがはっきりします。これにより、作業の効率が向上します。 - コミュニケーションの効率化:
Issueが整理されていると、コミュニケーションが効率的になります。どのIssueが現在問題として残っているのか、どのIssueに対するフィードバックや提案が必要なのかが明確になります。 - 新しいコントリビューターの導入の助け:
新しいコントリビューターは整理されたIssueリストを通じて、プロジェクトにどのように貢献できるかを迅速に理解できます。 - モチベーションの維持:
解決済みのIssueがクローズされることで、達成感を得られます。これは、コントリビューターのモチベーションを維持する上で重要です。
Issueを整理した結果、OpenなIssueの数が約70個から25個に減少しました。これで、プロジェクト管理がより効率的になり、メンテナンスのコミュニケーションコストが削減されたように感じています。
非アクティブなIssueを自動で閉じるGitHub Actionsを導入する
以下のGitHub公式ドキュメントで紹介されているGitHub Actionsを使用しました。
https://docs.github.com/en/actions/managing-issues-and-pull-requests/closing-inactive-issues
例えば、Issueが30日間非アクティブな場合、コメントをして参加者にアクションを促すことができます。その後、もし14日以上追加のアクティビティがなければ、Issueをクローズすることができます。
これにより、長期間放置されたIssueを自動的に減らし、必要なIssueのみに焦点を当てることができます。メンテナが自分で声をかけなくすむので心理的にも作業的にもとても楽になります。
まとめ
今回のコントリビューションはPull Requestだけではなく、プロジェクト管理の側面でも大きな成果を得ることができました。
最初はGo OpenAIのユーザとして始まり、その後積極的なコントリビューターになり、最終的にメンテナとなる過程は、ソフトウェア開発の深い理解を得るための重要なステップだったと思います。これは、「CODE TO COMMUNITY」という本記事の元になったセッションのタイトルのとおり、1行のコードが開発者コミュニティの活動へと変化していく旅のように感じられました。
この記事が皆さんのオープンソースプロジェクトへの参加を考えてみるきっかけになることを願っています。また、私たちヌーラボは、サービス開発においてOSSから大いに恩恵を受けています。OSSからの恩恵をただ享受するだけでなく、これからも継続的にOSSコミュニティへの貢献を続けていきたいと考えています。
謝辞
Go OpenAIのメンテナンスを継続する上で、著者であるAlexさんの尽力と貢献は欠かせません。彼に深い敬意と感謝を述べたいと思います。
以下はAlexさんのGitHubスポンサーページのリンクです。スポンサーになることもOSSへの貢献方法の中で非常に重要な方法の一つなので、是非ご検討いただければ幸いです。