Classi開発者ブログ

教育プラットフォーム「Classi」を開発・運営するClassi株式会社の開発者ブログです。

Amazon GuardDutyの保護プランで実現する脅威検知とマルウェア対策

こんにちは、プロダクト本部プラットフォーム部SREチームの坪井(@boy2)です。

AWSをご利用のみなさん、Amazon GuardDuty (以下 GuardDuty)は活用されていますか? GuardDutyの設定を有効にしただけで、安心していませんか?それだけではGuardDutyの機能を十分に活用できているとは言えません。この記事では、GuardDutyの基本的な機能から、さらに効果的な活用方法までを解説していきます。GuardDutyをすでに利用されている方も、これから利用を検討されている方も、ぜひ最後までご覧ください。

要約

  • GuardDutyはAWSの脅威検知サービスであり、標準機能に加えて保護プランで検知範囲を拡大できる
  • 保護プランにはS3、EKS、ランタイム、マルウェア、RDSに対するものがある
  • ClassiではS3マルウェアProtection、RDS Protection、Lambda Protectionを有効化し、セキュリティを強化している
  • S3マルウェアProtection では、マルウェア検知時にオブジェクトにタグを付与し、アクセス制御に利用できる
  • GuardDutyの運用には、検出結果の通知、分析、対応策の実施といったルールを定めることが重要
  • 保護プランの導入前には、費用を見積もること

GuardDutyとは

GuardDutyは、AWSアカウントやワークロードを継続的にモニタリングし、悪意のあるアクティビティがないかを確認して脅威を検出するサービスです。標準機能は、AWS CloudTrail 管理イベント、Amazon VPC Flow Logs、DNS クエリログをモニタリングします。

特徴の一つとして、追加のインフラストラクチャのデプロイや保守が不要であることが挙げられます。導入はとても簡単で、ワンクリックで導入でき、フルマネージドであるため、インフラ管理やスケーラビリティを心配する必要がありません。

GuardDutyの保護プランとは

GuardDutyは標準的な基本データソースを監視できる便利なサービスですが、様々なリソースに対する保護プランを活用することで、脅威検出の対象範囲を大幅に拡大し、セキュリティを強化することができます。

GuardDutyコンソールを開くと、左側のメニューに以下の「Protection plans(保護プラン)」が表示されます。

  • S3 Protection:バケットへの不正なアクセスやデータの改ざんを検出
  • EKS Protection:EKSクラスターの不正なアクティビティを検出
  • 拡張脅威検出:AWSアカウント内の様々な情報源から、複数種類のAWSリソースにまたがり時間をかけて少しずつ進められる複雑な攻撃を検出
  • ランタイムモニタリング:EC2インスタンスやコンテナワークロードの不正なプロセスやネットワークアクティビティを検出
  • マルウェア Protection
    • EC2インスタンスにアタッチされたEBSボリュームをスキャンし、マルウェアの存在を検出
    • S3マルウェア Protection:Amazon S3にアップロードされたオブジェクトのマルウェアを検出
  • RDS Protection:データベースへの不正なアクセスを検出
  • Lambda Protection:不審な呼び出しパターンや悪意のあるペイロードを検出

GuardDutyを有効化すると、一部の基本的な保護プランが自動的に有効になります。ただし、追加の保護プランはオプションであり、個別に有効化する必要があります。最新の情報と詳細については、AWS公式ドキュメントをご参照ください。 これらの保護プランには、多くの場合30日間の無料トライアルが提供されています。無料トライアルを活用することで、GuardDutyの機能を実際に体験し、自社の環境に最適な保護プランを選択できます。

Classiでのセキュリティ強化

保護プランを見直す

Classiでは、セキュリティ体制をより強固なものとするため、GuardDutyの保護プランの見直しを行いました。既存の保護プランに加え、未活用のプランの中に、Classiのシステム環境においてセキュリティリスクを低減する上で特に有効と考えられる、以下の3つのプランを選定し、有効化しました。

  • S3マルウェア Protection:特定アカウントのバケットで有効化
  • RDS Protection:全AWSアカウントで有効化
  • Lambda Protection:全AWSアカウントで有効化

ここでは、S3マルウェアProtection について例にあげて説明していきます。これは2024年6月に一般リリースされた保護プランです。オブジェクトをスキャンしたあとにS3にタグを付けて、そのタグの付いたオブジェクトに対するアクセス制御を実施することができます。 例えば、マルウェアが発見されたオブジェクトを「アクセス禁止」にしたり、lambda関数等の実装が必要ですがタグが付与されたタイミングで「オブジェクト削除」することも可能です。

プランを有効化して試す

S3マルウェアProtection を有効化した際、マルウェア検知されどのような動作をするのか試してみました。EICARテストファイルをS3バケットへアップロードして実施します。 EICARテストファイルは、ウイルス対策ソフトの動作を安全に確認するための無害なファイルです。以下の手順で簡単に作成できます。 テキストエディタを開きます 以下の文字列をコピーして貼り付けます

X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*

ファイルを任意の名前で、拡張子を.comとして保存します。 このファイルをウイルス対策ソフトでスキャンすることで、検出機能や警告動作などをテストできます。

S3マルウェアProtectionの有効化

試しに、作成したEICARテストファイルをS3バケットにアップロードします

% aws s3 cp eicar.com s3://classi-malware-protection-test/

