giftee Tech Blog

ギフティの開発を支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信しています。

業務で使っていたgemに欲しい機能があったのでPRを出したらマージされた話

eyecatch

こんにちは、ギフティでエンジニアをしているaokiです。法人向けeギフトサービス「giftee for Business」の開発を担当しています。

今回は、業務で使っていたgemに欲しい機能があったためOSSにPRを出したらマージされたので、その経緯と学びを紹介します。

背景

業務でLLMを活用した機能検証を行っており、複数のプロバイダー(Gemini・OpenAI・Anthropic)の回答を比較する必要がありました。

AI連携の実装にあたり、チームで検討した結果ActiveAgentというgemを採用しました。

ActiveAgentを採用した理由

ActiveAgentは、ActionMailerと同様のパターンをLLMとの連携に応用したRails gemです。採用にあたっては大きく3つの理由がありました。

1. Railsで馴染みのある書き方でLLMを扱える

class TravelAgent < ApplicationAgent
  generate_with :anthropic

  def search(prompt_text:)
    prompt(prompt_text)
  end
end

# ActionMailerの.deliver_nowと同じ感覚で書ける
TravelAgent.search(prompt_text: request.prompt).generate_now

ActiveAgentはActionMailerのようにクラスとメソッドに処理を定義して、.generate_nowで実行するインターフェースになっています。

プロバイダーやモデルの設定もconfig/active_agent.ymlに集約されているので、呼び出し側はLLMの差異を意識することなくRailsで馴染みのある書き方でLLMとやりとりを行うことができます。

2. 信頼できるコントリビューターが関わっている

palkan(AnyCable作者)やmarcoroth(Hotwire/Stimulus エコシステムの中心人物)など、Railsコミュニティで実績のある開発者が参加しています。Railsの規約から大きく外れない設計になっているだろうという安心感がありました。

3. 発展途上だからこそOSSコミュニティに貢献できる

成熟しきったgemではプロダクション利用を通じたフィードバックの機会は少ないですが、ActiveAgentはまだ発展途上です。「便利なものを一緒により便利にしていく」という関わり方ができることをポジティブに捉えて採用を決めました。

問題:Geminiプロバイダーが未対応だった

仕様を確認しながら実装を進めていくうちにActiveAgentがGeminiプロバイダーに対応していないことに気が付きました。

OpenAI・Anthropic は公式サポートされていましたが、Geminiは未対応でGitHubのIssuesにも同様のリクエストが上がっていました。

回避策としてFaradayを使ってGeminiのREST APIを直接叩く実装にしましたが、

  • Geminiだけ実装が分かれてしまう
  • ActiveAgentの抽象化のメリットが崩れる
  • 保守コストが増える

といった問題がありました。

Issueも上がっていたため、せっかくなら自分で修正してみようと思い、思い切ってPRを出してみることにしました。

実装方針:GeminiのOpenAI互換APIを活用する

PRの実装にあたり、まずActiveAgentのコードを読み込み、実装方針を整理しました。 特に他の既存プロバイダーを確認したところ、OllamaやOpenRouterがOpenAIのAPI仕様に乗っかる形で実装されていました。

GeminiもOpenAI互換のAPIエンドポイントを提供していることが確認できたため、同様のアプローチで実装できることが分かりました。

この調査結果を踏まえて、実装では以下のようにGeminiProviderOpenAI::ChatProviderのサブクラスとして実装し、Gemini固有の設定だけを上書きする方針をとりました。

module ActiveAgent
  module Providers
    class GeminiProvider < OpenAI::ChatProvider
      def self.service_name
        # Gemini固有のサービス名
      end

      def self.options_klass
        # Gemini固有のオプション定義
      end

      def self.prompt_request_type
        # Gemini固有のリクエスト型
      end
    end
  end
end

API通信の実装はOpenAIプロバイダーをそのまま流用できるため、差分は最小限に抑えられました。提出したPRはこちらです: PR #324: Add Gemini Provider Support

OSSへのPRは初めてでしたが、無事マージしてもらうことができました。

やってみて思ったこと

OSSへのハードルが下がった

OSSへの貢献はしてみたいと思っていましたが、なかなか一歩踏み出す勇気がありませんでした。今回は自分のプロダクトで使用しているgemであり、これによって自分のプロダクトにも還元される点が、強いモチベーションになりました。 今回、コードを読み、修正を行ってPRを出したことで、次へのハードルが下がった気がします。

gemの見え方が変わった

今までgemは「外部のライブラリ」という感覚で、中身を深く読むことはあまりありませんでした。しかし今回の経験を経て、gem もプロダクトを構成する一部分 という感覚に変わりました。何をしているのかをちゃんと知りたいし、貢献できる箇所があるなら積極的に貢献していきたいと思うようになりました。

最後まで読んでいただきありがとうございました。ActiveAgentを作ったJustin Bowenが RubyKaigi 2026のスピーカー として登壇予定とのことなので、実際にPRを送ったgemの作者の話を直接聞いたり、話したりできると思うと、今から楽しみです。