TECH PLAY

株式会社マイナビ デジタルテクノロジー戦略本部

株式会社マイナビ デジタルテクノロジー戦略本部 の技術ブログ

233

TSKaigi2025 TSKaigi2025 「学び、繋がり、”型”を破ろう」をテーマに、TypeScript に関するあらゆるテーマを扱う国内最大級のカンファレンスとして、まさに「型破り」なイベントを目指し成長を続けるカンファレンスです。 朝から夕方までTypeScriptについての講演があり、事前に自分が気になるセッションを聞きに行く方式でした。 開催日 2025/05/23、2025/05/24 印象に残ったセッション SignalとObservable―新たなデータモデルを解きほぐす AI Coding Agent Enablement in TypeScript TypeScriptとは何であって何でなく、誰のもので、どこへ向かうのか TS特化Clineプログラミング Pragmatic Functional Programming in TypeScript 付録: TSKaigi2025の発表資料まとめ 内製開発業務にどのように活かすか 【難易度_易】Panda CSSは継続して採用していきたい マイナビでもLocusで採用されたPanda CSS、他者も業務レベルで使い始めている 補足① TailwindからPanda CSSへの完全移行ガイド 【I難易度_易】フルスタックTypeScriptの案件の比率を増やすのもあり GraphQLを活用しまずはサブシステムとして使い始めてもいい 【難易度_易】 type-challenges でTypeScript技術力養成 勉強会、もうこれでいいのでは 【難易度_中】 スキーマ駆動開発、はじめました の潮流でバックエンドテストも正していきたい(Railsならcommitteeなど) TSKaigiと言いつつスキーマ駆動の話があったので、内製採用率の高いRailsではどうやって行こうという観点 【難易度_中~高】部署間連携で生産性向上(例. Figma MCPなど) TSKaigiと言いつつFigmaMCPからのコード生成の話があったので、これやるなら生産性向上のためにUXDとかと部門間連携必要 本題 組織開発という目線でみたカンファレンスのレポート 2024年に産声をあげ、昨年同様大盛況のうちに幕を閉じたTSKaigi2025。 公式のカンファレンス概要に書いてある通り、TSKaigiは非常に若いカンファレンスイベントです。 RubyKaigiの第一回が2006年、PHPカンファレンスは2000年、GoConはちょっといつからか分からないですが2013年くらいからはあるはず。とまぁこんな感じで若いイベントです。 GOが2009年生まれTypeScriptが2012年生まれとほぼ同期なので、イベント発足が遅めなのが分かります。connpassのイベントを遡って調べてもまあ2015年くらいからチラホラとサブタイトル的にイベントがあったくらいです。 (*生まれの2012から自分が新卒で入るまでの時間軸で絞り込んだのと、npm trendsで2016年くらいをマウスオーバー。) 出典: connpass - エンジニアをつなぐIT勉強会支援プラットフォーム 出典: typescript | npm trends TypeScriptがここまでの一大勢力になったのは静的型付け機能もありますが、ReactやVueやAngularの勢力が後押ししているのは間違いないと思います。 これはどの言語でももしかしたら共通して言えることかもしれませんが、例えばRubyであってもRuby on Railsが勢力を持っていないかったらここまで愛されたかどうか…言語の生みの親が日本人なのでそういった意味ではパイは取れたと思いますが…。 余談で私が新卒で入った会社はRuby on Railsでの開発を強みにしてましたがRails5でCoffeeScript が標準サポートされてました。Rails6はwebpackが入ってきてそれなりにまだフロントも書いてましたが7系でwebpackが剝がされて「フロントどうするかな」で「reactだな」という流れがWebアプリ開発者界隈ではそれなりにいたのではないでしょうかね。他のバックエンドフレームワーク事情は知らないのですが。 話を戻します。 TSKaigiですが来場者数も多く話題もSNSでそれなりにトピック化してたのでエンジニア界隈では結構HOTだった印象で、実際に会場にも多くの学生が来ておりました。ただカンファレンスとしてはわりと緩くてTSKaigiでPHPトークをしたという話題でプチ炎上があるとかないとか。CFP要綱を読んでもそれは頷けます。 【TSKaigiのCFP要綱】 トークの条件は、TSKaigi 2024と同様に「TypeScriptに関係する話題であること」、これだけです。 以下はすべて例です。 ・言語特性やエコシステムに関しての話題 ・TypeScriptの言語機能 ・Compiler API、内部実装、型の理論、いわゆる型パズル ・TypeScriptでの利用に特徴あるライブラリ、フレームワーク、ランタイム ・ベストプラクティス、アンチパターン、それらを包括する議論や問題提起 ・エコシステムそのもの、言語そのものに関するその他なんでも ・多様な利用領域での活用事例、ノウハウ ・Webフロントエンド、バックエンドやインフラはもちろんOK ・スマートフォンアプリ、デスクトップアプリ、ゲーム、IoT、XR(VR, AR, MR) そのほか、ここに予想もしないものまで TypeScriptを使った開発に関する話題 ・チーム開発、CI/CD、テスト、デバッグ、モニタリング、デプロイ、運用 ・開発ツール、エディタ、IDE、ツールチェーン、ツールの開発、ドキュメンテーション ・うまくいったこと、うまくいかなかったこと こんな使い方もできるのか、TypeScript! という驚きを得られるような話題に出会えたら、と思っています。 高度で専門的な話でなくても構いません。あなたの経験や気づきを、ぜひ共有してください。 RubyKaigiとは雰囲気がだいぶ違うんだなという印象でしたが、そもそもRubyKaigiが特殊なだけと良く言われているのでTypeScriptの言語学的なセッションを期待していると歪みが起こりそうです。 実際、トークセッションの内容の6割以上くらいは生成AIの文脈は絡んできてた感じで、それ完全にもうLLM周りの話ではないのか?ともありました。 個人的にはLLMとの上手い付き合い方みたいな感じでも十分に学びになりましたが、純粋にオープニングキーを担当したAnthony FuさんのESLint ConfigみたいなTypeScriptエコシステムの内部仕様みたいな話を期待しているとやっぱり乖離ありです。 発表のあったLTセッションを参考に、マイナビの内製開発におけるTypeScriptの話をしてもウケそうだなとも思いました。マイナビでもフルスタックTypeScript構成の案件やorval、OpenAPIスキーマからTypeScriptクライアントコードを生成している案件やPanda.CSSを使った案件もあるのでTSKaigiの登壇にチャレンジしていきたいですね。 ではスポンサー枠の状況やメリットは何だろうか。 国内最大級のTypeScriptカンファレンス「TSKaigi 2025」、 スポンサー募集中です TSKaigi 2025 スポンサーの一次募集終了と御礼 資料によると、2024はオフライン+オンラインで2,400人くらいの参加だったが、今年は公式では計画段階で3,600人くらいのようでしたが、チケット完売も早かったのでオンラインの上振れ考えても、倍は見込んだとして5,000人は直接リーチはありそうです。(個人推論です) 協賛ボードはプラチナ150万円、ゴールド100万円、シルバー50万円、ブロンズ20万円です。 ブースやランチタイムLT枠はシルバー以上で応募可能になっており、ランチタイムLTは10分で10万円なので、登壇枠を買うなら最低60万円で可能です。 ブースは30万円でスタートアップからレバレジーズやサイバーエージェントやdwangoやfreeeなどの社会的な知名度がある事業会社もありました。 WEBエンジニアはカンファレンスボードをみて転職可能企業を見つけている部分もあるとは思うので、開発分野での組織的なブランド価値と求人広告周り出すよりはコスパはいいかもしれない。実際ブース出展からカジュアル面談にという経路もいくつかあるはず。 マイナビに限って言えば組織が大きすぎてカジュアル面談とかないかもしれないがまあ方法論はいくつかあると思います。 TypeScriptはトレンドすぎてカンファレンスに協賛しなくても新卒採用では当たり前のように興味持った学生がデジ戦に応募してくるかもしれないのですが、TSKaigiに限らず技術カンファレンスのスポンサーとかプロポーザル活動やっていきたいですね。 (RubyKaigiとかKaigi on RailsとかGoConとか)
こんにちは、新卒2年目でビジネスイノベーション統括本部ITD1-2-0のS.Hです。 今回、私が普段の業務で使用しているTypeScriptをテーマにした大型カンファレンス『TSKaigi 2025』の参加レポートを書かせていただきました! 研修後、現在の部署に配属されてからもうすぐ1年。ほぼ新人の視点から、TSKaigiに参加して感じた魅力などを発信していきます! TSKaigi 2025 カンファレンス概要 情報区分 カンファレンス詳細 イベント名 TSKaigi 2025 開催日 2025/05/23、2025/05/24 開催場所 ベルサール神田 都営新宿線小川駅から徒歩5分 ミッション 学び、繋がり、"型"を破ろう タイムテーブル Day1 10:00 開場 10:50 ~ 11:00 トグルルーム:オープニングトーク アセンドトラック:サテライト レバレジーズトラック:クローズ 11:00 ~ 11:40 トグルルーム 種別:招待講演 タイトル: The New Powerful ESLint Config with Type Safety 登壇者:Anthony Fu アセンドトラック:サテライト レバレジーズトラック:クローズ 11:40 ~ 11:50 休憩 11:50 ~ 12:20 トグルルーム 種別:セッション タイトル: checker.tsに対して真剣に向き合う 登壇者:Kaoru アセンドトラック 種別:セッション タイトル: 高度な型付け、どう教える? 登壇者:progfay レバレジーズトラック 種別:セッション タイトル: スキーマと型で拓く Full-Stack TypeScript 登壇者:Sohei Takeno 12:20 ~ 12:30 ランチ配布 12:30 ~ 13:30 トグルルーム 種別:スポンサーLT 撤退危機からのピボット:4年目エンジニアがリードする TypeScript で挑む事業復活  / 横沢 諒 推し活を支えるAngularアプリ量産体制  / Hayato Okumoto 生成AI時代にフルスタックTypeScriptの夢を見る  / matano AsyncAPIを使ってPub/Subを型安全にする  / 高橋 修平 アセンドトラック:ランチ レバレジーズトラック:ランチ 13:30 ~ 13:40 休憩 13:40 ~ 14:10 トグルルーム 種別:セッション タイトル: TypeScriptで実践するクリーンアーキテクチャ ― WebからもCLIからも使えるアプリ設計 登壇者:プログラミングをするパンダ アセンドトラック 種別:セッション タイトル: 静的解析で実現したいことから逆算して学ぶTypeScript Compiler 登壇者:Kazushi Konosu レバレジーズトラック 種別:セッション タイトル: SignalとObservable―新たなデータモデルを解きほぐす 登壇者:lacolaco 14:10 ~ 14:20 休憩 14:20 ~ 14:50 トグルルーム 種別:セッション タイトル: 堅牢なデザインシステムをつくるためのTypeScript活用 登壇者:takanorip アセンドトラック 種別:セッション タイトル: Language Serverと喋ろう 登壇者:ぴざきゃっと レバレジーズトラック 種別:セッション タイトル: TSConfigからTypeScriptの世界を覗く 登壇者:らいと 14:50 ~ 15:00 休憩 15:00 ~ 15:30 トグルルーム 種別:セッション タイトル: AI Coding Agent Enablement in TypeScript 登壇者:Yuku Kotani アセンドトラック 種別:LT 推論された型の移植性エラーTS2742に挑む  / elecdeer TSConfig Solution Style & subpath imports でファイル単位で型を切り替える  / kotori 主要ライブラリの実例に学ぶ、TypeScriptで実現する型安全な座標定義  / 原口 公輔 コンポーネントライブラリで実現する、アクセシビリティの正しい実装パターン  / たじまん レバレジーズトラック 種別:LT 学生でもここまで出来る!ハッカソンで爆速開発して優勝した話  / かわちゃん 『Python→TypeScript』オンボーディング奮闘記  / 龍野 卓己 転生したらTypeScriptのEnumだった件~型安全性とエコシステムの変化で挫けそうになっているんだが~  / やまのく URLPatternから始めるWebフレームワーク開発入門  / ryuapp 15:30 ~ 15:50 休憩 15:50 ~ 16:20 トグルルーム 種別:セッション タイトル: TypeScriptとReactで、WAI-ARIAの属性を正しく利用する 登壇者:ymrl アセンドトラック 種別:セッション タイトル: AWS LambdaをTypeScriptで動かして分かった、Node.jsのTypeScriptサポートの利点と課題 登壇者:Masaki Suzuki レバレジーズトラック 種別:セッション タイトル: TypeScriptエンジニアがAndroid開発の世界に飛び込んだ話 登壇者:yui_tang 16:20 ~ 16:30 休憩 16:30 ~ 17:00 トグルルーム 種別:セッション タイトル: TypeScriptとは何であって何でなく、誰のもので、どこへ向かうのか 登壇者:Sosuke Suzuki アセンドトラック 種別:セッション タイトル: fast-checkとneverthrowのPBT+Result型で堅牢なビジネスロジックを実現する 登壇者:上田慶祐 レバレジーズトラック 種別:セッション タイトル: Valibot Schema Driven UI - ノーコードWebサイトビルダーを実装してみよう! 登壇者:宮城広隆(@MH4GF) 17:00 ~ 17:10 休憩 17:10 ~ 17:40 トグルルーム 種別:セッション タイトル: Rust製JavaScript/TypeScript Linterにおけるプラグイン実装の裏側 登壇者:unvalley アセンドトラック 種別:LT Interface vs Types 〜型推論が過多推論〜  / omote Wasmを用いて他言語資産をTypeScriptで活用する  / 赤木 勇統 型パズルを好きになるために、競プロを型システムだけで解いてみることにした  / いまいまい タイプレベルリファクタリング奮闘記〜この「型パズル」は読めません!〜  / Yugo Yagita レバレジーズトラック 種別:LT Rust製JavaScript EngineのTypeScriptサポート  / yossydev TypeScript だけを書いて Tauri でデスクトップアプリを作ろう  / 小松 翔 (tris) 型安全なDrag and Dropの設計を考える  / yudppp GitHub ActionsをTypeScriptで作ろう!  / じょーし(上司陽平) Day2 9:30 開場 9:50 ~ 10:00 トグルルーム:オープニングトーク アセンドトラック:サテライト レバレジーズトラック:クローズ 10:00 ~ 10:40 トグルルーム 種別:主催者講演 タイトル: TypeScriptネイティブ移植観察レポート TSKaigi 2025 登壇者:berlysia アセンドトラック:サテライト レバレジーズトラック:クローズ 10:40 ~ 10:50 休憩 10:50 ~ 11:20 トグルルーム 種別:セッション タイトル: TypeScript Language Service Plugin で CSS Modules の開発体験を改善する 登壇者:mizdra アセンドトラック 種別:セッション タイトル: フロントエンドがTypeScriptなら、バックエンドはPHPでもいいじゃない 登壇者:富所 亮 レバレジーズトラック 種別:セッション タイトル: TypeScriptとVercel AI SDKで実現するLLMアプリケーション開発:フロントエンドからバックエンド、そしてChrome拡張まで 登壇者:加瀬健太(Kesin11) 11:20 ~ 11:30 休憩 11:30 ~ 12:00 トグルルーム 種別:セッション タイトル: 複雑なフォームを継続的に開発していくための技術選定・設計・実装 登壇者:izumin5210 アセンドトラック 種別:セッション タイトル: Pragmatic Functional Programming in TypeScript 登壇者:yasaichi レバレジーズトラック 種別:セッション タイトル: feature flag 自動お掃除のための TypeScript プログラム変換 登壇者:azrsh 12:00 ~ 12:10 ランチ配布 12:10 ~ 13:10 トグルルーム 種別:スポンサーLT バックエンドのコードファーストなOpenAPIスキーマ駆動開発 / 鳥居 雄仁 バランスを見極めよう!実装の意味を明示するための型定義 / 畑田祥太 PandaCSSでつくる、型で守られたスタイリング基盤 ~TypeScript × デザインシステム管理の実践アーキテクチャ~ / 田代 敬太 TSでシステムが堅牢になっていくさまをスポンサーになるたびに報告 〜型定義から始めるリファクタリング編 / 井上 心太 アセンドトラック:ランチ レバレジーズトラック:ランチ 13:10 ~ 13:20 休憩 13:20 ~ 13:50 トグルルーム 種別:セッション タイトル: 技術書をソフトウェア開発する - jsprimerの10年から学ぶ継続的メンテナンスの技術 登壇者:azu アセンドトラック 種別:セッション タイトル: 型システムを活用した ESLint カスタムルール開発入門 〜固有ドメインにおけるコーディング規約を開発する〜 登壇者:山梨 蓮 レバレジーズトラック 種別:セッション タイトル: Web Streams APIの基本と実践、TypeScriptでの活用法 登壇者:tasshi 13:50 ~ 14:00 休憩 14:00 ~ 14:30 トグルルーム 種別:セッション タイトル: ts-morphで、人間も編集できるコード生成を実現しよう! 登壇者:池奥裕太 / @yuta-ike アセンドトラック 種別:LT VueUse から学ぶ実践 TypeScript / ツノ 型推論の扉を開く―集合論と構造的型制約で理解する中級へのステップ / 栃川晃佑 TypeScript ASTとJSDocで実現するコードの自動削除 / 川野賢一 これは型破り?型安全?真実はいつもひとつ!(じゃないかもしれない)TypeScriptクイズ / 君田 祥一 レバレジーズトラック 種別:LT Result型、自前で書くか、ライブラリ使うか / majimaccho 型付け力を強化するための Hoogle のすゝめ / TAKASE Kazuyuki (@Guvalif) React19で変化したuseReducerの型から学ぶTypeScriptの型推論 / k8o クラサバ境界を失った現代 TypeScript コードベースに秩序をもたらしたい / Yo Iwamoto 14:30 ~ 14:40 休憩 14:40 ~ 15:10 トグルルーム 種別:セッション タイトル: 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する 登壇者:NoritakaIkeda アセンドトラック 種別:セッション タイトル: Lookback TypeScript ESM support and what should we do now. 登壇者:Saji レバレジーズトラック 種別:セッション タイトル: 君だけのオリジナル async / await を作ろう 登壇者:susisu 15:10 ~ 15:30 休憩 15:30 ~ 16:00 トグルルーム 種別:セッション タイトル: TS特化Clineプログラミング 登壇者:mizchi アセンドトラック 種別:セッション タイトル: "良い"TSのコードを書く為のマインドセット 登壇者:Kei レバレジーズトラック 種別:セッション タイトル: TypeScript製IaCツールのAWS CDKが様々な言語で実装できる理由 〜他言語変換の仕組み〜 登壇者:k.goto 16:00 ~ 16:10 休憩 16:10 ~ 16:50 トグルルーム 種別:LT 型がない世界に生まれ落ちて 〜TypeScript運用進化の歴史〜 / 成原 聡一朗 Type ChallengesにPRを出して新しい問題を追加した話 / Kanon ProxyとTypeScriptのおいしい関係 / Motoki Shakagori / ほとけ Panda-CSS はどのように型安全にしているのか / 加藤貴裕 アセンドトラック 種別:LT 令和最新版TypeScriptでのnpmパッケージ開発 / odan コンパイルオプションで変わる型世界 / 池田敬祐 TypeScriptのmoduleオプションを改めて整理する / おおいし (bicstone) Project Referencesを活用した実行環境ごとのtsconfig最適化 / Toshiki Itai レバレジーズトラック 種別:LT ts-morph実践:型を利用するcodemodのテクニック / ypresto declaration mergingの威力:ライブラリアップデート時の書き換え作業を90%短縮するテクニック  / Yuma Takei バリデーションライブラリ徹底比較  / 田中勇太 Standard Schema: スキーマライブラリの統一規格とは何か  / Nozomu Ikuta 17:00 ~ 18:00 トグルルーム:懇親会準備 アセンドトラック:休憩スペース レバレジーズトラック 種別:現地参加者向け企画 タイトル:タイトル: OST (Open Space Technology) 18:00 ~ 20:10 トグルルーム:懇親会 アセンドトラック:クローズ レバレジーズトラック:クローズ セッション内容 TypeScriptネイティブ移植観察レポート TSKaigi 2025 登壇者: berlysia氏 セッション時間:2日目朝 株式会社ドワンゴでWebフロントエンドエンジニアをされているberlysia氏によるts-goの体験レポートです。 5/23深夜、つまりTSKaigiの1日目夜にMicrosoftから公開された tsgoのプレビュー に触れてみた観察者としての調査報告になります。 夜中に公開されたので、TSKaigi1日目を終えてそこから触り、登壇資料を作成されたみたいです。バイタリティとメンタルが素直に凄すぎます。 ts-go1番の特徴はMicrosoftが動画のタイトルにも取り上げられている 「A 10x Faster TypeScript with Ander Hejlsberg(10倍早いTypeScript)」 従来はTypeScriptで書かれた構文をNode.jsを用いて実行されていました。それらを全てfunction単位でGo言語に置き換えており、 Go言語の利点を十全に活かすことで10倍という圧倒的なパフォーマンスを実現するに至ったそうです。 具体的には ソースコードをその場で解釈して実行する Node.jsから、事前に機械語に翻訳されるGo言語へ変わったことによる処理速度の向上 (約3~3.5倍) Node.jsはシングルスレッドモデルの制約があり、単一の処理しかできなかったが、Go言語になったことで並列処理が可能になった (約3~4倍) 上記の2点が組み合わさったことで、本当にすぐに動くようになったとのことでした。 しかも、実行するプログラムの規模が大きくなるほど、その早さを実感するそうです。 「ts-goというものが話題になっている」ということはそれとなく把握していつつも、それが具体的に何なのか知らなかった自分でしたが、10倍という数値だけでもその凄さを容易に想像することができました。 処理速度が上がったことで、それまでのバージョンでは見送っていたライブラリなども採用されるようになり、より発展的で高度な技術力が求められるようになる、というのが私の所感です。 そして、そんなに凄いts-goをまだ私は触ることができていないので、このレポートを書き終えたら早速触ろうと思います。 OST(Open Space Technology) タイムテーブルにある通り、本当に多くのセッションがあり、学びが沢山ありました。 ですが、TSKaigiは登壇者だけが発信する場ではありません。2日目の最後には、セッションルームに集まった参加者が各々好きなテーマについて話し合えるOSTが設けられていました。 OSTはそのテーマについて熱く語りたいから参加する人はもちろん、ほとんど詳しくないけど、知見を広げたいから参加する人もいらっしゃいました。 自分はもちろん後者です笑 今回、TSKaigi中の募集を通して選出されたテーマは下記画像に記載された10個。皆さんはどのテーマに興味を惹かれましたか? 私は5番の「フロントエンドのディレクトリ構成、どう設計している?」に参加しました。 というのも、私の周りにはそういった開発理論について考えることが好きな同期がおり、この機を活かして『完全に理解した!』ぐらいの理解度を得てみたいと感じたためです。 結論からお伝えすると『だいぶん理解したかも??』ぐらいの理解度を得ることができました。 もう少し実際に試していくことで『完全に理解した!』の理解度を得られそうです。 今回のOSTで話題に上がった内容はFSDとBCDデザインの組み合わせです。 FSD まず、 FSD(Feature-Sliced Design) とは、機能単位で構造化する設計パターンで、公式では「Architectural methodology for frontend projects(フロントエンドのアーキテクチャ手法)」と定められています。 機能単位で構造化する、つまり機能ごとに責務を持たせることで、拡張性・保守性・可読性を高めることが可能になることがFSDの特徴です。 FSDは、レイヤーという役割の抽象的な区分と、そこから実際の機能単位で切り分けるスライス、そしてhookやUIなどの各機能を構成するセグメントで成り立っています。 出典: FSD(Feature-Sliced Design) 機能単位でディレクトリを構成することは多いと思いますが、そこから更に責務という名の「機能の持つ役割や義務」に着目して分類する手法がFSDなのです。 BCDデザイン 次にBCDデザインは株式会社ドワンゴ所属のmisuken氏が考案されたコンポーネントの分類手法です。 同期からの受け売りですが、 「BCDデザインとは Base(UI) Case(動作) Domain(対象)に着目してコンポーネント名を『何をどうするUI』の法則に則って命名すること」なんだそうです。 私なりの解釈で説明すると、誰でもそのコンポーネント名を見ただけで、役割が分かるようにする手法になります。 出典: misuken氏のBCDデザイン解説記事 例えば、ここにフィードバック専用のモーダル画面のコンポーネントがあったとしたら、どのような名前にしますか? 恐らく、多くの人が FeedbackModal.tsx という名前にするのではないでしょうか? それがBCDデザインの命名規則に当てはめると、 FeedbackSendModal.tsx になります。 フィードバックを編集する(Edit)でもなく、削除する(Delete)でもなく、送る(Send)ためのモーダル画面。それが誰にでも伝わる命名規則です。 ちなみに、この場合でも何のフィードバックなのかが不透明なので、DomainをDomainとCommon(抽象的対象)に分割するBCCDデザインというものもあります。 また、 misuken氏のBCDデザイン解説記事 では、「"命名" するのではなく "明名" すると考える」と表現されていました。 OSTまとめ FSD × BCDデザイン 「抽象的な責務で分類し、機能単位でディレクトリ構造化を図るFSD」と 「誰にでも伝わるコンポーネント明名手法のBCDデザイン」 この2つを組み合わせることで構造も名前もはっきりと定まった方針で分かりやすくフロントエンドのディレクトリ構成が実現できる、というのが今回のOSTに関する結論になりました。 今回のディレクトリ構成OSTには15名近くの方が集まり、その中でもFSDとBCDデザイン、それぞれの知見を深く持った方が2人ずついらっしゃったので、本当に濃い議論をすることができました。 個人的には、今回のBCDデザイン側に株式会社ドワンゴの社員さんがいらっしゃったことに一番衝撃を受けました。流石TSKaigi、一般参加される方も凄い。 企業ブース 私は今回のカンファレンスでブースを設けられていた企業様のイベントも目一杯体験してきました。 恐らく、会社から一緒に行った7人の中で最も詳しく企業ブースのレポートを書けると思うので、こちらも詳しく書かせていただきます! (各ブースで実施されたスタンプラリーを1日目で制覇しました) 流石に全ブースについて書くと時間が足りないので、今回は2つほど特に面白いと感じた企業様のブースを取り上げさせていただきます AVITA株式会社 AVITA株式会社は大阪大学の教授が代表を務めるスタートアップ企業です。 大学の研究室と企業、両方から特許申請を活発に行い、週に1回ペースで特許申請会議があるそうです。 "特許申請会議"。字面がとても強い 提供しているwebサービスは、 「フロント・バックエンドがフルスタックのTypeScriptで実装されたWebアプリ」 と 「Unity × TypeScriptで運用されているスマホアプリ」です。 色々お話をお伺いした中で、私が特に面白いと感じた点は真ん中のディスプレイで動いているトラッキング技術です。 これはディスプレイ上部のカメラから取り込んだ映像を元にフェイストラッキングとハンドトラッキングを同時に実行されています。 この処理には、Googleが提供している画像解析によるトラッキング専用ライブラリMediaPipeが利用されています。 その中でも面白いと感じた要素は、とにかくトラッキングの精度が高すぎる点です。 MediaPipeは機械学習によって手の状態を画像解析を行い、「恐らくこの辺りに手の関節があるだろう」といった判定を下します。そのため、動画のように連続してトラッキング対象が動く状況だと著しく精度が落ちます。 実際、以前に自分がMediaPipeを試したときは指関節がはちゃめちゃに暴れ回り、画面内に描写された手は見事に潰れていました。 それなのに、こちらのプログラムは滑らかに動く実物の手に追従して、関節座標も暴れることなくしっかり手の動きを再現していました。 Unityアプリの出来から垣間見えた開発者さんの膨大な努力に圧倒され、ものすごく興奮しました。 TypeScriptの話はできませんでした… お土産にシールやアクスタをいただきました笑 スパゲッティコード、マジックナンバー イラストはとても可愛いのにワードが全く可愛くない アクスタも作るぐらいIP展開にも力を入れ始めたらしいです。いつか販売されるのを楽しみにしています! 株式会社TwoGate TypeScriptベースのWebアプリフレームワーク"Angular"を使ったエンターテイメント領域向けに利用者専用にカスタマイズしたスマホアプリをリリースしている会社です。 1日目のお昼にLT登壇もされていました。 推し活を支えるAngularアプリ量産体制 こちらの企業から沢山お話を聞いた内容はサービスの根幹部分の「Core Library Repository」についてです。 TwoGate様はアーティストの要望に沿って推し活の専用アプリをリリースしており、開発エンジニア15名ほどに対し、リリースしているアプリ数は200個を超えています。 平均1人当たりアプリを15個ほど担当しているような状況でも成り立っている理由はリリースしたアプリ全てが「Core Library Repository」というこの企業独自のライブラリから作られているからです。 つまり、Core Library Repositoryがあれば、多少のカスタマイズをすることで誰でもチケット販売や整理券配布などの機能を持った専用アプリを作ることできる。そんなライブラリを作り上げ、それを元にサービスを提供しているとのことでした。 私たち開発エンジニアが普段活用しているようなライブラリを作り、その運用を事業として行なっている一風変わった企業様でした。 TSKaigiに参加して 今回、私は下記の2つのセッションに参加し、その上で実際にそれぞれの技術に触れてみたので、それぞれから感じた今後に対する所感をまとめさせていただきます。 PandaCSSでつくる、型で守られたスタイリング基盤 ~TypeScript × デザインシステム管理の実践アーキテクチャ~ TS特化Clineプログラミング CSSライブラリ - PandaCSS PandaCSSを知ったことをきっかけに、実際に個人で触れてみる中で、TailwindCSS以外のCSSライブラリにも視野を広げる良い機会になりました。使ってみることで、それぞれのライブラリが持つ特徴や考え方の違いも見えてきて、フロントエンドに対する理解が一段深まったように感じます。 一方で、実際にNext.jsのプロジェクトに組み込もうとすると、TailwindCSSのように簡単にセットアップできるわけではなく、PandaCSSは別途インストールや設定が必要です。その分、Dockerfileなどの構成にも工夫が必要で、開発や保守とはまた違った知識が求められる点には留意する必要があると感じました。 こうした経験から、CSSライブラリの選定には機能性だけでなく、導入のしやすさやチームの開発環境との相性も含めて検討することの重要性を実感しました。 コーディングエージェントについて 最近よく耳にする「コーディングエージェント」ですが、これまでは最低限のサポートツールとして使っている程度でした。実際のところ、補助的にコードを書いてくれる便利な存在、という認識しか持っていませんでした。 しかし今回、プロンプトをしっかり設計してAIとやり取りしてみたことで、印象が大きく変わりました。AIを効果的に活用するには、「どう指示するか(=プロンプト)」の工夫がとても重要で、これ自体が一つのスキルだと感じました。いわゆる“プロンプトコーディング”が、今後開発エンジニアに求められる新しい力になると思います。 また、個人的な気づきとして、会社でコーディングエージェントを活用していくには、プロンプトの作り方だけでなく、それをどう管理するかも大切になってきそうです。チームでの共有や再利用、更新のしやすさなど、コードと同じように「プロンプトの品質」も考えていく必要があると感じました。 TSKaigiの感想 私はTSKaigiに初めて参加し、本当に貴重な経験を沢山することができました。 今回のカンファレンスに対して、職場環境以外からのインプットを求めて参加をしました。 職場という技術的にも思想的にもある程度方針が固まっている環境以外からの発信に多く触れることで、技術研鑽というアウトプットに対するモチベーションへ繋げたいと考えていました。 実際、先ほどの章で触れた通り、これまで触れていなかった技術や概念に挑戦するきっかけとなりました。 その結果、壁にぶつかったことで新たな課題が見つかりましたが、それでもTSKaigiに参加しなかったら得られなかったものなので、職場環境以外からのインプットはとても価値のあるものだと実感しています。 まだ、技術スタック的に業務と直接交わることのないものですが、今後PJや状況が変化した先で繋がるタイミングがあると思うので、これからもアウトプットを続けていきたいと考えています。 また、今回の1日目は平日に開催されていましたが、外部研修の一環で公休の申請が降りたため、参加しやすい環境になっていた点がとても有難かったです。 同期や先輩を含め7人での参加は初めての経験だったこともあり、自分が見て回れなかったブースやセッションの話も聞くことができ、TSKaigiをより楽しむことができたと実感しています。 お弁当も美味しかったです おまけ TSKaigiでいただいたサプライ品が気に入りすぎて、社用にカスタマイズしました。 右のタグはTSKaigiで知り合った方に作り方を教わって作成した自己紹介タグです。 イベントで大活躍間違いなし!爆速相互フォローを実現するNFCタグキーホルダーを作ろう
システム開発における命名の重要性 「命名」について、考えたことはありますか? 命名とは、文字通り命を与えることです。つまり、システムの肝となりうるものということです。 したがって、不適切に命名されたシステムはその生命を十分に発揮することができません。 軽く考えられがちな「名前をつける行為」ですが、この行為の質がプロジェクト全体の健全性を左右すると言っても過言ではありません。 今回は、「命名」を「ドキュメンテーションにおける最も基本的な手段」にまで昇華させるために重要なことについて説明をします。 命名における関心とは 命名における関心 (Domain)とは、対象となるビジネス領域の本質的な概念や規則を反映する要素です。 これは単なる技術的な区分ではなく、実際のビジネスプロセスや業務知識に基づく分類軸を意味します。 関心は以下のような要素から構成されます。 ドメインエンティティ:業務上の主要概念 生徒 会員 商品 注文 ドメインプロセス:業務上の重要な処理 入会 退会 請求 集計 ドメイン状態:業務上の条件や状態 アクティブ 休会中 プレミアム 月次 ドメイン規則:業務特有のルールや制約 割引条件 承認フロー ドメインコンテキスト:適用される業務文脈 販売 マーケティング 教育 中核の関心 例えば、「プレミアム会員の月次利用統計」には「プレミアム会員」という顧客区分と「月次」という集計期間、「利用統計」という分析対象といったように複数の関心が含まれています。 しかし、この中でもっとも重要な関心は「利用統計」です。 このように、複数の関心が組み合わさっていてもその中にひとつ主軸となる「中核の関心」が存在することがわかります。 そして、命名において重要なことは、中核の関心と補助の関心(それ以外の関心)の境界を明確に理解し、関心を過不足なく表現しきることです。 また、分類においても中核の関心は重要なポイントとなります。 例えば、「プレミアム会員」、「月次」、「利用統計」というカテゴリーがあったとき、「プレミアム会員の月次利用統計」はどこに分類するべきでしょうか? ここまでの話から、中核の関心をもとに「利用統計」に分類するべきであることがわかるとおもいます。 関心を表現し切ることの重要性 よくある命名の失敗のひとつが、「関心を表現しきれていないこと」です。 例えば、「プレミアム会員の月次利用統計」の中核の関心は「利用統計」ですが、「プレミアム会員」と「月次」も重要な関心であることに変わりはありません。 しかし、命名する際に中核の関心以外の補助の関心が抜け落ちてしまうことがあります。 「プレミアム会員の月次利用統計」を英訳すると PremiumMemberMonthlyUsageStatistics となります。そして、これがそのまま関心名となっているべきなのです。 例えば、「プレミアム会員の月次利用統計を取得する関数」の命名は getPremiumMemberMonthlyUsageStatistics であるべきです( get が妥当かは別問題として)。 名前が長くなるのをおそれて、 getUsageStatistics のように関心を省略してしまうのは典型的なアンチパターンとなるため注意しましよう。 関心は後方一致でまとめよう 中核の関心は、関心名の最後に現れる状態にしましょう。 なぜなら、日本語の語順において中核の関心は最後に現れるためです。 例えば、「プレミアム会員の月次利用統計」の中核の関心は「利用統計」ですが、関心名の最後に現れています。 この「関心名の末尾に中核の関心が現れる状態」は認知上のパフォーマンスにおいて非常に重要となります。 日本語の語順のまま英訳しよう 実を言えば、日本語において中核の関心が名前の最後に現れるのは当たり前のことです。 というよりも、名前の最後に現れている関心が中核の関心であるといった方が正しいです。 ではなぜこのような話をしているのかと言うと、英語にした際にこの当たり前が崩れることがあるためです。 「契約プラン別のユーザ」を例にみていきましょう。 まず、「契約プラン別のユーザ」を直訳すると「Users by Contract Plan」となります。 そして、「契約プラン別のユーザ」の中核の関心は「ユーザ」です。 しかし、「Users by Contract Plan」の末尾についているのは「Contract Plan」です。 このように、日本語では問題なかった命名が英語にした途端問題が発生してしまうことがあります。 では、どのように英訳すればよかったのでしょうか? 重要なポイントは、「日本語の語順のまま英訳する」ことです。 英訳する際に、日本語の語順を保つようにすれば中核の関心を最後に持ってくることが可能となります。 私は英語の専門家ではないので、AIに頼ってみましょう(Claude 3.7 Sonnetを使用)。 Q: 「〇〇別のXxx」を、「Xxx by 〇〇」ではなく、Xxxの前方につく修飾語で表現できると嬉しいのですが、何か案はありますか? A: 修飾語による表現方法の提案 「〇〇別のXxx」を「Xxx by 〇〇」ではなく、前方修飾語で表現する方法としては、以下のようなパターンがあります: 一般的なパターン 形容詞形に変換する 例:「地域別の売上」→ "Regional sales" 例:「性別の統計」→ "Gender-specific statistics" 所有格を使う 例:「部門別の予算」→ "Departmental budget" 例:「季節別の計画」→ "Seasonal plan" 複合語を形成する 例:「年齢別の分析」→ "Age-based analysis" または "Age-group analysis" 例:「業界別のレポート」→ "Industry-specific report" 接頭辞を活用する 例:「国別のデータ」→ "Country-specific data" または "Per-country data" 例:「時間別の記録」→ "Time-segmented records" Q: このようなケースで使用できる前方修飾語をまとめてください。 修飾表現別の「示す基準」の比較 各表現が示す基準や関係性の違いを整理します: 分類・区分に関する表現 based: 「〜に基づいた」- 分類や区分の基準を示す 例: Age-based grouping(年齢に基づくグループ分け) 適した用途: データ分類、カテゴリ分け、客観的基準による整理 specific: 「〜に特化した」- 特定対象への限定性を示す 例: Industry-specific regulations(業界に特化した規制) 適した用途: 特定分野だけに適用される事柄、限定された適用範囲 segmented: 「〜で区分された」- セグメント化の基準を示す 例: Price-segmented offerings(価格帯で区分された商品) 適した用途: マーケティング分析、戦略的区分け classified: 「〜で分類された」- 分類のカテゴリーを示す 例: Skill-classified employees(スキルで分類された従業員) 適した用途: 階層化された分類、評価基準による区分 焦点・方向性に関する表現 focused: 「〜に焦点を当てた」- 注目点や重点を示す 例: Solution-focused approach(解決策に焦点を当てたアプローチ) 適した用途: 戦略や方法論、特に重視する側面の強調 oriented: 「〜志向の」- 方向性や傾向を示す 例: Result-oriented metrics(結果志向の指標) 適した用途: 思想的傾向、アプローチの方向性 centric: 「〜中心の」- 中心的要素を示す 例: User-centric design(ユーザー中心の設計) 適した用途: 設計思想、価値観の中心 対象・用途に関する表現 targeted: 「〜を対象とした」- 明確なターゲットを示す 例: Senior-targeted services(高齢者を対象としたサービス) 適した用途: マーケティング、サービス提供の対象指定 tailored: 「〜向けに調整された」- カスタマイズの対象を示す 例: Client-tailored solutions(クライアント向けに調整されたソリューション) 適した用途: パーソナライズされたサービス、個別調整された提案 dedicated: 「〜専用の」- 専用化・排他的用途を示す 例: Developer-dedicated tools(開発者専用のツール) 適した用途: 特定グループだけが使用するもの、専用設計された製品 観点・視点に関する表現 wise: 「〜の観点から」- 視点や観点を示す 例: Department-wise budgeting(部門の観点からの予算編成) 適した用途: 分析視点、複数の観点からの考察 AIの回答を表にまとめてみます。 修飾表現別の「示す基準」比較表 分類・区分に関する表現 表現 意味 例 適した用途 based 〜に基づいた Age-based grouping(年齢に基づくグループ分け) データ分類、カテゴリ分け、客観的基準による整理 specific 〜に特化した Industry-specific regulations(業界に特化した規制) 特定分野だけに適用される事柄、限定された適用範囲 segmented 〜で区分された Price-segmented offerings(価格帯で区分された商品) マーケティング分析、戦略的区分け classified 〜で分類された Skill-classified employees(スキルで分類された従業員) 階層化された分類、評価基準による区分 焦点・方向性に関する表現 表現 意味 例 適した用途 focused 〜に焦点を当てた Solution-focused approach(解決策に焦点を当てたアプローチ) 戦略や方法論、特に重視する側面の強調 oriented 〜志向の Result-oriented metrics(結果志向の指標) 思想的傾向、アプローチの方向性 centric 〜中心の User-centric design(ユーザー中心の設計) 設計思想、価値観の中心 対象・用途に関する表現 表現 意味 例 適した用途 targeted 〜を対象とした Senior-targeted services(高齢者を対象としたサービス) マーケティング、サービス提供の対象指定 tailored 〜向けに調整された Client-tailored solutions(クライアント向けに調整されたソリューション) パーソナライズされたサービス、個別調整された提案 dedicated 〜専用の Developer-dedicated tools(開発者専用のツール) 特定グループだけが使用するもの、専用設計された製品 観点・視点に関する表現 表現 意味 例 適した用途 wise 〜の観点から Department-wise budgeting(部門の観点からの予算編成) 分析視点、複数の観点からの考察 AIに質問したところ、上記のような前方修飾語に変換する方法をまとめてもらえました。 では、これをもとに「契約プラン別のユーザ」を英訳してみましょう。 「契約プラン別」というのは、分類・区分に基づく表現です。 そのため、今回のケースでは「based」が適切であると考えられます。 よって、「契約プラン別のユーザ」を英訳したものは ContractPlanBasedUser となります。 このように、中核の関心が後方一致になるように命名することが重要です。 Byは使用できないのか? すべての命名においてByが使用できないのかというと、そうではありません。 あくまでも、関心な名前として語順が崩れる場合に使用できないということに留意してください。 例えば、データベースからユーザIDでユーザを取得してくる関数の名前は getUserByUserId などと命名することが一般的だと思います。 このとき、関心は User であり、 ByUserId は関心ではありません。 したがって、このようなケースではByを使用することができます。 ただし、契約プランIDで契約プラン別のユーザ一覧を取得してくる関数の場合は getContractPlanBasedUsersByContractPlanId となります。 なぜなら、この場合の関心は「契約プラン別のユーザ(一覧)」であり、「契約プラン別」も関心に含まれているためByを避け日本語の語順を保つようにするためです。 後方一致であることのメリット 関心を後方一致でまとめることのメリットは、語順を保つことができる点です。 そして、語順を保つことのメリットは、一貫性と対称性を担保することができる点です。 一貫性 命名における一貫性とは、概念順序規則にしたがっていることです。 概念順序規則とは、「名前の要素を意味のある概念ごとに順序立てて配置する考え方」です。 例えば、 getUserByUserId を概念ごとで分けると get / User / ByUserId となります。 まず、 get は操作(動詞)です。他には、 find や remove 、 create など。 次に、 User は関心です。そして、 ByUserId は方法・条件です。 つまり、データベースの操作というパターンにおいて、その関数名は常に操作 -> 関心 -> 方法・条件という概念順序で命名されるべきであるということです。 この、概念順序規則にしたがって命名されている状態を一貫性が高いと表現します。 もちろん、パターンが異なればその概念順序も異なります。 例えば、コンポーネント名であれば、その概念順序は関心 -> 状況・状態 -> UIの型となります。 ここで重要なのは、同一パターン内では常に一定の概念順序規則にしたがっているという点です。 概念順序規則を遵守することで、命名の一貫性を向上させることができます。 対称性 命名における対称性とは、複数の命名を並べてみたときに、それぞれの概念が対応していることです。 例えば、コンポーネント名における概念順序が関心 -> 状況・状態 -> UIの型のとき、「記事を投稿するフォーム」が ArticlePostForm なら「コメントを投稿するフォーム」は CommentPostForm であるべきということです。 日本語において「コメント」という単語は名詞だけでなく「コメントする」という動詞的な意味も含まれがちですが、それに引っ張られて「コメントを投稿するフォーム」を CommentForm と命名するのは早計かもしれません。 もし、あとから「コメントを編集するフォーム」が必要になった場合、そのコンポーネントは CommentEditForm と命名されるでしょう。ところが、「コメントを投稿するフォーム」は CommentForm と命名されているのです。 これが対称性を崩す要因となります。  あとから類似のものが出てきた際に、対称性が崩れないかに注意して命名しましょう。 まとめ 今回の記事で重要なポイントは以下の4つです。 関心には、「中核の関心」と「補助の関心」が存在する。命名する際は「補助の関心」も含めて表現しきる必要がある 中核の関心が英語名の末尾になるように、日本語の語順のまま英訳する 命名は「操作 -> 関心 -> 条件」や「関心 -> 状況・状態 -> UIの型」など、概念順序規則に従うことで一貫性を保つ。 同じパターンの命名間で構造を揃えることで対称性を実現する。 これらのポイントを意識し、一貫性と対称性が高い命名ができていると、迷いが生じなくなっていきます。 なぜならば、名前自身がどうするべきか教えてくれるようになるからです。 例えば、適切に命名されたディレクトリ構造では、ファイルをどこにいれるべきか、どのようなファイル名・ディレクトリ名にするべきかに迷うことはなくなります。 命名は、コードにコメントを書くことと同様、もしくはそれ以上に「それが一体何なのか・何ではないのか」を説明することができます。 こうして「命名」は「ドキュメンテーションにおける最も基本的な手段」となっていくのです。
見事統計検定1級で撃沈してしまいました。 そこで2024年の問題で問われた「フィッシャー情報量」「クラメール・ラオの下限」について、整理し、メモを供養します。 ↓2024年統計検定1級 統計数理第1問 統計検定1級の過去問 https://www.toukei-kentei.jp/preparation/kakomon パラメータ推定 パラメータ推定の話を考えます。 母集団から得られたサンプルデータ群から、母集団の平均や標準偏差を推定することをパラメータ推定といいます。 特に、例えば「この母集団の平均値は1.4だ!」などのようにジャストでパラメータを言い当てることを「点推定」といいます。 実際には、実験して得られた標本(実現値)から、パラメータを推定することになるので、点推定は「標本から推定値を得るための関数を作り、その関数に実現値を代入して推定値を求める」ことになります。 数式的には、パラメータ θ を持つ母集団から得られるサンプルを X = ( X 1 , . . . , X n ) とおき、その関数 θ ^ ( X ) を構築します。 実験によって実現値 x = ( x 1 , . . . , x n ) を得られたとき、 θ ^ ( x ) が点推定値となります。 点推定の方法 最尤 さいゆう 推定法(MLE, Maximum Likelihood Estimation) 一番有名な点推定の方法かなと思います。理論上、最も良い推定量を得られることが証明されているようです。 サンプル X = ( X 1 , . . . , X n ) の同時確率関数を、パラメータ θ の関数として、 L ( θ ) = ∏ i = 1 n f ( x i | θ ) とします。つまり、サンプル中のデータ x i すべてに対して、それが得られる確率を計算し、すべて掛け合わせた関数です。 この関数のことを「尤度関数」と呼びます。 この尤度関数を最大にする θ を、最尤推定量(MLE)と呼びます。θ^のようにあらわします。 L ( θ ^ ) = max θ L ( θ ) 例 ある養鶏所にて、採取される卵の重さが正規分布に従うとして、平均値(母平均)と分散(母分散)がそれぞれ未知の値 μ , σ 2 であるとし、 μ を推定するケースを考えます。 n個の卵を採取し、その重さがそれぞれ X 1 , X 2 , . . . , X n だった場合、 L ( μ , σ 2 ) = ∏ i = 1 n N ( X i | μ , σ 2 ) この対数をとって、 μ について微分すると、 ∂ ∂ μ l o g L ( μ , σ 2 ) = − n σ 2 ( μ − n − 1 ∑ i = 1 n X i ) となるため、 μ = 1 n ∑ i = 1 n X i とすると対数尤度が最大になることがわかります。 したがって、標本平均 1 n ∑ i = 1 n X i は μ の最尤推定量となります。 ここで上げた以外にも、「モーメント法」や「ベイズ法」などの推定方法もありますが、 前述の「最尤推定法」が最も精度が良い推定法となります。 クラメールの下限とフィッシャー情報量 点推定における、推定量の「よさ」とはどのようなものでしょうか。 点推定を行うとき、気になるのは、その推定量がどの程度ブレがあるか、というところになると思います。なるべくブレの少ない推定量を使いたいですよね。 この推定量の「ブレ」についてすこし議論してみたいとおもいます。 結論から言うと、最尤推定法による点推定の方法が、最もブレを少なくする方法となります。 これを示すためには、 1.推定量の分散 V a r θ ( θ ^ ) の下限を求める つまり、その母数(母集団のパラメータ)を推定したときに、どこまでブレを抑えられるのかを求める 2.最尤推定量が1. の下限(クラメール・ラオの下限)に一致する この2つを示す必要があります。 1.を示したのが、「クラメール・ラオの不等式」となります。 ・クラメール・ラオの不等式 適当な正則条件のもと、パラメータ θ の推定量 θ ^ = θ ^ ( X 1 , X 2 , . . , X n ) が不偏推定量(実験を繰り返すことにより真値に収束していく推定量)であるとき、次の不等式が任意の θ について成り立つ V a r θ ( θ ^ ) ≥ 1 n I ( θ ) (証明は こちら 参考に) この不等式は、任意の母数 θ に対して、不偏推定量 θ ^ の分散が 1 / n I ( θ ) より小さくならないことを示しており、この右辺の値を「クラメール・ラオの下限」と呼びます。 で、この 式の中に含まれている I(θ)I(θ)が、「フィッシャー情報量」という関数になります。 フィッシャー情報量は、次のような式です。 I ( θ ) = E [ ( d d θ l o g f ( X | θ ) ) 2 ] = ∫ X ( d d θ l o g f ( X | θ ) ) 2 f ( X | θ ) d X (𝓧は𝑿の定義域) つまり、「対数尤度関数をパラメータ変数で微分したやつを2乗した関数の、観測値に関する期待値」 一方、2. のほうは、クラメール・ラオの下限を求めたうえで、最尤推定量の分散がそれに一致することを証明する必要があります。 例 先ほどの例の「卵の重さの平均」の例を考えます。 各データが、独立同一分布に従うとき、 I ( θ ) は、1つのデータについてのフィッシャー情報量を求めればよいので、 先ほどの例の(*)の式で、 n = 1 と置いて、2乗した関数をXについて積分すれば、フィッシャー情報量が求まります。 つまりどういうこと? ここまでくると数式を解釈しての理解は困難ですが、フィッシャー情報量が、クラメール・ラオの下限の分母に来ていることを考えると、 「フィッシャー情報量が大きいほど、推定量の分散を下げられる(ブレを抑えられる)」 といえそうです。 フィッシャー情報量は、「その母数を推定するための情報がどれだけ得られるか」と解釈してよいでしょう。 冒頭の「問1」に関しては、線形単回帰モデルの「回帰係数ββ」の不偏推定量をまず求めないと、フィッシャー情報量やクラメール・ラオの下限の話に行けないという構造になっていますので、どこかで線形単回帰モデルについても触れたいと思っています。
はじめに 今話題のMCPですが、WebデザインやUIデザイン向けツールのFigmaでもMCPが公開されました。 本記事では、下記をメイン書いています FigmaMCPを利用できるまで 簡単チュートリアル MCPとは AIモデルと外部サービス(システム)を連携するための「共通ルール(プロトコル)」です。 各社がこのルールに沿った「AI専用コントローラー(MCPサーバー)」を提供し、AIアプリ開発者(MCPクライアント)はそれを呼び出すだけで各種サービスと連携可能になります。 これにより、様々なサービスをAIエージェントで操作できるようになります。 FigmaMCPとは AI が Figma のデザインデータに直接アクセスし、そのデータを理解・操作することができるようになります。 実際に、Figmaはこの辺りを参照するようです。 デザイン情報 (色・フォント・サイズ・間隔 etc..) コンポーネント間の関係 Figma内のコメント この辺りが参照できると今までできなかった下記タスクもAIにお願いできるようになりそうです。 Figmaのデザインをより忠実に再現 レスポンシブ対応 コンポーネント関係の解釈 ( 参考 Figma MCPとは?何ができるの? ) 利用できるまでの作業 本手順は、github copilot でFigmaMCPを導入する手順です。 ①個人のアカウントでFigmaのAccount settingsを開く ②トークン発行 security>Personal access tokens>Generate new token token名を入力 → File content を read-only にする → Generate token ③MCPの設定をvscodeで行う vscodeの設定用のjsonを修正します。 mcp.servers の中に下記の設定を追記します。 "mcp": { "inputs": [], "servers": { "figma-developer-mcp": { "command": "npx", "args": [ "-y", "figma-developer-mcp", "--figma-api-key=${③のトークン}", "--stdio" ] } } " mcp " : { " inputs " : [] , " servers " : { " figma-developer-mcp " : { " command " : " npx " , " args " : [ " -y " , " figma-developer-mcp " , " --figma-api-key=${③のトークン} " , " --stdio " ] } } ④vscode の github copilotのエージェントモードを有効にする vscodeの設定から chat > Agent: Enabled を有効にする (出てこない場合は、アップデート&再起動をする) ⑤github copilotをエージェントモードに変更する github copilotのチャットを開き、エージェントモードに変更する 以上です!これで利用できるようになります。 試しにコンポーネントを出力してもらう 今回は、簡単なカードコンポーネントを出力してもらいます。 ①FigmaからフレームのURLを取得します。 該当フレームを選択しながら、右上のシェアボタンを教えて、コピーします。 ②実際に書いててもらう 今回はこんなプロンプトで指示してみました figmaを参照して、カードコンポーネントを作成してください・外部からプロップスを渡せるようにしてください。・tailwindで実装してください・セマンティックなHTMLを心がけてください。card component:https://www.figma.com/design/.... figmaを参照して 、 カードコンポーネントを作成してください ・ 外部からプロップスを渡せるようにしてください 。 ・ tailwindで実装してください ・ セマンティックなHTMLを心がけてください 。 card component : https : //www.figma.com/design/.... Figmaを参照するか聞かれますが、続行してください。 気になる出力結果は... かなり高クオリティーです... ! タグの絶対配置の部分だけ設定されていませんね (出力している感じ、絶対配置はあまり得意でないように見えます) コードベースで見るとこんな感じです。 プロップスも想定通り渡せるようになっていたり、HTMLタグの使い分けもいい感じにできていて、ほぼ期待通りの実装に見えます。 import type { FC } from 'react';interface CardProps { image: string; tags: string[]; title: string; description: string; onClickDetail: () => void;}export const Card: FC<CardProps> = ({ image, tags, title, description, onClickDetail,}) => { return ( <article className="flex flex-col bg-white border border-[rgba(17,17,17,0.1)] rounded-2xl overflow-hidden"> <div className="relative aspect-video"> <img src={image} alt="" className="w-full h-full object-cover" /> </div> <div className="flex flex-col justify-between p-6 flex-1"> <div className="space-y-4"> <div className="flex gap-2.5 flex-wrap"> {tags.map((tag, index) => ( <span key={index} className="px-4 py-3 text-sm font-bold text-[#111111] border-2 border-[rgba(17,17,17,0.1)] rounded-full" > {tag} </span> ))} </div> <div className="space-y-2"> <h3 className="text-lg font-bold text-[#111111] leading-[1.3]"> {title} </h3> <p className="text-base text-[#111111] leading-6"> {description} </p> </div> </div> <button onClick={onClickDetail} className="w-full mt-4 px-6 py-4 text-sm font-bold text-[#111111] border-2 border-[rgba(17,17,17,0.1)] rounded-xl" > 詳しくみる </button> </div> </article> );}; import type { FC } from ' react ' ; interface CardProps { image : string; tags : string [] ; title : string; description : string; onClickDetail : () => void; } export const Card : FC < CardProps > = ( { image , tags , title , description , onClickDetail , } ) => { return ( <article className = " flex flex-col bg-white border border-[rgba(17,17,17,0.1)] rounded-2xl overflow-hidden " > <div className = " relative aspect-video " > <img src ={ image } alt = "" className = " w-full h-full object-cover " /> </div> <div className = " flex flex-col justify-between p-6 flex-1 " > <div className = " space-y-4 " > <div className = " flex gap-2.5 flex-wrap " > { tags . map ( ( tag , index ) => ( <span key ={ index } className = " px-4 py-3 text-sm font-bold text-[#111111] border-2 border-[rgba(17,17,17,0.1)] rounded-full " > { tag } </span> )) } </div> <div className = " space-y-2 " > <h3 className = " text-lg font-bold text-[#111111] leading-[1.3] " > { title } </h3> <p className = " text-base text-[#111111] leading-6 " > { description } </p> </div> </div> <button onClick ={ onClickDetail } className = " w-full mt-4 px-6 py-4 text-sm font-bold text-[#111111] border-2 border-[rgba(17,17,17,0.1)] rounded-xl " > 詳しくみる </button> </div> </article> ) ; } ; まとめ 今のところの感想としては、LovableやReplitの各種AIツール や Figma to Codeなどのコード生成系のFigmaプラグイン と比較して、精度がはるかに高いように見えます...! 今回は、FigmaMCPの使い方 + チュートリアルをやってみましたが、次回はもう少し高難易度の出力や、他ツールとの比較とかもやってみようと思います!
初めに 2025/4/9から4/11(現地時間)にかけて、Google Cloud Next 25がラスベガスにて開催されました! マイナビからは、企画、データサイエンティスト、エンジニア、マーケターの4名が現地に向かい、参加してきました。 本記事では、Cloud Next 25のイベントの概要や、面白かった内容について、レポート形式でご紹介いたします。 概要編:Cloud Next 25ってどんなイベント? Cloud Nextとは、Google Cloudによって主催される、おもにGoogle Cloudについてのテックカンファレンスです。 毎年、KeyNoteセッションで、GoogleCloudの新しいプロダクトや、新しいコンピューティング技術などが発表されます。KeyNoteが世界で初めてその内容を知ることができる場所であり、毎年異様な盛り上がりを見せます。 KeyNote以外には、Google Cloudを利用している企業から、利用事例についてのプレゼンテーションであるBreakout Sessionや、Google Cloudの技術ノウハウをライトに共有する場であるLightning Talk、Cloud Skills Boostの体験や、デモを実際にさわりながらGoogle Cloudを学ぶことができるハンズオンのコーナー、ほかの参加者と交流ができるMeetup、実際にGoogler(Google社員)やGoogle Cloudのカスタマー企業にデモを見せてもらいながら、ノウハウを教えてもらったり技術交流ができるExpoがありました。 会場 会場となったMandalay Bayのコンベンションセンターですが、とにかく大きな会場でした。 1日中あちこちでセッションを聞いていると、気づいたら4万歩ぐらい歩いていました。 参加レポート KeyNote 会場に入ると、そこはライブ会場のよう。 基調講演の前には、VeoVJによるパフォーマンスで出迎えてくれました。 Veoによって生成された映像を、曲に合わせて選択して流してくれるAI、ということでしょうか。 Veo を使ったメディア制作のデモをKeyNote中で実施してくれています。 →  https://www.youtube.com/live/Md4Fs-Zc3tg?si=qDTeEgSNU2cG8Q9o&t=1996 セッションの具体的な内容については、すでに様々なブログで触れられているのですが、主な内容としては 動画生成AI「Veo 2」、作曲AI「Lyria」の公開 新TPU「Ironwood」 Agentspace データエージェント このあたりのアップデートがとても大きかったと思います。 内容について少し触れます。 Veo 2 Veo 2は、Google DeepMind社によって開発された、動画生成AIです。 Next以前からVeoに関する発表は2024/11月にあったのですが、この日をもってGAとなり、 Vertex AI Media Studio から利用できるようになりました。 映像生成については、DeepMind社の公式サイトからプロンプトと生成された映像のサンプルを確認できます。 https://deepmind.google/technologies/veo/veo-2/ 中でもすごいと思ったのは、2枚の写真を用意すると、その間を補間するように映像を生成する、というものでした。 ( 参考動画 ) これはすごいですね!映像編集の幅が広がりそうです。 Lyria こちらもDeepMind社の開発した音楽生成AIです。 プロンプトを入力すると、30秒ほどの音楽が生成されます。 なんとボーカルも入れられるそうです。 公式サイトから、プロンプトとそれによって生成された音楽が視聴可能です。 https://deepmind.google/technologies/lyria/ もはや人の作ったものと区別がつかなくなってきていますね・・・! なお、前述のVeoや、音声合成AIのChirp 3、画像生成AIのImagen 3と合わせて、 Vertex AI Media Studio というプロダクトから利用可能となっています。 Google I/Oでさらに上位モデルが登場 これらのマルチモーダル生成AIのうち、Veo, Imagen, Lyriaに関しては、すでに新しいバージョンのモデルが発表されています。 Veo 3 Imagen 4 Lyria 2 Next 25の開催からなんとたった1か月後にこれらのアナウンス。。AIの進展の速さを肌身で感じます。 参考記事⇒  Google Cloud 公式ブログ Ironwood Ironwoodは、Google社によって開発中の第7世代TPUの名称で、 2025年末にはリリースされる予定とのことでした。 これは初代のTPUと比べて3600倍のパフォーマンス向上です。 ひとつ前のモデルと比べても900倍近く向上しています。 これは劇的な進化ですね。 KeyNoteでIronwoodに触れられているシーンは こちら です。 Agentspace Agentspaceは、自社で利用しているオフィススイート(MS OfficeやGoogle Workspace, Salesforceなど)のツールと結合可能なAIエージェントのインタフェースです。 AIエージェントは、Googleが提供するもの以外にも自社内で作成され共有されたものも、Agentspaceから利用できます。 自社組織の意思決定の内容について、SharePointやGoogleDriveにある資料を探すのは大変だと思いますが、 Agentspaceで質問すると、SharepointやGoogleDriveに散乱する資料の中から情報を抽出してユーザーに回答してくれます。 生成された回答の内容のもととなった資料も示してくれます。 デモの中では、リクエストの内容によって、エンタープライズデータ、時系列予測モデル、音声合成、メール送信など多くのツールを利用していますが、すべてAgentspace経由で、1行もコードを書いていません。 Agentspaceを使ったデモンストレーションのシーンは こちら になります。 メールや要約内容については、妥当性を自分で確認する必要はありますが、ノーコードで多くのエージェントツールにアクセスできるようになり、文面作成など多くの手間のかかるシーンで活躍することが期待できます。 データエージェント データクレンジングやインサイト抽出がGeminiによって強化された「Data Agent」の紹介です。 これまでは、まずBigQueryにデータを定期的に連携し、扱いやすい形(データマート)に変換し、 抽出したいインサイトを得るためのクエリをガシガシと書いて、実行していたと思います。 この作業で大変なことは、まず複数のデータソースに散らばっているデータを結合やクレンジングし、分析に使用できる状態にするところ。 ここで活躍するのが「Data Engineering Agent」で、プロンプトベースでデータ処理パイプライン構築していくことができます。 編集中のパイプラインの結果を逐一確認しながら構築を進めていくことができます。 日付の表記もばらばらになっていますが、動画を見るとこの名寄せに関しても直してくれています。これはありがたいですね。 使っているプロダクトとしては少し前からGAになっていた「BigQuery パイプライン」というものなのですが、その構築をGeminiが支援してくれるというものですね。 また、インサイト抽出に関しても、プロンプトでどんなインサイトを抽出したいかをリクエストすると、Geminiがクエリの生成とクエリの実行をしてくれます。 クエリの内容は、カラム名などのメタ情報から生成しているようでした。 実際は、クエリの内容や可視化内容が正しいことに関しては、分析者が最終的にレビューをする必要がありますが、 クエリを作成する時間がこれによって大きく節約できることや、クエリを書くことへの技術的ハードルが大きく下げられることが期待できます。 Data Agentの紹介シーンは こちら です。 Expo Expoでは、KeyNoteで発表のあった新しいサービスのデモを、実際にGoogle社員の方に実演してもらったり直接質問ができるコーナーがありました。 デモのご紹介: GeminiとVertex AI Searchによるマルチモーダル検索 スマホで部屋の写真を撮って、「この部屋に合う家具を探して」という検索方法であったり、もちろん音声検索も対応。 1回検索を実行した後、元のクエリから拡張されたクエリで再検索し、そのアイテムも表示する、ということもやっていました。 こちらはYouTubeでデモ映像が公開されていました。 https://youtu.be/LwHPYyw7u6U?si=vOtgUrk9yK_F4Uqf Meetup Cloud Nextの楽しみ方の一つとして、ほかのエンジニアの交流があります。 そのひとつが Meetup というもので、Cloud RunやVertex AIなどのプロダクトについてのものであったり、Platform EngineeringやObservabilityなど、ホットなトピックについてエンジニアが熱い意見を交わすというもの。 非常に貴重な経験でした。 全体を通して 全体的な感想として、AIやコンピューティングの分野における技術巣発展スピードは非常に早いと思いました。 Veoのアップデートの件でも触れましたが、1年前の技術が今年はレガシーになっているような世界です。 こういった新しい技術は、簡単に試せるようなものも多いので、自社サービスや業務フローに取り入れることを検討してみてもよいかもしれません。 終わりに 本イベントでの内容を踏まえて、マイナビでIT職の仕事をしている社員向けにデモを交えた共有会を開いたところ、80名以上の参加がありました! 実際に使ってみたい、自身の業務の中で取り入れてみたい、などの声もいただいた一方で、 業務での活かし方がわからないといった意見もありました。 こういった視察活動を続けていくことで、少しずつAI活用の輪を広げていけるのではないかな、と感じた次第です。 また、来年も4月末に、ラスベガスでCloud Nextを開催するとのことでした。 会場も同じMandalay Bay Convention Centerです。 もし興味を持たれた方は、ぜひ参加してみてください!
CXMツールとは お疲れ様です。MA推進課のA.Kと申します。 普段は就職・ライフキャリア事業本部のオウンドメディアとMarketoなど、事業部のマーケティング担当者が使うシステムの運用保守を担当しています。 今回、MA推進課としては初となる、アルバイト情報事業本部のCS部門(カスタマーサクセス)と連携した新たな施策を試みたので内容を共有できればと思います。 タイトルにもある「CXM」という言葉について聞き馴染みがある方は少ないのではないでしょうか。 CXMとは Customer Experience Managementの略で、顧客体験の管理や改善を行い、顧客体験(CX)を最大化するための手法のことを指します。 商品やサービスの価格やスペックなどだけでなく、その商品・サービスにより得られる満足感や感動、心地よさなどを向上させることは、顧客ロイヤルティを醸成し収益向上へとつながります。具体的な目的は、顧客との関係性の強化やブランド構築などが挙げられます。 ※引用:顧客体験マネジメント(CXM)とは?CRMとの違いや成功のポイント、企業事例を紹介, docomo business, https://www.nttcoms.com/service/nps/column/cxm/ CXMをどう活用していくか 売り上げ向上には、新規顧客の発掘だけでなく、既存顧客がどうやったら離反しないか、継続して利用してくれるのかも重要になります。 そのため「お客様の声を聴く」ことで、顧客体験とマイナビ認識の乖離をキャッチアップし、営業活動改善や、社内に対して成功事例の共有をすることを目指していきます。 デジ戦が手伝ったこと 前置きが長くなりましたが、本題に入れればと思います。 上記のCXMの運用は事業部側が行っています。以下に今回のシステムの全体図を載せておきます。 SFA/CRM 企業の情報や商談の情報などが入っています。 掲載情報DB マイナビバイトに掲載されている情報が格納されている社内向けのDBです。 GCS BigQuery S3 Trocco クラウド・サービス間のデータ転送に使うツールです。今回の主役。 CXMツールでは、「送付リスト」という形でcsvファイルを渡すことで、自動的に配信が行われる仕組みとなっております。ただし、そのリストに持たせる情報には、通知先はもちろん、分析に使う項目など、複数DBを参照する必要があるうえ、細かい送付条件が多数ありました。そこで、デジ戦側でリスト作成のシステムを作ってみることにしました。 今回はtroccoを使用して以下のステップでCXのアンケートを送る送付リストを作ります。 ①各DBの情報をBQに集約させる ②頑張ってテーブルを結合して必要な情報を抽出する ③作成した配信リストをS3に格納→あとはCXMツール側で自動で配信してくれます。 ①各DBの情報をBQに集約させる 最初にSFA/CRMのデータをtroccoを用いてBQに格納します。 案件の情報を始め、掲載情報やマイナビ社員の情報を参照します。 ここで大苦戦したのが履歴テーブルの存在です。 単純に1つのテーブルを参照すればよいわけではなく、履歴テーブルからも一部情報を参照する必要があり、複数テーブルを行ったり来たりしていました。 以下苦労点です。 参照しなければいけないテーブルが5個あり結合および参照に苦戦 数ある商品から特定の商品を省くロジックを正規表現を使って実装 タイムゾーンがUTCとJSTが入り混じってタイムスタンプ型が嫌いになる 次に掲載情報DBのデータをBQに格納します。 アンケート送付タイミングのタッチポイントを判別できるように必要な項目に絞って取得します。 こちらも4テーブル参照して結合を繰り返していました…。 最後に営業マスタを手動でアップロードして準備完了です。 ②BQ内でデータを結合する BQで集めたデータを結合しつつ、送付対象の条件を追記してリストを作成していきます。 使っているSQLのコードは自身が初学者ということもありかなりシンプルです。 ROW_NUMBER() OVER (PARTITION BY ○ ORDER BY ○ ASC,□ DESC) AS rownumber マスキングかけてるので分かりにくいかもですが 1つの項目に対して複数のデータが紐づいているので、データを降順に並び替えて一番最新のデータの情報を使用します。 後はSFA/CRMと掲載情報DBのデータをキーとなる項目で紐づけてリストの大元は完成です。 後は事業部サイドの運用上の条件を実装していきます。 --送付条件の実装DELETE FROM 対象のテーブル WHERE 特定のカラム NOT IN (SELECT CAST( 特定のカラム AS STRING) –元のDBによって型が違うので一致させるFROM ○○テーブル ); このような条件を要望に沿って同様に実装していきます。 この際にも複数DBを参照し、同じ項目でもデータの内容に相違がないか、空の項目がないかなど確認しています。 コードを見ていただいて分かるように、大元のリストから条件に当てはまらないレコードを削除しています。 この時点で残ったデータをCXMツールに受け渡しできるような形に成形して処理は完了です。 ③ワークフローの作成 今までクエリを書いてリストの作成ができましたが、実際に運用するのは事業部の方たちです。 なので専門的な知識がなくても、「最小限の工数で」運用してもらえるようにしていきます。 ここで重宝したのがtroccoのワークフロー機能です。 上記で記述したデータ抽出&リスト作成のクエリを登録し、それを順次自動で実行してくれます。 スクショの例でいうと、SFA/CRMからBQへデータ連携→掲載情報DBからBQへデータ連携→BQでリストの作成→BQからS3へファイルの格納をボタン一つで実行してくれます。 (加えてスケジュール設定ができるので自動化も可能) ここまで作成して細かい仕様もお伝えし、事業部にお渡しします! 初めての開発経験をしてみて 学生時代も情報系の学部にいましたが、このレベルの開発をするのも今回が初めてでした。 配属から1年ちょっとは開発はベンダーさんにお任せしていたのですが、自分で作るとこんなに大変なんだなとベンダーさんの凄さを実感しました。 一方で、SQLをがっつり触ったことで知識が付いたのはもちろん、マイナビの掲載の仕組み・営業フローについても知れる良い機会でした! コード自体は基本自分で書きましたが、「仕様書も作成する」「ワークフローはなるべく簡略化する」「変更を加える際はデータ不整合が起きないことを確認してから本番適用する」など、開発の基本的な部分も開発経験が豊富なK.S課長にサポートしていただき、無事にリリース&安定稼働まで繋げることができました。 (前職の経験を踏まえた上長のアドバイス×新卒社員のDCPA精神で大分デジ戦らしい取り組みになったのでは!?と個人的には思っています。) 我々ができるのはメールリストの作成までですが、これを利用して今後事業部内でどのような動きが起きていくのか、引き続き技術的なサポートができる箇所があるのか、リリースして終わりではなく、その先にあるビジネスインパクトまで協力して取り組んでいければと思います! 以上、SaaSを組み合わせてメールリストを作成できるよというお話でした!
はじめに こんにちは、ゼネラルコンテンツ推進課のI.Tです。 私の課で運営しているエンジニア向けメディアでは、毎月SEO記事を新規・リライト含め30~40本程度リリースしております。(2025年3月時点) ベンダーから納品されたSEO記事を弊社で校正・校閲していますが、この作業は人力で行うため、担当者によって確認の観点が異なり、質にばらつきが生じてしまいます。 校正・校閲の質を上げ、作業時間を削減できないかと考え、まずはJAPAN AIの利用が頭に浮かびました。しかし、より柔軟に対応できるCopilotエージェントを利用できないかということで、同じ課のY.Kさんが校正・校閲のCopilotエージェント「校正エディター」を提案しました。 Y.Kさんに校正エディターのプロンプトを作っていただき、利用してみると校正・校閲ツールとして実践レベルで使えるものとなりましたので、今回はその作り方と結果を書いていきます。 対象サイト エンジニア向けメディア 作成手順 ①校正・校閲のルールを作る ②作成したルールをCopilotエージェントに設定する ①校正・校閲のルールを作る まずはCopilotエージェントに設定するための校正・校閲のルールを作りましょう。 (内容は社外秘の為、本記事では伏せさせて頂きます。) ②作成したルールをCopilotエージェントに設定する 上記で作成したルールをCopilotエージェントに設定します。 Copilotエージェントの作成画面にある「構成」の各項目に、作成したルールを当てはめる形で設定します。 ▼設定した構成はこちら 名前:校正エディター説明:Webメディアの編集者として、原稿の校正・校閲を行うエージェントです。誤字脱字、表現ミス、表記揺れ、口語表現、単語の繰り返しなどを具体的に指摘します。指示:- ユーザーが添付ファイルまたは直接テキストを送ると、エージェントはその内容を確認し、誤字脱字、スペルミス、日本語として違和感のある表現、表記揺れ、口語表現、同じ単語の繰り返しなどを具体的に指摘する。- 読者が明らかに違和感を持つ点については、強調して指摘する。- 原稿の記号(マークダウンなど)については指摘しない。- 指摘は以下の形式で出力する。 - 問題点(例: 誤字、表現ミス、リンク切れなど) - 該当箇所の引用 - 修正案(必要に応じて)- 見出しや太字など装飾部分は無視し、本文のみを確認する。- 校正校閲の専門家として、専門的な立場で対応する。▼スタータープロンプト※下記を「タイトル」と「メッセージ」にそれぞれいれてください。 誤字脱字の指摘この文章に誤字脱字がないか確認してください。 表現の違和感の指摘この文章に日本語として違和感のある表現がないか確認してください。 表記揺れの指摘この文章に表記揺れがないか確認してください。 口語表現の指摘この文章に口語表現が含まれていないか確認してください。 単語の繰り返しの指摘この文章に同じ単語が繰り返されていないか確認してください。 総合的な校正・校閲この文章を総合的に校正・校閲してください。 Copilotエージェントへの設定方法は、下記の記事で紹介していますので気になる方はご確認ください。 Copilotエージェントでエンジニア向けメディアのペルソナを作ってみた https://marketingblog.mynavi.jp/marketing/copilot_persona_creation/ 実際に作成したエージェントは以下です。 これで「Copilot へメッセージを送る」の箇所に記事の全文をコピペすれば、勝手に校正してくれるようになりました。 実際に使ってみた どの程度の質で校正・校閲してくれるのでしょうか。 試しに、3年ほど更新していなかった以下記事について校正エディターに校正してもらいました。 質問:記事内容コピペ 回答:校正エディター 以下の点について指摘します。1. 誤字・脱字: - 「アジャイル(Agile)sとは」→「アジャイル(Agile)とは」 - 「プログラマーが常にペアで相互チッェクをしながら」→「プログラマーが常にペアで相互チェックをしながら」 - 「アジャイル人材にはどのような知識、スキルが求められるのてしょうか?」→「アジャイル人材にはどのような知識、スキルが求められるのでしょうか?」 - 「DX(テジタル・トランスフォーメーション)」→「DX(デジタル・トランスフォーメーション)」2. 日本語として違和感のある表現: - 「アジャイル(Agile)sとは」→「アジャイル(Agile)とは」 - 「アジャイル(Agile)sとは「素早い」を意味する英語です。」→「アジャイル(Agile)とは「素早い」を意味する英語です。」3. 表記揺れ: - 「アジャイル開発」→「アジャイル開発」 - 「ウォーターフォール開発」→「ウォーターフォール開発」4. 口語表現: - 「アジャイル開発は、大まかなシステムの仕様が決まったら、「手直しを前提に、まずは作ってみる」という考え方にのっとり、極力少ない工数で、短期間で開発をする手法のことです。」→「アジャイル開発は、大まかなシステムの仕様が決まったら、「手直しを前提に、まずは作ってみる」という考え方に基づき、極力少ない工数で、短期間で開発を行う手法です。」5. 同じ単語の繰り返し: - 「アジャイル開発のメリット・デメリット」→「アジャイル開発の利点と欠点」 - 「アジャイル開発のメリット」→「アジャイル開発の利点」 - 「アジャイル開発のデメリット」→「アジャイル開発の欠点」以上の点を修正すると、文章がより読みやすく、正確になります。何か他にご質問や修正が必要な箇所があれば、お知らせください。 「1. 誤字・脱字:」については、さすがAIなので正しく添削してくれますね。 「4. 口語表現:」についても、正しく直してくれています。 しかし、「3. 表記揺れ:」の部分では、表記揺れをしていないのにもかかわらず指摘をしています。 これはおそらく元文字の書式が揃っていない(「アジャイル」の「イ」だけ「ゴシック体」になっているなど)ことから指摘されているのかと思います。 「5. 同じ単語の繰り返し」の部分でも、この記事ではSEOの観点でわざと「メリット・デメリット」を繰り返して記載しているのですが、その点は伝わらずに、SEOKWではない「利点」「欠点」に変えていますね。 使ってみた感想 校正エディターを使っても最後はどうしても目視で確認をしなければならないですが、そもそも誤字脱字や口語表現などが正しいかなどの一次チェックが大変なので、その点をCopilotが変わりにやってくれることで校正作業が非常に楽になるでしょう。 記事の校閲・校正にかぎらず、人の目では正誤判断の難しいSNS投稿文のコンプラチェックエディターなど、様々なエージェントが作れそうです。
はじめに こんにちは、ゼネラルコンテンツ推進課のI.Tです。 私の課で運営しているエンジニア向けメディアでは、毎月SEO記事を新規・リライト含め30~40本程度リリースしております。(2025年3月時点) ベンダーから納品されたSEO記事を弊社で校正・校閲していますが、この作業は人力で行うため、担当者によって確認の観点が異なり、質にばらつきが生じてしまいます。 校正・校閲の質を上げ、作業時間を削減できないかと考え、まずはJAPAN AIの利用が頭に浮かびました。しかし、より柔軟に対応できるCopilotエージェントを利用できないかということで、同じ課のY.Kさんが校正・校閲のCopilotエージェント「校正エディター」を提案しました。 Y.Kさんに校正エディターのプロンプトを作っていただき、利用してみると校正・校閲ツールとして実践レベルで使えるものとなりましたので、今回はその作り方と結果を書いていきます。 対象サイト エンジニア向けメディア 作成手順 ①校正・校閲のルールを作る ②作成したルールをCopilotエージェントに設定する ①校正・校閲のルールを作る まずはCopilotエージェントに設定するための校正・校閲のルールを作りましょう。 (内容は社外秘の為、本記事では伏せさせて頂きます。) ②作成したルールをCopilotエージェントに設定する 上記で作成したルールをCopilotエージェントに設定します。 Copilotエージェントの作成画面にある「構成」の各項目に、作成したルールを当てはめる形で設定します。 ▼設定した構成はこちら 名前:校正エディター説明:Webメディアの編集者として、原稿の校正・校閲を行うエージェントです。誤字脱字、表現ミス、表記揺れ、口語表現、単語の繰り返しなどを具体的に指摘します。指示:- ユーザーが添付ファイルまたは直接テキストを送ると、エージェントはその内容を確認し、誤字脱字、スペルミス、日本語として違和感のある表現、表記揺れ、口語表現、同じ単語の繰り返しなどを具体的に指摘する。- 読者が明らかに違和感を持つ点については、強調して指摘する。- 原稿の記号(マークダウンなど)については指摘しない。- 指摘は以下の形式で出力する。 - 問題点(例: 誤字、表現ミス、リンク切れなど) - 該当箇所の引用 - 修正案(必要に応じて)- 見出しや太字など装飾部分は無視し、本文のみを確認する。- 校正校閲の専門家として、専門的な立場で対応する。▼スタータープロンプト※下記を「タイトル」と「メッセージ」にそれぞれいれてください。 誤字脱字の指摘この文章に誤字脱字がないか確認してください。 表現の違和感の指摘この文章に日本語として違和感のある表現がないか確認してください。 表記揺れの指摘この文章に表記揺れがないか確認してください。 口語表現の指摘この文章に口語表現が含まれていないか確認してください。 単語の繰り返しの指摘この文章に同じ単語が繰り返されていないか確認してください。 総合的な校正・校閲この文章を総合的に校正・校閲してください。 Copilotエージェントへの設定方法は、下記の記事で紹介していますので気になる方はご確認ください。 Copilotエージェントでエンジニア向けメディアのペルソナを作ってみた https://marketingblog.mynavi.jp/marketing/copilot_persona_creation/ 実際に作成したエージェントは以下です。 これで「Copilot へメッセージを送る」の箇所に記事の全文をコピペすれば、勝手に校正してくれるようになりました。 実際に使ってみた どの程度の質で校正・校閲してくれるのでしょうか。 試しに、3年ほど更新していなかった以下記事について校正エディターに校正してもらいました。 質問:記事内容コピペ 回答:校正エディター 以下の点について指摘します。1. 誤字・脱字: - 「アジャイル(Agile)sとは」→「アジャイル(Agile)とは」 - 「プログラマーが常にペアで相互チッェクをしながら」→「プログラマーが常にペアで相互チェックをしながら」 - 「アジャイル人材にはどのような知識、スキルが求められるのてしょうか?」→「アジャイル人材にはどのような知識、スキルが求められるのでしょうか?」 - 「DX(テジタル・トランスフォーメーション)」→「DX(デジタル・トランスフォーメーション)」2. 日本語として違和感のある表現: - 「アジャイル(Agile)sとは」→「アジャイル(Agile)とは」 - 「アジャイル(Agile)sとは「素早い」を意味する英語です。」→「アジャイル(Agile)とは「素早い」を意味する英語です。」3. 表記揺れ: - 「アジャイル開発」→「アジャイル開発」 - 「ウォーターフォール開発」→「ウォーターフォール開発」4. 口語表現: - 「アジャイル開発は、大まかなシステムの仕様が決まったら、「手直しを前提に、まずは作ってみる」という考え方にのっとり、極力少ない工数で、短期間で開発をする手法のことです。」→「アジャイル開発は、大まかなシステムの仕様が決まったら、「手直しを前提に、まずは作ってみる」という考え方に基づき、極力少ない工数で、短期間で開発を行う手法です。」5. 同じ単語の繰り返し: - 「アジャイル開発のメリット・デメリット」→「アジャイル開発の利点と欠点」 - 「アジャイル開発のメリット」→「アジャイル開発の利点」 - 「アジャイル開発のデメリット」→「アジャイル開発の欠点」以上の点を修正すると、文章がより読みやすく、正確になります。何か他にご質問や修正が必要な箇所があれば、お知らせください。 「1. 誤字・脱字:」については、さすがAIなので正しく添削してくれますね。 「4. 口語表現:」についても、正しく直してくれています。 しかし、「3. 表記揺れ:」の部分では、表記揺れをしていないのにもかかわらず指摘をしています。 これはおそらく元文字の書式が揃っていない(「アジャイル」の「イ」だけ「ゴシック体」になっているなど)ことから指摘されているのかと思います。 「5. 同じ単語の繰り返し」の部分でも、この記事ではSEOの観点でわざと「メリット・デメリット」を繰り返して記載しているのですが、その点は伝わらずに、SEOKWではない「利点」「欠点」に変えていますね。 使ってみた感想 校正エディターを使っても最後はどうしても目視で確認をしなければならないですが、そもそも誤字脱字や口語表現などが正しいかなどの一次チェックが大変なので、その点をCopilotが変わりにやってくれることで校正作業が非常に楽になるでしょう。 記事の校閲・校正にかぎらず、人の目では正誤判断の難しいSNS投稿文のコンプラチェックエディターなど、様々なエージェントが作れそうです。
はじめに 私のチームではエンジニア向けメディアを運営しているのですが、メディア運営の意見出しや壁打ち相手ができるAIを作りたいなと思っていました。 そこで、実際にいるエンジニアを想定したペルソナをCopilot エージェントに設定し、壁打ち相手として対応できるようにしました。 今回は、Copilot エージェントの設定方法や使ってみた感想を書いていきます。 作成手順 ①ペルソナを作る ②作成したペルソナをCopilot エージェントに設定する ①ペルソナを作る まずはCopilot エージェントに設定するためのペルソナを作りましょう。 作成済のペルソナ 佐藤は27歳のエンジニアで 、 千葉県在住 。 彼は普通科高校を卒業後 、 理系大学を卒業し 、 ITの資格やクラウド ・ ネットワーク ・ 免許や数学英語の資格も保有している 。 彼は父 、 母 、 姉 、 妹と家族構成を持ち 、 最近結婚した 。 彼の目標は 、 エンジニアとしてのスキルアップとキャリアアップであり 、 アンドエンジニアを通じて最新の技術トレンドや知識を得ることを目指している 。 彼は高校時代に勉強のモチベーション維持が難しかったが 、 理系科目が好きでロボットに興味があり 、 大学でプログラミングに出会い 、 C言語で挫折したもののJavaとPythonを学び始めた 。 大学卒業後はエンジニアとして就職せず 、 輸入転売会社で倉庫整理の業務に従事 。 その後 、 エンジニアの道を目指し 、 パーソルテクノロジー株式会社に入社し 、 金融システムのネットワーク保守 ・ 運用業務に3年間従事 。 現在はSES ・ ネットワークエンジニアとしてソーシャルゲームのネットワークシステムの運用 ・ 保守業務に従事している 。 正社員として自宅から通い 、在宅と出社の比率は5 : 5 、 残業は平均30時間 / 月 。 現年収は450万円で 、 希望年収は550万円 。 現職の環境はお堅い社風で 、 セキュリティ意識が高く 、 成果主義の人事制度 。 クラウドエンジニアを目指して資格を取得したが 、 オンプレミスの現場に配属されていることに不満を抱いている 。 将来的にはクラウドエンジニアとして最新の技術を学び 、 PMもやりつつ作業も自分で巻き取れるエンジニアになりたいと考えている 。 趣味はゲーム 、 アニメ 、 自転車 、 ボルタリングで 、 業務後は家事をやった後にゲームをすることが多い 。 休日は自転車で遠出したり 、 ボルタリングをしたり 、 ネットワークの勉強をしたり 、 友人と飲みに行ったりしている 。 PCはプライベート ・ 会社貸与両方Windowsを使用し 、 携帯はAndroid 、 ブラウザはChromeを使用 。 よく見るサイトは業務ではCiscoの公式HP 、 非業務ではYouTubeやPing - t 。 交友関係は中学 、 高校 、 大学の同級生 、 家族 、 前職の同期 、 現職場の同期で 、 妻は前職の同期 。 友人は多いが 、 人見知りの面もあり 、 外部のコミュニティに連れて行っても交友関係が広がりにくい 。 ②作成したペルソナをCopilot エージェントに設定する Copilot エージェントとは 簡単に説明すると、特定の利用目的のためにCopilotを最適化できる機能です。 ChatGPTで言うカスタムGPTのようなものですね。 今回は、アンドエンジニアのペルソナの壁打ち相手をCopilot エージェントに設定したいので、そのように記載します。 Copilot エージェントの設定方法 ①Copilotを開き、右サイドバーの「エージェントの作成」をクリック ②Copilot Studio(Copilot エージェントを設定する画面)が開くので、ここで設定する このように、会話形式でエージェントを設定することが可能なのだ! 右上の「構成」を押すと、会話した内容に基づいてCopilotが設定したCopilot エージェントの構成が表示されます。この構成画面でCopilot エージェントの詳細設定を編集できます。 実際に作ってみた例 今回は、27歳のエンジニア「佐藤」というCopilot エージェントを作りました。 会話形式で指示し、以下のようなエージェントを設定しています。(会話のログが残っていなかったので会話部分はナシで失礼します。) 「構成」に設定した内容 名前 : 佐藤 説明 : 佐藤は27歳のエンジニアで 、 千葉県在住 。 彼は普通科高校を卒業後 、 理系大学を卒業し 、 ITの資格やクラウド ・ ネットワーク ・ 免許や数学英語の資格も保有している 。 彼は父 、 母 、 姉 、 妹と家族構成を持ち 、 最近結婚した 。 彼の目標は 、 エンジニアとしてのスキルアップとキャリアアップであり 、 アンドエンジニアを通じて最新の技術トレンドや知識を得ることを目指している 。 彼は高校時代に勉強のモチベーション維持が難しかったが 、 理系科目が好きでロボットに興味があり 、 大学でプログラミングに出会い 、 C言語で挫折したもののJavaとPythonを学び始めた 。 大学卒業後はエンジニアとして就職せず 、 輸入転売会社で倉庫整理の業務に従事 。 その後 、 エンジニアの道を目指し 、 パーソルテクノロジー株式会社に入社し 、 金融システムのネットワーク保守 ・ 運用業務に3年間従事 。 現在はSES ・ ネットワークエンジニアとしてソーシャルゲームのネットワークシステムの運用 ・ 保守業務に従事している 。 正社員として自宅から通い 、在宅と出社の比率は5 : 5 、 残業は平均30時間 / 月 。 現年収は450万円で 、 希望年収は550万円 。 現職の環境はお堅い社風で 、 セキュリティ意識が高く 、 成果主義の人事制度 。 クラウドエンジニアを目指して資格を取得したが 、 オンプレミスの現場に配属されていることに不満を抱いている 。 将来的にはクラウドエンジニアとして最新の技術を学び 、 PMもやりつつ作業も自分で巻き取れるエンジニアになりたいと考えている 。 趣味はゲーム 、 アニメ 、 自転車 、 ボルタリングで 、 業務後は家事をやった後にゲームをすることが多い 。 休日は自転車で遠出したり 、 ボルタリングをしたり 、 ネットワークの勉強をしたり 、 友人と飲みに行ったりしている 。 PCはプライベート ・ 会社貸与両方Windowsを使用し 、 携帯はAndroid 、 ブラウザはChromeを使用 。 よく見るサイトは業務ではCiscoの公式HP 、 非業務ではYouTubeやPing - t 。 交友関係は中学 、 高校 、 大学の同級生 、 家族 、 前職の同期 、 現職場の同期で 、 妻は前職の同期 。 友人は多いが 、 人見知りの面もあり 、 外部のコミュニティに連れて行っても交友関係が広がりにくい 。 指示 : - アンドエンジニアの読者であるペルソナとして振る舞う 。 - Webディレクターからの質問に答え 、 エンジニアとしての働き方やキャリアについて話す 。 - 施策の提案に対して 、 読者目線でのフィードバックを提供する 。 - エンジニアの働き方やキャリア ・ 技術について 、 本当に人物としている前提で会話を行う 。 - 話し方は丁寧で親切にする 。 ▼ スタータープロンプト ※ 下記を 「 タイトル 」 と 「 メッセージ 」 にそれぞれいれてください 。 エンジニアの悩み ・ エンジニアとしての悩みを教えてください 。 最新の技術トレンド ・ 最近注目している技術トレンドは何ですか ? キャリアアップの方法 ・ キャリアアップのためにどのような方法を取っていますか ? 学びのリソース ・ エンジニアとして学びのために利用しているリソースは何ですか ? 転職の準備 ・ 転職を考える際に準備していることは何ですか ? スキルアップの目標 ・ 今後のスキルアップの目標を教えてください 。 「スタータープロンプト」は、Copilotを開いた際に表示されるプロンプト例です。 最大で6つまで設定することができます。 色々と聞いてみた 普通のCopilotと、Copilot エージェント佐藤さんに同じ質問をしてみました。 普通のCopilotに比べて、佐藤さんがエンジニア視点で答えてくれるのか試してみました。 質問:あなたの趣味は何ですか 回答: Copilot(通常) Copilot エージェント(佐藤) Copilot(通常)は特定の趣味は持たないようですが、佐藤さんはエンジニアらしく?技術書やビジネスを読むことや、プログラミングが趣味のようです。 さらにどんな技術書がおすすめか聞いてみました。 質問:最近読んだ技術書の中でおすすめの書籍を10冊教えてください 回答: Copilot(通常) Copilot エージェント(佐藤) Copilotに聞くと技術書を「技術書典」と読み取り技術書典について答えてしまったり、おすすめしている本もマーケティング寄りの本が多いですね。 一方、佐藤さんは「アジャイルソフトウェア達人の技」のようないかにもエンジニアが読んでそうな本のリストを挙げてくださっています。 エンジニアの具体的な趣味を知る時に使えそうな気配はしています。 他にも質問してみました。 質問:好きなプログラミング言語やフレームワークは何ですか? 回答: Copilot(通常) Copilot エージェント(佐藤) Copilotは一般的にエンジニアが使っている言語を複数挙げていますが、佐藤さんはPythonが好きだと話した上で、お気に入りのフレームワークを理由を添えて教えてくれています。 質問:システム開発の工程で行われる「設計」について詳しく教えてください。 設計を行う流れと、大変な面ややりがいなど教えて 回答: Copilot(通常) Copilot エージェント(佐藤) やはりCopilotよりも佐藤さんの方が具体的に話してくださっています。 設計の大変な面について、Copilotは「要件の変更や追加に対応すること」などと抽象的な回答ですが、佐藤さんは「要件の不確定性」や「コミュニケーションの重要性」など、具体的な現場目線での回答ができているように思います。 実際に業務で使用できるのか気になったので、アンドエンジニアのX投稿案を作成するよう指示してみました。 質問: 以下の記事について 、 Xで投稿する投稿案を考えてください 。 ▼ 記事 https : //and-engineer.com/articles/Z33qRhAAACEA1jWT ▼ ターゲット エンジニア ▼ 文量 110 字以内 ▼ 雰囲気 ・ カジュアル ・ 具体的 ・ キャッチコピーを添える ▼ 装飾 ・ 改行を加える 回答: Copilot(通常) Copilot エージェント(佐藤) こちらの質問だとあんまり回答が変わらないですね。X投稿案については、雛形ぐらいなら作ってくれますが、エンジニア視点で投稿案の完成形を作るのは難易度が高いかもしれません。 使ってみた感想 ・Copilot(通常)に比べると、実際のエンジニアが思っていそうな具体的な回答をしてくれるので、エンジニアの知識の取っ掛かりとして参考にする分には使えるかも。 ・佐藤単体だと偏りが起きそうなので、佐藤以外のペルソナも作成することでより多様な視点からのフィードバックが得られるかも
TL;DR BigQuery MLを使うと、BigQuery上のデータに対する推論処理が BigQuery内 で完結できます。 Dataformからも当然呼び出せるので、推論対象データをBigQueryで準備できるのなら、 推論バッチ全体がBigQueryで完結します。 AIモデルは、Googleマネージドなモデルや、各自で用意したローカルモデルも利用できます。 きっかけ AI戦略室では、BigQueryで処理したデータに対して、AIモデルを用いたバッチ推論を多数実施しております。 その際、Vertex AI Pipelinesを利用して、BigQueryによるオペレーションの後にVertex AI Batch Predictionによる推論を実行する実装を行っています。しかし、このVertex AI Batch Predictionには、実装が大変だったり、オブザバビリティが低かったりと問題がいくつかありました。( IssueTracker で要望は上がってるみたいだが。。)そこで、Batch Predictionに代わるプロダクトが無いか調べていたところ、Dataflow MLとBigQuery MLがいい感じだったので、今回はBigQuery MLの方を調査してみます。 BigQueryからモデルを呼び出すクエリのイメージ SELECT * FROM ML . GENERATE_TEXT ( -- [ 1 ] MODEL ` <PROJECT_ID>.sano_bqml_test.gemini_flash ` , -- [ 2 ] TABLE ` <PROJECT_ID>.sano_bqml_test.questions_for_gemini ` , -- [ 3 ] STRUCT ( -- [ 4 ] 1024 AS max_output_tokens , 0.1 AS temperature , 5 AS top_k , 0.3 AS top_p , TRUE AS flatten_json_output ) ) ; [1]: テキスト生成メソッドを生成する関数は、  ML.GENERATE_TEXT関数 です。 [2]: データセット内にあらかじめ登録したモデルのみ呼び出すことができます。モデルは以下の3種類が使用できます。 リモートモデル(Vertex AI Endpoint) Geminiを含む、Vertex AI上にデプロイされたモデル Cloud VisionやCloud Natural LanguageなどのマネージドAI API ローカルモデル BigQuery内でホストされ、BigQuery内で使用されるモデル 線形回帰や、ARIMAモデルなどが該当 インポートモデル 自前のモデルをGCSに配置し、インポートして使う 今回はGemini Flash 1.5を使って検証してみようと思いますので、「 リモートモデル 」に該当します。 [3]: プロンプトが含まれているテーブルを指定します。ML.GENERATE_TEXT関数は、promptという固定のカラム名からプロンプトを読み込み、 決められたカラム名 で生成結果を出力します。 [4]: いわゆる「温度」や「TopK」といったパラメータを、構造体を経由して渡します。 BigQueryからモデルを呼び出すインフラのイメージ(たぶんこう) リモートモデルはあくまでVertex AIでデプロイされているもので、 BigQueryの外の世界 になります。 こういったBigQueryの外の世界にBigQueryがアクセスするときは、「 外部接続 」というものを経由します。 外部接続は、外部リソースへのアクセスのためにサービスアカウントを利用しているので、サービスアカウントには外部リソースに適したロールをアタッチする必要があります。 また、外部接続を作った後は、CREATE MODEL関数を使って、データセット内にモデルオブジェクトを作成する必要があります。BQからリモートモデルにアクセスするための「アクセスポイント」みたいなものです。 このアクセスポイント経由で、Vertex AIのリモートモデルに推論をリクエストし、その結果をBigQueryに返すことで、(ユーザーから見た際の)BQ内での推論処理の完結が成り立っています。 BQのクエリからGeminiを呼び出してテキスト生成する ここからはBQ上でのデモ画像が続きます。 外部接続を作成する まずは外部のモデルにBigQueryから接続できるように、「外部接続」を作成します。 BigQuery Studioの[ +ADD ]ボタンから、[ Connections to external data sources ]を作成。 外部接続とは、BigQueryからBigQuery外のデータソース(GCSやCloudSQLなど)に接続するためのリソースです。今回は Vertex AI Remote models を選択します。 適当なConnection IDを設定します。 すると プロジェクト の下に、External Connectionという項目ができます。 このとき、外部接続するためのサービスアカウントが払い出されるので、このサービスアカウントに対して、 Vertex AI User ロールを割り当てておきます。 リモートモデルを登録する CREATE OR REPLACE MODEL `<PROJECT_ID>.sano_bqml_test.gemini_flash` --[1] REMOTE WITH CONNECTION `<PROJECT_ID>.asia-northeast1.sano-bqml-test` -- [2] OPTIONS (ENDPOINT = 'gemini-1.5-flash-002'); --[3] [1]:モデルはデータセット内部に作成する [2]:先ほど作成した外部接続のパス [3]:エンドポイント名 入力データ ML.GENERATE_TEXT関数ではpromptというカラムをAIに入力するように設定されているので、promptというカラムを必ず含めます。 今回は、下記のようなテーブルを作成しました。 user_id prompt 123456 GeminiをBigQueryMLから呼び出す方法を教えてください 234567 Vertex AI Workbenchインスタンスを作成するために必要な最低限のIAMロールを教えてください 345678 プロジェクトAのBigQueryで管理されているデータセットに、プロジェクトBのBigQuery Studioからクエリを実行した場合、課金はどちらのプロジェクトに発生しますか 推論を実行する 前述のクエリを少し改良しました。 ML.GENERATE_TEXT関数の出力をSELECT *で受けるとカラムがたくさん増えてしまうので、 ちゃんとカラムを選択しました。 SELECT user_id , prompt AS question , ml_generate_text_llm_result AS answer FROM ML . GENERATE_TEXT ( MODEL ` <PROJECT_ID>.sano_bqml_test.gemini_flash ` , TABLE ` <PROJECT_ID>.sano_bqml_test.questions_for_gemini ` , STRUCT ( 1024 AS max_output_tokens , 0.1 AS temperature , 5 AS top_k , 0.3 AS top_p , TRUE AS flatten_json_output ) ) ; 実行結果 ユーザID123456さんの質問への回答は、、 BigQuery MLから直接Geminiを呼び出す方法は、現時点ではありません。 いや、今呼び出したやないかーい!! って感じですが、おそらくグラウンディングがうまくできていないからでしょう。 外部接続を通して呼び出しているので、確かに「直接」は呼び出してないですが、外部接続によって呼び出す方法を提示して欲しいですね。 Vertex AIではグランディング系の機能も充実してきているので、いずれBQMLでもグラウンディング付きの推論を生成して、ハルシネーションも考慮した生成もできるようになると、勝手に期待しております。 料金 リモートモデルの場合、推論料金=Vertex AIのマネージドモデルの料金。つまりBigQuery MLからGemini Flashに推論を行った場合、Vertex AIでGemini Flashで推論を行った場合の料金+スキャン量が課金されます。 このあたりのインフラの比較等、あらかじめ知見としてためておくと、いざシステム構築するときの判断材料となるでしょう。 私はバリバリのエンジニア人間でこういったインフラはとても興味があるので、どんどん調べていきたいです。 考察 BigQueryにはDataformというサブプロダクトがあり、これはdbtのように、SQLジョブに依存関係をつけて実行できるものです。BQMLはDataformからも当然呼び出せるので、日々のデータマートの更新にも簡単に活用できます。また、従来のバッチ処理でVertex Pipelinesすら使う必要もなくなるでしょう。 Dataform自体には課金が発生しないため、これは有用ではないかと思います。 また、 ONNX  オニキス   形式 に変換できるモデルであれば、今回紹介しませんでしたが インポートモデル でGCSからモデルをインポートして利用できるので、MLOps等で学習モデルを定期的にGCSに出力している場合と愛称は抜群によいと思います。
目標:AutoGluonを試してみる! 今回は近年話題になっているAutoMLの一つであるAutoGluonのクイックスタートを試してみて、実際にどのようなことができるのかを理解しようと思います! なぜこのチャレンジをするのか 近年、生成AIやデータサイエンス業界の発展により、学生のデータサイエンス力が年々向上しています。その影響もあり、毎年開催しているデータサイエンティストコースのインターンシップでは、模範解答の精度が学生の精度よりも低いという結果になってしまったことも…。 これはまずいということで、AutoMLが予測精度向上に役立つのではないかと思い、試してみることにしました! AutoML(自動機械学習)は、機械学習モデルの構築プロセスを簡素化し、専門知識がなくても高精度なモデルを作成できるようにする技術です。AutoGluonはその中でも特に使いやすく、強力なツールとして注目されています。 私はAutoMLは全く触ったことがなく初心者ですが、このチャレンジを通じてAutoGluonの基本的な使い方を学び、実際のデータセットで試してみることで、その利便性と性能を体感したいと思います! AutoGluonとは AutoGluonは、Amazonが開発したオープンソースのAutoMLフレームワークです。以下の特徴があります。 簡単なインストールと使用 :数行のコードでモデルのトレーニングと予測が可能です。 多様なデータ形式に対応 :表形式データ、画像、テキスト、時系列データなど、さまざまなデータタイプをサポートします。 自動化されたプロセス :特徴量エンジニアリング、モデル選択、ハイパーパラメータ調整など、多くのステップが自動化されています。 AutoGluon Tabular - クイックスタート 英語のクイックスタートを翻訳しているので、多少の日本語の変なところはスルーしていただけると助かります。 実際に実行したい方はこちらのリンクから実行できます! インストール まずは、AutoGluonの TabularDataset と TabularPredictor をインポートします。 TabularDataset :データをロードする際に利用 TabularPredictor :モデルをトレーニングと予測に利用 ! python - m pip install -- upgrade pip ! python - m pip install autogluon from autogluon . tabular import TabularDataset , TabularPredictor サンプルデータ 使用するデータセット: Nature issue 7887 このデータセットの目的は、結び目の特性に基づいてそのシグネチャを予測することです。 元のデータから10,000のトレーニング例と5,000のテスト例をサンプリングしています( オリジナルデータ )。 サンプリングされたデータセットにより、このチュートリアルは迅速に実行できますが、必要に応じてAutoGluonはフルデータセットを処理することもできます。 data_url = ' https://raw.githubusercontent.com/mli/ag-docs/main/knot_theory/ ' train_data = TabularDataset ( f ' { data_url } train.csv' ) train_data . head () Unnamed: 0 chern_simons cusp_volume hyperbolic_adjoint_torsion_degree hyperbolic_torsion_degree injectivity_radius longitudinal_translation meridinal_translation_imag meridinal_translation_real short_geodesic_imag_part short_geodesic_real_part Symmetry_0 Symmetry_D3 Symmetry_D4 Symmetry_D6 Symmetry_D8 Symmetry_Z/2 + Z/2 volume signature 0 70746 0.090530 12.226322 0 10 0.507756 10.685555 1.144192 -0.519157 -2.760601 1.015512 0 0 0 0 1.0 11.393225 -2 1 240827 0.232453 13.800773 0 14 0.413645 10.453156 1.320249 -0.158522 -3.013258 0.827289 0 0 0 0 1.0 12.742782 0 2 155659 -0.144099 14.761030 0 14 0.436928 13.405199 1.101142 0.768894 2.233106 0.873856 0 0 0 0 0 15.236505 2 3 239963 -0.171668 13.738019 0 22 0.249481 27.819496 0.493827 -1.188718 -2.042771 0.498961 0 0 0 0 0 17.279890 -8 4 90504 0.235188 15.896359 0 10 0.389329 15.330971 1.036879 0.722828 -3.056138 0.778658 0 0 0 0 0 16.749298 4 目的変数は「signature」列に格納されており、18種類のユニークな整数が含まれています。pandasはこのデータ型をカテゴリカルとして正しく認識しませんでしたが、AutoGluonがこの問題を修正してくれます。 label = ' signature ' train_data [ label ]. describe () 実行結果 count 10000.000000 mean - 0.022000 std 3.025166 min - 12.000000 25 % - 2.000000 50 % 0.000000 75 % 2.000000 max 12.000000 Name : signature , dtype : float64 学習 predictor = TabularPredictor ( label = label ). fit ( train_data ) 実行結果 No path specified . Models will be saved in : " AutogluonModels/ag-20241205_012036 " Verbosity : 2 ( Standard Logging ) =================== System Info =================== AutoGluon Version : 1.2 Python Version : 3.10 . 12 Operating System : Linux Platform Machine : x86_64 Platform Version : #1 SMP PREEMPT_DYNAMIC Thu Jun 27 21:05:47 UTC 2024 CPU Count : 2 Memory Avail : 11.57 GB / 12.67 GB ( 91.3 % ) Disk Space Avail : 74.20 GB / 107.72 GB ( 68.9 % ) =================================================== No presets specified! To achieve strong results with AutoGluon , it is recommended to use the available presets . Defaulting to ` ' medium ' ` ... Recommended Presets ( For more details refer to https : // auto . gluon . ai / stable / tutorials / tabular / tabular - essentials . html #presets): presets = ' experimental ' : New in v1 . 2 : Pre - trained foundation model + parallel fits . The absolute best accuracy without consideration for inference speed . Does not support GPU . presets = ' best ' : Maximize accuracy . Recommended for most users . Use in competitions and benchmarks . presets = ' high ' : Strong accuracy with fast inference speed . presets = ' good ' : Good accuracy with very fast inference speed . presets = ' medium ' : Fast training time , ideal for initial prototyping . Beginning AutoGluon training ... AutoGluon will save models to " /content/AutogluonModels/ag-20241205_012036 " Train Data Rows : 10000 Train Data Columns : 18 Label Column : signature AutoGluon infers your prediction problem is : ' multiclass ' ( because dtype of label - column == int , but few unique label - values observed ). First 10 ( of 13 ) unique label values : [ - 2 , 0 , 2 , - 8 , 4 , - 4 , - 6 , 8 , 6 , 10 ] If ' multiclass ' is not the correct problem_type , please manually specify the problem_type parameter during Predictor init ( You may specify problem_type as one of : [ ' binary ' , ' multiclass ' , ' regression ' , ' quantile ' ]) Problem Type : multiclass Preprocessing data ... Warning : Some classes in the training set have fewer than 10 examples . AutoGluon will only keep 9 out of 13 classes for training and will not try to predict the rare classes . To keep more classes , increase the number of datapoints from these rare classes in the training data or reduce label_count_threshold . Fraction of data from classes with at least 10 examples that will be kept for training models : 0.9984 Train Data Class Count : 9 Using Feature Generators to preprocess the data ... Fitting AutoMLPipelineFeatureGenerator... Available Memory : 11841.83 MB Train Data ( Original ) Memory Usage : 1.37 MB ( 0.0 % of available memory ) Inferring data type of each feature based on column values . Set feature_metadata_in to manually specify special dtypes of the features . Stage 1 Generators : Fitting AsTypeFeatureGenerator... Note : Converting 5 features to boolean dtype as they only contain 2 unique values . Stage 2 Generators : Fitting FillNaFeatureGenerator... Stage 3 Generators : Fitting IdentityFeatureGenerator... Stage 4 Generators : Fitting DropUniqueFeatureGenerator... Stage 5 Generators : Fitting DropDuplicatesFeatureGenerator... Useless Original Features ( Count : 1 ): [ ' Symmetry_D8 ' ] These features carry no predictive signal and should be manually investigated . This is typically a feature which has the same value for all rows . These features do not need to be present at inference time . Types of features in original data ( raw dtype , special dtypes ): ( ' float ' , []) : 14 | [ ' chern_simons ' , ' cusp_volume ' , ' injectivity_radius ' , ' longitudinal_translation ' , ' meridinal_translation_imag ' , ... ] ( ' int ' , []) : 3 | [ ' Unnamed: 0 ' , ' hyperbolic_adjoint_torsion_degree ' , ' hyperbolic_torsion_degree ' ] Types of features in processed data ( raw dtype , special dtypes ): ( ' float ' , []) : 9 | [ ' chern_simons ' , ' cusp_volume ' , ' injectivity_radius ' , ' longitudinal_translation ' , ' meridinal_translation_imag ' , ... ] ( ' int ' , []) : 3 | [ ' Unnamed: 0 ' , ' hyperbolic_adjoint_torsion_degree ' , ' hyperbolic_torsion_degree ' ] ( ' int ' , [ ' bool ' ]) : 5 | [ ' Symmetry_0 ' , ' Symmetry_D3 ' , ' Symmetry_D4 ' , ' Symmetry_D6 ' , ' Symmetry_Z/2 + Z/2 ' ] 0. 3s = Fit runtime 17 features in original data used to generate 17 features in processed data . Train Data ( Processed ) Memory Usage : 0.96 MB ( 0.0 % of available memory ) Data preprocessing and feature engineering runtime = 0. 42s ... AutoGluon will gauge predictive performance using evaluation metric : ' accuracy ' To change this , specify the eval_metric parameter of Predictor () Automatically generating train / validation split with holdout_frac = 0.1 , Train Rows : 8985 , Val Rows : 999 User - specified model hyperparameters to be fit : { ' NN_TORCH ' : [{}], ' GBM ' : [{ ' extra_trees ' : True , ' ag_args ' : { ' name_suffix ' : ' XT ' }}, {}, { ' learning_rate ' : 0.03 , ' num_leaves ' : 128 , ' feature_fraction ' : 0.9 , ' min_data_in_leaf ' : 3 , ' ag_args ' : { ' name_suffix ' : ' Large ' , ' priority ' : 0 , ' hyperparameter_tune_kwargs ' : None }}], ' CAT ' : [{}], ' XGB ' : [{}], ' FASTAI ' : [{}], ' RF ' : [{ ' criterion ' : ' gini ' , ' ag_args ' : { ' name_suffix ' : ' Gini ' , ' problem_types ' : [ ' binary ' , ' multiclass ' ]}}, { ' criterion ' : ' entropy ' , ' ag_args ' : { ' name_suffix ' : ' Entr ' , ' problem_types ' : [ ' binary ' , ' multiclass ' ]}}, { ' criterion ' : ' squared_error ' , ' ag_args ' : { ' name_suffix ' : ' MSE ' , ' problem_types ' : [ ' regression ' , ' quantile ' ]}}], ' XT ' : [{ ' criterion ' : ' gini ' , ' ag_args ' : { ' name_suffix ' : ' Gini ' , ' problem_types ' : [ ' binary ' , ' multiclass ' ]}}, { ' criterion ' : ' entropy ' , ' ag_args ' : { ' name_suffix ' : ' Entr ' , ' problem_types ' : [ ' binary ' , ' multiclass ' ]}}, { ' criterion ' : ' squared_error ' , ' ag_args ' : { ' name_suffix ' : ' MSE ' , ' problem_types ' : [ ' regression ' , ' quantile ' ]}}], ' KNN ' : [{ ' weights ' : ' uniform ' , ' ag_args ' : { ' name_suffix ' : ' Unif ' }}, { ' weights ' : ' distance ' , ' ag_args ' : { ' name_suffix ' : ' Dist ' }}], } Fitting 13 L1 models , fit_strategy = " sequential " ... Fitting model : KNeighborsUnif ... 0.2232 = Validation score ( accuracy ) 9. 92s = Training runtime 0. 02s = Validation runtime Fitting model : KNeighborsDist ... 0.2132 = Validation score ( accuracy ) 0. 05s = Training runtime 0. 02s = Validation runtime Fitting model : NeuralNetFastAI ... 0.9409 = Validation score ( accuracy ) 16. 79s = Training runtime 0. 04s = Validation runtime Fitting model : LightGBMXT ... / usr / local / lib / python3 . 10 / dist - packages / dask / dataframe / __init__ . py : 42 : FutureWarning : Dask dataframe query planning is disabled because dask - expr is not installed . You can install it with `pip install dask [ dataframe ] ` or `conda install dask` . This will raise in a future version . warnings . warn ( msg , FutureWarning ) 0.9459 = Validation score ( accuracy ) 10. 69s = Training runtime 0. 24s = Validation runtime Fitting model : LightGBM ... 0.956 = Validation score ( accuracy ) 9. 71s = Training runtime 0. 33s = Validation runtime Fitting model : RandomForestGini ... 0.9449 = Validation score ( accuracy ) 8. 86s = Training runtime 0. 12s = Validation runtime Fitting model : RandomForestEntr ... 0.9499 = Validation score ( accuracy ) 10. 04s = Training runtime 0. 11s = Validation runtime Fitting model : CatBoost ... 0.956 = Validation score ( accuracy ) 73. 03s = Training runtime 0. 01s = Validation runtime Fitting model : ExtraTreesGini ... 0.9469 = Validation score ( accuracy ) 4. 42s = Training runtime 0. 13s = Validation runtime Fitting model : ExtraTreesEntr ... 0.9429 = Validation score ( accuracy ) 2. 84s = Training runtime 0. 13s = Validation runtime Fitting model : XGBoost ... 0.957 = Validation score ( accuracy ) 16. 0s = Training runtime 0. 35s = Validation runtime Fitting model : NeuralNetTorch ... 0.9419 = Validation score ( accuracy ) 79. 07s = Training runtime 0. 01s = Validation runtime Fitting model : LightGBMLarge ... 0.9499 = Validation score ( accuracy ) 16. 1s = Training runtime 0. 42s = Validation runtime Fitting model : WeightedEnsemble_L2 ... Ensemble Weights : { ' RandomForestEntr ' : 0.25 , ' ExtraTreesGini ' : 0.25 , ' KNeighborsUnif ' : 0.167 , ' NeuralNetFastAI ' : 0.167 , ' XGBoost ' : 0.083 , ' NeuralNetTorch ' : 0.083 } 0.965 = Validation score ( accuracy ) 0. 25s = Training runtime 0. 0s = Validation runtime AutoGluon training complete , total runtime = 264. 38s ... Best model : WeightedEnsemble_L2 | Estimated inference throughput : 1512.1 rows / s ( 999 batch size ) TabularPredictor saved . To load , use : predictor = TabularPredictor . load ( " /content/AutogluonModels/ag-20241205_012036 " ) 予測 test_data = TabularDataset ( f ' { data_url } test.csv' ) y_pred = predictor . predict ( test_data . drop ( columns = [ label ])) 評価 predictor . evaluate ( test_data , silent =True ) 実行結果 { ' accuracy ' : 0.9478 , ' balanced_accuracy ' : 0.754478262473782 , ' mcc ' : 0.9360368834449522 } AutoGluonのTabularPredictorは、 leaderboard() 関数も提供しており、これを使用して各トレーニング済みモデルのテストデータに対する性能を評価することができます。 predictor . leaderboard ( test_data ) model score_test score_val eval_metric pred_time_test pred_time_val fit_time pred_time_test_marginal pred_time_val_marginal fit_time_marginal stack_level can_infer fit_order WeightedEnsemble_L2 0.9478 0.964965 accuracy 2.777582 0.660655 136.499218 0.025436 0.001888 0.246331 2 True 14 LightGBM 0.9456 0.955956 accuracy 0.704927 0.331303 9.709910 0.704927 0.331303 9.709910 1 True 5 XGBoost 0.9448 0.956957 accuracy 1.877720 0.350646 16.003580 1.877720 0.350646 16.003580 1 True 11 LightGBMLarge 0.9444 0.949950 accuracy 3.199392 0.421252 16.101254 3.199392 0.421252 16.101254 1 True 13 CatBoost 0.9432 0.955956 accuracy 0.065079 0.011186 73.033620 0.065079 0.011186 73.033620 1 True 8 RandomForestEntr 0.9384 0.949950 accuracy 0.284559 0.108530 10.044177 0.284559 0.108530 10.044177 1 True 7 NeuralNetFastAI 0.9364 0.940941 accuracy 0.102912 0.041506 16.789817 0.102912 0.041506 16.789817 1 True 3 ExtraTreesGini 0.9360 0.946947 accuracy 0.413286 0.126837 4.417963 0.413286 0.126837 4.417963 1 True 9 ExtraTreesEntr 0.9358 0.942943 accuracy 0.434792 0.127124 2.836171 0.434792 0.127124 2.836171 1 True 10 RandomForestGini 0.9352 0.944945 accuracy 0.266627 0.117757 8.860353 0.266627 0.117757 8.860353 1 True 6 NeuralNetTorch 0.9320 0.941942 accuracy 0.035788 0.012760 79.072856 0.035788 0.012760 79.072856 1 True 12 LightGBMXT 0.9320 0.945946 accuracy 1.222842 0.243437 10.694745 1.222842 0.243437 10.694745 1 True 4 KNeighborsDist 0.2210 0.213213 accuracy 0.038112 0.016588 0.045746 0.038112 0.016588 0.045746 1 True 2 KNeighborsUnif 0.2180 0.223223 accuracy 0.037879 0.018488 9.924494 0.037879 0.018488 9.924494 1 True 1 結論 このクイックスタートチュートリアルでは、 TabularDataset と TabularPredictor を使用してAutoGluonの基本的なフィットおよび予測機能を見てきました。 AutoGluonは、特徴量エンジニアリングやモデルのハイパーパラメータ調整を必要とせずに、モデルのトレーニングプロセスを簡素化します。 トレーニングや予測ステップのカスタマイズ、カスタム特徴量生成器、モデル、メトリクスの拡張など、AutoGluonの他の機能について詳しく学ぶには詳細なチュートリアルをチェックしてください。 まとめ いかがだったでしょうか? 最後の表にある通り、多くのモデルを数少ないコードで比較してくれました!! 色々なモデルを試すため学習時間は多くなってしまいますが、どのモデルを使用するのかを検討する際にAutoGluonはとても使えるのではないでしょうか? データ前処理、ハイパーパラメータ最適化、アーキテクチャー最適化、モデルアンサンブル(スタッキング)までを一度に行ってくれるAutoGluonは今後も目が離せません! また、今回の表データだけでなく、画像分類・画像セグメンテーション・物体検出・自然言語・マルチモーダル予測など様々な分野に対応しているため、気になった方はぜひそれぞれのクイックスタートを試してみてください! 参考リンク https://atmarkit.itmedia.co.jp/ait/articles/2203/24/news004.html https://auto.gluon.ai/stable/index.html https://pages.awscloud.com/rs/112-TZM-766/images/1.AWS_AutoML_AutoGluon.pdf
マイナビジョブサーチ 担当のM.Sです。 "余暇で何か物作りをしよう" となったときにどうしても時間が足りなくなりますよね... 😢 今回は、lovableを使って簡単にWebアプリケーションを生成してみようと思います。 lovableとは lovableは、2024年11月にGPTエンジニアからリブランディングされて公開されたツールになります。 lovable公式サイト 下記がlovableの特徴です。 Webアプリ開発、動作確認、デプロイまでlovableのみで完結できる シンプルなプロンプトでウェブサイト生成できる デザイン面で他のツールより優れている Figmaから書き出すことができる テンプレートから開発を開始できる 費用面的には、使い放題ではないスターターでも$20と少しお高めですが、払う価値があるくらいすごいツールだと思います。 lovableでWebアプリを作成してみよう 今回は、このような TODO のアプリケーションを作成してみようと思います。 やり方の手順としては、下記のような感じです。 ChatGPTを使って要件定義する lovableで開発してもらう 細かいチューニング 要件定義 簡単にこんな感じに要件を設定してみました。 データの形は、lovableがよしなにやってくれるので、外側の仕様を中心に定義してみました。 ## 概要 TODOリストアプリ ## 技術 - React - Supabase ## 機能 - タスクを追加することができる - タスクを削除することができる - BackLogのようにドラックアンドドロップでタスクのステータスを変更することができる - supabaseで非同期のステータス更新 - TODOリストのステータス - 未着手 - 処置中 - 完了 ## デザイン - モダンでかっこいいデザイン - 白黒基調のシンプルな配色 lovableに開発してもらう 先ほどの要件定義のプロンプトをそのまま、lovableに投げてみます。 先ほどのプロンプトを投げるといい感じに、TODOリストのUIを作成してくれました。 プレビューのところまでは、チャットと同時に作成してくれます。 挙動を見ると分かるように、かなりモダンな感じで作られています。  画面収録 2024-12-12 13.43.54.mov コマンド一発でこれが実装できるのはかなりすごいですね... 次にsupabaseとの連携も行っていこうと思います。 現状では、ローカルでデータが保存されているだけなので、supabaseも実装していきます。 lovable上のsupabaseボタンから連携を行っていきます。 連携を行ったらコードも修正してもらいます。 コードを修正したら、対象のsupabase側でテーブルを用意します。 supabaseのSQL Editorで、lovableから吐き出されたSQLをそのまま実行します。 こんな感じでテーブルも作成されています。 supabaseの設定まで完了するとこんな感じになります。 画面収録 2024-12-12 12.24.23.mov ログイン機能などが備わっていないものの、バックエンドの実装までは簡単にできました。 すごいですね...😳 細かいチューニング 細かいチューニングに関しては、GitHubを通して行うか、lovableのチャットで行うかになります。 lovable上のGitHubボタンからGitHubを登録すると、GitHubで修正してmainブランチにプッシュされたものを確認し、lovable上に反映させてくれます。 大きな部分はlovableで、細かい部分はGitHubで自分でソースを修正することで、効率よく開発できそうですね。 デプロイ ちなみにパブリッシュボタンからは、デプロイも簡単に行うことができます。 かかった時間 実際にかかった時間を測っていたのですが、だいたいこれくらいでした。 要件定義: 10分 開発時間: 20分 簡易的とはいえ、FE/BE込みのWebアプリを30分くらいで作るのはかなり難しいと思います... コードの質はどうなのか 今回のファイルの出力は、こんな感じになっていました。 一般的なディレクトリ構成になっていて、追加開発や細かいチューニングもしやすそうです。 taskory - dash ├── node_modules ├── public └── src ├── components │ ├── ui │ │ └── // 細かいUIが入っている │ ├── Column . tsx │ └── Task . tsx ├── hooks │ ├── use - mobile . tsx │ └── use - toast . ts ├── lib │ ├── supabase . ts │ └── utils . ts ├── types │ └── task . ts └── pages ├── Index . tsx ├── App . css ├── App . tsx ├── index . css ├── main . tsx └── vite - env . d . ts 下記は、TODOのタスクカードの部分です。修正しやすそうですね。 /comopnents/ui/Task.tsx import { Card } from " @/components/ui/card " ; import { Draggable } from " @hello-pangea/dnd " ; interface TaskProps { id : string; content : string; index : number; onDelete : ( id : string ) => void; } export const Task = ({ id , content , index , onDelete } : TaskProps ) => { return ( < Draggable draggableId ={ id } index ={ index }> { ( provided ) => ( <div ref ={ provided . innerRef } {... provided . draggableProps } {... provided . dragHandleProps } className = " mb-2 " > < Card className = " p-4 hover:shadow-md transition-shadow bg-white " > <div className = " flex justify-between items-center " > <p className = " text-sm text-gray-800 " >{ content }</p> <button onClick ={ () => onDelete ( id ) } className = " text-red-500 hover:text-red-700 transition-colors " > × </button> </div> </ Card > </div> ) } </ Draggable > ) ; } ; まとめ お値段がかなり高め + 課金版でも使い放題ではないですが、ノーコードで開発もしくは、大枠を作ってもらうというところは、使えそうなのかなと思いました。 今後何か作ってみたいものがあれば、lovableに作ってもらうことも検討しようと思います! 最後まで見ていただきありがとうございました🙇 外部リンク lovable公式サイト
はじめに みなさん、こんにちは。 オペレーションデザイン統括本部オペレーションデザイン統括部セールステック推進部SFA・CRM推進課のU.Kです。 先日、2024年12月19日に新宿ミライナオフィスのTOWNHOLLで「なぜなにオペデザ発表会」が開催されました。 このイベントはオペレーションデザイン統括本部(通称オペデザ)について、みんなで理解を深めるための機会+年末の懇親会+クリスマスパーティとして開催されました。 ここでは、発表内容を主に開催レポートとしてみなさんにその内容を共有させていただければと思います。 オペレーションデザイン統括本部とは 発表の目的 この発表会の目的は、「オペレーションデザイン統括本部の業務とシステムについて、みんなが一つのチームとして理解する」ことです。 マイナビは事業部の数も業務領域も担当システムを利用する社員も膨大です。 そのために私も入社時に自部署のことを理解するのでやっとで、いつも近くにいて同じミッションを背負っているはずなのに、広大すぎるがゆえになかなか掴み切れないような感覚があったのを覚えています。 今回のイベントはそのような感覚の解消をすべく、新入社員にとっては、オペレーションデザイン統括本部内のつながりを相互理解すること、また長く在籍している社員にとっては、マイナビ全体のオペレーションデザインを相互理解することを目的としました。 発表の概要 発表会は下記の流れで進行しました。 はじめに:オペレーションデザイン統括本部の目的と事業について セールステック推進部MA推進課 セールステック推進部SFA・CRM推進課 ビジネスシステム部2課 ERPシステム1部2課 ERPシステム1部1課 オペレーション開発部開発課 ERPシステム2部 総評 1.セールステック推進部MA推進課:toB向けのマーケティングツールでの顧客獲得 2.セールステック推進部SFA・CRM推進課:SFA・CRMでの商談顧客管理 3.ビジネスシステム部2課:受注後の掲載業務システム 4.ERPシステム1部2課:ERPでの請求入金管理 5.ERPシステム1部1課:予算管理システムについて 6.オペレーション開発部開発課:iPaaS・RPAによる業務の自動化について 7.ERPシステム2部:今後のマイナビのオペレーションシステムの展望 8.総評 期待以上の結果・感想 なんと、オンライン・オフライン合わせて100名を超える方に参加者いただきました! オペレーションデザイン統括本部以外からもたくさんの方の参加があり発表者ながらびっくりしていました。 その後の懇親会では、システム・部門・統括本部に関わらず交流が活発に行われました。 発表会をきっかけに多くの社員が互いの業務について知識を深めることができました。 まとめと今後の展望 今回の「なぜなにオペデザ発表会」は、みんながオペレーションデザイン統括本部の業務とシステムについて理解を深める非常に有意義な機会となりました。 各部門がどのような役割を果たし、どのようにして業務を遂行しているのかを知ることで、統括本部内の連携がさらに強化できると感じることができたのと同時に、そのためのきっかけとつながりを今回の発表会で得ることができたと感じています。 私自身、これまで関わりのなかった方々と多く関わる機会を得て、入社時に感じた「いつも近くにいて同じミッションを背負っているはずなのに、広大すぎるがゆえになかなか掴み切れない」感覚を拭うような良い刺激を受けることができました。 社員一人ひとりが、自分の業務だけでなく、他部門の業務についても理解を深めることで、マイナビ全体の成長に寄与することができると感じられる、そんな一日になったのではないかと思います。
マイナビジョブサーチWebチームのT.Yです。 今回は、Google Chromeの拡張機能の、React Developer Toolsで出来ることについてお話しします。 React Developer Toolsとは Chrome DevToolsでReactアプリケーションのデバックができるツールです。 React Developer Toolsでは、以下のことを確認することができます。 コンポーネントの構造 コンポーネントに渡されているpropsやstateの値の確認、値の更新 レンダリングしたコンポーネントをハイライトで表示 パフォーマンスの計測、レンダリングした回数の計測 ...など これから4つの機能について紹介していきたいと思います。 インストール react-developer-tools インストール後に、Chrome DevToolsのナビバーに、「Components *」と「Profiler *」タブが表示されます。 Components コンポーネントの構造の確認や、コンポーネントに渡されているprops、stateの値の確認、値を更新することができます。 サンプルコード 【コード】app/page.tsx "use client"import { CSSProperties, useState } from "react";import { CountUp } from "@/components/CountUp";const style: CSSProperties = { display: "flex", flexDirection: "column", alignItems: "center", gap: "3rem",}export default function Home() { const [isShow, setIsShow] = useState<boolean>(false); return ( <div style={{ ...style }}> <div> <CountUp isShow={isShow} styleProps={style} /> </div> </div > ); 【コード】components/CountUp/index.tsx "use client"import { useState } from "react"import { CSSProperties } from "react";type Props = { isShow: boolean, styleProps: CSSProperties}export const CountUp: React.FC<Props> = ({ isShow, styleProps }) => { const [count, setCount] = useState(0) return ( <div style={{ ...styleProps, outline: "1px solid red" }}> <p>count : {count}</p> <button onClick={() => setCount(count + 1)}>COUNT UP</button> <p>⇩⇩⇩countが10になったら下にテキストが表示⇩⇩⇩</p> {count === 10 && <p>10回クリックされました</p>} {isShow && <p>propsのisShowがtrueの時に表示</p>} </div> )} 使い方 Chrome DevToolsを開き、黄色枠で囲んでいる「components」を選択すると、ReactのDOM構造が表示される 緑枠で囲んでいるボタンを選択した状態で、CountUpコンポーネント(赤枠)を選択すると、親からコンポーネントに渡っているpropsやstateが確認できる COUNTUPボタンをクリックしてみると、動画画面の右側に表示されているstateの値も変更される  Screen Recording 2024-12-05 at 15.04.35.mov 動画画面の右側に表示されているpropsの値、stateの値を直接変更することも可能で、変更した値に応じて表示が切り替わる  Screen Recording 2024-12-05 at 15.10.27.mov 「components」では、画面上でprops、stateの値の確認、値の変更ができるので、わざわざソース上で値を変更したり、ボタンを実際にクリックする必要もなくデバッグすることができます。 Profiler 再レンダリングしたコンポーネントをハイライトしてくれるので、レンダリングされた箇所を確認することが出来ます。また、コンポーネントが表示するまでにかかった時間や、コンポーネントがレンダリングされた回数も確認できます。 サンプルコード 【コード】app/page.tsx "use client"import { CSSProperties, useState } from "react";import { CountUp } from "@/components/CountUp";const style: CSSProperties = { display: "flex", flexDirection: "column", alignItems: "center", gap: "3rem",}export default function Home() { const [reRender, setReRender] = useState<boolean>(false); return ( <div style={{ ...style }}> <button onClick={() => setReRender(!reRender)}>stateを更新する</button> <div> <CountUp styleProps={style} /> </div> </div > );} 【コード】components/CountUp/index.tsx "use client"import { useState } from "react"import { CSSProperties } from "react";type Props = { styleProps: CSSProperties}export const CountUp: React.FC<Props> = ({ styleProps }) => { const [count, setCount] = useState(0) return ( <div style={{ ...styleProps }}> <p>count : {count}</p> <button onClick={() => setCount(count + 1)}>COUNT UP</button> </div> )} レンダリングしたコンポーネントをハイライトする方法 「Profiler」タブを選択し、歯車マークを押し、「General」タブの、黄色枠で囲んだ箇所にチェックをつける 画面を操作して、state、propsが更新されると、再レンダリングされたコンポーネントがハイライトされる  Screen Recording 2024-12-05 at 17.23.03.mov 「COUNT UP」ボタンをクリックした際は、CountUpコンポーネントのみがレンダリングされたが、親コンポーネントでstate管理している「stateを更新する」ボタンをクリックすると、CountUp子コンポーネントも再レンダリングされていることが確認できる 今回はやらないが、CountUp子コンポーネントをmemo化すると、ハイライトされない結果になる  Screen Recording 2024-12-05 at 17.32.50.mov このように、ハイライトをもとに、意図したレンダリングになっているかを確認することができます。 パフォーマンスの計測、レンダリングした回数の計測 丸いボタンをクリックすることでレコーディングが開始される レコーディング中にボタンをクリックしたり、そのほか操作をする レコーディングボタンを再度クリックし停止すると、レコーディング中のデータを確認することができる  Screen Recording 2024-12-05 at 19.07.08.mov 上記の動画で「COUNT UP」ボタンを5回クリックした結果、意図通りにCountUpコンポーネントが5回レンダリングされていること、CountUpコンポーネントがそれぞれのレンダリングにかかった時間も確認できる Screen Recording 2024-12-05 at 19.09.00.mov 「Profiler」タブを選択し、歯車マークを押し、「Profiler」タブの、黄色枠で囲んだ箇所にチェックをつけることで、レンダリングされた要因の説明が表示できる 「Profiler」を活用すれば、パフォーマンス向上につながる手がかりや、ボトルネックの原因を見つけることができます。 まとめ この記事では「React Developer Tools」を利用することで、Chrome DevTools上でpropsやstateのデバックが出来ること、レンダリングの可視化やパフォーマンスを確認できることを紹介しました。 他にも、今回紹介した機能以外に出来ることはありますが、まだ有効に活用できていないので、使いこなせるようにしていきたいです。 最後まで読んでいただき、ありがとうございました。
マイナビジョブサーチ WebチームのK.Kです。 今回は、Next.jsの画像最適化のお話をしたいと思います。 先日インフラ担当の方からこんな指摘をされました。 「Acceptヘッダーを転送していないから、画像の最適化が活かされていない。」 最初よくわからなくて「?」となったので、これをきっかけにNext.jsの画像最適化について調べてみました。 Next.jsの画像最適化とは? Next.jsではimgタグ、または、next/imageのImageコンポーネントを使用して画像を描画します。 この内、next/imageを使用すると、画像の最適化を行うことができます。 「リサイズ」「フォーマット変換」 などを行うことで、ページ読み込み速度が向上するなどといった恩恵が得られます。 リサイズ リサイズの機能では、表示するディスプレイの大きさに合わせて、 画像のサイズを最適化 してくれます。 例えばジョブサーチ上のこの画像、以下のように記載されています。 srcset属性に複数のURL が並んでいることがわかります。 これらが、画面サイズごとの画像のパスを示しています。 PC https://jobsearch.mynavi.jp/_next/image?url=%2Fapi%2FserveImage%3Furl%3Dhttps%253A%252F%252Fmynavi-agent.jp%252Fjobsearch%252Fimg%252Fjobimg%252Fjob1_01%252F02.webp&w=3840&q=75 SP(よりさらに少し狭めた場合) https://jobsearch.mynavi.jp/_next/image?url=%2Fapi%2FserveImage%3Furl%3Dhttps%253A%252F%252Fmynavi-agent.jp%252Fjobsearch%252Fimg%252Fjobimg%252Fjob1_01%252F02.webp&w=640&q=75 PC だと「690 × 398」SPだと「640 × 369」の画像が表示に使用されます。 容量を比べると23KB vs 18KBと、SPの方が小さくなっています。 特にSPは通信環境によってページ表示が遅くなりがちなので、容量が小さくなるのは嬉しいですね! 元画像がもっと大きい場合は、より大きな差になるかと思います。 画面サイズごとの画像のパスは、next/imageを使用すると、自動で生成されます。 これらの中から 最適な画像をブラウザ上に表示 してくれます。 フォーマット変換 フォーマット変換では、ブラウザが対応している画像フォーマットに合わせて、 画像フォーマットを最適化 してくれます。 どういったフォーマットに変換するかは設定で変更することができ、デフォルトではWebPに変換される設定になっています。 module.exports = { images: { formats: ['image/webp'], }, } WebPは、JPEGやPNGより 圧縮率の高いフォーマット です。 他にAVIFも指定できますが、こちらはより圧縮率が高い代わりに、エンコードに時間がかかるようです。 さて、ジョブサーチの こちらの画像 を取り上げます。 この画像、元はJPEGとして保存されています。 これをジョブサーチ上で開くと、JPEGで表示されます。 WebPじゃないの? Chromeの最新版だから対応しているはずだけど? そう、これが冒頭で書いた、今回指摘された内容です。 「ブラウザが対応している画像フォーマットに合わせて」画像フォーマットを最適化 ブラウザが対応している画像フォーマット、これは何で判断しているのでしょうか? それが、 Acceptヘッダー なんです。 ↓ Acceptヘッダーとは? HTTP の Accept リクエストヘッダーは、クライアントが理解できるコンテンツタイプを MIME タイプで伝えます。 参照元: https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Accept 実際にChromeでリクエストヘッダーのAcceptを見ると 「image/webp」の記載 があります。 Next.jsではこの値を見て、WebPで返すかどうかを判断しているわけですね。 公式ドキュメントにも以下の記載がありました。 Good to know: ~ 省略 ~ If you self-host with a Proxy/CDN in front of Next.js, you must configure the Proxy to forward the Accept header. 参照元: https://nextjs.org/docs/app/api-reference/components/image#formats ジョブサーチでは、 Next.jsの動作環境にAcceptヘッダーを転送していなかった ため、WebPのブラウザがフォーマットに対応しているか判定できず、そのままのJPEGフォーマットで返していたようです。 それでは、実際にAcceptヘッダーを転送して、フォーマットの変換をするとどうなるのか、検証環境で動作させてみました。 すると、 元画像(JPEG, 2000×1333):120KB Acceptヘッダーなし(JPEG, 916×611):41KB Acceptヘッダーあり(WebP, 916×611):20KB ということで、 画像の容量が半分 になりました!(圧縮されすぎでは?) 見た目上は変化がわからないのに、これはすごいですね! 最後に next/imageで画像の最適化をすることで、ページの読み込みが早くなったり、他にも視覚的な安定性が得られたりといったメリットがあります。 SEOにおいてもメリットがあるので、せっかくNext.jsを使用しているなら、動作しているのかちゃんと確認しないとなと思いました。 参考 https://nextjs.org/docs/app/building-your-application/optimizing/images https://nextjs.org/docs/app/api-reference/components/image#formats https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Accept
はじめに マイナビジョブサーチのWebチームのT.Yです。 みなさんは何のエディタを使っていますか? 私はVsCodeを使っているのですが、もう少し使いやすくならないかな〜、見やすくならないかな〜、と思ったことはないでしょうか? 思ったことがあるそんなあなたに、悩みを解決してくれるかもしれない設定をいくつか紹介します。 タブを多段表示する 設定:ワークベンチ>エディターの管理:Wrap Tabsにチェック setting.json:"workbench.editor.wrapTabs": true 新規タブを常に新しいタブとして表示 設定:ワークベンチ>エディターの管理:Enable Previewのチェックを解除 setting.json: "workbench.editor.enablePreview": false ファイルのインデントの表示幅を変更 設定:ワークベンチ>外観>Tree:Indent(デフォルト8) setting.json: "workbench.tree.indent": 20 ファイルのインデントにガイド線を表示 設定:ワークベンチ>外観>Tree:Render Indent Guides setting.json: "workbench.tree.renderIndentGuides": "always" ファイルのインデントのガイド線の色を変更 設定:ワークベンチ>外観> Color Customizations(setting.jsonで変更) setting.json:"workbench.colorCustomizations": { "tree.indentGuidesStroke": "#555555" // Default is #555555. }, フォルダを常に階層で表示する 設定:機能>エクスプローラー>Compact Foldersのチェックを解除 setting.json: "explorer.compactFolders": false 行番号の色と、カーソル行の行番号の色を変更 設定:ワークベンチ>外観> Color Customizations(setting.jsonで変更) setting.json:"workbench.colorCustomizations": { "editorLineNumber.foreground": "#7bff00", // 行番号 "editorLineNumber.activeForeground": "#ff0000", // カーソル行の行番号 }, 最後に いかがでしたでしょうか? 他にもたくさんの設定があるので、他の設定も色々と試してみてより良い開発環境にしてみてください。
今回はAPIのモックを用いたユニットテストとE2Eテストについて実際のコードを使いながら紹介しようと思います。 モックを用いたユニットテストの概要 モックとは まずモックとは何かについて説明します。 テストしたい関数が他のクラスに依存していることはよくあると思います。 例えば、SNSのとあるユーザーの投稿を取得するAPIのサービスクラスは投稿が公開か未公開か確認のために投稿のレポジトリクラスに依存し、またユーザーの存在を確認するためにユーザーのレポジトリクラスにも依存しています。 この状況下において、モックを使わずにUTを実装すると、ユーザーのレポジトリクラスのUTが失敗した場合、サービスクラスのUTも失敗しているということになります。そのため、原因特定に時間がかかります。 モックを使って実装すると、依存しているクラスや関数が想定通りの挙動をするように設定できるため、ユーザーのレポジトリクラスのUTが失敗した場合でも、サービスクラスのUTは成功します。そのため、瞬時にユーザーのレポジトリクラスのみでバグが生じていることがわかります。 つまり、モックとはUTの責任範囲を明確にし、 UTを実装しやすくする存在です。 依存注入 ただ、注意しなくてはいけないのは UTの対象関数の内、UTが制御できるのは対象関数の呼び方のみであるということです。 つまり、モックを使ってUTを制御するためには、モックするクラスを関数またはクラスの引数に設定する必要があります。 そのため、対象関数またはクラスの引数はクラスを注入できるように実装する必要があります。 これを 依存注入 と呼びます。 モックを用いたUTの実例 モックや依存注入について説明が終わったため、実際のコードを使って説明したいと思います。 今回はSNSのとあるユーザーの投稿を取得するAPIとそのUTコードを実装しました。 コントローラー コントローラーのコードは以下のようになっています。 get_post_info 関数の引数にサービスクラスを依存注入しています。 / controllers / get_post_controller . py < code >from dependency_injector . wiring import Provide , inject from fastapi import APIRouter , Depends from app . api_schemas . get_post_schema import ( GetPostRequest , GetPostResponse , GetPostSchema ) from app . core . container import Container from app . services . get_post_service import GetPostService router = APIRouter () @ router . get ( " /posts/ {post_id} " ) @ inject def get_post_info ( get_post_request : GetPostRequest = Depends (), service : GetPostService = Depends ( Provide [ Container . get_post_service ]), ) -> GetPostResponse : if post := service . get_post_info ( get_post_request . post_id , get_post_request . user_id ): return GetPostResponse ( result =True , post = GetPostSchema ( title = post . title , description = post . description ), ) return GetPostResponse ( result =False , post =None ) </ code > これに対するUTコードは以下のようになっています。 mock_get_post_service 関数でサービスのモックを作成し、各テストケースで利用しています。 コントローラーの get_post_info 関数の中で使うサービスのメソッドの返り値をモックで設定することで関数内の条件分岐を制御しています。 / tests / controllers / test_get_post_controller . pyfrom unittest . mock import MagicMock import pytest import requests from app . api_schemas . get_post_schema import ( GetPostRequest , GetPostResponse , GetPostSchema ) from app . controllers . get_post_controller import get_post_info from app . models . post import PostTable from app . models . user import UserTable from tests . base_test import BaseTest @ pytest . fixture () def mock_get_post_service (): return MagicMock () def test_get_post_succeeds ( mock_get_post_service ): mock_get_post_service . get_post_info . return_value = ( PostTable . test_public_post_by_user1_data () ) request = GetPostRequest ( post_id = 1 , user_id = 1 ) response = get_post_info ( get_post_request = request , service = mock_get_post_service ) assert response == GetPostResponse ( result =True , post = GetPostSchema ( title = PostTable . test_public_post_by_user1_data (). title , description = PostTable . test_public_post_by_user1_data (). description , ), ) def test_get_post_fails ( mock_get_post_service ): mock_get_post_service . get_post_info . return_value = None request = GetPostRequest ( post_id = 1 , user_id = 1 ) response = get_post_info ( get_post_request = request , service = mock_get_post_service ) assert response == GetPostResponse ( result =False , post =None ) サービス サービスのコードは以下のようになっています。 サービスはクラスにまとめているため、クラスの __init__ 関数で依存するユーザーレポジトリクラスと投稿レポジトリクラスを注入しています。 これによってクラス内の関数のインスタンスから依存先を利用できるようになっています。 / services / get_post_service . py < code >from app . models . post import PostTable from app . repositories . post_repository import PostRepository from app . repositories . user_repository import UserRepository class GetPostService : def __init__ ( self , post_repository : PostRepository , user_repository : UserRepository ): self . post_repository = post_repository self . user_repository = user_repository def get_post_info ( self , post_id , user_id ) -> PostTable : if not self . user_repository . get_user ( user_id ): return None post = self . post_repository . get_post ( post_id ) if not post or not self . __is_visible ( post , user_id ): return None return post def __is_visible ( self , post : PostTable , user_id ) -> bool : if post . user_id == user_id : return True elif not post . is_private : return True else : return False </ code > これに対するUTコードは以下のようになっています。 get_post_service 関数で依存するユーザーレポジトリクラスと投稿レポジトリクラスをモックしています。 サービスの get_post_info 関数の中で使うレポジトリのメソッドの返り値をモックで設定することで関数内の条件分岐を制御しています。 tests / services / test_get_post_service . py < code >from datetime import datetime from unittest . mock import MagicMock import pytest from app . models . post import PostTable from app . models . user import UserTable from app . services . get_post_service import GetPostService @ pytest . fixture () def get_post_service (): return GetPostService ( post_repository = MagicMock (), user_repository = MagicMock ()) def test_non_existing_user ( get_post_service ): non_existing_user_id = 1 get_post_service . user_repository . get_user . return_value = None assert get_post_service . get_post_info ( 1 , non_existing_user_id ) == None def test_non_existing_post ( get_post_service ): non_existing_post_id = 1 get_post_service . user_repository . get_user . return_value = ( UserTable . test_not_login_user1_data () ) get_post_service . post_repository . get_post . return_value = None assert ( get_post_service . get_post_info ( non_existing_post_id , UserTable . test_not_login_user1_data (). id ) == None ) def test_get_private_post_from_non_author ( get_post_service ): get_post_service . user_repository . get_user . return_value = ( UserTable . test_not_login_user1_data () ) get_post_service . post_repository . get_post . return_value = ( PostTable . test_private_post_by_user1_data () ) assert ( get_post_service . get_post_info ( UserTable . test_not_login_user1_data (). id , PostTable . test_private_post_by_user1_data (). id , ) == None ) def test_get_private_post_from_author ( get_post_service ): get_post_service . user_repository . get_user . return_value = ( UserTable . test_not_login_user1_data () ) get_post_service . post_repository . get_post . return_value = ( PostTable . test_private_post_by_user1_data () ) assert ( get_post_service . get_post_info ( PostTable . test_private_post_by_user1_data (). id , UserTable . test_not_login_user1_data (). id , ) == get_post_service . post_repository . get_post . return_value ) def test_get_public_post_from_non_author ( get_post_service ): get_post_service . user_repository . get_user . return_value = ( UserTable . test_login_user2_data () ) get_post_service . post_repository . get_post . return_value = ( PostTable . test_public_post_by_user1_data () ) assert ( get_post_service . get_post_info ( PostTable . test_public_post_by_user1_data (). id , UserTable . test_login_user2_data (). id , ) == get_post_service . post_repository . get_post . return_value ) </ code > レポジトリ レポジトリのコードは以下のようになっています。 レポジトリはクラスにまとめているため、クラスの __init__ 関数で依存するDBを注入しています。 DBを注入することで開発環境のDBとは別のDBにデータを入れることができるため、開発環境のDBに影響を与えずに済みます。 これによってクラス内の関数のインスタンスから依存先を利用できるようになっています。 / repositories / post_repository . py < code >from pydantic import BaseModel from sqlalchemy . orm import Session from app . models . post import PostTable from app . models . user import UserTable class PostRepository : def __init__ ( self , db : Session ): self . db = db def get_post ( self , post_id ) -> PostTable : return ( self . db . query ( PostTable ) . join ( UserTable , UserTable . id == PostTable . user_id ) . filter ( PostTable . id == post_id ) . first () ) </ code > これに対するUTコードは以下のようになっています。 レポジトリはAPIの最奥層であるため、何もモックせずに実際にテスト用DBにデータを入れた上でUTを書いています。 テスト用DBとアプリ用DBの切り替えは base_test.py で行っていますが、ここでは省略します。 / tests / repositories / test_post_repository . py < code >from app . helpers . helper import get_datetime_now_db_format from app . models . post import PostTable from app . models . user import UserTable from app . repositories . post_repository import PostRepository from tests . base_test import BaseTest class TestPostRepository ( BaseTest ): @ classmethod def _initialize_repository ( cls ): cls . post_repository = PostRepository ( cls . db ) @ classmethod def _insert_data ( cls ): cls . db . add_all ( [ PostTable . test_public_post_by_user1_data (), UserTable . test_not_login_user1_data (), ] ) cls . db . commit () @ classmethod def test_get_existing_post ( cls ): response = cls . post_repository . get_post ( PostTable . test_public_post_by_user1_data (). id ) assert response . id == PostTable . test_public_post_by_user1_data (). id assert response . title == PostTable . test_public_post_by_user1_data (). title assert ( response . description == PostTable . test_public_post_by_user1_data (). description ) assert response . user_id == PostTable . test_public_post_by_user1_data (). user_id assert ( response . is_private == PostTable . test_public_post_by_user1_data (). is_private ) @ classmethod def test_get_non_existing_post ( cls ): non_existing_post_id = 2 response = cls . post_repository . get_post ( non_existing_post_id ) assert response == None </ code > E2Eテスト E2Eテストとはシステム全体をテストするものです。 E2Eテストを実行することで関数間の値の受け渡しが正常であることを担保し、UTのみではカバーできないところをカバーし、バグが発生する可能性を下げることができます。 このレポジトリはAPIしか作成していないため、フロントエンドの挙動までは確認しません。 ここでは特定のパスにリクエストが来てからレスポンスが返されるまでの一連の動作を確認します。 そのため、ここでも実際にデータを入れます。 / tests / controllers / test_get_post_controller . py < code >from unittest . mock import MagicMock import pytest import requests from app . api_schemas . get_post_schema import ( GetPostRequest , GetPostResponse , GetPostSchema ) from app . controllers . get_post_controller import get_post_info from app . models . post import PostTable from app . models . user import UserTable from tests . base_test import BaseTest class TestGetPostController ( BaseTest ): @ classmethod def _insert_data ( cls ): cls . db . add_all ( [ UserTable . test_not_login_user1_data (), PostTable . test_public_post_by_user1_data (), ] ) cls . db . commit () @ classmethod def test_e2e ( cls ): post_id = str ( PostTable . test_public_post_by_user1_data (). id ) user_id = str ( UserTable . test_not_login_user1_data (). id ) response = cls . client . get ( " /posts/ " + post_id , params = { " post_id " : post_id , " user_id " : user_id } ) assert response . status_code == 200 assert response . json () == { " result " : True , " post " : { " title " : PostTable . test_public_post_by_user1_data (). title , " description " : PostTable . test_public_post_by_user1_data (). description , }, } </ code > 最後に APIのモックを用いたユニットテストは、自分が書いたコードが仕様を正しく反映していることを迅速に確認するための非常に効率的な手法です。 一定期間が経過して仕様を忘れてしまった場合でも、仕様がコードとして明確に表現されているため、再確認が容易になります。 また、E2Eテストはユニットテストだけでは見落としがちな、全体の動作を確認するのに非常に有効です。 これにより、実際の動作環境での問題を早期に発見し、修正することができます。 ぜひ、これらのテスト手法を取り入れて、効率的にAPI開発を進めていただければと思います。 今後も、テストの重要性を意識しながら、より良いソフトウェアを作り上げていきましょう。
今回は、GoogleChromeの拡張機能をサクッと作ってみようと思います。 概要 Google Chromeの拡張機能を作成する際には、主に3つの重要要素があります。 ポップアップ(popup) バックグラウンドスクリプト(background) コンテントスクリプト(content script) ポップアップ ポップアップは、ユーザーがChromeの拡張機能アイコンをクリックしたときに表示されるものです。 参考: DeepL コンテントスクリプト コンテントスクリプトは、ユーザーが閲覧しているウェブページに直接挿入されるJavaScriptです。ページのDOMを操作したり、ページからデータを取得したりするために使用されます。 参考: Dimentions (青い線 と 19px*214pxのツールチップ) バックグラウンドスクリプト バックグラウンドスクリプトは、拡張機能の背後で動作し、イベント駆動型で特定のイベントが発生したときにのみ起動します。バックグラウンドスクリプトは、APIへのアクセスやデータの管理、他のスクリプトとの通信を担当します。 コンテントスクリプトを発火させる時にも使用していたりします。 必要なファイル この後のチュートリアルでは、 chrome-extension-v3-starter  を参考にして作成していきます。 このリポジトリのファイルを参考に細くしてみると大体こんな感じです。 my-chrome-extension/├── manifest.json // ---------------- 拡張機能の概要やファイル定義を書く├── logo/ // ------------------------ アイコン画像│ └── (ロゴ画像ファイルなど)├── popup/ // ----------------------- ポップアップを書く│ ├── index.html│ └── style.css├── foreground.js // ---------------- コンテンツスクリプトを書く└── service-worker.js // ------------ バックグラウンドスクリプトを書く チュートリアル (スクリーンショット撮れる拡張機能) ポップアップボタンまたは、ショートカットキーでスクリーンショットを取れる拡張機能を作成してみようと思います。  画面収録 2024-12-03 9.55.40.mov ※開発のための準備に関しては割愛します。( こちら を参照してください。) ①コマンドを実行して、スクリーンショットのバイナリをコンソールに出力させる manifest.json にコマンドを追加する バックグラウンドスクリプト (service-worker.js) にて、画面キャプチャを書く manifest.json には、コマンドを追加するのと、スクリーンショットをするために、permissionを更新しています。①~③で更新が必要な箇所をまとめて追加していきます。気になる人は、 公式ドキュメント などを参照してみてください。 manifest.json { "manifest_version": 3, "name": "Chrome Extension v3 Starter", "description": "A minimal example of a chrome extension using manifest v3", "version": "0.0.1", "icons": { "16": "logo/logo-16.png", "48": "logo/logo-48.png", "128": "logo/logo-128.png" }, "options_page": "settings/settings.html", "action": { "default_title": "Chrome Addon v3 Starter", "default_popup": "popup/popup.html" }, "permissions": ["tabs", "commands", "activeTab"], // update permission "host_permissions": ["*://*/*"], "background": { "service_worker": "service-worker.js" }, "content_scripts": [ { "js": ["foreground.js"], "matches": ["https://*/*"] // update url pattern } ], // ↓ add commands "commands": { "take-screenshot": { "suggested_key": { "default": "Ctrl+Shift+F", "mac": "Command+Shift+F" }, "description": "Take a screenshot of the current page" } // ↑ add commands }} バックグラウンドスクリプト(service-worker.js)も修正します。manifest.json で定義した、"take-screenshot"を確認して、スクリーンショットを行い、バイナリを出力しています。 service-worker.js // add ↓ chrome.commands.onCommand.addListener((command) => { if (command === "take-screenshot") { chrome.tabs.captureVisibleTab(null, {}, (image) => { console.log(image); }); } }); // add ↑ こんな感じになります。 ※コマンドがうまく反映されない人は、 ショートカットの設定 から設定してみてください。 ② コンテンツスクリプトを使用して、開いているWebサイトで、スクリーンショットを表示させる コンテンツスクリプトにて、messageAPIを用いて、スクリーンショットを発火させる。 発火したスクリーンショットをWebサイトにレンダリングする。 先ほど、バックグラウンドスクリプトの、take-screenshotコマンド実行時の関数を修正します。 内容としては、スクリーンショットを行い、そのデータをsendMessageで渡そうとしています。 service-worker.js // update ↓ chrome.commands.onCommand.addListener(async (command) => { if (command === "take-screenshot") { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); chrome.tabs.captureVisibleTab(tab.windowId, { format: "png" }, (dataUrl) => { if (chrome.runtime.lastError) { console.error("Error capturing screenshot:", chrome.runtime.lastError); return; } console.log("Screenshot captured, sending data URL"); chrome.tabs.sendMessage(tab.id, { type: "screenshot", dataUrl }); }); } }); // update ↑ sendMessageで渡ってきたものを検知して、コンテンツスクリプト(foreground.js)にて、イベントを発火させています。 表示しているWebサイトの左下に撮ったスクリーンショットを同じものをレンダリングしています。 foreground.js // add ↓ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { console.log("Received message in foreground script", message); if (message.type === "screenshot") { const img = document.createElement("img"); img.src = message.dataUrl; img.onload = () => console.log("Image loaded successfully"); img.onerror = (e) => console.error("Error loading image:", e); img.style.position = "fixed"; img.style.bottom = "10px"; img.style.left = "10px"; img.style.width = "400px"; img.style.height = "300px"; img.style.zIndex = "10000"; img.style.border = "2px solid #000"; document.body.appendChild(img); } }); // add ↑ コマンドを実行すると、下記のよう左側に表示されます。 ③ポップアップから実装したスクリーンショットの機能を発火させる ポップアップのHTMLとJSを修正 バックグラウンドスクリプトで、スクリーンショットの機能を発火させるように設定 ポップアップ用のHTMLを更新します。JSのインポートとボタンの追加を行っています。 popup/popup.html <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <link rel="stylesheet" href="popup.css"> <title>Chrome Addon v3: popup</title> <script src="/popup/popup.js"></script> <!-- add import --> </head><body> <button id="take-screenshot">キャプチャする</button> <!-- add button --> </body></html> 新しくJSを追加します。追加したボタンにクリックイベントを付与して、take-screenshot のメッセージを送るように設定します。 popup/popup.js // ↓ add click event document.addEventListener('DOMContentLoaded', function () { document.getElementById('take-screenshot').addEventListener('click', () => { console.log('take-screenshot'); chrome.runtime.sendMessage({ name: 'take-screenshot' }); });}); バックグラウンドスクリプトで、新しく関数を追加します。take-screenshot のメッセージを感知して、スクリーンショット機能を発火させています。(中身の処理は②で書いた関数とほとんど同じ) service-worker.js // ↓ add chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => { if (request.name === "take-screenshot") { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); chrome.tabs.captureVisibleTab(tab.windowId, { format: "png" }, (dataUrl) => { if (chrome.runtime.lastError) { console.error("Error capturing screenshot:", chrome.runtime.lastError.message); return; } console.log("Screenshot captured, sending data URL"); chrome.tabs.sendMessage(tab.id, { type: "screenshot", dataUrl }); }); } }); // ↑ add chrome.commands.onCommand.addListener(async (command) => { ... こんな感じで実行すると、ポップアップからもスクリーンショット機能を発火することができるようになります。 画面収録 2024-12-03 9.55.40.mov チュートリアルは以上です。 まとめ 概念を理解すると意外と簡単に作れるので、今後業務効率化できそうなところがあれば、自作してみようと思います。 最後まで見ていただきありがとうございました🙇
はじめに 皆さんこんにちは! ラスベガスで開催された AWS の re:Invent にマイナビからも 4 名のエンジニアで現地参戦してきました! re:Invent がどういったイベントなのか、Keynote から SWAG、認定者ラウンジまで、実際に参加してみたレポートをご紹介します。 AWS re:Invent とは? AWS re:Invent  は Amazon Web Service(AWS)社が年に一度開催する、AWS 最大規模の技術カンファレンスです。2012 年から開催されており、今年で 13 回目の開催となります。 多くの参加者が注目する Keynote(基調講演)から、2,000 以上の技術セッションへの参加、Expo(展示会)などさまざまなプログラムが用意されており、毎年イベント中に多くの新サービスや、新機能が発表されます。 イベント概要 開催日程:12 月 2 日(月)- 12 月 6 日(木)の 5 日間 開催地 :アメリカ ネバダ州 ラスベガス 参加者 :現地参加者:約 60,000 名(うち日本からの参加者:約 2,000 名) 参加費用:$2,099 会場 引用: https://reinvent.awsevents.com/experience/plan-your-trip/ キャンパス(会場)はラスベガスの中心地のホテルで、全部で 6 つあります。 会場のホテルがかなり大きいため、ホテル間の移動は無料のシャトルバスまたはモノレールを使います。 基本的にはシャトルバス、人が多く集まるイベントが開催されるタイミングでの移動はモノレールでの移動をオススメします。 プログラム ここからは re:Invent で実際に体験したプログラムを紹介していきます。 Keynote(基調講演) EXPO(展示会) 技術セッション SWAG AWS 認定者ラウンジ Keynote(基調講演) Keynote とは、AWS のトップリーダが今年注目の新サービスや新機能についての発表を行う講演で、開催期間中は毎日行われます。 オンラインでの視聴も可能で、動画配信もされています。 Keynote現地参加レポート 今回はその中でも最注目だった Day2 の Keynote、AWS の CEO である Matt Garman の講演に参加しました。 Matt Garman は EC2 の最初のプロダクトマネージャーで、24 年の 6 月に AWS の新 CEO に就任したため、今回が初の Keynoteとなります。 朝 8:00 から始まる講演でしたが、会場に入りたかったので1 時間半ほど前から並びました。 メンバーと 6:00 に集合で MGM からローカルバスに乗り(シャトルバスが出ていない時間)移動。 6:45 に講演会場の Venetian に到着して待ち列へ。 7:00 には入場規制されていたので、開始 1 時間前には並んでいないと会場には入れないようです。 早朝とは思えない人の多さ 7:15 すぎにメイン会場へ。ここもとにかく広かったです。 運よく先頭の方に並んでいたので、会場の真ん中のあたりに着席できました。 ノリノリの DJ がいる会場 講演開始の 8:00 になるとオープニングムービーが流れた後、AWS の CEO である Matt Garman 氏が登場し、会場の盛り上がりも最高潮に! 登場の瞬間を激写することに成功 この後 3 時間ほどかけて、たくさんの新サービスや新機能が発表され、発表のたびに拍手喝采が起こっていました。 Amazon EC2 Trn2 インスタンス/ Trn2 UltraServers Amazon S3 関連 Amazon S3 Tables Amazon S3 Metadata Amazon Aurora DSQL Amazon DynamoDB global tables Amazon EKS Auto Mode Amazon EventBridge と AWS StepFunctions のプライベート接続 Amazon Nova Amazon Bedrock 関連アップデート Amazon Q 関連アップデート etc... Amazon の CEO 兼社長である Andy Jassy も登壇 早朝から 3 時間の講演はかなりハードでしたが、AWS のスケールの大きさや影響力を肌で感じることができました。 EXPO(展示会) EXPO とはいわゆる企業展示会場のことで、AWS のエキスパートやパートナー企業の方と直接お話ししたり、最新の情報収集をすることができます。 この会場もとにかく広いです。日本の企業もちらほら見かけました。すべてのブースを回ろうと思うと数時間かかるのではないでしょうか... EXPO は Day 2 の 16 時からの開場で、初日はフードやドリンクの提供もあり、ちょっとしたパーティーのようでした。 EXPO 会場の様子 企業ブースがたくさん 気になる企業でサービスについて詳しく話を聞くのもよし、いつもお世話になっている企業のブースに行ってグッズをもらうのもよしです。 頂いたぬいぐるみ自慢 技術セッション イベント中には3,000 以上の技術セッションが開催されます。 セッションタイプや対象業種/業界、レベルが分かれており、予約または事前に並んで(Walk-Up Seats)参加します。 セッションタイプ 主要なセッションについて説明します。 Breakout Session 1 時間の講義形式で特定のトピックについて深掘りする。のちにオンデマンドで動画配信あり Workshop 2 時間の実践型セッション。個人やグループでのハンズオン Chalk Talk 10~15 分の講義の後、45~50 分の質疑応答を行う。少人数での開催 Builders Session 1 時間の小グループセッション。AWS エキスパートとのハンズオン GameDay ゲーム形式のセッション。 レベル 100 レベル(入門)から 400 レベル(エキスパート)まで難易度が分かれているので、自分のスキルレベルや興味に合わせてセッションを選択できます。 参加してみて セッション選びが難しい かなりの数なので、開催場所やスケジュールを考慮しながら自分に合ったセッションを選択するのが難しかったです。 事前にセッションカタログの絞り込み機能を活用しつつ、タイトルだけではなく詳細まで確認して、よくよく検討しておけば良かったなぁと反省しました。 予約なしでも意外といける 人気そうなセッションでも、30〜45 分前に並んでいれば予約なしで入れました。 予約いっぱいでも、直前に席が空いて予約できたりもしたので、諦めずにチェックするのも一つの手です! 予約すべきセッション Breakout Session は配信があるので、それ以外のグループワークや質疑応答ができるセッションに参加するのがおすすめです。 特に GameDay のようなゲーム形式のセッションは、すぐ予約が埋まってしまうので、参加したい場合はセッション予約開始時にすぐ予約するのが良いです。(私はぼけっとしていて予約できなかった) SWAG SWAGとは企業のオリジナルグッズのことです。 re:Invent 参加者全員がもらえる AWS のグッズから、EXPO に出展している企業のグッズまで様々なアイテムが手に入ります。 AWS の認定を取得している人だけがもらえるグッズもありました。 SWAG 受け取り会場 戦利品のご紹介 企業ごとに特色がありますね。 AWS パーカーや T シャツはもらってそのまま着ている人が結構いました。 持って帰るのが大変な量 認定者ラウンジ 認定者ラウンジとは、AWS 認定資格取得者だけが入ることのできるラウンジです。 資格を 1 つでも保有していると入ることができます。 入場するためには、受付で Credly での認定バッジを提示する必要があるので、あらかじめ準備しておくのが良いと思います。 ラウンジの中には作業スペースがあり、ドリンクや軽食が提供されています。 私がステッカーをもらったり撮影したりと喜んでいる隣で、みなさん黙々と作業されてました。 照明がオシャレ 広々とした作業スペースで黙々と作業する人々 おわりに AWS re:Invent 2024 参加レポート、いかがだったでしょうか。 なんとなくでも雰囲気を感じ取っていただけたら幸いです。 私自身、かなり開発や語学学習のモチベーションが上がりました。 興味のある方はぜひ来年参加してみてください! 参加メンバーと会場にあった大きな黒板の前でパシャリ 📷✨ マイナビロゴはどこにあるでしょう?👀 イベントURL: AWS re:Invent 2025 | December 1 – 5, 2025