アップロード後、即時にスキャンが実行されオブジェクトに、キー「GuardDutyMalwareScanStatus」、値「THREATS_FOUND」にてタグ付されました。

EICARファイルスキャン後のタグ

正常なファイルをアップロードした際の値は「NO_THREATS_FOUND」です。

画像ファイルスキャン後のタグ

タグキーと値のペアを利用してユーザーのアクセスをコントロールする例については、AWSドキュメントを参照ください。 GuardDutyの検出結果です。

GuardDutyの検出結果

詳細を開きます。

脅威の内容詳細

権限設定に関する注意点:スキャンされたオブジェクトにタグが付与されるため、リソースにオブジェクトを操作する権限がないとオブジェクトのコピーに失敗します。オブジェクトを操作する場合には、操作元のIAMロールポリシーにs3:GetObjectTagging と s3:PutObjectTagging の権限が付いていることを確認ください(参考)。

GuardDutyの運用ルールを決める

GuardDutyの運用を始める前に、運用ルールを定義しておくことを推奨します。ClassiではSREチームが中心となってGuardDutyの運用を担当していますが、セキュリティに関わる重要な情報であるため、SREチームのメンバーに限らず、社内の誰もが参照できるように運用ルールを詳細に文書化し、共有しています。 検出結果の通知:Slackアラートチャンネルに検出結果を通知

  • 重要度「高」以上を通知する
  • 検出結果の分析:検出結果の詳細を分析し、誤検出か否か、および脅威の性質を特定する
    • 重要度「重大」については可及的速やかに「高」については優先的に対応する
    • 保護プロテクションにおいては悪意ある攻撃や不正利用の可能性が高いため
  • 対応策の実施
    • 脅威に対応するために必要な対策(侵入防止、アクセス制限、システム修復など)を実施する

脅威の重要度と内容によっては既に不正なアクセスが行われている可能性があるため、アラート通知を設定してすぐに気付く仕組みを取り入れます。脅威は、重要度が中程度以下であっても、定期的に検出内容を確認し影響を受けているリソースを速やかに確認するのをおすすめします。

保護プランの費用を見積る

いよいよ運用開始ですね!でも、その前にちょっと待ってください。 費用を見積もることで、後々「こんなはずじゃなかった…」という事態を避けられます。東京リージョンをご利用の場合は、以下のような料金体系になっているので、事前に確認しておくことをおすすめします。

S3マルウェアProtection の料金  (東京リージョン)

  • GB あたり/月:USD 0.1185/GB
  • 評価されるオブジェクト数/月:USD 0.282/1,000 個のオブジェクト *最新の費用は公式ページにてご確認ください。

費用算出例 例えば、東京リージョンにて1ヶ月あたり100GBのデータをスキャンし、100,000個のオブジェクトを評価する場合、以下の費用が発生します。

  • データ量による費用:0.1185USD/GB * 100GB = 11.85USD
  • オブジェクト数による費用:0.282USD/1,000オブジェクト * 100 = 28.2USD
  • 合計費用:11.85USD + 28.2USD = 40.05USD

したがって、このケースでは1ヶ月あたり約40.05USDの費用が発生します。S3バケットのデータ量やオブジェクト数に応じて、費用は大きく変動する可能性があるため、事前にAWSの料金計算ツールを活用し、詳細な見積もりを行うことをおすすめします。 また、S3のStorage Lensを有効化しておくと、無料の範囲内で過去14日間のS3バケットのオブジェクト増加数とストレージの合計サイズが閲覧できるため、費用の算出に役立ちます。

さいごに

GuardDutyの保護プランは、脅威検知の範囲を広げ、よりセキュリティの高い環境を構築するために非常に有効な手段です。ぜひ、ご自身の環境に合わせて活用してみてください。

狼少年と化していたSentryが機能するようになるまで

こんにちは。エンジニアのすずまさです。

弊社では、エラー監視ツールとしてSentryを利用しています。

私のチームではSentryをうまく活用できていない状態が続いていましたが、最近は運用負荷を軽減しつつ障害を早期発見するなど、以前よりも運用が改善されてきたので今回はその紹介をします。

当時の課題「とにかくエラーが多すぎる」

トリアージがほとんどされておらず、S/N比が低くなっていました。 そのため、ノイズが多く重要なエラーを判別するのが難しい状況でした。

具体的には以下のようなエラーがノイズとなっていました。

  • ユーザに悪影響はないが、ハンドリング漏れで大量に発生しているエラー
    • ex. API通信で status code 0 が返ってきた場合1 のハンドリング漏れによるエラー
  • 原因がはっきりせず、ユーザ影響の有無を判断できないエラー
    • ex. Stack Traceに情報がなかったり、Source Mapsの導入ができていないなどの理由で発生箇所が特定できないエラー

当時、毎日24時間以内に発生したエラーを確認するようにしていましたが、数が多すぎて想定以上に時間をかけてしまうことも多く、ひとつひとつ確認するだけで疲弊していっていました。

ノイズを減らす

まずは以下を行い、ノイズを減らそうと努めました。

  • 積極的にArchiveする
  • Issue Groupingを活用する

順番に解説していきます。

積極的にArchiveする

ArchiveはSentryのアラートを一時停止する機能です。

(Google翻訳) アーカイブは、緊急性が低い、または自分やチームに関係のない、ノイズの多い問題に適しています。アーカイブすると、issueのステータスがアーカイブ済みに変更され、issueが「未解決」から「アーカイブ済み」タブに移動します

https://docs.sentry.io/product/issues/states-triage/#archive

Archiveを使うと、エラー自体は確認できるものの、普段は目に入らないようにできます。

また、Archiveには永久的なものだけでなく、期間や回数の条件をつけることもできます。 例えば「今から100回発生するまで」「1時間のうちに10回発生するまで」「今からさらに1ユーザに影響が出るまで」のような条件の指定が可能です。

この機能を使い、先ほど例に挙げたノイズとなっていたエラーは次のようにトリアージしました。

  • ユーザに悪影響はないが、ハンドリング漏れで大量に出ているエラー
    • → エラーハンドリングのチケットを作成した後、永久にArchive
  • 原因がはっきりせず、ユーザ影響の有無を判断できないエラー
    • → 様子を見るため、内容に応じて条件付きでArchive

こうしたトリアージについては以下の記事でも紹介しています。

tech.classi.jp

Issue Groupingを活用する

同じようなエラーでも、発生要因が微妙に異なると別のエラーとして検知されてしまうことがあります。 手動でマージすれば一つのエラーとして扱うことができますが、別エラーとして検知されるたびに毎回マージするのは面倒です。

こうした手間を削減するため、見覚えのあるエラーが通知されたらIssue Groupingを設定するようにしました。

https://docs.sentry.io/concepts/data-management/event-grouping/

エラー内容や、スタックトレースの発生箇所をもとにグループ化するよう設定できます。詳しくは以下をご覧ください。

https://docs.sentry.io/concepts/data-management/event-grouping/fingerprint-rules/

Issue Groupingの設定ページ

重要なエラーだけピックアップ可能にする

上述した取り組みにより、少しずつノイズは減っていきました。しかし、頑張ってトリアージを続けても中々すべてのエラーを処理しきれず、運用負荷は高いままでした。 そこで、既存のエラーをすべて確認するのではなく、重要なエラーだけピックアップして確認するようにしました。

重要なエラーとしては、以下のようなものが挙げられるかと思います。

  • 新規エラー
    • 直近のリリース起因で発生した可能性がある
  • 急増エラー
    • 障害の可能性がある

上記の2つを検知する仕組みをSentry Alertsで設定し、検知したらSlackに通知するようにしました。 特に、急増エラーは障害の可能性があるため、エンジニアにメンションするよう設定してすぐ対応できるようにしました。

https://docs.sentry.io/product/alerts/

新規エラーを検知するSentry Alerts

急増エラーを検知するSentry Alerts

これにより、エラーを確認する範囲が以前より絞られ、最低限Slackの通知だけチェックすれば良くなったため、運用負荷が大幅に軽減しました。 また、急増エラーの通知により、実際に障害を早期発見できたこともありました。

現在の運用

こういった取り組みを経て、現在私のチームでのSentryの運用は次のようになっています。

  • 日ごとの当番制を導入し、ランダムで指名された人がエラーを確認する
  • 当番は以下を行う
    • その日Slackに通知されたエラーを全て確認し、SlackのスレッドおよびSentryのActivityに調査ログを残す
    • 一週間のエラー一覧を確認し、エラー数の推移を見て気になるものがあればそれも確認する
  • 急増エラーは、当番に限らず気づいた人が即時対応する

Issues画面からTime Rangeで絞り込み検索後、Graphカラムから推移を確認できる

また、詳細な運用手順やTipsについてはドキュメントにまとめ、新規参入者が理解しやすいように整備しています。

まとめ

以上、狼少年と化していたSentryが機能するようになるまでの紹介でした。

現在の運用を始めてから一年弱経ちますが、日々の運用によりノイズはどんどん減ってきており、調査ログも蓄積されてきたため、より一層運用を効率良く回せるようになってきた実感があります。

Sentryの運用で悩んでいる方の参考になれば幸いです。ここまで読んでいただきありがとうございました。

Prompt Engineering勉強会をやった

こんにちは。データサイエンティストのオウです。先日、チームで生成AIのPrompt Engineeringに関する勉強会を実施しました。本記事ではその勉強会について紹介します。

なぜ取り組んだか?

現在、生成AIの時代が本格的に到来しています。Classiでは以前からAI技術を活用し、英語の自動作問技術を開発してきましたが、今後は生成AIを活用することでさらに大きな価値を生み出せると考えています。

そのためには、生成AIの全体像を把握し、適切に活用することが重要です。一方で、生成AIの制限やリスクについても考慮しなければ、安全で信頼できるサービスを提供することはできません。生成AIを効果的かつ安全に活用していく上で不可欠な技術の一つがPrompt Engineering です。

そこで、データサイエンスチームで Prompt Engineering の勉強会を開催しました。参考資料として『Prompt Engineering Guide』を活用し、生成AIに関する幅広い知識を学びました。

なぜこの本を選んだのか?

『Prompt Engineering Guide』は GitHub 上で公開されているガイドで、194人の貢献者(2025年3月12日時点)によって執筆されています。この本は、多くの論文を基にしており、生成AIを活用する上で重要な技術について詳しく解説しています。

このガイドでは、異なるタスクでより良い結果を得るためのプロンプト設計技術を紹介しており、Zero-shot や Few-shot、Chain-of-Thoughtなどの基本技術は一通り網羅されています。また、さまざまな応用例を通じて生成AIの活用方法を説明し、タスク別の具体的なプロンプト例を提供する PromptHub も存在します。さらに、生成AIのさまざまなモデルの概要や、それぞれの特性について紹介されているほか、利用時のリスクや制限についても深く掘り下げられています。

このガイドは継続的に更新されており、新しい技術や研究成果が追加されるため、常に最新の知識を得ることができます。そのため、今回の勉強会では、参加者全員が意識的に新しい更新内容もチェックしながら学習を進めました。

どのように読み進めたか?

隔週で1時間の読書会を実施しました。データサイエンスチームの3人が担当章を事前に読み、共有したい内容をまとめて発表しました。

各回の発表では、担当者が事前に章の内容を整理し、重要なポイントを抜粋して共有しました。本の内容は基本的に論文のサマリー形式であるため、結論のみを紹介することが多いものの、特に興味深いトピックについては原論文を読み込み、その内容をより深く掘り下げて議論しました。結果として、毎回の読書会は議論が活発に行われ、1時間がギリギリになるほど充実した時間となりました。

勉強会のチャットログの一部。参加者の名前伏せてますは伏せてます。

当初は半年間で11回の予定でしたが、本の内容の更新や追加情報の整理が必要になったこともあり、結果的に1年間継続することになりました。さらに、読書会の内容をドキュメント化し、チームメンバー以外の関係者にも共有しました。これにより、勉強会に参加していないメンバーも、必要な時に知識を活用できるようにしました。

得られたこと

今回の勉強会を通じて、Prompt Engineering の技法やその背景理論を勉強できて生成AIの活用方法についても理解が深まりました。目的の出力を得るために、どのようにプロンプトを設計すればよいのかを学びました。単なるプロンプト入力にとどまらず、エージェント開発についても知見を得ることができました。例えば、外部知識を活用する RAG 技術は、知識を問う問題の自動作成に役立つことが分かりました。

出典:Prompt Engineering Guide

また、論文を読むことで、プロンプトの設計方法だけでなく、その背景にある理論や技術の根拠についても理解が深まりました。How だけでなく、What と Why についての知識も身につき、より理論的にアプローチできるようになりました。

さらに、さまざまなモデルについて学習し、それぞれの性能や違いを把握することができました。

(出典:Prompt Engineering Guide)

Prompt Engineering Guide

今回の勉強会で得た知識を基に、社内検討中の企画に対する生成AIの活用方法についてより具体的な検討を進めることができました。

感想

技術を学ぶだけでなく、Classi における活用シーンも検討することができ、良い雰囲気の中で勉強が進められました。また、本の後半を読む際に、前の章が更新されていることがあり、技術の進化の速さを実感しました。

特に、生成AIのリスクに関する部分は関心が高く、企業として責任を持つためには、生成AIの性能だけでなく、制限やリスクを理解しながら技術を活用することが重要であると再認識しました。

今回の勉強会を通じて、生成AIの可能性と課題の両面を深く理解することができました。今後も継続的に学びながら、実際のプロダクト開発に活かしていきたいと考えています!

ノンデザイナーの開発チームで『ノンデザイナーズ・デザインブック』読書会を行いました

開発グループ2(通称:kobitoチーム)のチームリーダーをしている前川です。今回は、チームメンバーを中心に『ノンデザイナーズ・デザインブック』という本の読書会を行いました。 kobitoチームでは以前『ルールズ・オブ・プログラミング』の読書会を行いました。その後、Angular コーディングスタイルガイドを読む会を行い、今回の『ノンデザイナーズ・デザインブック』を行いました。

『ノンデザイナーズ・デザインブック』

『ノンデザイナーズ・デザインブック』は、デザインの基本原則をわかりやすく解説した入門書です。コントラスト・反復・整列・近接という4つの原則を中心に、非デザイナーでも見やすく美しいデザインを作る方法を学べます。プレゼン資料やWebデザインなど、実践的に役立つ内容が詰まっています。

マイナビの『ノンデザイナーズ・デザインブック』紹介ページ

20年以上愛されている、デザインの定番書です。デザインはデザイナーだけに必要な要素ではありません。いまやエクセルやワード、パワーポイントを使って資料を作る際にも重要となります。読みやすいデザイン、伝わるプレゼン資料、わかりやすいレイアウトを作りたい方におすすめの一冊です。

「20年以上愛されている」とあるとおり、長く読まれている本です。書名の『ノンデザイナーズ・デザインブック』をキーワードに指定したGoogleトレンドを見てもロングセラーな感じがうかがえます。

ちなみに私がこの本を購入したのは2016年6月でした。

きっかけは日々の開発から

私たちのチームは社内で利用する学習コンテンツ管理システムを開発しています。機能の追加などでUIを新しく組んだり、変更したりするときは、タスクを担当したメンバーがシステムを一緒に担当しています。みんなノンデザイナーなので、画面やモックを見ながらディスカッションをしていました。 その中で、以下の状態になると良いのではということで、@lacolacoの勧めもあり読書会のテーマに採用しました。

  • 全メンバーが一定のデザインの知識を持っている
  • デザインに関するチーム内での共通の基準が存在する

読書会の進め方

全体的な方針

  • 対象はPart1の基本的な項目のみとする
  • 各回1Chapterを取り上げる

実際に書籍ではPart3までありますが、まずは「基本」をチームで一緒に押さえようということでPart1のみを対象に限定しました。

会の進行

各回30分で進行しました。1Chapterごとに担当者が15分程度で要約を発表し、残りの時間で感想やディスカッション、書籍内の課題にトライするなどを行いました。他に、そのチャプターの内容が実際にチームで作っているシステムにどう当てはまるのか、という話もしました。

実際のシステムの画面を例として

勉強会を行ってみて

メンバーの感想

有名な4つの原則、「近接」「整列」「反復」「コントラスト」はさすがに印象的でしたが、その後の色のチャプターも見逃せない内容でした。特にカラーについてのChapter7の内容はどのメンバーも新鮮だったようです。

ノンデザイナーが読む書籍として「専門用語など変に高度な内容になってなかったのも良い」「主題が明確になり、具体的な例とともに説明されているので、理解しやすい」「内容的にもスラスラ読みやすく且つ理解しやすく、納得感も強かった」など、参加したメンバーも理解できたようでした。

専門外ということもあり「なんとなく」とらえていたことを、論理的に言語化された形で提示されたことに感動の声もありました。

今後に向けて

振り返り会の中で、作っているシステムに活かせるところなどをディスカッションして、チケットを切ったりしました。たとえば「反復」の原則から考えると、一貫していないUIや、色の持つ意味の確認などが上がりました。

勉強会の振り返りで登録されたチケット達

まとめ

『ノンデザイナーズ・デザインブック』は有名な本なので、読んだ人や概要を知っているという人も多いと思いますが、ノンデザイナーズなチームで一緒に読むというのは、基準や知識などを揃えるという効果があるのでおすすめです。

スマートフォンアプリの改善、進めます

プロダクト本部のディレクター、安田です。
2024年6月からアプリ改善の担当をしています。

私がアサインされる直前に組織変更があり、ホームアプリの改善はほぼ新しいチームでのスタートとなりました。

先日、第一弾の改善として「ホームアイコンの並べ替え」と「カラーテーマの変更」の2つの機能をリリースしましたので、その経緯をご紹介したいと思います。

社として何をやるべきか?チームは何をやりたいのか?

最初に取り組んだのは、もちろんチームメンバーとの関係構築です。
ただし、今回はその話は省略し、以下の2点に絞って説明します。

  • Classiホームアプリを社としてどうするべきか?
  • チームメンバーは何をやりたいのか?

ほぼ新規チームで右も左も分からない状態でしたので、まずはお客様からいただいている要望をもとに改善に着手しようと決めました。

蓄積された改善要望を確認すると、次のようなものがありました。

  • 戻るボタンをなんとかしてほしい
  • ダークモードがほしい
  • 学習状況をトップに表示してほしい
  • などなど

どれも重要な要望ですが、アプリチーム単独では改善することができないものが多く、他チームの作業中心となってしまうことから、現段階では対応が難しいと判断しました(本当に申し訳ありません)

一方で、チームメンバーと話し合った結果、「メンバーのやりたいこと」も規模の大きいものが多いことが分かりました。

例えば・・・

  • マルチプラットフォーム化
  • 通知バッジ
  • 完全な新機能
  • などなど

将来的にはこれらの要望を実現し、メンバーのやりたい施策も形にしたい。
そのためには、まずチーム力の強化が必要だという結論に至りました。

今できることをやって、チームを強化する

チーム力を強化するとはいえ、お客様の要望に応えることが最優先です。
そこで、比較的短期間かつ低リスクで実現可能な以下の2つの課題に取り組むことにしました。

  • ホームアプリのアイコンを並べ替えたい
  • アカウントを切り替えた際に、誰のアカウントかすぐ判断できるようにしたい

並べ替え機能はそのまま実装しましたが、2つ目の課題についてはアカウントごとにカラーテーマを変更できる機能を追加することで対応しました。

これらは幸いトラブルなくリリースすることができましたが、予想外の反応がありました。

学校からの意外な反応

リリース後、想定とは全く異なる反応が寄せられました。
当初、アイコン並べ替え機能がメインで、カラーテーマ変更は「おまけ程度」と考えていたのですが…。

なんと、カラーテーマ変更機能の利用者数がリリースからわずか2ヶ月で10万人に迫る勢いに。
生徒の皆さんからは「色を変えられるようになって嬉しい」という声や、「アイコンも色が変わるともっと嬉しいです!」といった要望もいただきました。

過去の記録を見ても、これほどポジティブな反響をいただいたことはありませんでした。

カラーテーマを変更した現在のClassi

ちなみに、特に人気だった色は「ピンク」でした!

まずやってみる、その後反省する(そして、たまには成果を喜ぶ)

今回の教訓は、「規模の小さな改善でも、まずはやってみる価値がある」ということです。

もちろん失敗もあります。実際、今回の改善でもバグを見落としてしまい、一部でクラッシュが発生しました(幸いなことに、すぐに修正することができました)

しかし、今回のリリースを通じてチームとして一丸となることができ、さらに周囲の信頼を得るきっかけにもなりました。

少しずつですが、チーム力の強化に成功しています。

今後の展望

次はもう少し大きな改善に挑戦する予定です。

ただし、生徒さんからいただいた「アプリのアイコンも色が変わるともっと嬉しいです!」という要望は最優先で対応を予定しています。

このブログが公開される頃には、すでにリリースされているかもしれません。

tetoru開発部にジョインしました。

こんにちは。tetoru開発部に所属しているエンジニアの中島です。

弊社には現在、主に高校をターゲットとする「Classi」というサービスと、主に小中学校をターゲットとする「tetoru」というサービスが存在します。 会社やサービスについて詳しくは下記資料を御覧ください。

tetoruについて(採用・エンジニア向け) - Speaker Deck

Classiを担当する部署とtetoruを担当する部署は分かれていて、筆者は2024年10月にClassiを開発するチームからtetoruを開発するチームへ社内異動しました。 今回の記事では、その経緯やtetoruでの現在の様子をお伝えします。

Classiに入社するまで

私がClassiに入社したのは、2020年4月です。入社の理由は、子どもたちを支える大人のサポートをしたいと考えたからです。

現在、私には小学生の子どもが二人います。これまで多くの方々に支えていただきましたが、特に保育園や小学校の先生方には親身になって助けていただきました。

Classiに入社する前、EDIX(教育分野の展示会)で先生方を取り巻く環境について知る機会がありました。先生方は子どもたちのことを深く想ってくださっているにもかかわらず、業務過多やさまざまな理由で休職を余儀なくされたり、体調を崩してしまうことが少なくありません。

私は、子どもの成長には多くの大人のサポートが必要だと考えています。そして、子どもたちを支える大人たちには心に余裕を持ってほしいと考えています。心に余裕がなければ子どもと対峙するのは難しいかもしれませんし、子ども側も相談しにくさを感じてしまうかもしれません。

そんな思いから、私は先生方をサポートできる仕事をしたいと考え、Classiに入社しました。 (配属は「Classi」側です)

tetoruへの興味

Classiに入社したとき、ちょうど私の息子が小学校に入学しました。今では息子は5年生、娘は2年生になりました。子どもたちの学校生活を見守るなかで、自然と小中学校向けのサービスであるtetoruにも興味を持つようになりました。

弊社では、情報共有がとてもオープンに行われています。Classi側のチームに所属していた時でも、tetoru側のチームがどのような人たちで構成されているのか、どんな施策が進められているのか、エンジニア間ではどのようなPull Requestが作られているかまで見ることができます。

普段から頻繁にチェックしていたわけではありませんが、イベントの際などに気になることはキャッチアップするようにしていました。

また、tetoruである機能を開発する際、保護者目線での意見を求められたことがありました。そのとき、デザイナーの方々のtetoruに対する想いに触れることもあり、自然とtetoruに惹かれていきました。いつか自分もtetoruに関わりたいと思うようになったのは、その頃からです。

tetoruの「伝えあう、通じ合う、手をとりあう。」というタグラインも心から好きで、是非下記記事も読んでいただきたいです。

tech.classi.jp

tetoruのチームメンバーが大事にしている価値観として下記がありますが、「子ども達のよりよい環境づくり」は、私がClassiに入社した理由の一つでもあります。

1 プロフェッショナルとして助け合い、議論を尽くそう。2 大事にするもの、捨てるもののメリハリをつけよう。3 スピード感を持ってサービスを安定運用し、進化し続けよう。4 家族にも誇れるプロダクトを目指そう。5 ユーザー要望の真意を探り、本質を見極めよう。
Oceans(tetoruチームメンバー)のケミストリーが生まれる5ヶ条

異動するにあたって

「いつか関わりたい」と思っていても、日々の業務に忙殺されるなかで、なかなか行動に移せずにいました。実際に異動するまでには年単位の時間がかかりましたが、いくつかの縁があり、ようやく動き出すことができました。

大きなきっかけのひとつは、tetoru側がClassi内のエンジニア向けに異動の公募を出したことです。まさに願ってもない機会でした。

もうひとつは、現在tetoruチームにジョインしてくださっている株式会社万葉の方々と話す機会があったことです。その時の様子は以下の記事に詳しく書かれていますが、ぜひ一緒にお仕事させていただきたいと思える良い会でした。

tech.classi.jp

また、部長や自チームのEM、他チームのEMと話す機会もあり、異動の決断を後押ししてもらいました。もっと早く異動希望を伝えてもよかったと反省点もありますが、この場を借りて、お話を聞いてくださった皆さんに感謝を伝えたいです。

異動してみて

tetoruの歴史はClassiよりは浅く、コードベースもまだ比較的小さいです。システム構成もClassiと大きくは変わらず理解しやすいと感じました。これは社内異動のメリットですね。

チームの文化については、以前のチームとは異なる点もあります。

以前のチームでは、朝会で進捗や状況を細かく確認し、非同期でできることはなるべく非同期で進める方針でした(もちろん同期的なミーティングもありました)。

一方、現在のチームでは、細かな進捗確認はあまり行わず、各自の裁量に任されていることが多く、その分、同期的なコミュニケーションが重視されていると感じています。

どちらが良い・悪いという話ではなく、チームの状況やメンバーによって最適なスタイルは異なるものだと思っています。

また、異動者の意見を柔軟に取り入れる文化があるのも素晴らしいと感じました。チームにジョインしたばかりで感じる違和感はしばらく経つと薄れてしまうことがわかっていたので、なるべく口に出すようにしていましたが(内心最初から出しすぎかなとは思っていましたが)、「それ、試してみる?」と前向きに受け止め、実際に試し、振り返る機会を作ってくれる点は、非常にありがたかったです。

特にMTGの多さやSlackのチャンネルが分かれすぎていることで生じるコストに着目しました。ただ、減らすことだけが必ずしも正しいことではありませんので、1週間減らしてみてどうだったか、やっぱり必要だった、減らしても問題なかった、など意見を出し合い改善することができました。

以前のチームでも今のチームでも、エンジニア同士だけでなく、デザイナー、プロダクトマネージャー、ディレクター、QAメンバーとも密に連携しながら、プロダクトを一緒に育てていく感覚を持てています。

チームのイベント

私たちのチームでは、毎日朝会でその日のタスクを共有し、週ごとにリファインメント・スプリントレビュー・スプリントレトロスペクティブ(振り返り会)を実施しています。

スプリントレビューでは、エンジニアが開発した機能を見せるだけでなく、「デザイナーの今」というタイトルで、デザイナーが思案中のUIを共有する時間もあります。エンジニアは、今後実装予定のUIを事前に把握し、考慮すべき点を整理できるため、有意義な場になっています。

デザイナーが思案中のUIを画面共有している様子
スプリントレビューの様子(🐶🐱が参加することも・・)

スプリントレトロスペクティブでは、良かったことや課題を共有し、投票で議論するテーマを決めて深掘りしています。

また、定期的にチームビルディングも行っていますが、現在は大きな機能開発の佳境にあるため一時休止中です。私はまだ参加したことがないので、次回の開催を楽しみにしています。詳しくはチームメンバーが書いた下記の記事をご覧ください。

tech.classi.jp

以上、tetoruチームにジョインした経緯と現在の様子についてお伝えしました。

教育に関わりたい、子どもたちのより良い未来を創りたい、先生方をサポートしたいなどの想いがある方、このような環境で一緒に働いてみたいと思う方がいましたらぜひお話しさせてください。

これからもより良いプロダクトを作るために精進していきます!

hrmos.co

speakerdeck.com

生成AIを使って、自動で議事録を生成した話

はじめに

こんにちは、データプラットフォームチームのマイン(id:manhnguyen1998)です。

近年、生成AIが急速に注目を集めています。皆さんの企業ではすでに活用されていますか? それとも、現在検討中でしょうか? あるいは、すでにプロダクトをリリースされた方もいるかもしれません。

Classiでもこの流れに乗り、数ヶ月前から生成AIを活用したプロダクト開発に取り組み、生産性向上を目的としたツールの開発を進めています。本記事では、データプラットフォームチームが開発した、AIを活用して会議の議事録を自動生成するツール「Turing」について紹介します。

tech.classi.jp

背景

Classiでは音声会議にGoogle Meetを利用しており、ほとんどの会議の議事録は手動でesaやConfluenceに記録されています。しかし、手動での記録には以下のような課題があります。

  • 100%の内容を記録するのは難しい
  • 記録ミスが発生する可能性がある
  • 記録しながら会議に参加するのが負担になる

これらの課題を解決するために、Turingを活用して自動的に情報を取得し、参加者がより会議に集中できる環境を作ることを目指しました。

AIを活用した議事録の自動生成

活用方法

Classiでは主にSlackを用いてコミュニケーションを行っています。そのため、Slackを中心とした運用が最も利便性が高いと考え、以下の2つの方法でTuringを活用できるようにしました。

  • Slack Workflowを利用する方法
    • ユースケース:
      • 適当に動画のリンクを入力して議事録を作成したい場合
      • まだ自動化設定できない場合
  • 自動化した方法
    • 会議が終わった後、自動で検知して許可があったら議事録を作成

Slack Workflowを利用する方法

SlackのWorkflow機能を活用し、簡単に議事録を作成できる仕組みを構築しました。 Workflowのイメージは以下です

Workflow開始
Slack Workflowのイメージ

利用の流れは以下の通りです。

  • Google Meetの録画機能を利用し、会議を録画
  • 記録した会議動画の編集権限をBot Userに付与Workflowを起動し、会議動画のGoogle Driveリンクを入力
  • モードを選択
    • 会議の種類に応じて異なるプロンプトをAIに適用
  • その他必要情報を入力して、「Submit」ボタンを押す
  • WorkflowがSlack Appにリクエストを送信
  • 議事録の生成が完了すると、リンクをSlackに通知

自動化した方法

Slack Workflowは便利ですが、毎回手動で情報を入力するのが手間となるため、一部のフローを自動化してもう一つの方法を開発しました。

会議後に自動送信されたメッセージの例

利用の流れは以下の通りです。

  • 会議開始前にBot Userを共同主催者として追加
  • 会議終了後、参加者宛にメンションされたSlack通知が届く
  • デフォルトの設定を確認し、ユーザーは「修正」または「そのまま作成」を選択
  • 議事録の生成が完了すると、リンクをSlackに通知

ここで「なぜユーザーのアクションが必要なのか? そのまま作成すればよいのでは?」という疑問があるかもしれません。意図としては、取得した情報と実際の内容が異なる可能性があるため、作成前に確認を行うことで精度を向上させることを目的としています。また、Bot Userを追加すると自動で議事録が作成されることを望まないユーザーもいるため、事前の確認を行うことで柔軟に対応できるようにしています。

システム構成

このシステムを実現するため、以下のアーキテクチャを構築しました。

アーキテクチャ

Slack Workflowを利用する場合

  1. WorkflowがSlack Appにメンションイベントを送信
  2. Cloud Runがリクエストを受信
  3. Google Driveの認証を行い、動画をダウンロード
  4. 動画を処理し、AI(Gemini)に入力
  5. AIの出力を基に議事録を作成し、esa/Confluence/Google Docsに保存
  6. 作成した議事録のリンクをSlackに通知

自動化を利用する場合

  1. Bot Userのメールボックスにメールが来たとき、自動でラベルをつける。
    • これは処理された/処理されてないメールを判定できるようにするためです
  2. 定期的にGoogle Apps Scriptで監視 (15分)
  3. 記録した動画のリンクを取得し、Webhook経由でSlackにメッセージを送る
  4. ユーザーが設定を確認し、リクエストを送信
  5. Cloud Runが処理を実行し、議事録を作成
  6. Slackにリンクを通知

技術的な工夫

Geminiには以下の制約があります。

  • 長時間の動画では精度が低下し、処理時間が長くなる
  • トークン数の制限があり、長い動画は処理できない

これらの問題を解決するために、以下の対策を講じました。

動画を10分ごとに分割し、並列処理を行うことで精度と処理時間を改善

長時間の動画では、AIが途中までしか理解できず、トークン制限に達すると生成が中断され、議事録のトランスクリプトが途中で途切れてしまう問題が発生していました。その結果、サマリーの生成もうまくいかないことが多くありました。しかし、動画を10分単位に分割し、それぞれを処理することで、AIが各部分をより深く理解できるようになり、結果として正確なサマリーを生成できるようになりました。

また、処理時間の改善にもつながりました。以前は1時間程度の動画を処理するのに約30分かかっており、非常に不便でした。しかし、動画を分割し、並列でAIに処理させることで、処理時間を約5分弱まで短縮することができました。

リクエストの多重送信を防ぐため、Backoffリトライを採用

動画を分割し並列でAIにリクエストを送ることで、一部のリクエストがRatelimitに引っかかり、時折500系エラーが発生する問題がありました。この問題を解決するために、BackoffリトライとSemaphoreを導入しました。

同時に最大4リクエストまで送信し、500系エラーが発生した場合は30秒間のBackoffリトライを行うように設定しました。その結果、Gemini AIのサーバー側のエラーは一度も発生せず、安定した処理が実現できました。

Semaphoreの例

MAX_CONCURRENT_REQUESTS = 4
semaphore = threading.Semaphore(MAX_CONCURRENT_REQUESTS)

def generate_transcript_with_limit(
    model: GenerativeModel, minutes_prompt: str, gcs_uri: str
):
    with semaphore:
        return generate_transcript_from_uri(model, minutes_prompt, gcs_uri)

Backoff リトライの例

def generate_transcript_from_uri(
    model: GenerativeModel,
    minutes_prompt: str,
    gcs_uri: str,
    max_retries: int = 5,
    base_delay: int = 30,
):
    attempt = 0
    while attempt < max_retries:
        try:
            video_part = Part.from_uri(gcs_uri, mime_type="video/mp4")
            response = model.generate_content([minutes_prompt, video_part])
            return response.text
        except Exception as e:
            attempt += 1
            print(f"Error processing {gcs_uri} (attempt {attempt}): {e}")
            if attempt >= max_retries:
                raise Exception(
                    f"Failed to generate transcript for {gcs_uri} after {max_retries} attempts: {e}"
                )

            # Exponential backoff starting from 30 seconds, with added jitter between 5 and 10 seconds
            backoff_time = base_delay * (1.5**attempt) + random.uniform(5, 10)
            print(f"Retrying in {backoff_time:.2f} seconds...")
            time.sleep(backoff_time)

結果

最終的な出力

生成された議事録は以下のような形式になります。

結果のイメージ

サマリーで全体の内容を把握可能

サマリーの例
サマリーの例

誰が何を話したかが記録され、情報の正確性を担保(白塗り部分には参加者の名前が入ります)

Transcriptの例

利用状況とユーザーの声

リリース以降、月平均13件の議事録が作成され、合計39件の議事録が生成されました。一見すると少なく感じるかもしれませんが、詳しく見ると状況は異なります。特に、自動化機能の改善をリリースしたのは1ヶ月前で、その後の1ヶ月間で合計30件の議事録が作成されました。これはほぼ毎日利用されている計算になり、利用頻度が大きく向上していることが分かります。

ユーザーの声

今後の展望

この記事を執筆している時点で、AIモデルは驚異的なスピードで進化を続けています。そのため、今後の議事録作成ツールの未来について、いくつかの展望を考えてみます。

まず、DeepSeekなどのリリースにより、AI市場の競争は本格化してきています。今後、さらに多くのAIモデルが登場し、精度の向上やコストの低減が進むことが予想されます。こうした流れの中で、議事録作成ツールもより高度な機能を求められるでしょう。

現在のTuringは、参加者リストをもとにプロンプトを生成していますが、情報の精度には限界があります。より正確な議事録を作成するためには、会議の詳細情報など、より多くの要素をプロンプトに組み込む必要があり、それによって精度向上が期待できます。

また、将来的にはGoogle Meetにも議事録生成機能が搭載される予定です。現時点では日本語には対応していませんが、今後リリースされた場合、Turingの役割や存続について再考する必要があるかもしれません。Turingが今後どのように進化していくのかは不透明ですが、少なくとも社内ツールとして有益に活用され、AIの認識を高めることができた点においては、一定の成果を達成できたと考えています。

まとめ

Turingは、Classiの社内において会議の効率化に貢献し、生成AIの有用性を示しました。今後も技術の進化に合わせて、より精度の高い議事録作成ツールへと発展させていきたいと考えています。

© 2020 Classi Corp.